Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
4e6de87
Port command discovery regression coverage from Laravel
binaryfire Sep 8, 2026
1b3fb87
Clarify database-specific schema column limits
binaryfire Sep 8, 2026
fb011c1
Fix fractional values in numeric collection sorting
binaryfire Sep 8, 2026
fa7957e
Complete Laravel's native casting update
binaryfire Sep 8, 2026
1933da7
Fix mailable assertions for Htmlable text views
binaryfire Sep 8, 2026
fc8f4f7
Preserve callback return types for shared input and locale helpers
binaryfire Sep 8, 2026
07b8bf2
Complete Fluent input documentation and enum-array coverage
binaryfire Sep 8, 2026
7de9299
Complete renderer query coverage and preserve displayed binding values
binaryfire Sep 8, 2026
e94df79
Document enum cases in translation replacements
binaryfire Sep 8, 2026
0237c87
Document HTTP request attributes for fake responses
binaryfire Sep 8, 2026
43b5054
Complete split-package upstream references and DI license
binaryfire Sep 8, 2026
0396460
Port HTTP client sync and async return-type inference
binaryfire Sep 8, 2026
4b070c8
Correct the JSON:API compound resource type example
binaryfire Sep 8, 2026
3550c9d
Fix string attributes in collection visibility merges
binaryfire Sep 8, 2026
117cf03
Complete HTTP response tap and macro coverage
binaryfire Sep 8, 2026
7e01692
Update the release action and correct 0.4 version preparation
binaryfire Sep 8, 2026
13e953e
Type the queue failover event job payload
binaryfire Sep 8, 2026
8a7cad6
Allow opting out of database lock pruning with null
binaryfire Sep 8, 2026
08039dd
Complete current-time date rule test coverage
binaryfire Sep 8, 2026
42fedf4
Complete collection callback contracts and grouping type coverage
binaryfire Sep 9, 2026
cb84337
Correct HTTP after-response callback return contracts
binaryfire Sep 9, 2026
3489ff5
Document optional and renamed pivot timestamp columns
binaryfire Sep 9, 2026
3220a89
Remove unused mail-render and job-failure tuple assignments
binaryfire Sep 9, 2026
860dbe0
Retain upstream invocation checks in null-key relationship tests
binaryfire Sep 9, 2026
91fca00
docs(auth): document the guest middleware static constructor
binaryfire Sep 9, 2026
0a7ec56
fix(filesystem): normalize the application URL for public files
binaryfire Sep 9, 2026
812acdc
Complete session authentication HMAC contract and Auth facade metadata
binaryfire Sep 9, 2026
2c10a94
Remove legacy remember-cookie deserialization
binaryfire Sep 9, 2026
4db0ab7
Port optional query binding masking and complete read/write event cov…
binaryfire Sep 9, 2026
c5416dc
Fix session enum key deletion and complete helper support
binaryfire Sep 9, 2026
3e94a67
Complete model inspection types and process assertions
binaryfire Sep 9, 2026
84749d7
Complete cache enum and Redis event parity documentation
binaryfire Sep 9, 2026
b67696c
Merge 0.4 into laravel-parity-58009
binaryfire Sep 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,14 @@ jobs:
core.setFailed('Workflow failed. Release version does not match with selected target branch. Did you select the correct branch?')

- name: Update Application.php version
run: sed -i "s/const VERSION = '.*';/const VERSION = '${{ steps.version.outputs.version }}';/g" src/foundation/src/Application.php
run: |
sed -i "s/const string VERSION = '.*';/const string VERSION = '${VERSION}';/g" src/foundation/src/Application.php
grep -Fq -- "const string VERSION = '${VERSION}';" src/foundation/src/Application.php
env:
VERSION: ${{ steps.version.outputs.version }}

- name: Commit version change
uses: stefanzweifel/git-auto-commit-action@v5
uses: stefanzweifel/git-auto-commit-action@4a55954c782fc1ea30b9056cd3e7a2b40ca8887d # v7.2.0
with:
commit_message: "Update version to v${{ steps.version.outputs.version }}"

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<p align="center"><a href="https://hypervel.org" target="_blank"><img src="https://hypervel.org/logo.png" width="400"></a></p>

<p align="center">
<a href="https://github.com/hypervel/components/actions"><img src="https://github.com/hypervel/components/workflows/tests/badge.svg" alt="Build Status"></a>
<a href="https://github.com/hypervel/components/actions"><img src="https://github.com/hypervel/components/actions/workflows/tests.yml/badge.svg" alt="Build Status"></a>
<a href="https://packagist.org/packages/hypervel/components"><img src="https://img.shields.io/packagist/dt/hypervel/framework" alt="Total Downloads"></a>
<a href="https://packagist.org/packages/hypervel/components"><img src="https://img.shields.io/packagist/v/hypervel/components" alt="Latest Stable Version"></a>
<a href="https://packagist.org/packages/hypervel/components"><img src="https://img.shields.io/packagist/l/hypervel/components" alt="License"></a>
Expand Down
4 changes: 2 additions & 2 deletions bin/release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ fi

