create-prisma-php-app 4.0.0-alpha.2 → 4.0.0-alpha.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/.htaccess +54 -41
  2. package/dist/bootstrap.php +143 -98
  3. package/dist/index.js +264 -99
  4. package/dist/settings/auto-swagger-docs.ts +196 -95
  5. package/dist/settings/bs-config.ts +56 -58
  6. package/dist/settings/files-list.json +1 -1
  7. package/dist/settings/restart-mcp.ts +58 -0
  8. package/dist/settings/restart-websocket.ts +51 -45
  9. package/dist/settings/utils.ts +240 -0
  10. package/dist/src/Lib/AI/ChatGPTClient.php +147 -0
  11. package/dist/src/Lib/Auth/Auth.php +544 -0
  12. package/dist/src/Lib/Auth/AuthConfig.php +89 -0
  13. package/dist/src/Lib/CacheHandler.php +121 -0
  14. package/dist/src/Lib/ErrorHandler.php +322 -0
  15. package/dist/src/Lib/FileManager/UploadFile.php +383 -0
  16. package/dist/src/Lib/Headers/Boom.php +192 -0
  17. package/dist/src/Lib/IncludeTracker.php +59 -0
  18. package/dist/src/Lib/MCP/WeatherTools.php +104 -0
  19. package/dist/src/Lib/MCP/mcp-server.php +80 -0
  20. package/dist/src/Lib/MainLayout.php +230 -0
  21. package/dist/src/Lib/Middleware/AuthMiddleware.php +154 -0
  22. package/dist/src/Lib/Middleware/CorsMiddleware.php +145 -0
  23. package/dist/src/Lib/PHPMailer/Mailer.php +169 -0
  24. package/dist/src/Lib/PHPX/Exceptions/ComponentValidationException.php +49 -0
  25. package/dist/src/Lib/PHPX/Fragment.php +32 -0
  26. package/dist/src/Lib/PHPX/IPHPX.php +22 -0
  27. package/dist/src/Lib/PHPX/PHPX.php +287 -0
  28. package/dist/src/Lib/PHPX/TemplateCompiler.php +641 -0
  29. package/dist/src/Lib/PHPX/TwMerge.php +346 -0
  30. package/dist/src/Lib/PHPX/TypeCoercer.php +490 -0
  31. package/dist/src/Lib/PartialRenderer.php +40 -0
  32. package/dist/src/Lib/PrismaPHPSettings.php +181 -0
  33. package/dist/src/Lib/Request.php +479 -0
  34. package/dist/src/Lib/Security/RateLimiter.php +33 -0
  35. package/dist/src/Lib/Set.php +102 -0
  36. package/dist/src/Lib/StateManager.php +127 -0
  37. package/dist/src/Lib/Validator.php +752 -0
  38. package/dist/src/{Websocket → Lib/Websocket}/ConnectionManager.php +1 -1
  39. package/dist/src/Lib/Websocket/websocket-server.php +118 -0
  40. package/dist/src/app/error.php +1 -1
  41. package/dist/src/app/index.php +24 -5
  42. package/dist/src/app/js/index.js +1 -1
  43. package/dist/src/app/layout.php +2 -2
  44. package/package.json +1 -1
  45. package/dist/settings/restart-websocket.bat +0 -28
  46. package/dist/src/app/assets/images/prisma-php-black.svg +0 -6
  47. package/dist/websocket-server.php +0 -22
  48. package/vendor/autoload.php +0 -25
  49. package/vendor/composer/ClassLoader.php +0 -579
  50. package/vendor/composer/InstalledVersions.php +0 -359
  51. package/vendor/composer/LICENSE +0 -21
  52. package/vendor/composer/autoload_classmap.php +0 -10
  53. package/vendor/composer/autoload_namespaces.php +0 -9
  54. package/vendor/composer/autoload_psr4.php +0 -10
  55. package/vendor/composer/autoload_real.php +0 -38
  56. package/vendor/composer/autoload_static.php +0 -25
  57. package/vendor/composer/installed.json +0 -825
  58. package/vendor/composer/installed.php +0 -132
  59. package/vendor/composer/platform_check.php +0 -26
