create-prisma-php-app 1.19.500 → 1.20.0

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.
@@ -32,6 +32,7 @@ $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ||
32
32
  $domainName = $_SERVER['HTTP_HOST'];
33
33
  $scriptName = dirname($_SERVER['SCRIPT_NAME']) . '/';
34
34
  $baseUrl = $protocol . $domainName . rtrim($scriptName, '/') . '/src/app/';
35
+ $referer = $_SERVER['HTTP_REFERER'] ?? 'Unknown';
35
36
 
36
37
  $params = [];
37
38
 
@@ -14,14 +14,15 @@ class ChatGPTClient
14
14
  private $client;
15
15
  private $apiUrl;
16
16
  private $apiKey;
17
+ private $cache = [];
17
18
 
18
19
  /**
19
20
  * Constructor initializes the Guzzle HTTP client and sets up API configuration.
20
21
  */
21
- public function __construct()
22
+ public function __construct(Client $client = null)
22
23
  {
23
- // Initialize the Guzzle HTTP client
24
- $this->client = new Client();
24
+ // Initialize the Guzzle HTTP client, allowing for dependency injection
25
+ $this->client = $client ?: new Client();
25
26
 
26
27
  // API URL for chat completions
27
28
  $this->apiUrl = 'https://api.openai.com/v1/chat/completions';
@@ -36,9 +37,8 @@ class ChatGPTClient
36
37
  * @param array $conversationHistory The conversation history array.
37
38
  * @return string The model name to be used.
38
39
  */
39
- private function determineModel(array $conversationHistory)
40
+ protected function determineModel(array $conversationHistory): string
40
41
  {
41
- // Example logic for model selection
42
42
  $messageCount = count($conversationHistory);
43
43
  $totalTokens = array_reduce($conversationHistory, function ($carry, $item) {
44
44
  return $carry + str_word_count($item['content'] ?? '');
@@ -53,6 +53,25 @@ class ChatGPTClient
53
53
  return 'gpt-3.5-turbo';
54
54
  }
55
55
 
56
+ /**
57
+ * Formats the conversation history to ensure it is valid.
58
+ *
59
+ * @param array $conversationHistory The conversation history array.
60
+ * @return array The formatted conversation history.
61
+ */
62
+ protected function formatConversationHistory(array $conversationHistory): array
63
+ {
64
+ $formattedHistory = [];
65
+ foreach ($conversationHistory as $message) {
66
+ if (is_array($message) && isset($message['role'], $message['content']) && Validator::string($message['content'])) {
67
+ $formattedHistory[] = $message;
68
+ } else {
69
+ $formattedHistory[] = ['role' => 'user', 'content' => (string) $message];
70
+ }
71
+ }
72
+ return $formattedHistory;
73
+ }
74
+
56
75
  /**
57
76
  * Sends a message to the OpenAI API and returns the AI's response.
58
77
  *
@@ -64,31 +83,29 @@ class ChatGPTClient
64
83
  */
65
84
  public function sendMessage(array $conversationHistory, string $userMessage): string
66
85
  {
67
- try {
68
- if (!Validator::string($userMessage)) {
69
- throw new \InvalidArgumentException("Invalid user message: must be a string.");
70
- }
86
+ if (!Validator::string($userMessage)) {
87
+ throw new \InvalidArgumentException("Invalid user message: must be a string.");
88
+ }
71
89
 
72
- // Optional: Convert emojis or special patterns in the message
73
- $userMessage = Validator::emojis($userMessage);
74
-
75
- // Ensure conversationHistory is properly formatted
76
- $formattedHistory = [];
77
- foreach ((array) $conversationHistory as $key => $message) {
78
- if (is_array($message) && isset($message['role'], $message['content']) && Validator::string($message['content'])) {
79
- $formattedHistory[] = $message;
80
- } else {
81
- // If the message is a string, assume it's a user message without a role
82
- $formattedHistory[] = ['role' => 'user', 'content' => (string) $message];
83
- }
84
- }
90
+ // Optional: Convert emojis or special patterns in the message
91
+ $userMessage = Validator::emojis($userMessage);
85
92
 
86
- // Add the new user message
87
- $formattedHistory[] = ['role' => 'user', 'content' => $userMessage];
93
+ // Format the conversation history
94
+ $formattedHistory = $this->formatConversationHistory($conversationHistory);
88
95
 
89
- // Determine the appropriate model to use
90
- $model = $this->determineModel($formattedHistory);
96
+ // Add the new user message
97
+ $formattedHistory[] = ['role' => 'user', 'content' => $userMessage];
98
+
99
+ // Check cache first
100
+ $cacheKey = md5(serialize($formattedHistory));
101
+ if (isset($this->cache[$cacheKey])) {
102
+ return $this->cache[$cacheKey];
103
+ }
91
104
 
105
+ // Determine the appropriate model to use
106
+ $model = $this->determineModel($formattedHistory);
107
+
108
+ try {
92
109
  // Sending a POST request to the AI API
93
110
  $response = $this->client->request('POST', $this->apiUrl, [
94
111
  'headers' => [
@@ -106,10 +123,18 @@ class ChatGPTClient
106
123
  $responseBody = $response->getBody();
107
124
  $responseContent = json_decode($responseBody, true);
108
125
 
109
- // Return the content of the AI's response message
110
- return $responseContent['choices'][0]['message']['content'];
126
+ // Check if response is in expected format
127
+ if (isset($responseContent['choices'][0]['message']['content'])) {
128
+ $aiMessage = $responseContent['choices'][0]['message']['content'];
129
+ // Cache the result
130
+ $this->cache[$cacheKey] = $aiMessage;
131
+ return $aiMessage;
132
+ }
133
+
134
+ throw new \RuntimeException('Unexpected API response format.');
111
135
  } catch (RequestException $e) {
112
- throw new \RuntimeException("API request failed: " . $e->getMessage());
136
+ // Log error here if you have a logger
137
+ throw new \RuntimeException("API request failed: " . $e->getMessage(), 0, $e);
113
138
  }
114
139
  }
115
140
 
@@ -119,7 +144,7 @@ class ChatGPTClient
119
144
  * @param string $gptResponse The raw response from GPT.
120
145
  * @return string The formatted HTML.
121
146
  */
122
- public function formatGPTResponseToHTML($gptResponse)
147
+ public function formatGPTResponseToHTML(string $gptResponse): string
123
148
  {
124
149
  try {
125
150
  // Decode all HTML entities including numeric ones
@@ -140,12 +165,10 @@ class ChatGPTClient
140
165
  }, $gptResponse);
141
166
 
142
167
  // Convert bold text (e.g., **text** or __text__ -> <strong>text</strong>)
143
- $gptResponse = preg_replace('/\*\*(.*?)\*\*/s', '<strong>$1</strong>', $gptResponse);
144
- $gptResponse = preg_replace('/__(.*?)__/s', '<strong>$1</strong>', $gptResponse);
168
+ $gptResponse = preg_replace('/(\*\*|__)(.*?)\1/', '<strong>$2</strong>', $gptResponse);
145
169
 
146
170
  // Convert italic text (e.g., *text* or _text_ -> <em>text</em>)
147
- $gptResponse = preg_replace('/(?<!\*)\*(?!\*)(.*?)\*(?!\*)/s', '<em>$1</em>', $gptResponse);
148
- $gptResponse = preg_replace('/(?<!_)_(?!_)(.*?)_(?!_)/s', '<em>$1</em>', $gptResponse);
171
+ $gptResponse = preg_replace('/(\*|_)(.*?)\1/', '<em>$2</em>', $gptResponse);
149
172
 
150
173
  // Convert strikethrough text (e.g., ~~text~~ -> <del>text</del>)
151
174
  $gptResponse = preg_replace('/~~(.*?)~~/s', '<del>$1</del>', $gptResponse);
@@ -16,16 +16,25 @@ class Auth
16
16
  public const ROLE_NAME = '';
17
17
  public const PAYLOAD = 'payload_2183A';
18
18
  public const COOKIE_NAME = 'pphp_aut_token_D36E5';
19
- private const PPHPAUTH = 'pphpauth';
20
19
 
20
+ private static ?Auth $instance = null;
21
+ private const PPHPAUTH = 'pphpauth';
21
22
  private $secretKey;
22
23
  private $defaultTokenValidity = '1h'; // Default to 1 hour
23
24
 
24
- public function __construct()
25
+ private function __construct()
25
26
  {
26
27
  $this->secretKey = $_ENV['AUTH_SECRET'];
27
28
  }
28
29
 
30
+ public static function getInstance(): Auth
31
+ {
32
+ if (self::$instance === null) {
33
+ self::$instance = new self();
34
+ }
35
+ return self::$instance;
36
+ }
37
+
29
38
  /**
30
39
  * Authenticates a user and generates a JWT (JSON Web Token) based on the specified user data
31
40
  * and token validity duration. The method first checks if the secret key is set, calculates
@@ -35,7 +44,7 @@ class Auth
35
44
  * @param mixed $data User data which can be a simple string or an instance of AuthRole.
36
45
  * If an instance of AuthRole is provided, its `value` property will be used as the role in the token.
37
46
  * @param string|null $tokenValidity Optional parameter specifying the duration the token is valid for (e.g., '10m', '1h').
38
- * If null, the default validity period set in the class property is used.
47
+ * If null, the default validity period set in the class property is used, which is 1 hour.
39
48
  * The format should be a number followed by a time unit ('s' for seconds, 'm' for minutes,
40
49
  * 'h' for hours, 'd' for days), and this is parsed to calculate the exact expiration time.
41
50
  *
@@ -91,7 +100,19 @@ class Auth
91
100
  */
92
101
  public function isAuthenticated(): bool
93
102
  {
94
- return isset($_SESSION[self::PAYLOAD]);
103
+ if (!isset($_COOKIE[self::COOKIE_NAME])) {
104
+ unset($_SESSION[self::PAYLOAD]);
105
+ return false;
106
+ }
107
+
108
+ $jwt = $_COOKIE[self::COOKIE_NAME];
109
+
110
+ $verifyToken = $this->verifyToken($jwt);
111
+ if ($verifyToken === false) {
112
+ return false;
113
+ }
114
+
115
+ return true;
95
116
  }
96
117
 
97
118
  private function calculateExpirationTime(string $duration): int
@@ -136,7 +157,7 @@ class Auth
136
157
  {
137
158
  try {
138
159
  return JWT::decode($jwt, new Key($this->secretKey, 'HS256'));
139
- } catch (\Exception $e) {
160
+ } catch (\Exception) {
140
161
  throw new \InvalidArgumentException("Invalid token.");
141
162
  }
142
163
  }
@@ -227,7 +248,8 @@ class Auth
227
248
  public function getPayload()
228
249
  {
229
250
  if (isset($_SESSION[self::PAYLOAD])) {
230
- return $_SESSION[self::PAYLOAD][self::PAYLOAD_NAME];
251
+ $value = $_SESSION[self::PAYLOAD][self::PAYLOAD_NAME];
252
+ return is_array($value) ? new \ArrayObject($value, \ArrayObject::ARRAY_AS_PROPS) : $value;
231
253
  }
232
254
 
233
255
  return null;
@@ -278,7 +300,7 @@ class Auth
278
300
  * @param mixed ...$providers An array of provider objects such as GoogleProvider or GithubProvider.
279
301
  *
280
302
  * Example:
281
- * $auth = new Auth();
303
+ * $auth = Auth::getInstance();
282
304
  * $auth->authProviders(new GoogleProvider('client_id', 'client_secret', 'redirect_uri'));
283
305
  */
284
306
  public function authProviders(...$providers)
@@ -461,8 +483,7 @@ class GoogleProvider
461
483
  public string $clientSecret,
462
484
  public string $redirectUri,
463
485
  public string $maxAge = '30d'
464
- ) {
465
- }
486
+ ) {}
466
487
  }
467
488
 
468
489
  class GithubProvider
@@ -471,6 +492,5 @@ class GithubProvider
471
492
  public string $clientId,
472
493
  public string $clientSecret,
473
494
  public string $maxAge = '30d'
474
- ) {
475
- }
495
+ ) {}
476
496
  }
@@ -13,26 +13,17 @@ enum AuthRole: string
13
13
  }
14
14
  }