# Initialize variables
NOW=$(date +%s)
RELEASE_BRANCH="0.3"
RELEASE_BRANCH="0.4"
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
BASEPATH=$(cd `dirname $0`; cd ../src/; pwd)
VERSION=$1
Expand Down Expand Up @@ -106,4 +106,4 @@ done

TIME=$(echo "$(date +%s) - $NOW" | bc)

printf "Execution time: %f seconds" $TIME
printf "Execution time: %f seconds" $TIME
3 changes: 2 additions & 1 deletion src/auth/src/Recaller.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ class Recaller
*/
public function __construct(string $recaller)
{
$this->recaller = @unserialize($recaller, ['allowed_classes' => false]) ?: $recaller;
// Cookie middleware owns serialization; legacy cookie decoding is intentionally omitted.
$this->recaller = $recaller;
$this->segments = explode('|', $this->recaller);
}

Expand Down
3 changes: 2 additions & 1 deletion src/broadcasting/src/AnonymousEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Hypervel\Contracts\Support\Arrayable;
use Hypervel\Foundation\Events\Dispatchable;
use Hypervel\Support\Arr;
use Hypervel\Support\Collection;

class AnonymousEvent implements ShouldBroadcast
{
Expand Down Expand Up @@ -75,7 +76,7 @@ public function with(array|Arrayable $payload): static
{
$this->payload = $payload instanceof Arrayable
? $payload->toArray()
: collect($payload)->map(
: (new Collection($payload))->map(
fn ($p) => $p instanceof Arrayable ? $p->toArray() : $p
)->all();

Expand Down
12 changes: 9 additions & 3 deletions src/cache/src/DatabaseLock.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@ class DatabaseLock extends Lock implements RefreshableLock

/**
* The prune probability odds.
*
* @var null|array{int, int}|array{}
*/
protected array $lottery;
protected ?array $lottery;

/**
* The default number of seconds that a lock should be held.
Expand All @@ -43,6 +45,8 @@ class DatabaseLock extends Lock implements RefreshableLock

/**
* Create a new lock instance.
*
* @param null|array{int, int}|array{} $lottery the prune probability odds, or null to disable automatic pruning
*/
public function __construct(
ConnectionResolverInterface $resolver,
Expand All @@ -51,7 +55,7 @@ public function __construct(
string $table,
int $seconds,
?string $owner = null,
array $lottery = [2, 100],
?array $lottery = [2, 100],
int $defaultTimeoutInSeconds = 86400
) {
parent::__construct($name, $seconds, $owner);
Expand All @@ -73,6 +77,8 @@ protected function connection(): ConnectionInterface

/**
* Attempt to acquire the lock.
*
* @throws Throwable
*/
public function acquire(): bool
{
Expand All @@ -99,7 +105,7 @@ public function acquire(): bool
$acquired = $updated >= 1;
}

if (count($this->lottery) === 2 && random_int(1, $this->lottery[1]) <= $this->lottery[0]) {
if (count($this->lottery ?? []) === 2 && random_int(1, $this->lottery[1]) <= $this->lottery[0]) {
$this->pruneExpiredLocks();
}

Expand Down
26 changes: 17 additions & 9 deletions src/collections/src/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
use Hypervel\Support\Traits\Macroable;
use Hypervel\Support\Traits\TransformsToResourceCollection;
use InvalidArgumentException;
use Override;
use SortDirection;
use stdClass;
use Stringable as BaseStringable;
use Traversable;
use UnitEnum;

Expand Down Expand Up @@ -505,7 +507,18 @@ public function getOrPut(mixed $key, mixed $value): mixed

/**
* Group an associative array by a field or using a callback.
*
* @template TGroupKey of array-key|bool|null|UnitEnum|BaseStringable
*
* @param array|(callable(TValue, TKey): (array<array-key, TGroupKey>|TGroupKey))|string $groupBy
* @return static<
* ($groupBy is (array|string)
* ? array-key
* : (TGroupKey is array-key ? TGroupKey : (TGroupKey is bool ? int : (TGroupKey is (BaseStringable|null) ? string : array-key)))),
* static<($preserveKeys is true ? TKey : int), ($groupBy is array ? mixed : TValue)>
* >
*/
#[Override]
public function groupBy(callable|array|string $groupBy, bool $preserveKeys = false): static
{
if (! $this->useAsCallable($groupBy) && is_array($groupBy)) {
Expand All @@ -529,7 +542,7 @@ public function groupBy(callable|array|string $groupBy, bool $preserveKeys = fal
$groupKey = match (true) {
is_bool($groupKey) => (int) $groupKey,
$groupKey instanceof UnitEnum => enum_value($groupKey),
$groupKey instanceof \Stringable => (string) $groupKey,
$groupKey instanceof BaseStringable => (string) $groupKey,
is_null($groupKey) => (string) $groupKey,
default => $groupKey,
};
Expand All @@ -554,13 +567,8 @@ public function groupBy(callable|array|string $groupBy, bool $preserveKeys = fal

/**
* Key an associative array by a field or using a callback.
*
* @template TNewKey of array-key|\UnitEnum
*
* @param array|(callable(TValue, TKey): TNewKey)|string $keyBy
* @return static<($keyBy is (array|string) ? array-key : (TNewKey is UnitEnum ? array-key : TNewKey)), TValue>
* @phpstan-ignore method.childReturnType (complex conditional types PHPStan can't match)
*/
#[Override]
public function keyBy(callable|array|string $keyBy): static
{
$keyBy = $this->valueRetriever($keyBy);
Expand Down Expand Up @@ -1572,7 +1580,7 @@ protected function sortByMany(array $comparisons = [], int $options = SORT_REGUL
}
} else {
$result = match ($options) {
SORT_NUMERIC => (int) $values[0] <=> (int) $values[1],
SORT_NUMERIC => (float) $values[0] <=> (float) $values[1],
SORT_STRING => strcmp((string) $values[0], (string) $values[1]),
SORT_NATURAL => strnatcmp((string) $values[0], (string) $values[1]),
SORT_LOCALE_STRING => strcoll((string) $values[0], (string) $values[1]),
Expand Down Expand Up @@ -1816,9 +1824,9 @@ public function count(): int
/**
* Count the number of items in the collection by a field or using a callback.
*
* @param null|(callable(TValue, TKey): (array-key|UnitEnum))|string $countBy
* @return static<array-key, int>
*/
#[Override]
public function countBy(callable|string|null $countBy = null): Collection
{
return $this->newInstance($this->lazy()->countBy($countBy)->all());
Expand Down
17 changes: 9 additions & 8 deletions src/collections/src/Enumerable.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
use JsonException;
use JsonSerializable;
use SortDirection;
use Stringable as BaseStringable;
use Traversable;
use UnexpectedValueException;
use UnitEnum;

/**
* Some transformations may return a base collection when an implementation
Expand Down Expand Up @@ -435,26 +437,25 @@ public function get(mixed $key, mixed $default = null): mixed;
/**
* Group an associative array by a field or using a callback.
*
* @template TGroupKey of array-key|\UnitEnum|\Stringable
* @template TGroupKey of array-key|bool|null|UnitEnum|BaseStringable
*
* @param array|(callable(TValue, TKey): (array<array-key, TGroupKey>|TGroupKey))|string $groupBy
* @return static<
* ($groupBy is (array|string)
* ? array-key
* : (TGroupKey is \UnitEnum ? array-key : (TGroupKey is \Stringable ? string : TGroupKey))),
* static<($preserveKeys is true ? TKey : int), ($groupBy is array ? mixed : TValue)>
* : (TGroupKey is array-key ? TGroupKey : (TGroupKey is bool ? int : (TGroupKey is (BaseStringable|null) ? string : array-key)))),
* Collection<($preserveKeys is true ? TKey : int), ($groupBy is array ? mixed : TValue)>
* >
* @phpstan-ignore generics.notSubtype (PHPStan cannot prove normalized conditional group keys satisfy array-key)
*/
public function groupBy(callable|array|string $groupBy, bool $preserveKeys = false): static;

/**
* Key an associative array by a field or using a callback.
*
* @template TNewKey of array-key
* @template TNewKey of array-key|UnitEnum|BaseStringable
*
* @param array|(callable(TValue, TKey): TNewKey)|string $keyBy
* @return static<($keyBy is string ? array-key : ($keyBy is array ? array-key : TNewKey)), TValue>
* @return static<($keyBy is (array|string) ? array-key : (TNewKey is array-key ? TNewKey : (TNewKey is BaseStringable ? string : array-key))), TValue>
*/
public function keyBy(callable|array|string $keyBy): static;

Expand Down Expand Up @@ -1085,8 +1086,8 @@ public function count(): int;
/**
* Count the number of items in the collection by a field or using a callback.
*
* @param null|(callable(TValue, TKey): array-key)|string $countBy
* @return static<array-key, int>
* @param null|(callable(TValue, TKey): (array-key|bool|UnitEnum))|string $countBy
* @return Collection<array-key, int>|static<array-key, int>
*/
public function countBy(callable|string|null $countBy = null): Collection|static;

Expand Down
9 changes: 2 additions & 7 deletions src/collections/src/LazyCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -354,9 +354,9 @@ public function crossJoin(Arrayable|iterable ...$arrays): static
/**
* Count the number of items in the collection by a field or using a callback.
*
* @param null|(callable(TValue, TKey): (array-key|UnitEnum))|string $countBy
* @return static<array-key, int>
*/
#[Override]
public function countBy(callable|string|null $countBy = null): static
{
$countBy = is_null($countBy)
Expand Down Expand Up @@ -557,13 +557,8 @@ public function groupBy(callable|array|string $groupBy, bool $preserveKeys = fal

/**
* Key an associative array by a field or using a callback.
*
* @template TNewKey of array-key|\UnitEnum
*
* @param array|(callable(TValue, TKey): TNewKey)|string $keyBy
* @return static<($keyBy is (array|string) ? array-key : (TNewKey is UnitEnum ? array-key : TNewKey)), TValue>
* @phpstan-ignore method.childReturnType (complex conditional return type PHPStan can't verify)
*/
#[Override]
public function keyBy(callable|array|string $keyBy): static
{
return $this->newInstance(function () use ($keyBy) {
Expand Down
6 changes: 6 additions & 0 deletions src/conditionable/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Conditionable for Hypervel
===

[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/hypervel/conditionable)

Ported from: https://github.com/laravel/framework/tree/13.x/src/Illuminate/Conditionable
7 changes: 3 additions & 4 deletions src/console/src/GeneratorCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -485,10 +485,9 @@ protected function userProviderModel(): ?string
*/
protected function isReservedName(string $name): bool
{
return in_array(
strtolower($name),
array_map('strtolower', $this->reservedNames)
);
$name = strtolower($name);

return array_any($this->reservedNames, fn ($reservedName) => strtolower($reservedName) === $name);
}

/**
Expand Down
6 changes: 4 additions & 2 deletions src/database/src/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ public function __construct(string $database = '', string $tablePrefix = '', arr
$this->configuredTablePrefix = $tablePrefix;

$this->config = $config;
$this->config['mask_bindings_in_exception_messages'] = (bool) ($config['mask_bindings_in_exception_messages'] ?? false);

$this->readWriteType = $config[self::READ_WRITE_TYPE_CONFIG_KEY] ?? null;

Expand Down Expand Up @@ -616,8 +617,8 @@ protected function runQueryCallback(string $query, array $bindings, Closure $cal
}

// If an exception occurs when attempting to run a query, we'll format the error
// message to include the bindings with SQL, which will make this exception a
// lot more helpful to the developer instead of just the database's errors.
// message to include the SQL and, unless masked, its bindings. This provides
// more context for the developer than just the database's original error.
catch (CanceledException $exception) {
throw $exception;
} catch (Exception $e) {
Expand All @@ -634,6 +635,7 @@ protected function runQueryCallback(string $query, array $bindings, Closure $cal
$e,
$this->getConnectionDetails(),
$this->latestReadWriteTypeUsed(),
$this->getConfig('mask_bindings_in_exception_messages'),
);

if ($isUniqueConstraintError && $queryException instanceof UniqueConstraintViolationException) {
Expand Down
6 changes: 3 additions & 3 deletions src/database/src/Eloquent/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -653,7 +653,7 @@ public function makeHidden(array|string $attributes): static
public function mergeHidden(array|string $attributes): static
{
// @phpstan-ignore return.type (HigherOrderProxy returns $this, not TModel)
return $this->each->mergeHidden($attributes);
return $this->each->mergeHidden((array) $attributes);
}

/**
Expand Down Expand Up @@ -686,7 +686,7 @@ public function makeVisible(array|string $attributes): static
public function mergeVisible(array|string $attributes): static
{
// @phpstan-ignore return.type (HigherOrderProxy returns $this, not TModel)
return $this->each->mergeVisible($attributes);
return $this->each->mergeVisible((array) $attributes);
}

/**
Expand Down Expand Up @@ -993,7 +993,7 @@ public function toQuery(): Builder

$class = get_class($model);

if ($this->reject(fn ($model) => $model instanceof $class)->isNotEmpty()) {
if ($this->contains(fn ($model) => ! $model instanceof $class)) {
throw new LogicException('Unable to create query for collection with mixed types.');
}

Expand Down
Loading