@@ -0,0 +1,490 @@
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Lib\PHPX;
6
+
7
+ use Lib\Validator;
8
+ use ReflectionType;
9
+ use ReflectionNamedType;
10
+ use ReflectionUnionType;
11
+ use ReflectionIntersectionType;
12
+ use DateTime;
13
+ use DateTimeImmutable;
14
+ use DateTimeInterface;
15
+ use Brick\Math\BigDecimal;
16
+ use Brick\Math\BigInteger;
17
+
18
+ class TypeCoercer
19
+ {
20
+ private static array $typeCache = [];
21
+ private static array $phpTypeMap = [
22
+ 'boolean' => 'bool',
23
+ 'integer' => 'int',
24
+ 'double' => 'float',
25
+ 'string' => 'string',
26
+ 'array' => 'array',
27
+ 'object' => 'object',
28
+ 'resource' => 'resource',
29
+ 'NULL' => 'null',
30
+ ];
31
+
32
+ public static function coerce(mixed $value, ?ReflectionType $type, array $validationRules = []): mixed
33
+ {
34
+ if ($type === null) {
35
+ return $value;
36
+ }
37
+ $typeKey = self::getTypeKey($type);
38
+ if (!isset(self::$typeCache[$typeKey])) {
39
+ self::$typeCache[$typeKey] = self::analyzeType($type);
40
+ }
41
+ $typeInfo = self::$typeCache[$typeKey];
42
+ return self::coerceWithTypeInfo($value, $typeInfo, $validationRules);
43
+ }
44
+
45
+ private static function coerceWithTypeInfo(mixed $value, array $typeInfo, array $validationRules = []): mixed
46
+ {
47
+ if ($value === null && $typeInfo['allowsNull']) {
48
+ return null;
49
+ }
50
+ if ($typeInfo['isUnion']) {
51
+ return self::coerceUnionTypeSmart($value, $typeInfo['types'], $validationRules);
52
+ }
53
+ if (count($typeInfo['types']) === 1) {
54
+ $currentType = self::getNormalizedType($value);
55
+ $targetType = $typeInfo['types'][0]['name'];
56
+ if ($currentType === $targetType) {
57
+ return $value;
58
+ }
59
+ return self::coerceSingleType($value, $typeInfo['types'][0], $validationRules);
60
+ }
61
+ return $value;
62
+ }
63
+
64
+ private static function analyzeType(ReflectionType $type): array
65
+ {
66
+ $info = [
67
+ 'isUnion' => false,
68
+ 'isIntersection' => false,
69
+ 'allowsNull' => false,
70
+ 'types' => [],
71
+ ];
72
+ if ($type instanceof ReflectionUnionType) {
73
+ $info['isUnion'] = true;
74
+ foreach ($type->getTypes() as $unionType) {
75
+ if ($unionType->getName() === 'null') {
76
+ $info['allowsNull'] = true;
77
+ } else {
78
+ $info['types'][] = [
79
+ 'name' => $unionType->getName(),
80
+ 'isBuiltin' => $unionType->isBuiltin(),
81
+ 'allowsNull' => $unionType->allowsNull(),
82
+ ];
83
+ }
84
+ }
85
+ } elseif ($type instanceof ReflectionNamedType) {
86
+ $info['allowsNull'] = $type->allowsNull();
87
+ if ($type->getName() !== 'null') {
88
+ $info['types'][] = [
89
+ 'name' => $type->getName(),
90
+ 'isBuiltin' => $type->isBuiltin(),
91
+ 'allowsNull' => $type->allowsNull(),
92
+ ];
93
+ }
94
+ } elseif ($type instanceof ReflectionIntersectionType) {
95
+ $info['isIntersection'] = true;
96
+ foreach ($type->getTypes() as $intersectionType) {
97
+ if ($intersectionType instanceof ReflectionNamedType) {
98
+ $info['types'][] = [
99
+ 'name' => $intersectionType->getName(),
100
+ 'isBuiltin' => $intersectionType->isBuiltin(),
101
+ 'allowsNull' => $intersectionType->allowsNull(),
102
+ ];
103
+ } else {
104
+ $info['types'][] = [
105
+ 'name' => (string) $intersectionType,
106
+ 'isBuiltin' => false,
107
+ 'allowsNull' => false,
108
+ ];
109
+ }
110
+ }
111
+ }
112
+ return $info;
113
+ }
114
+
115
+ private static function coerceUnionTypeSmart(mixed $value, array $types, array $validationRules = []): mixed
116
+ {
117
+ $typeNames = array_column($types, 'name');
118
+ return match (true) {
119
+ self::hasTypes($typeNames, ['string', 'bool']) =>
120
+ self::coerceStringBoolUnion($value, $types, $validationRules),
121
+ self::hasTypes($typeNames, ['int', 'string']) =>
122
+ self::coerceIntStringUnion($value, $types, $validationRules),
123
+ self::hasTypes($typeNames, ['float', 'string']) =>
124
+ self::coerceFloatStringUnion($value, $types, $validationRules),
125
+ self::hasTypes($typeNames, ['array', 'string']) =>
126
+ self::coerceArrayStringUnion($value, $types, $validationRules),
127
+ self::hasDateTimeTypes($typeNames) =>
128
+ self::coerceDateTimeUnion($value, $types, $validationRules),
129
+ default => self::coerceUnionTypePhpCompliant($value, $types, $validationRules)
130
+ };
131
+ }
132
+
133
+ private static function coerceStringBoolUnion(mixed $value, array $types, array $validationRules = []): mixed
134
+ {
135
+ if (is_bool($value)) {
136
+ return $value;
137
+ }
138
+ if (is_string($value) && self::isBooleanLike($value)) {
139
+ return self::coerceBool($value, $validationRules);
140
+ }
141
+ return self::coerceString($value, $validationRules);
142
+ }
143
+
144
+ private static function coerceIntStringUnion(mixed $value, array $types, array $validationRules = []): mixed
145
+ {
146
+ if (is_int($value)) {
147
+ return $value;
148
+ }
149
+ if (self::isIntegerLike($value)) {
150
+ return self::coerceInt($value, $validationRules);
151
+ }
152
+ return self::coerceString($value, $validationRules);
153
+ }
154
+
155
+ private static function coerceFloatStringUnion(mixed $value, array $types, array $validationRules = []): mixed
156
+ {
157
+ if (is_float($value)) {
158
+ return $value;
159
+ }
160
+ if (is_string($value) && is_numeric($value)) {
161
+ return self::coerceFloat($value, $validationRules);
162
+ }
163
+ return self::coerceString($value, $validationRules);
164
+ }
165
+
166
+ private static function coerceArrayStringUnion(mixed $value, array $types, array $validationRules = []): mixed
167
+ {
168
+ if (is_array($value)) {
169
+ return $value;
170
+ }
171
+ if (is_string($value) && self::isArrayLike($value)) {
172
+ return self::coerceArray($value, $validationRules);
173
+ }
174
+ return self::coerceString($value, $validationRules);
175
+ }
176
+
177
+ private static function coerceDateTimeUnion(mixed $value, array $types, array $validationRules = []): mixed
178
+ {
179
+ if ($value instanceof DateTimeInterface) {
180
+ return $value;
181
+ }
182
+ if (is_string($value) || is_numeric($value)) {
183
+ foreach ($types as $typeInfo) {
184
+ if (in_array($typeInfo['name'], ['DateTime', 'DateTimeImmutable', 'DateTimeInterface'])) {
185
+ $coerced = self::coerceCustomType($value, $typeInfo['name'], $validationRules);
186
+ if ($coerced !== $value) {
187
+ return $coerced;
188
+ }
189
+ }
190
+ }
191
+ }
192
+ return self::coerceString($value, $validationRules);
193
+ }
194
+
195
+ private static function coerceUnionTypePhpCompliant(mixed $value, array $types, array $validationRules = []): mixed
196
+ {
197
+ foreach ($types as $typeInfo) {
198
+ $coerced = self::coerceSingleType($value, $typeInfo, $validationRules);
199
+ if (self::isValidCoercion($value, $coerced, $typeInfo['name'])) {
200
+ return $coerced;
201
+ }
202
+ }
203
+ return $value;
204
+ }
205
+
206
+ private static function hasTypes(array $typeNames, array $requiredTypes): bool
207
+ {
208
+ foreach ($requiredTypes as $required) {
209
+ if (!in_array($required, $typeNames, true)) {
210
+ return false;
211
+ }
212
+ }
213
+ return true;
214
+ }
215
+
216
+ private static function hasDateTimeTypes(array $typeNames): bool
217
+ {
218
+ $dateTimeTypes = ['DateTime', 'DateTimeImmutable', 'DateTimeInterface'];
219
+ return !empty(array_intersect($typeNames, $dateTimeTypes));
220
+ }
221
+
222
+ private static function isBooleanLike(mixed $value): bool
223
+ {
224
+ if (!is_string($value)) {
225
+ return false;
226
+ }
227
+ return in_array(strtolower(trim($value)), [
228
+ 'true',
229
+ 'false',
230
+ '1',
231
+ '0',
232
+ 'yes',
233
+ 'no',
234
+ 'on',
235
+ 'off',
236
+ 'checked',
237
+ ''
238
+ ], true);
239
+ }
240
+
241
+ private static function isIntegerLike(mixed $value): bool
242
+ {
243
+ return is_string($value) && is_numeric($value) && (string)(int)$value === trim($value);
244
+ }
245
+
246
+ private static function isArrayLike(string $value): bool
247
+ {
248
+ return Validator::json($value) === true
249
+ || str_contains($value, ',')
250
+ || str_contains($value, '[')
251
+ || str_contains($value, '{');
252
+ }
253
+
254
+ private static function getNormalizedType(mixed $value): string
255
+ {
256
+ $type = gettype($value);
257
+ return self::$phpTypeMap[$type] ?? $type;
258
+ }
259
+
260
+ private static function coerceSingleType(mixed $value, array $typeInfo, array $validationRules = []): mixed
261
+ {
262
+ if (!$typeInfo['isBuiltin']) {
263
+ return self::coerceCustomType($value, $typeInfo['name'], $validationRules);
264
+ }
265
+ return match ($typeInfo['name']) {
266
+ 'bool' => self::coerceBool($value, $validationRules),
267
+ 'int' => self::coerceInt($value, $validationRules),
268
+ 'float' => self::coerceFloat($value, $validationRules),
269
+ 'string' => self::coerceString($value, $validationRules),
270
+ 'array' => self::coerceArray($value, $validationRules),
271
+ 'object' => self::coerceObject($value, $validationRules),
272
+ 'mixed' => $value,
273
+ default => $value,
274
+ };
275
+ }
276
+
277
+ private static function coerceBool(mixed $value, array $rules = []): mixed
278
+ {
279
+ $validated = Validator::boolean($value);
280
+ if ($validated !== null) {
281
+ return $validated;
282
+ }
283
+ if (is_string($value)) {
284
+ return match (strtolower(trim($value))) {
285
+ 'true', '1', 'yes', 'on', 'checked' => true,
286
+ 'false', '0', 'no', 'off', '' => false,
287
+ default => filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ?? false,
288
+ };
289
+ }
290
+ return (bool) $value;
291
+ }
292
+
293
+ private static function coerceInt(mixed $value, array $rules = []): mixed
294
+ {
295
+ $validated = Validator::int($value);
296
+ return $validated ?? (int) $value;
297
+ }
298
+
299
+ private static function coerceFloat(mixed $value, array $rules = []): mixed
300
+ {
301
+ $validated = Validator::float($value);
302
+ return $validated ?? (float) $value;
303
+ }
304
+
305
+ private static function coerceString(mixed $value, array $rules = []): mixed
306
+ {
307
+ $escapeHtml = $rules['escapeHtml'] ?? true;
308
+ if (isset($rules['type'])) {
309
+ return match ($rules['type']) {
310
+ 'email' => Validator::email($value) ?? Validator::string($value, $escapeHtml),
311
+ 'url' => Validator::url($value) ?? Validator::string($value, $escapeHtml),
312
+ 'uuid' => Validator::uuid($value) ?? Validator::string($value, $escapeHtml),
313
+ 'ulid' => Validator::ulid($value) ?? Validator::string($value, $escapeHtml),
314
+ 'cuid' => Validator::cuid($value) ?? Validator::string($value, $escapeHtml),
315
+ 'cuid2' => Validator::cuid2($value) ?? Validator::string($value, $escapeHtml),
316
+ 'ip' => Validator::ip($value) ?? Validator::string($value, $escapeHtml),
317
+ 'html' => Validator::html(Validator::string($value, false)),
318
+ 'emojis' => Validator::emojis(Validator::string($value, $escapeHtml)),
319
+ default => Validator::string($value, $escapeHtml),
320
+ };
321
+ }
322
+ return Validator::string($value, $escapeHtml);
323
+ }
324
+
325
+ private static function coerceArray(mixed $value, array $rules = []): array
326
+ {
327
+ if (is_array($value)) {
328
+ return $value;
329
+ }
330
+ if (is_string($value)) {
331
+ if (Validator::json($value) === true) {
332
+ $decoded = json_decode($value, true);
333
+ if (is_array($decoded)) {
334
+ return $decoded;
335
+ }
336
+ }
337
+ if (str_contains($value, ',')) {
338
+ return array_map('trim', explode(',', $value));
339
+ }
340
+ return [$value];
341
+ }
342
+ return (array) $value;
343
+ }
344
+
345
+ private static function coerceObject(mixed $value, array $rules = []): object
346
+ {
347
+ if (is_object($value)) {
348
+ return $value;
349
+ }
350
+ if (is_array($value)) {
351
+ return (object) $value;
352
+ }
353
+ if (is_string($value) && Validator::json($value)) {
354
+ $decoded = json_decode($value);
355
+ if (is_object($decoded)) {
356
+ return $decoded;
357
+ }
358
+ }
359
+ return (object) $value;
360
+ }
361
+
362
+ private static function coerceCustomType(mixed $value, string $typeName, array $rules = []): mixed
363
+ {
364
+ return match ($typeName) {
365
+ 'DateTime' => self::coerceDateTime($value, $rules),
366
+ 'DateTimeImmutable' => self::coerceDateTimeImmutable($value, $rules),
367
+ 'DateTimeInterface' => self::coerceDateTimeInterface($value, $rules),
368
+ 'BigDecimal' => self::coerceBigDecimal($value, $rules),
369
+ 'BigInteger' => self::coerceBigInteger($value, $rules),
370
+ default => $value,
371
+ };
372
+ }
373
+
374
+ private static function coerceDateTime(mixed $value, array $rules = []): mixed
375
+ {
376
+ if ($value instanceof DateTime) {
377
+ return $value;
378
+ }
379
+ if ($value instanceof DateTimeImmutable) {
380
+ return DateTime::createFromImmutable($value);
381
+ }
382
+ $format = $rules['format'] ?? null;
383
+ try {
384
+ if ($format) {
385
+ return DateTime::createFromFormat($format, (string)$value) ?: $value;
386
+ } else {
387
+ return new DateTime((string)$value);
388
+ }
389
+ } catch (\Exception) {
390
+ return $value;
391
+ }
392
+ }
393
+
394
+ private static function coerceDateTimeImmutable(mixed $value, array $rules = []): mixed
395
+ {
396
+ if ($value instanceof DateTimeImmutable) {
397
+ return $value;
398
+ }
399
+ if ($value instanceof DateTime) {
400
+ return DateTimeImmutable::createFromMutable($value);
401
+ }
402
+ $format = $rules['format'] ?? null;
403
+ try {
404
+ if ($format) {
405
+ return DateTimeImmutable::createFromFormat($format, (string)$value) ?: $value;
406
+ } else {
407
+ return new DateTimeImmutable((string)$value);
408
+ }
409
+ } catch (\Exception) {
410
+ return $value;
411
+ }
412
+ }
413
+
414
+ private static function coerceDateTimeInterface(mixed $value, array $rules = []): mixed
415
+ {
416
+ if ($value instanceof DateTimeInterface) {
417
+ return $value;
418
+ }
419
+ return self::coerceDateTimeImmutable($value, $rules);
420
+ }
421
+
422
+ private static function coerceBigDecimal(mixed $value, array $rules = []): mixed
423
+ {
424
+ $scale = $rules['scale'] ?? 30;
425
+ return Validator::decimal($value, $scale) ?? $value;
426
+ }
427
+
428
+ private static function coerceBigInteger(mixed $value, array $rules = []): mixed
429
+ {
430
+ return Validator::bigInt($value) ?? $value;
431
+ }
432
+
433
+ private static function isValidCoercion(mixed $original, mixed $coerced, string $typeName): bool
434
+ {
435
+ if (gettype($original) === gettype($coerced)) {
436
+ return match ($typeName) {
437
+ 'string' => true,
438
+ 'array' => $original !== $coerced,
439
+ default => $original === $coerced,
440
+ };
441
+ }
442
+ return match ($typeName) {
443
+ 'bool' => is_bool($coerced),
444
+ 'int' => is_int($coerced),
445
+ 'float' => is_float($coerced),
446
+ 'string' => is_string($coerced),
447
+ 'array' => is_array($coerced),
448
+ 'object' => is_object($coerced),
449
+ 'DateTime' => $coerced instanceof DateTime,
450
+ 'DateTimeImmutable' => $coerced instanceof DateTimeImmutable,
451
+ 'DateTimeInterface' => $coerced instanceof DateTimeInterface,
452
+ 'BigDecimal' => $coerced instanceof BigDecimal,
453
+ 'BigInteger' => $coerced instanceof BigInteger,
454
+ default => true,
455
+ };
456
+ }
457
+
458
+ private static function getTypeKey(ReflectionType $type): string
459
+ {
460
+ if ($type instanceof ReflectionUnionType) {
461
+ $types = array_map(fn($t) => $t->getName(), $type->getTypes());
462
+ sort($types);
463
+ return 'union:' . implode('|', $types);
464
+ }
465
+ if ($type instanceof ReflectionNamedType) {
466
+ return 'named:' . $type->getName() . ($type->allowsNull() ? '|null' : '');
467
+ }
468
+ if ($type instanceof ReflectionIntersectionType) {
469
+ $types = array_map(function ($t) {
470
+ return $t instanceof ReflectionNamedType ? $t->getName() : (string) $t;
471
+ }, $type->getTypes());
472
+ sort($types);
473
+ return 'intersection:' . implode('&', $types);
474
+ }
475
+ return 'unknown:' . get_class($type);
476
+ }
477
+
478
+ public static function clearCache(): void
479
+ {
480
+ self::$typeCache = [];
481
+ }
482
+
483
+ public static function getCacheStats(): array
484
+ {
485
+ return [
486
+ 'type_cache_size' => count(self::$typeCache),
487
+ 'cached_types' => array_keys(self::$typeCache),
488
+ ];
489
+ }
490
+ }
@@ -0,0 +1,40 @@
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Lib;
6
+
7
+ use DOMDocument;
8
+ use DOMXPath;
9
+ use DOMElement;
10
+
11
+ final class PartialRenderer
12
+ {
13
+ /** @return array<string,string> selector => outerHTML */
14
+ public static function extract(string $html, array $selectors): array
15
+ {
16
+ $doc = new DOMDocument('1.0', 'UTF-8');
17
+ libxml_use_internal_errors(true);
18
+ $doc->loadHTML($html, LIBXML_NOERROR | LIBXML_NOWARNING | LIBXML_HTML_NOIMPLIED);
19
+ libxml_clear_errors();
20
+
21
+ $xpath = new DOMXPath($doc);
22
+ $payload = [];
23
+
24
+ foreach ($selectors as $rawSel) {
25
+ $sel = preg_replace('/[^-_\w]/', '', (string)$rawSel);
26
+
27
+ /** @var DOMElement|null $node */
28
+ $node = $xpath->query("//*[@pp-sync='{$sel}']")->item(0);
29
+
30
+ if (!$node && $sel !== '') {
31
+ $node = $xpath->query("//*[@id='{$sel}' or contains(concat(' ',normalize-space(@class),' '),' {$sel} ')]")->item(0);
32
+ }
33
+
34
+ if ($node) {
35
+ $payload[$sel] = $doc->saveHTML($node);
36
+ }
37
+ }
38
+ return $payload;
39
+ }
40
+ }