15
15
 
16
- class AuthConfig
16
+ final class AuthConfig
17
17
  {
18
18
  public const ROLE_IDENTIFIER = 'role';
19
19
  public const IS_ROLE_BASE = false;
20
20
  public const IS_TOKEN_AUTO_REFRESH = false;
21
21
 
22
- /**
23
- * An array listing the public routes that do not require authentication.
24
- * Routes should be listed as string paths.
25
- * By default, all routes are public.
26
- * Example: public static $publicRoutes = ['/']; // This means the home page and others are public and do not require authentication.
27
- * NOTE: This $publicRoutes = ['/']; is the default setting and does not need to be modified.
28
- */
29
- public static $publicRoutes = ['/'];
30
-
31
22
  /**
32
23
  * An array of private routes that are accessible to all authenticated users
33
24
  * without specific role-based access control. Routes should be listed as string paths.
34
25
  * Example: public static $privateRoutes = ['/']; // This makes the home page private
35
- * Example: public static $privateRoutes = ['profile', 'dashboard/settings']; // These routes are private
26
+ * Example: public static $privateRoutes = ['/profile', '/dashboard/settings']; // These routes are private
36
27
  */
37
28
  public static $privateRoutes = [];
38
29
 
@@ -5,7 +5,7 @@ namespace Lib\Middleware;
5
5
  use Lib\Auth\Auth;
6
6
  use Lib\Auth\AuthConfig;
7
7
 
8
- class AuthMiddleware
8
+ final class AuthMiddleware
9
9
  {
10
10
  public static function handle($requestUri)
11
11
  {
@@ -17,13 +17,11 @@ class AuthMiddleware
17
17
  // Check if the user is authorized to access the route or redirect to login
18
18
  if (!self::isAuthorized()) {
19
19
  redirect('/auth/login');
20
- exit;
21
20
  }
22
21
 
23
22
  // Check if the user has the required role to access the route or redirect to denied
24
23
  if (AuthConfig::IS_ROLE_BASE && !self::hasRequiredRole($requestUri)) {
25
24
  redirect('/denied');
26
- exit;
27
25
  }
28
26
  }
29
27
 
@@ -39,7 +37,7 @@ class AuthMiddleware
39
37
 
40
38
  protected static function isAuthorized(): bool
41
39
  {
42
- $auth = new Auth();
40
+ $auth = Auth::getInstance();
43
41
  $cookieName = Auth::COOKIE_NAME;
44
42
  if (!isset($_COOKIE[$cookieName])) {
45
43
  unset($_SESSION[Auth::PAYLOAD]);
@@ -68,7 +66,7 @@ class AuthMiddleware
68
66
 
69
67
  protected static function hasRequiredRole($requestUri): bool
70
68
  {
71
- $auth = new Auth();
69
+ $auth = Auth::getInstance();
72
70
  $roleBasedRoutes = AuthConfig::$roleBasedRoutes ?? [];
73
71
  foreach ($roleBasedRoutes as $pattern => $data) {
74
72
  if (self::getUriRegex($pattern, $requestUri)) {
@@ -11,7 +11,7 @@ enum ArrayType: string
11
11
  case Value = 'value';
12
12
  }
13
13
 
14
- abstract class Utility
14
+ final class Utility
15
15
  {
16
16
  public static function checkFieldsExistWithReferences(
17
17
  array $select,
@@ -7,14 +7,12 @@ namespace Lib;
7
7
  */
8
8
  class StateManager
9
9
  {
10
+ private static ?StateManager $instance = null;
10
11
  private const APP_STATE = 'app_state_F989A';
11
12
  private array $state = [];
12
13
  private array $listeners = [];
13
14
 
14
- /**
15
- * Initializes a new instance of the StateManager class.
16
- */
17
- public function __construct()
15
+ private function __construct()
18
16
  {
19
17
  global $isWire;
20
18
 
@@ -25,6 +23,14 @@ class StateManager
25
23
  }
26
24
  }
27
25
 
26
+ public static function getInstance(): StateManager
27
+ {
28
+ if (self::$instance === null) {
29
+ self::$instance = new self();
30
+ }
31
+ return self::$instance;
32
+ }
33
+
28
34
  /**
29
35
  * Gets the state value for the specified key.
30
36
  *
@@ -71,7 +77,7 @@ class StateManager
71
77
  {
72
78
  $this->listeners[] = $listener;
73
79
  $listener($this->state);
74
- return fn () => $this->listeners = array_filter($this->listeners, fn ($l) => $l !== $listener);
80
+ return fn() => $this->listeners = array_filter($this->listeners, fn($l) => $l !== $listener);
75
81
  }
76
82
 
77
83
  /**
@@ -5,7 +5,7 @@ namespace Lib;
5
5
  use HTMLPurifier;
6
6
  use HTMLPurifier_Config;
7
7
 
8
- class Validator
8
+ final class Validator
9
9
  {
10
10
  // String Validation
11
11
 
@@ -351,4 +351,226 @@ class Validator
351
351
 
352
352
  return strtr($content, $emojiMap);
353
353
  }
354
+
355
+ /**
356
+ * Validate a value against a set of rules.
357
+ *
358
+ * @param mixed $value The value to validate.
359
+ * @param string $rules A pipe-separated string of rules (e.g., 'required|min:2|max:50').
360
+ * @param mixed $confirmationValue The value to confirm against, if applicable.
361
+ * @return bool|string|null True if validation passes, string with error message if fails, or null for optional field.
362
+ */
363
+ public static function withRules($value, string $rules, $confirmationValue = null)
364
+ {
365
+ $rulesArray = explode('|', $rules);
366
+ foreach ($rulesArray as $rule) {
367
+ // Handle parameters in rules, e.g., 'min:10'
368
+ if (strpos($rule, ':') !== false) {
369
+ [$ruleName, $parameter] = explode(':', $rule);
370
+ $result = self::applyRule($ruleName, $parameter, $value, $confirmationValue);
371
+ } else {
372
+ $result = self::applyRule($rule, null, $value, $confirmationValue);
373
+ }
374
+
375
+ // If a validation rule fails, return the error message
376
+ if ($result !== true) {
377
+ return $result;
378
+ }
379
+ }
380
+ return true;
381
+ }
382
+
383
+ /**
384
+ * Apply an individual rule to a value.
385
+ *
386
+ * @param string $rule The rule to apply.
387
+ * @param mixed $parameter The parameter for the rule, if applicable.
388
+ * @param mixed $value The value to validate.
389
+ * @return bool|string True if the rule passes, or a string with an error message if it fails.
390
+ */
391
+ private static function applyRule(string $rule, $parameter, $value, $confirmationValue = null)
392
+ {
393
+ switch ($rule) {
394
+ case 'required':
395
+ if (empty($value) && $value !== '0') {
396
+ return "This field is required.";
397
+ } else {
398
+ return true;
399
+ }
400
+ break;
401
+ case 'min':
402
+ if (strlen($value) < (int)$parameter) {
403
+ return "This field must be at least $parameter characters long.";
404
+ } else {
405
+ return true;
406
+ }
407
+ break;
408
+ case 'max':
409
+ if (strlen($value) > (int)$parameter) {
410
+ return "This field must not exceed $parameter characters.";
411
+ } else {
412
+ return true;
413
+ }
414
+ break;
415
+ case 'startsWith':
416
+ if (strpos($value, $parameter) !== 0) {
417
+ return "This field must start with $parameter.";
418
+ } else {
419
+ return true;
420
+ }
421
+ break;
422
+ case 'endsWith':
423
+ if (substr($value, -strlen($parameter)) !== $parameter) {
424
+ return "This field must end with $parameter.";
425
+ } else {
426
+ return true;
427
+ }
428
+ break;
429
+ case 'confirmed':
430
+ if ($confirmationValue !== $value) {
431
+ return "The $rule confirmation does not match.";
432
+ } else {
433
+ return true;
434
+ }
435
+ break;
436
+ case 'email':
437
+ return self::email($value) ? true : "This field must be a valid email address.";
438
+ case 'url':
439
+ return self::url($value) ? true : "This field must be a valid URL.";
440
+ case 'ip':
441
+ return self::ip($value) ? true : "This field must be a valid IP address.";
442
+ case 'uuid':
443
+ return self::uuid($value) ? true : "This field must be a valid UUID.";
444
+ case 'cuid':
445
+ return self::cuid($value) ? true : "This field must be a valid CUID.";
446
+ case 'int':
447
+ return self::int($value) !== null ? true : "This field must be an integer.";
448
+ case 'float':
449
+ return self::float($value) !== null ? true : "This field must be a float.";
450
+ case 'boolean':
451
+ return self::boolean($value) !== null ? true : "This field must be a boolean.";
452
+ case 'in':
453
+ if (!in_array($value, explode(',', $parameter), true)) {
454
+ return "The selected value is invalid.";
455
+ } else {
456
+ return true;
457
+ }
458
+ break;
459
+ case 'notIn':
460
+ if (in_array($value, explode(',', $parameter), true)) {
461
+ return "The selected value is invalid.";
462
+ } else {
463
+ return true;
464
+ }
465
+ break;
466
+ case 'size':
467
+ if (strlen($value) !== (int)$parameter) {
468
+ return "This field must be exactly $parameter characters long.";
469
+ } else {
470
+ return true;
471
+ }
472
+ break;
473
+ case 'between':
474
+ [$min, $max] = explode(',', $parameter);
475
+ if (strlen($value) < (int)$min || strlen($value) > (int)$max) {
476
+ return "This field must be between $min and $max characters long.";
477
+ } else {
478
+ return true;
479
+ }
480
+ break;
481
+ case 'date':
482
+ return self::date($value, $parameter ?: 'Y-m-d') ? true : "This field must be a valid date.";
483
+ case 'dateFormat':
484
+ if (!\DateTime::createFromFormat($parameter, $value)) {
485
+ return "This field must match the format $parameter.";
486
+ } else {
487
+ return true;
488
+ }
489
+ break;
490
+ case 'before':
491
+ if (strtotime($value) >= strtotime($parameter)) {
492
+ return "This field must be a date before $parameter.";
493
+ } else {
494
+ return true;
495
+ }
496
+ break;
497
+ case 'after':
498
+ if (strtotime($value) <= strtotime($parameter)) {
499
+ return "This field must be a date after $parameter.";
500
+ } else {
501
+ return true;
502
+ }
503
+ break;
504
+ case 'json':
505
+ return self::json($value) ? true : "This field must be a valid JSON string.";
506
+ break;
507
+ case 'timezone':
508
+ if (!in_array($value, timezone_identifiers_list())) {
509
+ return "This field must be a valid timezone.";
510
+ } else {
511
+ return true;
512
+ }
513
+ break;
514
+ case 'regex':
515
+ if (!preg_match($parameter, $value)) {
516
+ return "This field format is invalid.";
517
+ } else {
518
+ return true;
519
+ }
520
+ break;
521
+ case 'digits':
522
+ if (!ctype_digit($value) || strlen($value) != $parameter) {
523
+ return "This field must be $parameter digits.";
524
+ } else {
525
+ return true;
526
+ }
527
+ break;
528
+ case 'digitsBetween':
529
+ [$min, $max] = explode(',', $parameter);
530
+ if (!ctype_digit($value) || strlen($value) < (int)$min || strlen($value) > (int)$max) {
531
+ return "This field must be between $min and $max digits.";
532
+ } else {
533
+ return true;
534
+ }
535
+ break;
536
+ case 'mimes':
537
+ $mimeTypes = explode(',', $parameter);
538
+ if (!self::isMimeTypeAllowed($value, $mimeTypes)) {
539
+ return "The file must be of type: " . implode(', ', $mimeTypes) . ".";
540
+ } else {
541
+ return true;
542
+ }
543
+ break;
544
+ case 'file':
545
+ if (!is_uploaded_file($value)) {
546
+ return "This field must be a valid file.";
547
+ } else {
548
+ return true;
549
+ }
550
+ break;
551
+ // Add additional rules as needed...
552
+ default:
553
+ return true;
554
+ }
555
+ }
556
+
557
+ private static function isMimeTypeAllowed($file, array $allowedMimeTypes)
558
+ {
559
+ // Check if the file is a valid uploaded file
560
+ if (!is_uploaded_file($file)) {
561
+ return false;
562
+ }
563
+
564
+ // Get the MIME type of the file using PHP's finfo_file function
565
+ $finfo = finfo_open(FILEINFO_MIME_TYPE);
566
+ $mimeType = finfo_file($finfo, $file);
567
+ finfo_close($finfo);
568
+
569
+ // Check if the MIME type is in the list of allowed MIME types
570
+ if (in_array($mimeType, $allowedMimeTypes, true)) {
571
+ return true;
572
+ }
573
+
574
+ return false;
575
+ }
354
576
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-prisma-php-app",
3
- "version": "1.19.500",
3
+ "version": "1.20.0",
4
4
  "description": "Prisma-PHP: A Revolutionary Library Bridging PHP with Prisma ORM",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",