build: полная сборка в git — серверные PHP-зависимости lib/Vendor (438 файлов, из пиннированного релиза v22.0.12)
Type checking / changes (push) Has been cancelled
Type checking / test (push) Has been cancelled
Type checking / typescript-summary (push) Has been cancelled
Node tests / changes (push) Has been cancelled
Node tests / test (push) Has been cancelled
Node tests / test-summary (push) Has been cancelled

Теперь master f7_talk = ПОЛНЫЙ деплой-артефакт: built js (наша сборка) + lib/Vendor (серверные
PHP-deps) + патч Listener + version 22.0.12.1. Готов к брендингу dev-f7 и подписи dev-appstore.
Апстрим-подписи (appinfo/signature.json) в репо нет — переподпишет dev-appstore при публикации.
This commit is contained in:
2026-07-06 22:20:16 +00:00
parent 9dad445d11
commit 49dd055a58
439 changed files with 24952 additions and 0 deletions
+16
View File
@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Utility;
/** @internal */
trait IsSingleton
{
private static self $instance;
public static function get(): static
{
return self::$instance ??= new static();
}
}
+19
View File
@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Utility;
use Composer\InstalledVersions;
/** @internal */
final class Package
{
private static string $version;
public static function version(): string
{
/** @infection-ignore-all */
return self::$version ??= InstalledVersions::getVersion('cuyz/valinor') ?? 'unknown';
}
}
+25
View File
@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Utility;
/** @internal */
final class Polyfill
{
/**
* PHP8.4 use native function `array_all` instead.
*
* @param array<mixed> $array
*/
public static function array_all(array $array, callable $callback): bool
{
foreach ($array as $key => $value) {
if (! $callback($value, $key)) {
return false;
}
}
return true;
}
}
@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Utility\Priority;
/**
* This interface can be implemented by objects which may be sorted
* using @see \OCA\Talk\Vendor\CuyZ\Valinor\Utility\Priority\PrioritizedList
*
* The higher the priority is for a given object, the more chance it has to
* be used first.
*
* @api
*/
interface HasPriority
{
public function priority(): int;
}
@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Utility\Priority;
use IteratorAggregate;
use Traversable;
/**
* @api
*
* @template T of object
* @implements IteratorAggregate<T>
*/
final class PrioritizedList implements IteratorAggregate
{
/** @var array<int, T[]> */
private array $objects = [];
/**
* @param T ...$objects
*/
public function __construct(object ...$objects)
{
foreach ($objects as $object) {
$this->objects[$this->priority($object)][] = $object;
}
krsort($this->objects, SORT_NUMERIC);
}
/**
* @return Traversable<T>
*/
public function getIterator(): Traversable
{
foreach ($this->objects as $priority => $objects) {
foreach ($objects as $object) {
yield $priority => $object;
}
}
}
private function priority(object $object): int
{
return $object instanceof HasPriority
? $object->priority()
: 0;
}
}
@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Utility\Reflection;
use PhpToken;
/** @internal */
final class NamespaceFinder
{
public function findNamespace(string $content): ?string
{
$tokens = PhpToken::tokenize($content);
$tokensCount = count($tokens);
/* @infection-ignore-all Unneeded because of the nature of namespace-related token */
$pointer = $tokensCount - 1;
while (! $tokens[$pointer]->is(T_NAMESPACE)) {
/* @infection-ignore-all Unneeded because of the nature of namespace-related token */
if ($pointer === 0) {
return null;
}
$pointer--;
}
while (! $tokens[$pointer]->is([T_NAME_QUALIFIED, T_STRING])) {
$pointer++;
}
return (string)$tokens[$pointer];
}
}
+100
View File
@@ -0,0 +1,100 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Utility\Reflection;
use ReflectionClass;
use ReflectionFunction;
use ReflectionMethod;
use SplFileObject;
/**
* @internal
*
* Imported from `doctrine/annotations`:
* @link https://github.com/doctrine/annotations/blob/4858ab786a6cb568149209a9112dad3808c8a4de/lib/Doctrine/Common/Annotations/PhpParser.php
*/
final class PhpParser
{
/** @var array<string, array<string, string>> */
private static array $statements = [];
/**
* @param ReflectionClass<object>|ReflectionFunction|ReflectionMethod $reflection
* @return array<string, string>
*/
public static function parseUseStatements(ReflectionClass|ReflectionFunction|ReflectionMethod $reflection): array
{
$signature = "{$reflection->getFileName()}:{$reflection->getStartLine()}";
// @infection-ignore-all
return self::$statements[$signature] ??= self::fetchUseStatements($reflection);
}
public static function parseNamespace(ReflectionFunction $reflection): ?string
{
$content = self::getFileContent($reflection);
if ($content === null) {
return null;
}
return (new NamespaceFinder())->findNamespace($content);
}
/**
* @param ReflectionClass<object>|ReflectionFunction|ReflectionMethod $reflection
* @return array<string, string>
*/
private static function fetchUseStatements(ReflectionClass|ReflectionFunction|ReflectionMethod $reflection): array
{
if ($reflection instanceof ReflectionMethod) {
$namespaceName = $reflection->getDeclaringClass()->getNamespaceName();
} elseif ($reflection instanceof ReflectionFunction && $reflection->getClosureScopeClass()) {
$namespaceName = $reflection->getClosureScopeClass()->getNamespaceName();
} else {
$namespaceName = $reflection->getNamespaceName();
}
$content = self::getFileContent($reflection);
if ($content === null) {
return [];
}
return (new TokenParser($content))->parseUseStatements($namespaceName);
}
/**
* @param ReflectionClass<object>|ReflectionFunction|ReflectionMethod $reflection
*/ private static function getFileContent(ReflectionClass|ReflectionFunction|ReflectionMethod $reflection): ?string
{
$filename = $reflection->getFileName();
$startLine = $reflection->getStartLine();
// @infection-ignore-all these values will never be `true`
if ($filename === false || $startLine === false) {
return null;
}
if (! is_file($filename)) {
return null;
}
// @infection-ignore-all no need to test with `-1`
$lineCnt = 0;
$content = '';
$file = new SplFileObject($filename);
while (! $file->eof()) {
if ($lineCnt++ === $startLine) {
break;
}
$content .= $file->fgets();
}
return $content;
}
}
@@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Utility\Reflection;
use Closure;
use ReflectionClass;
use ReflectionFunction;
use UnitEnum;
use function class_exists;
use function enum_exists;
use function interface_exists;
use function ltrim;
use function spl_object_hash;
/** @internal */
final class Reflection
{
/** @var array<class-string, ReflectionClass<object>> */
private static array $classReflection = [];
/** @var array<string, ReflectionFunction> */
private static array $functionReflection = [];
/** @var array<string, bool> */
private static array $classOrInterfaceExists = [];
/** @var array<string, bool> */
private static array $enumExists = [];
/**
* Case-sensitive implementation of `class_exists` and `interface_exists`.
*
* @phpstan-assert-if-true class-string $name
*/
public static function classOrInterfaceExists(string $name): bool
{
// @infection-ignore-all / We don't need to test the cache
return self::$classOrInterfaceExists[$name] ??= (class_exists($name) || interface_exists($name))
&& self::class($name)->name === ltrim($name, '\\');
}
/**
* @phpstan-assert-if-true class-string<UnitEnum> $name
*/
public static function enumExists(string $name): bool
{
// @infection-ignore-all / We don't need to test the cache
return self::$enumExists[$name] ??= enum_exists($name);
}
/**
* @param class-string $className
* @return ReflectionClass<object>
*/
public static function class(string $className): ReflectionClass
{
return self::$classReflection[$className] ??= new ReflectionClass($className);
}
public static function function(callable $function): ReflectionFunction
{
$closure = Closure::fromCallable($function);
return self::$functionReflection[spl_object_hash($closure)] ??= new ReflectionFunction($closure);
}
}
@@ -0,0 +1,132 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Utility\Reflection;
use LogicException;
use PhpToken;
/**
* @internal
*
* Imported from `doctrine/annotations`:
* @link https://github.com/doctrine/annotations/blob/de990c9a69782a8b15fa8c9248de0ef4d82ed701/lib/Doctrine/Common/Annotations/TokenParser.php
*/
final class TokenParser
{
/** @var array<PhpToken> */
private array $tokens;
private int $numTokens;
private int $pointer = 0;
public function __construct(string $content)
{
$this->tokens = PhpToken::tokenize($content);
$this->numTokens = count($this->tokens);
}
/**
* @return array<string, string>
*/
public function parseUseStatements(string $namespaceName): array
{
$currentNamespace = '';
$statements = [];
while ($token = $this->next()) {
if ($currentNamespace === $namespaceName && $token->is(T_USE)) {
$statements = [...$statements, ...$this->parseUseStatement()];
continue;
}
if (! $token->is(T_NAMESPACE)) {
continue;
}
$currentNamespace = $this->parseNamespace();
// Get fresh array for new namespace. This is to prevent the parser
// to collect the use statements for a previous namespace with the
// same name (this is the case if a namespace is defined twice).
$statements = [];
}
return $statements;
}
/**
* @return array<string, string>
*/
private function parseUseStatement(): array
{
$groupRoot = '';
$class = '';
$alias = '';
$statements = [];
$explicitAlias = false;
while ($token = $this->next()) {
$name = (string)$token;
if (! $explicitAlias && $token->is(T_STRING)) {
$class = $alias = $name;
} elseif ($explicitAlias && $token->is(T_STRING)) {
$alias = $name;
} elseif ($token->is([T_NAME_QUALIFIED, T_NAME_FULLY_QUALIFIED])) {
$class = $name;
$classSplit = explode('\\', $name);
$alias = $classSplit[count($classSplit) - 1];
} elseif ($token->is(T_NS_SEPARATOR)) {
$class .= '\\';
$alias = '';
} elseif ($token->is(T_AS)) {
$explicitAlias = true;
$alias = '';
} elseif ($name === ',') {
$statements[strtolower($alias)] = $groupRoot . $class;
$class = $alias = '';
$explicitAlias = false;
} elseif ($name === ';') {
if ($alias !== '') {
$statements[strtolower($alias)] = $groupRoot . $class;
}
break;
} elseif ($name === '{') {
$groupRoot = $class;
$class = '';
}
}
return $statements;
}
private function next(): ?PhpToken
{
for ($i = $this->pointer; $i < $this->numTokens; $i++) {
$this->pointer++;
if (! $this->tokens[$i]->isIgnorable()) {
return $this->tokens[$i];
}
}
return null;
}
private function parseNamespace(): string
{
while ($token = $this->next()) {
if ($token->is([T_NAME_QUALIFIED, T_NAME_FULLY_QUALIFIED, T_STRING])) {
return (string)$token;
}
}
/** @infection-ignore-all */
// @codeCoverageIgnoreStart
throw new LogicException('Namespace not found.');
// @codeCoverageIgnoreEnd
}
}
+51
View File
@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Utility\String;
use function substr;
/** @internal */
final class StringCutter
{
public static function cut(string $s, int $length): string
{
if (function_exists('mb_strcut')) {
return mb_strcut($s, 0, $length);
}
return self::cutPolyfill($s, $length);
}
public static function cutPolyfill(string $s, int $length): string
{
$s = substr($s, 0, $length);
$cur = strlen($s) - 1;
// U+0000 - U+007F
if ((ord($s[$cur]) & 0b1000_0000) === 0) {
return $s;
}
$cnt = 0;
while ((ord($s[$cur]) & 0b1100_0000) === 0b1000_0000) {
++$cnt;
if ($cur === 0) {
// @infection-ignore-all // Causes infinite loop
break;
}
--$cur;
}
assert($cur >= 0);
return match (true) {
default => substr($s, 0, $cur),
// U+0080 - U+07FF
$cnt === 1 && (ord($s[$cur]) & 0b1110_0000) === 0b1100_0000,
// U+0800 - U+FFFF
$cnt === 2 && (ord($s[$cur]) & 0b1111_0000) === 0b1110_0000,
// U+10000 - U+10FFFF
$cnt === 3 && (ord($s[$cur]) & 0b1111_1000) === 0b1111_0000 => $s
};
}
}
@@ -0,0 +1,74 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Utility\String;
use OCA\Talk\Vendor\CuyZ\Valinor\Mapper\Tree\Message\HasParameters;
use IntlException;
use MessageFormatter;
use function class_exists;
use function preg_match;
use function preg_quote;
use function preg_replace;
/** @internal */
final class StringFormatter
{
public const DEFAULT_LOCALE = 'en';
/**
* @param array<string, string> $parameters
*/
public static function format(string $locale, string $body, array $parameters = []): string
{
return class_exists(MessageFormatter::class)
? self::formatWithIntl($locale, $body, $parameters)
: self::formatWithRegex($body, $parameters);
}
public static function for(HasParameters $message): string
{
return self::formatWithRegex($message->body(), $message->parameters());
}
/**
* @param array<string, string> $parameters
*/
private static function formatWithIntl(string $locale, string $body, array $parameters): string
{
try {
$formatted = MessageFormatter::formatMessage($locale, $body, $parameters);
if ($formatted === false) {
throw new StringFormatterError($body, intl_get_error_message());
}
return $formatted;
} catch (IntlException $e) {
throw new StringFormatterError($body, $e->getMessage(), $e);
}
}
/**
* @param array<string, string> $parameters
*/
private static function formatWithRegex(string $body, array $parameters): string
{
$message = $body;
if (preg_match('/{\s*[^}]*[^}a-z_]+\s*}?/', $body)) {
throw new StringFormatterError($body);
}
foreach ($parameters as $name => $value) {
$name = preg_quote($name, '/');
/** @var string $message */
$message = preg_replace("/{\s*$name\s*}/", $value, $message);
}
return $message;
}
}
@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Utility\String;
use RuntimeException;
/** @internal */
final class StringFormatterError extends RuntimeException
{
public function __construct(string $body, string $message = '', ?\Throwable $previous = null)
{
if ($message !== '') {
$message = ": $message";
}
parent::__construct("Message formatter error using `$body`$message.", 1652901203, $previous);
}
}
+88
View File
@@ -0,0 +1,88 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Utility;
use OCA\Talk\Vendor\CuyZ\Valinor\Mapper\Object\Argument;
use OCA\Talk\Vendor\CuyZ\Valinor\Mapper\Object\Arguments;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\BooleanType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\CompositeType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\FixedType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\FloatType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\IntegerType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\ObjectType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\StringType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Type;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\EnumType;
/** @internal */
final class TypeHelper
{
/**
* Sorting the scalar types by priority: int, float, string, bool.
*/
public static function typePriority(Type $type): int
{
return match (true) {
$type instanceof IntegerType => 4,
$type instanceof FloatType => 3,
$type instanceof StringType => 2,
$type instanceof BooleanType => 1,
default => 0,
};
}
public static function dump(Type $type, bool $surround = true): string
{
if ($type instanceof EnumType) {
$text = $type->readableSignature();
} elseif ($type instanceof FixedType) {
return $type->toString();
} elseif (self::containsObject($type)) {
$text = '?';
} else {
$text = $type->toString();
}
return $surround ? "`$text`" : $text;
}
public static function dumpArguments(Arguments $arguments): string
{
if (count($arguments) === 0) {
return 'array';
}
if (count($arguments) === 1) {
return self::dump($arguments->at(0)->type());
}
$parameters = array_map(
function (Argument $argument) {
$name = $argument->name();
$type = $argument->type();
$signature = self::dump($type, false);
return $argument->isRequired() ? "$name: $signature" : "$name?: $signature";
},
[...$arguments],
);
return '`array{' . implode(', ', $parameters) . '}`';
}
public static function containsObject(Type $type): bool
{
if ($type instanceof CompositeType) {
foreach ($type->traverse() as $subType) {
if (self::containsObject($subType)) {
return true;
}
}
}
return $type instanceof ObjectType;
}
}
+146
View File
@@ -0,0 +1,146 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Utility;
use BackedEnum;
use OCA\Talk\Vendor\CuyZ\Valinor\Utility\String\StringCutter;
use DateTimeInterface;
use Generator;
use UnitEnum;
use function implode;
use function is_array;
use function is_bool;
use function is_float;
use function is_int;
use function is_iterable;
use function is_object;
use function is_string;
use function str_contains;
use function str_replace;
use function strlen;
/** @internal */
final class ValueDumper
{
private const MAX_STRING_LENGTH = 50;
private const MAX_ARRAY_ENTRIES = 5;
private const DATE_FORMAT = 'Y/m/d H:i:s';
public static function dump(mixed $value): string
{
return self::doDump($value);
}
private static function doDump(mixed $value, bool $goDeeper = true): string
{
if ($value === null) {
return 'null';
}
if (is_bool($value)) {
return $value ? 'true' : 'false';
}
if (is_int($value) || is_float($value)) {
return (string)$value;
}
if (is_string($value)) {
$value = self::crop($value);
if (str_contains($value, "'") && str_contains($value, '"')) {
return "'" . str_replace("'", "\'", $value) . "'";
}
if (str_contains($value, "'")) {
return '"' . $value . '"';
}
return "'" . $value . "'";
}
if ($value instanceof BackedEnum) {
return is_string($value->value)
? "'$value->value'"
: (string)$value->value;
}
if ($value instanceof UnitEnum) {
return "'$value->name'";
}
if ($value instanceof DateTimeInterface) {
return $value->format(self::DATE_FORMAT);
}
if (is_iterable($value) && ! $value instanceof Generator) {
/** @var iterable<string|int, mixed> $value */
if (is_array($value)) {
$type = 'array';
} else {
$type = 'iterable';
}
$values = self::listValues($value);
if (empty($values)) {
return "$type (empty)";
}
if (! $goDeeper) {
return "$type{…}";
}
return "$type{" . implode(', ', $values) . '}';
}
if (is_object($value)) {
return 'object(' . $value::class . ')';
}
// @codeCoverageIgnoreStart
return 'unknown';
// @codeCoverageIgnoreEnd
}
/**
* @param iterable<string|int, mixed> $iterable
* @return array<mixed>
*/
private static function listValues(iterable $iterable): array
{
$values = [];
$index = 0;
foreach ($iterable as $key => $value) {
$values[] = "$key: " . self::doDump($value, false);
if ($index++ >= self::MAX_ARRAY_ENTRIES) {
$values[] = '…';
break;
}
}
return $values;
}
private static function crop(string $string): string
{
if (strlen($string) <= self::MAX_STRING_LENGTH) {
return $string;
}
$string = StringCutter::cut($string, self::MAX_STRING_LENGTH + 1);
for ($i = strlen($string) - 1; $i > 10; $i--) {
if ($string[$i] === ' ') {
return StringCutter::cut($string, $i) . '…';
}
}
return $string . '…';
}
}