create-prisma-php-app 1.22.0 → 1.22.1

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.
@@ -25,8 +25,17 @@ function determineContentToInclude()
25
25
  $baseDir = APP_PATH;
26
26
  $includePath = '';
27
27
  $layoutsToInclude = [];
28
- writeRoutes();
28
+
29
+ /**
30
+ * ============ Middleware Management ============
31
+ * AuthMiddleware is invoked to handle authentication logic for the current route ($uri).
32
+ * ================================================
33
+ */
29
34
  AuthMiddleware::handle($uri);
35
+ /**
36
+ * ============ End of Middleware Management ======
37
+ * ================================================
38
+ */
30
39
 
31
40
  $isDirectAccessToPrivateRoute = preg_match('/\/_/', $uri);
32
41
  if ($isDirectAccessToPrivateRoute) {
@@ -107,31 +116,23 @@ function getFilePrecedence()
107
116
  global $_filesListRoutes;
108
117
 
109
118
  // Normalize the file paths for consistent comparison
110
- $_filesListRoutes = array_map(function ($route) {
111
- return str_replace('\\', '/', $route);
112
- }, $_filesListRoutes);
113
-
114
- // Look for route.php in the /src/app/ directory
115
- $routeFile = array_filter($_filesListRoutes, function ($route) {
116
- return preg_match('/^\.\/src\/app\/route\.php$/', $route);
117
- });
118
-
119
- // If route.php is found, return just the file name
120
- if (!empty($routeFile)) {
121
- return '/route.php';
119
+ foreach ($_filesListRoutes as $route) {
120
+ $normalizedRoute = str_replace('\\', '/', $route);
121
+ // First, check for route.php
122
+ if (preg_match('/^\.\/src\/app\/route\.php$/', $normalizedRoute)) {
123
+ return '/route.php';
124
+ }
122
125
  }
123
126
 
124
- // If route.php is not found, look for index.php in the /src/app/ directory
125
- $indexFile = array_filter($_filesListRoutes, function ($route) {
126
- return preg_match('/^\.\/src\/app\/index\.php$/', $route);
127
- });
128
-
129
- // If index.php is found, return just the file name
130
- if (!empty($indexFile)) {
131
- return '/index.php';
127
+ // If no route.php is found, check for index.php
128
+ foreach ($_filesListRoutes as $route) {
129
+ $normalizedRoute = str_replace('\\', '/', $route);
130
+ if (preg_match('/^\.\/src\/app\/index\.php$/', $normalizedRoute)) {
131
+ return '/index.php';
132
+ }
132
133
  }
133
134
 
134
- // If neither file is found, return null or handle the case as needed
135
+ // If neither file is found, return null
135
136
  return null;
136
137
  }
137
138
 
@@ -145,42 +146,19 @@ function uriExtractor(string $scriptUrl): string
145
146
  }
146
147
 
147
148
  $escapedIdentifier = preg_quote($projectName, '/');
148
- $pattern = "/(?:.*$escapedIdentifier)(\/.*)$/";
149
- if (preg_match($pattern, $scriptUrl, $matches)) {
150
- if (!empty($matches[1])) {
151
- $leftTrim = ltrim($matches[1], '/');
152
- $rightTrim = rtrim($leftTrim, '/');
153
- return "$rightTrim";
154
- }
149
+ if (preg_match("/(?:.*$escapedIdentifier)(\/.*)$/", $scriptUrl, $matches) && !empty($matches[1])) {
150
+ return rtrim(ltrim($matches[1], '/'), '/');
155
151
  }
156
152
 
157
153
  return "/";
158
154
  }
159
155
 
160
- function writeRoutes()
156
+ function getFilesListRoutes()
161
157
  {
162
- global $_filesListRoutes;
163
- $directory = './src/app';
164
-
165
- if (is_dir($directory)) {
166
- $filesList = [];
167
-
168
- $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory));
169
-
170
- foreach ($iterator as $file) {
171
- if ($file->isFile()) {
172
- $filesList[] = $file->getPathname();
173
- }
174
- }
175
-
176
- $jsonData = json_encode($filesList, JSON_PRETTY_PRINT);
177
- $jsonFileName = SETTINGS_PATH . '/files-list.json';
178
- file_put_contents($jsonFileName, $jsonData);
158
+ $jsonFileName = SETTINGS_PATH . '/files-list.json';
159
+ $routeFiles = file_exists($jsonFileName) ? json_decode(file_get_contents($jsonFileName), true) : [];
179
160
 
180
- if (file_exists($jsonFileName)) {
181
- $_filesListRoutes = json_decode(file_get_contents($jsonFileName), true);
182
- }
183
- }
161
+ return $routeFiles;
184
162
  }
185
163
 
186
164
  function findGroupFolder($uri): string
@@ -213,14 +191,21 @@ function dynamicRoute($uri)
213
191
  foreach ($_filesListRoutes as $route) {
214
192
  $normalizedRoute = trim(str_replace('\\', '/', $route), '.');
215
193
  $routeSegments = explode('/', ltrim($normalizedRoute, '/'));
194
+
195
+ $filteredRouteSegments = array_values(array_filter($routeSegments, function ($segment) {
196
+ return !preg_match('/\(.+\)/', $segment); // Skip segments with parentheses (groups)
197
+ }));
198
+
216
199
  $singleDynamic = preg_match_all('/\[[^\]]+\]/', $normalizedRoute, $matches) === 1 && !strpos($normalizedRoute, '[...');
217
200
  if ($singleDynamic) {
218
- $segmentMatch = singleDynamicRoute($uriSegments, $routeSegments);
219
- if (!empty($segmentMatch)) {
201
+ $segmentMatch = singleDynamicRoute($uriSegments, $filteredRouteSegments);
202
+ $index = array_search($segmentMatch, $filteredRouteSegments);
203
+ if ($index !== false && isset($uriSegments[$index])) {
220
204
  $trimSegmentMatch = trim($segmentMatch, '[]');
221
- $dynamicRouteParams = new \ArrayObject([$trimSegmentMatch => $uriSegments[array_search($segmentMatch, $routeSegments)]], \ArrayObject::ARRAY_AS_PROPS);
222
-
223
- $dynamicRouteUri = str_replace($segmentMatch, $uriSegments[array_search($segmentMatch, $routeSegments)], $normalizedRoute);
205
+ $dynamicRouteParams = new \ArrayObject([$trimSegmentMatch => $uriSegments[$index]], \ArrayObject::ARRAY_AS_PROPS);
206
+ $dynamicRouteUri = str_replace($segmentMatch, $uriSegments[$index], $normalizedRoute);
207
+ $dynamicRouteUri = preg_replace('/\(.+\)/', '', $dynamicRouteUri);
208
+ $dynamicRouteUri = preg_replace('/\/+/', '/', $dynamicRouteUri);
224
209
  $dynamicRouteUriDirname = dirname($dynamicRouteUri);
225
210
  $dynamicRouteUriDirname = rtrim($dynamicRouteUriDirname, '/');
226
211
 
@@ -235,30 +220,41 @@ function dynamicRoute($uri)
235
220
  }
236
221
  }
237
222
  } elseif (strpos($normalizedRoute, '[...') !== false) {
238
- $cleanedRoute = preg_replace('/\[\.\.\..*?\].*/', '', $normalizedRoute);
239
- if (strpos('/src/app/' . $normalizedUri, $cleanedRoute) === 0) {
240
-
241
- $normalizedUriEdited = "/src/app/$normalizedUri";
242
- $trimNormalizedUriEdited = str_replace($cleanedRoute, '', $normalizedUriEdited);
243
- $explodedNormalizedUri = explode('/', $trimNormalizedUriEdited);
244
- $pattern = '/\[\.\.\.(.*?)\]/';
245
- if (preg_match($pattern, $normalizedRoute, $matches)) {
246
- $contentWithinBrackets = $matches[1];
247
- $dynamicRouteParams = new \ArrayObject([$contentWithinBrackets => $explodedNormalizedUri], \ArrayObject::ARRAY_AS_PROPS);
223
+ // Clean and normalize the route
224
+ $cleanedNormalizedRoute = preg_replace('/\(.+\)/', '', $normalizedRoute); // Remove any public/private segment
225
+ $cleanedNormalizedRoute = preg_replace('/\/+/', '/', $cleanedNormalizedRoute); // Replace multiple slashes
226
+ $dynamicSegmentRoute = preg_replace('/\[\.\.\..*?\].*/', '', $cleanedNormalizedRoute); // Remove the dynamic segment
227
+
228
+ // Check if the normalized URI starts with the cleaned route
229
+ if (strpos("/src/app/$normalizedUri", $dynamicSegmentRoute) === 0) {
230
+ $trimmedUri = str_replace($dynamicSegmentRoute, '', "/src/app/$normalizedUri");
231
+ $uriParts = explode('/', trim($trimmedUri, '/'));
232
+
233
+ // Extract the dynamic segment content
234
+ if (preg_match('/\[\.\.\.(.*?)\]/', $normalizedRoute, $matches)) {
235
+ $dynamicParam = $matches[1];
236
+ $dynamicRouteParams = new \ArrayObject([$dynamicParam => $uriParts], \ArrayObject::ARRAY_AS_PROPS);
248
237
  }
238
+
239
+ // Check for 'route.php'
249
240
  if (strpos($normalizedRoute, 'route.php') !== false) {
250
241
  $uriMatch = $normalizedRoute;
251
242
  break;
252
- } else {
253
- if (strpos($normalizedRoute, 'index.php') !== false) {
254
- $segmentMatch = "[...$contentWithinBrackets]";
255
- $dynamicRouteUri = str_replace($segmentMatch, $uriSegments[array_search($segmentMatch, $routeSegments)], $normalizedRoute);
256
- $dynamicRouteUriDirname = dirname($dynamicRouteUri);
257
- $dynamicRouteUriDirname = rtrim($dynamicRouteUriDirname, '/');
243
+ }
244
+
245
+ // Handle matching routes ending with 'index.php'
246
+ if (strpos($normalizedRoute, 'index.php') !== false) {
247
+ $segmentMatch = "[...$dynamicParam]";
248
+ $index = array_search($segmentMatch, $filteredRouteSegments);
249
+
250
+ if ($index !== false && isset($uriSegments[$index])) {
251
+ // Generate the dynamic URI
252
+ $dynamicRouteUri = str_replace($segmentMatch, implode('/', $uriParts), $cleanedNormalizedRoute);
253
+ $dynamicRouteUriDirname = rtrim(dirname($dynamicRouteUri), '/');
258
254
 
259
- $expectedUri = '/src/app/' . $normalizedUri;
260
- $expectedUri = rtrim($expectedUri, '/');
255
+ $expectedUri = rtrim("/src/app/$normalizedUri", '/');
261
256
 
257
+ // Compare the expected and dynamic URIs
262
258
  if ($expectedUri === $dynamicRouteUriDirname) {
263
259
  $uriMatch = $normalizedRoute;
264
260
  break;
@@ -280,6 +276,7 @@ function isGroupIdentifier($segment): bool
280
276
  function matchGroupFolder($constructedPath): ?string
281
277
  {
282
278
  global $_filesListRoutes;
279
+
283
280
  $bestMatch = null;
284
281
  $normalizedConstructedPath = ltrim(str_replace('\\', '/', $constructedPath), './');
285
282
 
@@ -330,6 +327,10 @@ function singleDynamicRoute($uriSegments, $routeSegments)
330
327
 
331
328
  function checkForDuplicateRoutes()
332
329
  {
330
+ if ($_ENV['APP_ENV'] !== 'development') {
331
+ return;
332
+ }
333
+
333
334
  global $_filesListRoutes;
334
335
  $normalizedRoutesMap = [];
335
336
  foreach ($_filesListRoutes as $route) {
@@ -363,72 +364,27 @@ function checkForDuplicateRoutes()
363
364
  }
364
365
  }
365
366
 
366
- function setupErrorHandling(&$content)
367
- {
368
- set_error_handler(function ($severity, $message, $file, $line) use (&$content) {
369
- $content .= "<div class='error'>Error: {$severity} - {$message} in {$file} on line {$line}</div>";
370
- });
371
-
372
- set_exception_handler(function ($exception) use (&$content) {
373
- $content .= "<div class='error'>Exception: " . htmlspecialchars($exception->getMessage(), ENT_QUOTES, 'UTF-8') . "</div>";
374
- });
375
-
376
- register_shutdown_function(function () use (&$content) {
377
- $error = error_get_last();
378
- if ($error !== null && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_RECOVERABLE_ERROR])) {
379
- $formattedError = "<div class='error'>Fatal Error: " . htmlspecialchars($error['message'], ENT_QUOTES, 'UTF-8') .
380
- " in " . htmlspecialchars($error['file'], ENT_QUOTES, 'UTF-8') .
381
- " on line " . $error['line'] . "</div>";
382
- $content .= $formattedError;
383
- modifyOutputLayoutForError($content);
384
- }
385
- });
386
- }
387
-
388
367
 
389
368
  function containsChildContent($filePath)
390
369
  {
391
370
  $fileContent = file_get_contents($filePath);
392
- if (
393
- (strpos($fileContent, 'echo $childContent') === false &&
394
- strpos($fileContent, 'echo $childContent;') === false) &&
395
- (strpos($fileContent, '<?= $childContent ?>') === false) &&
396
- (strpos($fileContent, '<?= $childContent; ?>') === false)
397
- ) {
398
- return true;
399
- } else {
400
- return false;
401
- }
371
+
372
+ // Regular expression to match different ways of echoing $childContent
373
+ $pattern = '/\<\?=\s*\$childContent\s*;?\s*\?>|echo\s*\$childContent\s*;?/';
374
+
375
+ // Return true if $childContent variables are found, false otherwise
376
+ return preg_match($pattern, $fileContent) === 1;
402
377
  }
403
378
 
404
379
  function containsContent($filePath)
405
380
  {
406
381
  $fileContent = file_get_contents($filePath);
407
- if (
408
- (strpos($fileContent, 'echo $content') === false &&
409
- strpos($fileContent, 'echo $content;') === false) &&
410
- (strpos($fileContent, '<?= $content ?>') === false) &&
411
- (strpos($fileContent, '<?= $content; ?>') === false)
412
- ) {
413
- return true;
414
- } else {
415
- return false;
416
- }
417
- }
418
-
419
- function modifyOutputLayoutForError($contentToAdd)
420
- {
421
- if ($_ENV['SHOW_ERRORS'] === "false") exit;
422
382
 
423
- $layoutContent = file_get_contents(APP_PATH . '/layout.php');
424
- if ($layoutContent !== false) {
425
- $newBodyContent = "<body class=\"fatal-error\">$contentToAdd</body>";
383
+ // Improved regular expression to match different ways of echoing $content
384
+ $pattern = '/\<\?=\s*\$content\s*;?\s*\?>|echo\s*\$content\s*;?/';
426
385
 
427
- $modifiedNotFoundContent = preg_replace('~<body.*?>.*?</body>~s', $newBodyContent, $layoutContent);
428
-
429
- echo $modifiedNotFoundContent;
430
- exit;
431
- }
386
+ // Return true if content variables are found, false otherwise
387
+ return preg_match($pattern, $fileContent) === 1;
432
388
  }
433
389
 
434
390
  function convertToArrayObject($data)
@@ -579,17 +535,7 @@ function getLoadingsFiles()
579
535
  return '';
580
536
  }
581
537
 
582
- function authenticateUserToken()
583
- {
584
- $token = getBearerToken();
585
- if ($token) {
586
- $auth = Auth::getInstance();
587
- $verifyToken = $auth->verifyToken($token);
588
- if ($verifyToken) {
589
- $auth->authenticate($verifyToken);
590
- }
591
- }
592
- }
538
+
593
539
 
594
540
  function getPrismaSettings(): \ArrayObject
595
541
  {
@@ -607,15 +553,103 @@ function getPrismaSettings(): \ArrayObject
607
553
  }
608
554
  }
609
555
 
610
- ob_start();
556
+ function modifyOutputLayoutForError($contentToAdd)
557
+ {
558
+ global
559
+ $baseUrl,
560
+ $metadata,
561
+ $content,
562
+ $childContent,
563
+ $uri,
564
+ $pathname,
565
+ $dynamicRouteParams,
566
+ $params,
567
+ $referer,
568
+ $mainLayoutHead,
569
+ $mainLayoutFooter;
570
+
571
+ $errorFile = APP_PATH . '/error.php';
572
+ $errorFileExists = file_exists($errorFile);
573
+
574
+ if ($_ENV['SHOW_ERRORS'] === "false") {
575
+ if ($errorFileExists) {
576
+ $contentToAdd = "<div class='error'>An error occurred</div>";
577
+ } else {
578
+ exit; // Exit if SHOW_ERRORS is false and no error file exists
579
+ }
580
+ }
581
+
582
+ if ($errorFileExists) {
583
+
584
+ $errorContent = $contentToAdd;
585
+
586
+ $layoutFile = APP_PATH . '/layout.php';
587
+ if (file_exists($layoutFile)) {
588
+
589
+ ob_start();
590
+ include_once $errorFile;
591
+ $content = ob_get_clean();
592
+ include $layoutFile;
593
+ } else {
594
+ echo $errorContent;
595
+ }
596
+ } else {
597
+ echo $contentToAdd;
598
+ }
599
+ exit;
600
+ }
601
+
602
+ set_error_handler(function ($severity, $message, $file, $line) {
603
+ if (!(error_reporting() & $severity)) {
604
+ // This error code is not included in error_reporting
605
+ return;
606
+ }
607
+
608
+ // Capture the specific severity types, including warnings (E_WARNING)
609
+ $errorContent = "<div class='error'>Error: {$severity} - {$message} in {$file} on line {$line}</div>";
610
+
611
+ // If needed, log it or output immediately based on severity
612
+ if ($severity === E_WARNING || $severity === E_NOTICE) {
613
+ modifyOutputLayoutForError($errorContent);
614
+ }
615
+ });
616
+
617
+ set_exception_handler(function ($exception) {
618
+ $errorContent = "<div class='error'>Exception: " . htmlspecialchars($exception->getMessage(), ENT_QUOTES, 'UTF-8') . "</div>";
619
+ modifyOutputLayoutForError($errorContent);
620
+ });
621
+
622
+ register_shutdown_function(function () {
623
+ $error = error_get_last();
624
+ if ($error !== null && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_RECOVERABLE_ERROR])) {
625
+ $formattedError = "<div class='error'>Fatal Error: " . htmlspecialchars($error['message'], ENT_QUOTES, 'UTF-8') .
626
+ " in " . htmlspecialchars($error['file'], ENT_QUOTES, 'UTF-8') .
627
+ " on line " . $error['line'] . "</div>";
628
+ $errorContent = $formattedError;
629
+ modifyOutputLayoutForError($errorContent);
630
+ }
631
+ });
632
+
611
633
  require_once SETTINGS_PATH . '/public-functions.php';
612
634
  require_once SETTINGS_PATH . '/request-methods.php';
613
635
  $_metadataFile = APP_PATH . '/metadata.php';
614
636
  $_metadataArray = file_exists($_metadataFile) ? require_once $_metadataFile : [];
615
- $_filesListRoutes = [];
637
+ $_filesListRoutes = getFilesListRoutes();
616
638
  $_prismaPHPSettings = getPrismaSettings();
617
639
  $_fileToInclude = '';
618
640
 
641
+ function authenticateUserToken()
642
+ {
643
+ $token = getBearerToken();
644
+ if ($token) {
645
+ $auth = Auth::getInstance();
646
+ $verifyToken = $auth->verifyToken($token);
647
+ if ($verifyToken) {
648
+ $auth->authenticate($verifyToken);
649
+ }
650
+ }
651
+ }
652
+
619
653
  /**
620
654
  * @var array $metadata Metadata information
621
655
  */
@@ -656,7 +690,10 @@ try {
656
690
  $_layoutsToInclude = $_determineContentToInclude['layouts'] ?? [];
657
691
  $uri = $_determineContentToInclude['uri'] ?? '';
658
692
  $pathname = $uri ? "/" . $uri : "/";
659
- $_fileToInclude = basename($_contentToInclude);
693
+ $_fileToInclude = null;
694
+ if (is_file($_contentToInclude)) {
695
+ $_fileToInclude = basename($_contentToInclude); // returns the file name
696
+ }
660
697
  authenticateUserToken();
661
698
 
662
699
  if (empty($_contentToInclude)) {
@@ -710,11 +747,11 @@ try {
710
747
 
711
748
  $_isContentIncluded = false;
712
749
  $_isChildContentIncluded = false;
713
- if (containsContent($_parentLayoutPath)) {
750
+ $_isContentVariableIncluded = containsContent($_parentLayoutPath);
751
+ if (!$_isContentVariableIncluded) {
714
752
  $_isContentIncluded = true;
715
753
  }
716
754
 
717
- ob_start();
718
755
  if (!empty($_contentToInclude)) {
719
756
  if (!$_isParentLayout) {
720
757
  ob_start();
@@ -726,7 +763,8 @@ try {
726
763
  continue;
727
764
  }
728
765
 
729
- if (containsChildContent($layoutPath)) {
766
+ $_isChildContentVariableIncluded = containsChildContent($layoutPath);
767
+ if (!$_isChildContentVariableIncluded) {
730
768
  $_isChildContentIncluded = true;
731
769
  }
732
770
 
@@ -759,18 +797,16 @@ try {
759
797
  }
760
798
  } else {
761
799
  if ($_isContentIncluded) {
762
- $content .= "<div class='error'>The parent layout file does not contain &lt;?php echo \$content; ?&gt; Or &lt;?= \$content ?&gt;<br>" . "<strong>$_parentLayoutPath</strong></div>";
763
- modifyOutputLayoutForError($content);
800
+ echo "<div class='error'>The parent layout file does not contain &lt;?php echo \$content; ?&gt; Or &lt;?= \$content ?&gt;<br>" . "<strong>$_parentLayoutPath</strong></div>";
764
801
  } else {
765
- $content .= "<div class='error'>The layout file does not contain &lt;?php echo \$childContent; ?&gt; or &lt;?= \$childContent ?&gt;<br><strong>$layoutPath</strong></div>";
766
- modifyOutputLayoutForError($content);
802
+ $errorDetails = "<div class='error'>The layout file does not contain &lt;?php echo \$childContent; ?&gt; or &lt;?= \$childContent ?&gt;<br><strong>$layoutPath</strong></div>";
803
+ modifyOutputLayoutForError($errorDetails);
767
804
  }
768
805
  }
769
806
  } catch (Throwable $e) {
770
- $content = ob_get_clean();
771
807
  $errorDetails = "Unhandled Exception: " . htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8');
772
808
  $errorDetails .= "<br>File: " . htmlspecialchars($e->getFile(), ENT_QUOTES, 'UTF-8');
773
809
  $errorDetails .= "<br>Line: " . htmlspecialchars($e->getLine(), ENT_QUOTES, 'UTF-8');
774
- $content .= "<div class='error'>" . $errorDetails . "</div>";
775
- modifyOutputLayoutForError($content);
810
+ $errorDetails = "<div class='error'>$errorDetails</div>";
811
+ modifyOutputLayoutForError($errorDetails);
776
812
  }
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{execSync}from"child_process";import fs from"fs";import{fileURLToPath}from"url";import path from"path";import chalk from"chalk";import prompts from"prompts";import https from"https";const __filename=fileURLToPath(import.meta.url),__dirname=path.dirname(__filename);let updateAnswer=null;const nonBackendFiles=["favicon.ico","\\src\\app\\index.php","metadata.php","not-found.php"],dockerFiles=[".dockerignore","docker-compose.yml","Dockerfile","apache.conf"];function bsConfigUrls(e){const s=e.indexOf("\\htdocs\\");if(-1===s)return{bsTarget:"",bsPathRewrite:{}};const n=e.substring(0,s+"\\htdocs\\".length).replace(/\\/g,"\\\\"),t=e.replace(new RegExp(`^${n}`),"").replace(/\\/g,"/");let i=`http://localhost/${t}`;i=i.endsWith("/")?i.slice(0,-1):i;const c=i.replace(/(?<!:)(\/\/+)/g,"/"),r=t.replace(/\/\/+/g,"/");return{bsTarget:`${c}/`,bsPathRewrite:{"^/":`/${r.startsWith("/")?r.substring(1):r}/`}}}function configureBrowserSyncCommand(e){const s=path.join(e,"settings","bs-config.cjs");return fs.writeFileSync(s,'const { createProxyMiddleware } = require("http-proxy-middleware");\nconst fs = require("fs");\n\nconst jsonData = fs.readFileSync("prisma-php.json", "utf8");\nconst config = JSON.parse(jsonData);\n\nmodule.exports = {\n proxy: "http://localhost:3000",\n middleware: [\n (req, res, next) => {\n res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");\n res.setHeader("Pragma", "no-cache");\n res.setHeader("Expires", "0");\n next();\n },\n createProxyMiddleware({\n target: config.bsTarget,\n changeOrigin: true,\n pathRewrite: config.bsPathRewrite,\n }),\n ],\n files: "src/**/*.*",\n notify: false,\n open: false,\n ghostMode: false,\n codeSync: true, // Disable synchronization of code changes across clients\n};',"utf8"),"browser-sync start --config settings/bs-config.cjs"}async function updatePackageJson(e,s){const n=path.join(e,"package.json");if(checkExcludeFiles(n))return;const t=JSON.parse(fs.readFileSync(n,"utf8")),i=configureBrowserSyncCommand(e);t.scripts=Object.assign(Object.assign({},t.scripts),{projectName:"node settings/project-name.cjs"});let c=[];s.tailwindcss&&(t.scripts=Object.assign(Object.assign({},t.scripts),{tailwind:"postcss ./src/app/css/tailwind.css -o ./src/app/css/styles.css --watch"}),c.push("tailwind")),s.websocket&&(t.scripts=Object.assign(Object.assign({},t.scripts),{websocket:"node ./settings/restart-websocket.cjs"}),c.push("websocket")),s.docker&&(t.scripts=Object.assign(Object.assign({},t.scripts),{docker:"docker-compose up"}),c.push("docker")),s.swaggerDocs&&(t.scripts=Object.assign(Object.assign({},t.scripts),{"create-swagger-docs":"node settings/swagger-setup.js"}),c.push("create-swagger-docs"));let r=Object.assign({},t.scripts);r.browserSync=i,r._dev=c.length>0?`npm-run-all -p ${c.join(" ")}`:'echo "No additional scripts to run"',r.startDev="node settings/start-dev.js",r.dev="npm run startDev",t.scripts=r,t.type="module",s.prisma&&(t.prisma={seed:"node prisma/seed.js"}),fs.writeFileSync(n,JSON.stringify(t,null,2))}async function updateComposerJson(e,s){const n=path.join(e,"composer.json");if(checkExcludeFiles(n))return;let t;if(fs.existsSync(n)){{const e=fs.readFileSync(n,"utf8");t=JSON.parse(e)}s.websocket&&(t.require=Object.assign(Object.assign({},t.require),{"cboden/ratchet":"^0.4.4"})),s.prisma&&(t.require=Object.assign(Object.assign({},t.require),{"ramsey/uuid":"5.x-dev","hidehalo/nanoid-php":"1.x-dev"})),fs.writeFileSync(n,JSON.stringify(t,null,2))}}async function updateIndexJsForWebSocket(e,s){if(!s.websocket)return;const n=path.join(e,"src","app","js","index.js");if(checkExcludeFiles(n))return;let t=fs.readFileSync(n,"utf8");t+='\n// WebSocket initialization\nconst ws = new WebSocket("ws://localhost:8080");\n',fs.writeFileSync(n,t,"utf8")}async function createUpdateGitignoreFile(e,s){const n=path.join(e,".gitignore");if(checkExcludeFiles(n))return;let t="";s.forEach((e=>{t.includes(e)||(t+=`\n${e}`)})),t=t.trimStart(),fs.writeFileSync(n,t)}function copyRecursiveSync(e,s,n){var t;const i=fs.existsSync(e),c=i&&fs.statSync(e);if(i&&c&&c.isDirectory()){const i=s.toLowerCase();if(!n.websocket&&i.includes("src\\lib\\websocket"))return;if(!n.prisma&&i.includes("src\\lib\\prisma"))return;if(n.backendOnly&&i.includes("src\\app\\js")||n.backendOnly&&i.includes("src\\app\\css"))return;if(!n.swaggerDocs&&i.includes("src\\app\\swagger-docs"))return;const c=s.replace(/\\/g,"/");if(null===(t=null==updateAnswer?void 0:updateAnswer.excludeFilePath)||void 0===t?void 0:t.includes(c))return;fs.existsSync(s)||fs.mkdirSync(s,{recursive:!0}),fs.readdirSync(e).forEach((t=>{copyRecursiveSync(path.join(e,t),path.join(s,t),n)}))}else{if(checkExcludeFiles(s))return;if(!n.tailwindcss&&(s.includes("tailwind.css")||s.includes("styles.css")))return;if(!n.websocket&&(s.includes("restart-websocket.cjs")||s.includes("restart-websocket.bat")))return;if(!n.docker&&dockerFiles.some((e=>s.includes(e))))return;if(n.backendOnly&&nonBackendFiles.some((e=>s.includes(e))))return;if(!n.backendOnly&&s.includes("route.php"))return;if(n.backendOnly&&!n.swaggerDocs&&(s.includes("start-dev.js")||s.includes("swagger-setup.js")))return;fs.copyFileSync(e,s,0)}}async function executeCopy(e,s,n){s.forEach((({srcDir:s,destDir:t})=>{copyRecursiveSync(path.join(__dirname,s),path.join(e,t),n)}))}function createOrUpdateTailwindConfig(e){const s=path.join(e,"tailwind.config.js");if(checkExcludeFiles(s))return;let n=fs.readFileSync(s,"utf8");const t=["./src/app/**/*.{html,js,php}"].map((e=>` "${e}"`)).join(",\n");n=n.replace(/content: \[\],/g,`content: [\n${t}\n],`),fs.writeFileSync(s,n,{flag:"w"})}function modifyPostcssConfig(e){const s=path.join(e,"postcss.config.js");if(checkExcludeFiles(s))return;fs.writeFileSync(s,"export default {\n plugins: {\n tailwindcss: {},\n autoprefixer: {},\n cssnano: {},\n },\n};",{flag:"w"})}function modifyLayoutPHP(e,s){const n=path.join(e,"src","app","layout.php");if(!checkExcludeFiles(n))try{let e=fs.readFileSync(n,"utf8"),t="";s.backendOnly||(t='\n <link href="<?= $baseUrl; ?>/css/index.css" rel="stylesheet">\n <script src="<?= $baseUrl; ?>/js/index.js"><\/script>');let i="";s.backendOnly||(i=s.tailwindcss?` <link href="<?= $baseUrl; ?>/css/styles.css" rel="stylesheet"> ${t}`:` <script src="https://cdn.tailwindcss.com"><\/script> ${t}`);const c=i.length>0?"\n":"";e=e.replace("</head>",`${i}${c} \x3c!-- Dynamic Head --\x3e\n <?= implode("\\n", $mainLayoutHead); ?>\n</head>`),fs.writeFileSync(n,e,{flag:"w"})}catch(e){}}async function createOrUpdateEnvFile(e,s){const n=path.join(e,".env");checkExcludeFiles(n)||fs.writeFileSync(n,s,{flag:"w"})}function checkExcludeFiles(e){var s,n;return!!(null==updateAnswer?void 0:updateAnswer.isUpdate)&&(null!==(n=null===(s=null==updateAnswer?void 0:updateAnswer.excludeFilePath)||void 0===s?void 0:s.includes(e.replace(/\\/g,"/")))&&void 0!==n&&n)}async function createDirectoryStructure(e,s){const n=[{src:"/bootstrap.php",dest:"/bootstrap.php"},{src:"/.htaccess",dest:"/.htaccess"},{src:"/../composer.json",dest:"/composer.json"}];(null==updateAnswer?void 0:updateAnswer.isUpdate)&&(n.push({src:"/tsconfig.json",dest:"/tsconfig.json"}),updateAnswer.tailwindcss&&n.push({src:"/postcss.config.js",dest:"/postcss.config.js"},{src:"/tailwind.config.js",dest:"/tailwind.config.js"}));const t=[{srcDir:"/settings",destDir:"/settings"},{srcDir:"/src",destDir:"/src"}];s.backendOnly&&s.swaggerDocs&&t.push({srcDir:"/swagger-docs-layout.php",destDir:"/src/app/layout.php"}),s.prisma&&t.push({srcDir:"/prisma",destDir:"/prisma"}),s.docker&&t.push({srcDir:"/.dockerignore",destDir:"/.dockerignore"},{srcDir:"/docker-compose.yml",destDir:"/docker-compose.yml"},{srcDir:"/Dockerfile",destDir:"/Dockerfile"},{srcDir:"/apache.conf",destDir:"/apache.conf"}),n.forEach((({src:s,dest:n})=>{const t=path.join(__dirname,s),i=path.join(e,n);if(checkExcludeFiles(i))return;const c=fs.readFileSync(t,"utf8");fs.writeFileSync(i,c,{flag:"w"})})),await executeCopy(e,t,s),await updatePackageJson(e,s),await updateComposerJson(e,s),s.backendOnly||await updateIndexJsForWebSocket(e,s),s.tailwindcss&&(createOrUpdateTailwindConfig(e),modifyPostcssConfig(e)),(s.tailwindcss||!s.backendOnly||s.swaggerDocs)&&modifyLayoutPHP(e,s);const i='# Prisma PHP Auth Secret Key For development only - Change this in production\nAUTH_SECRET=uxsjXVPHN038DEYls2Kw0QUgBcXKUyrjv416nIFWPY4= \n \n# PHPMailer\n# SMTP_HOST=smtp.gmail.com or your SMTP host\n# SMTP_USERNAME=john.doe@gmail.com or your SMTP username\n# SMTP_PASSWORD=123456\n# SMTP_PORT=587 for TLS, 465 for SSL or your SMTP port\n# SMTP_ENCRYPTION=ssl or tls\n# MAIL_FROM=john.doe@gmail.com\n# MAIL_FROM_NAME="John Doe"\n\n# SHOW ERRORS - Set to true to show errors in the browser for development only - Change this in production to false\nSHOW_ERRORS=true\n\n# ChatGPT API Key\n# CHATGPT_API_KEY=sk-your-api-key\n\n# APP TIMEZONE - Set your application timezone - Default is "UTC"\nAPP_TIMEZONE="UTC"';if(s.prisma){const s=`${'# Environment variables declared in this file are automatically made available to Prisma.\n# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema\n\n# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.\n# See the documentation for all the connection string options: https://pris.ly/d/connection-strings\n\nDATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public"'}\n\n${i}`;await createOrUpdateEnvFile(e,s)}else await createOrUpdateEnvFile(e,i);await createUpdateGitignoreFile(e,["vendor",".env","node_modules"])}async function getAnswer(e={}){var s,n,t,i,c,r,o,a,l,p,d,u,h;const g=[];e.projectName||g.push({type:"text",name:"projectName",message:"What is your project named?",initial:"my-app"}),e.backendOnly||g.push({type:"toggle",name:"backendOnly",message:"Would you like to create a backend-only project?",initial:!1,active:"Yes",inactive:"No"});const m=()=>{process.exit(0)},f=await prompts(g,{onCancel:m}),y=[];f.backendOnly||e.backendOnly?(e.swaggerDocs||y.push({type:"toggle",name:"swaggerDocs",message:`Would you like to use ${chalk.blue("Swagger Docs")}?`,initial:!1,active:"Yes",inactive:"No"}),e.websocket||y.push({type:"toggle",name:"websocket",message:`Would you like to use ${chalk.blue("Websocket")}?`,initial:!0,active:"Yes",inactive:"No"}),e.prisma||y.push({type:"toggle",name:"prisma",message:`Would you like to use ${chalk.blue("Prisma PHP ORM")}?`,initial:!0,active:"Yes",inactive:"No"}),e.docker||y.push({type:"toggle",name:"docker",message:`Would you like to use ${chalk.blue("Docker")}?`,initial:!1,active:"Yes",inactive:"No"})):(e.swaggerDocs||y.push({type:"toggle",name:"swaggerDocs",message:`Would you like to use ${chalk.blue("Swagger Docs")}?`,initial:!1,active:"Yes",inactive:"No"}),e.tailwindcss||y.push({type:"toggle",name:"tailwindcss",message:`Would you like to use ${chalk.blue("Tailwind CSS")}?`,initial:!0,active:"Yes",inactive:"No"}),e.websocket||y.push({type:"toggle",name:"websocket",message:`Would you like to use ${chalk.blue("Websocket")}?`,initial:!0,active:"Yes",inactive:"No"}),e.prisma||y.push({type:"toggle",name:"prisma",message:`Would you like to use ${chalk.blue("Prisma PHP ORM")}?`,initial:!0,active:"Yes",inactive:"No"}),e.docker||y.push({type:"toggle",name:"docker",message:`Would you like to use ${chalk.blue("Docker")}?`,initial:!1,active:"Yes",inactive:"No"}));const w=await prompts(y,{onCancel:m});return{projectName:f.projectName?String(f.projectName).trim().replace(/ /g,"-"):null!==(s=e.projectName)&&void 0!==s?s:"my-app",backendOnly:null!==(t=null!==(n=f.backendOnly)&&void 0!==n?n:e.backendOnly)&&void 0!==t&&t,swaggerDocs:null!==(c=null!==(i=w.swaggerDocs)&&void 0!==i?i:e.swaggerDocs)&&void 0!==c&&c,tailwindcss:null!==(o=null!==(r=w.tailwindcss)&&void 0!==r?r:e.tailwindcss)&&void 0!==o&&o,websocket:null!==(l=null!==(a=w.websocket)&&void 0!==a?a:e.websocket)&&void 0!==l&&l,prisma:null!==(d=null!==(p=w.prisma)&&void 0!==p?p:e.prisma)&&void 0!==d&&d,docker:null!==(h=null!==(u=w.docker)&&void 0!==u?u:e.docker)&&void 0!==h&&h}}async function installDependencies(e,s,n=!1){fs.existsSync(path.join(e,"package.json"))||execSync("npm init -y",{stdio:"inherit",cwd:e}),s.forEach((e=>{}));const t=`npm install ${n?"--save-dev":""} ${s.join(" ")}`;execSync(t,{stdio:"inherit",cwd:e})}async function uninstallDependencies(e,s,n=!1){s.forEach((e=>{}));const t=`npm uninstall ${n?"--save-dev":"--save"} ${s.join(" ")}`;execSync(t,{stdio:"inherit",cwd:e})}function fetchPackageVersion(e){return new Promise(((s,n)=>{https.get(`https://registry.npmjs.org/${e}`,(e=>{let t="";e.on("data",(e=>t+=e)),e.on("end",(()=>{try{const e=JSON.parse(t);s(e["dist-tags"].latest)}catch(e){n(new Error("Failed to parse JSON response"))}}))})).on("error",(e=>n(e)))}))}const readJsonFile=e=>{const s=fs.readFileSync(e,"utf8");return JSON.parse(s)};function compareVersions(e,s){const n=e.split(".").map(Number),t=s.split(".").map(Number);for(let e=0;e<n.length;e++){if(n[e]>t[e])return 1;if(n[e]<t[e])return-1}return 0}function getInstalledPackageVersion(e){try{const s=execSync(`npm list -g ${e} --depth=0`).toString().match(new RegExp(`${e}@(\\d+\\.\\d+\\.\\d+)`));return s?s[1]:null}catch(e){return null}}async function main(){var e,s,n,t,i,c,r,o,a;try{const l=process.argv.slice(2);let p=l[0],d=null;if(p){const a={projectName:p,backendOnly:l.includes("--backend-only"),swaggerDocs:l.includes("--swagger-docs"),tailwindcss:l.includes("--tailwindcss"),websocket:l.includes("--websocket"),prisma:l.includes("--prisma"),docker:l.includes("--docker")};if(d=await getAnswer(a),null===d)return;const u=process.cwd(),h=path.join(u,"prisma-php.json"),g=readJsonFile(h);let m=[];null===(e=g.excludeFiles)||void 0===e||e.map((e=>{const s=path.join(u,e);fs.existsSync(s)&&m.push(s.replace(/\\/g,"/"))})),updateAnswer={projectName:p,backendOnly:null!==(s=null==d?void 0:d.backendOnly)&&void 0!==s&&s,swaggerDocs:null!==(n=null==d?void 0:d.swaggerDocs)&&void 0!==n&&n,tailwindcss:null!==(t=null==d?void 0:d.tailwindcss)&&void 0!==t&&t,websocket:null!==(i=null==d?void 0:d.websocket)&&void 0!==i&&i,prisma:null!==(c=null==d?void 0:d.prisma)&&void 0!==c&&c,docker:null!==(r=null==d?void 0:d.docker)&&void 0!==r&&r,isUpdate:!0,excludeFiles:null!==(o=g.excludeFiles)&&void 0!==o?o:[],excludeFilePath:null!=m?m:[],filePath:u}}else d=await getAnswer();if(null===d)return;const u=await fetchPackageVersion("create-prisma-php-app"),h=getInstalledPackageVersion("create-prisma-php-app");h?-1===compareVersions(h,u)&&(execSync("npm uninstall -g create-prisma-php-app",{stdio:"inherit"}),execSync("npm install -g create-prisma-php-app",{stdio:"inherit"})):execSync("npm install -g create-prisma-php-app",{stdio:"inherit"});const g=await fetchPackageVersion("browser-sync"),m=getInstalledPackageVersion("browser-sync");m?-1===compareVersions(m,g)&&(execSync("npm uninstall -g browser-sync",{stdio:"inherit"}),execSync("npm install -g browser-sync",{stdio:"inherit"})):execSync("npm install -g browser-sync",{stdio:"inherit"}),p||fs.mkdirSync(d.projectName);const f=process.cwd();let y=p?f:path.join(f,d.projectName);p||process.chdir(d.projectName);const w=["typescript","@types/node","ts-node","http-proxy-middleware@^3.0.0","chalk","npm-run-all"];d.swaggerDocs&&w.push("swagger-jsdoc"),d.tailwindcss&&w.push("tailwindcss","autoprefixer","postcss","postcss-cli","cssnano"),d.websocket&&w.push("chokidar-cli"),d.prisma&&w.push("prisma","@prisma/client"),await installDependencies(y,w,!0),p||execSync("npx tsc --init",{stdio:"inherit"}),d.tailwindcss&&execSync("npx tailwindcss init -p",{stdio:"inherit"}),d.prisma&&(fs.existsSync(path.join(y,"prisma"))||execSync("npx prisma init",{stdio:"inherit"})),await createDirectoryStructure(y,d);const k=path.join(y,"public");if(fs.existsSync(k)||fs.mkdirSync(k),d.swaggerDocs){const e=path.join(y,"src","app","swagger-docs");fs.existsSync(e)||fs.mkdirSync(e,{recursive:!0}),execSync(`git clone https://github.com/TheSteelNinjaCode/prisma-php-swagger-docs.git ${e}`,{stdio:"inherit"})}if(null==updateAnswer?void 0:updateAnswer.isUpdate){const e=[];if(updateAnswer.backendOnly){nonBackendFiles.forEach((e=>{const s=path.join(y,"src","app",e);fs.existsSync(s)&&fs.unlinkSync(s)}));["js","css"].forEach((e=>{const s=path.join(y,"src","app",e);fs.existsSync(s)&&fs.rmSync(s,{recursive:!0,force:!0})}))}if(!updateAnswer.swaggerDocs){const s=path.join(y,"src","app","swagger-docs");fs.existsSync(s)&&fs.rmSync(s,{recursive:!0,force:!0}),e.push("swagger-jsdoc")}if(!updateAnswer.tailwindcss){["postcss.config.js","tailwind.config.js"].forEach((e=>{const s=path.join(y,e);fs.existsSync(s)&&fs.unlinkSync(s)})),e.push("tailwindcss","autoprefixer","postcss","postcss-cli","cssnano")}if(updateAnswer.websocket||e.push("chokidar-cli"),updateAnswer.prisma||e.push("prisma","@prisma/client"),!updateAnswer.docker){[".dockerignore","docker-compose.yml","Dockerfile","apache.conf"].forEach((e=>{const s=path.join(y,e);fs.existsSync(s)&&fs.unlinkSync(s)}))}e.length>0&&await uninstallDependencies(y,e,!0)}const b=y.replace(/\\/g,"\\"),S=bsConfigUrls(b),j=d.prisma?"src/Lib/Prisma/Classes":"",v={projectName:d.projectName,projectRootPath:b,phpEnvironment:"XAMPP",phpRootPathExe:"C:\\xampp\\php\\php.exe",phpGenerateClassPath:j,bsTarget:S.bsTarget,bsPathRewrite:S.bsPathRewrite,backendOnly:d.backendOnly,swaggerDocs:d.swaggerDocs,tailwindcss:d.tailwindcss,websocket:d.websocket,prisma:d.prisma,docker:d.docker,version:u,excludeFiles:null!==(a=null==updateAnswer?void 0:updateAnswer.excludeFiles)&&void 0!==a?a:[]};fs.writeFileSync(path.join(y,"prisma-php.json"),JSON.stringify(v,null,2),{flag:"w"}),(null==updateAnswer?void 0:updateAnswer.isUpdate)?execSync("C:\\xampp\\php\\php.exe C:\\ProgramData\\ComposerSetup\\bin\\composer.phar update",{stdio:"inherit"}):execSync("C:\\xampp\\php\\php.exe C:\\ProgramData\\ComposerSetup\\bin\\composer.phar install",{stdio:"inherit"})}catch(e){process.exit(1)}}main();
2
+ import{execSync}from"child_process";import fs from"fs";import{fileURLToPath}from"url";import path from"path";import chalk from"chalk";import prompts from"prompts";import https from"https";const __filename=fileURLToPath(import.meta.url),__dirname=path.dirname(__filename);let updateAnswer=null;const nonBackendFiles=["favicon.ico","\\src\\app\\index.php","metadata.php","not-found.php"],dockerFiles=[".dockerignore","docker-compose.yml","Dockerfile","apache.conf"];function bsConfigUrls(e){const s=e.indexOf("\\htdocs\\");if(-1===s)return{bsTarget:"",bsPathRewrite:{}};const n=e.substring(0,s+"\\htdocs\\".length).replace(/\\/g,"\\\\"),t=e.replace(new RegExp(`^${n}`),"").replace(/\\/g,"/");let i=`http://localhost/${t}`;i=i.endsWith("/")?i.slice(0,-1):i;const c=i.replace(/(?<!:)(\/\/+)/g,"/"),o=t.replace(/\/\/+/g,"/");return{bsTarget:`${c}/`,bsPathRewrite:{"^/":`/${o.startsWith("/")?o.substring(1):o}/`}}}function configureBrowserSyncCommand(e){const s=path.join(e,"settings","bs-config.cjs");return fs.writeFileSync(s,'const { createProxyMiddleware } = require("http-proxy-middleware");\nconst fs = require("fs");\nconst chokidar = require("chokidar");\nconst { execSync } = require("child_process");\n\nconst jsonData = fs.readFileSync("prisma-php.json", "utf8");\nconst config = JSON.parse(jsonData);\n\n// Watch for file changes (create, delete, save)\nconst watcher = chokidar.watch("src/**/*", {\n ignored: /(^|[\\/\\\\])\\../,\n persistent: true,\n usePolling: true,\n interval: 1000,\n});\n\nwatcher\n .on("add", async (path) => {\n execSync("node settings/files-list-config.js");\n })\n .on("change", async (path) => {\n execSync("node settings/files-list-config.js");\n })\n .on("unlink", async (path) => {\n execSync("node settings/files-list-config.js");\n });\n\nmodule.exports = {\n proxy: "http://localhost:3000",\n middleware: [\n (req, res, next) => {\n res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");\n res.setHeader("Pragma", "no-cache");\n res.setHeader("Expires", "0");\n next();\n },\n createProxyMiddleware({\n target: config.bsTarget,\n changeOrigin: true,\n pathRewrite: config.bsPathRewrite,\n }),\n ],\n files: "src/**/*.*",\n notify: false,\n open: false,\n ghostMode: false,\n codeSync: true, // Disable synchronization of code changes across clients\n watchOptions: {\n usePolling: true,\n interval: 1000,\n },\n};',"utf8"),"browser-sync start --config settings/bs-config.cjs"}async function updatePackageJson(e,s){const n=path.join(e,"package.json");if(checkExcludeFiles(n))return;const t=JSON.parse(fs.readFileSync(n,"utf8")),i=configureBrowserSyncCommand(e);t.scripts=Object.assign(Object.assign({},t.scripts),{projectName:"node settings/project-name.cjs"});let c=[];s.tailwindcss&&(t.scripts=Object.assign(Object.assign({},t.scripts),{tailwind:"postcss ./src/app/css/tailwind.css -o ./src/app/css/styles.css --watch"}),c.push("tailwind")),s.websocket&&(t.scripts=Object.assign(Object.assign({},t.scripts),{websocket:"node ./settings/restart-websocket.cjs"}),c.push("websocket")),s.docker&&(t.scripts=Object.assign(Object.assign({},t.scripts),{docker:"docker-compose up"}),c.push("docker")),s.swaggerDocs&&(t.scripts=Object.assign(Object.assign({},t.scripts),{"create-swagger-docs":"node settings/swagger-setup.js"}),c.push("create-swagger-docs"));let o=Object.assign({},t.scripts);o.browserSync=i,o.npmRunAll=c.length>0?`npm-run-all -p ${c.join(" ")}`:'echo "No additional scripts to run"',o.dev="node settings/start-dev.js",t.scripts=o,t.type="module",s.prisma&&(t.prisma={seed:"node prisma/seed.js"}),fs.writeFileSync(n,JSON.stringify(t,null,2))}async function updateComposerJson(e,s){const n=path.join(e,"composer.json");if(checkExcludeFiles(n))return;let t;if(fs.existsSync(n)){{const e=fs.readFileSync(n,"utf8");t=JSON.parse(e)}s.websocket&&(t.require=Object.assign(Object.assign({},t.require),{"cboden/ratchet":"^0.4.4"})),s.prisma&&(t.require=Object.assign(Object.assign({},t.require),{"ramsey/uuid":"5.x-dev","hidehalo/nanoid-php":"1.x-dev"})),fs.writeFileSync(n,JSON.stringify(t,null,2))}}async function updateIndexJsForWebSocket(e,s){if(!s.websocket)return;const n=path.join(e,"src","app","js","index.js");if(checkExcludeFiles(n))return;let t=fs.readFileSync(n,"utf8");t+='\n// WebSocket initialization\nconst ws = new WebSocket("ws://localhost:8080");\n',fs.writeFileSync(n,t,"utf8")}async function createUpdateGitignoreFile(e,s){const n=path.join(e,".gitignore");if(checkExcludeFiles(n))return;let t="";s.forEach((e=>{t.includes(e)||(t+=`\n${e}`)})),t=t.trimStart(),fs.writeFileSync(n,t)}function copyRecursiveSync(e,s,n){var t;const i=fs.existsSync(e),c=i&&fs.statSync(e);if(i&&c&&c.isDirectory()){const i=s.toLowerCase();if(!n.websocket&&i.includes("src\\lib\\websocket"))return;if(!n.prisma&&i.includes("src\\lib\\prisma"))return;if(n.backendOnly&&i.includes("src\\app\\js")||n.backendOnly&&i.includes("src\\app\\css"))return;if(!n.swaggerDocs&&i.includes("src\\app\\swagger-docs"))return;const c=s.replace(/\\/g,"/");if(null===(t=null==updateAnswer?void 0:updateAnswer.excludeFilePath)||void 0===t?void 0:t.includes(c))return;fs.existsSync(s)||fs.mkdirSync(s,{recursive:!0}),fs.readdirSync(e).forEach((t=>{copyRecursiveSync(path.join(e,t),path.join(s,t),n)}))}else{if(checkExcludeFiles(s))return;if(!n.tailwindcss&&(s.includes("tailwind.css")||s.includes("styles.css")))return;if(!n.websocket&&(s.includes("restart-websocket.cjs")||s.includes("restart-websocket.bat")))return;if(!n.docker&&dockerFiles.some((e=>s.includes(e))))return;if(n.backendOnly&&nonBackendFiles.some((e=>s.includes(e))))return;if(!n.backendOnly&&s.includes("route.php"))return;if(!n.swaggerDocs&&s.includes("swagger-setup.js"))return;fs.copyFileSync(e,s,0)}}async function executeCopy(e,s,n){s.forEach((({srcDir:s,destDir:t})=>{copyRecursiveSync(path.join(__dirname,s),path.join(e,t),n)}))}function createOrUpdateTailwindConfig(e){const s=path.join(e,"tailwind.config.js");if(checkExcludeFiles(s))return;let n=fs.readFileSync(s,"utf8");const t=["./src/**/*.{html,js,php}"].map((e=>` "${e}"`)).join(",\n");n=n.replace(/content: \[\],/g,`content: [\n${t}\n],`),fs.writeFileSync(s,n,{flag:"w"})}function modifyPostcssConfig(e){const s=path.join(e,"postcss.config.js");if(checkExcludeFiles(s))return;fs.writeFileSync(s,"export default {\n plugins: {\n tailwindcss: {},\n autoprefixer: {},\n cssnano: {},\n },\n};",{flag:"w"})}function modifyLayoutPHP(e,s){const n=path.join(e,"src","app","layout.php");if(!checkExcludeFiles(n))try{let e=fs.readFileSync(n,"utf8"),t="";s.backendOnly||(t='\n <link href="<?= $baseUrl; ?>/css/index.css" rel="stylesheet">\n <script src="<?= $baseUrl; ?>/js/index.js"><\/script>');let i="";s.backendOnly||(i=s.tailwindcss?` <link href="<?= $baseUrl; ?>/css/styles.css" rel="stylesheet"> ${t}`:` <script src="https://cdn.tailwindcss.com"><\/script> ${t}`);const c=i.length>0?"\n":"";e=e.replace("</head>",`${i}${c} \x3c!-- Dynamic Head --\x3e\n <?= implode("\\n", $mainLayoutHead); ?>\n</head>`),fs.writeFileSync(n,e,{flag:"w"})}catch(e){}}async function createOrUpdateEnvFile(e,s){const n=path.join(e,".env");checkExcludeFiles(n)||fs.writeFileSync(n,s,{flag:"w"})}function checkExcludeFiles(e){var s,n;return!!(null==updateAnswer?void 0:updateAnswer.isUpdate)&&(null!==(n=null===(s=null==updateAnswer?void 0:updateAnswer.excludeFilePath)||void 0===s?void 0:s.includes(e.replace(/\\/g,"/")))&&void 0!==n&&n)}async function createDirectoryStructure(e,s){const n=[{src:"/bootstrap.php",dest:"/bootstrap.php"},{src:"/.htaccess",dest:"/.htaccess"},{src:"/../composer.json",dest:"/composer.json"}];(null==updateAnswer?void 0:updateAnswer.isUpdate)&&(n.push({src:"/tsconfig.json",dest:"/tsconfig.json"}),updateAnswer.tailwindcss&&n.push({src:"/postcss.config.js",dest:"/postcss.config.js"},{src:"/tailwind.config.js",dest:"/tailwind.config.js"}));const t=[{srcDir:"/settings",destDir:"/settings"},{srcDir:"/src",destDir:"/src"}];s.backendOnly&&s.swaggerDocs&&t.push({srcDir:"/swagger-docs-layout.php",destDir:"/src/app/layout.php"}),s.prisma&&t.push({srcDir:"/prisma",destDir:"/prisma"}),s.docker&&t.push({srcDir:"/.dockerignore",destDir:"/.dockerignore"},{srcDir:"/docker-compose.yml",destDir:"/docker-compose.yml"},{srcDir:"/Dockerfile",destDir:"/Dockerfile"},{srcDir:"/apache.conf",destDir:"/apache.conf"}),n.forEach((({src:s,dest:n})=>{const t=path.join(__dirname,s),i=path.join(e,n);if(checkExcludeFiles(i))return;const c=fs.readFileSync(t,"utf8");fs.writeFileSync(i,c,{flag:"w"})})),await executeCopy(e,t,s),await updatePackageJson(e,s),await updateComposerJson(e,s),s.backendOnly||await updateIndexJsForWebSocket(e,s),s.tailwindcss&&(createOrUpdateTailwindConfig(e),modifyPostcssConfig(e)),(s.tailwindcss||!s.backendOnly||s.swaggerDocs)&&modifyLayoutPHP(e,s);const i='# Prisma PHP Auth Secret Key For development only - Change this in production\nAUTH_SECRET=uxsjXVPHN038DEYls2Kw0QUgBcXKUyrjv416nIFWPY4= \n \n# PHPMailer\n# SMTP_HOST=smtp.gmail.com or your SMTP host\n# SMTP_USERNAME=john.doe@gmail.com or your SMTP username\n# SMTP_PASSWORD=123456\n# SMTP_PORT=587 for TLS, 465 for SSL or your SMTP port\n# SMTP_ENCRYPTION=ssl or tls\n# MAIL_FROM=john.doe@gmail.com\n# MAIL_FROM_NAME="John Doe"\n\n# SHOW ERRORS - Set to true to show errors in the browser for development only - Change this in production to false\nSHOW_ERRORS=true\n\n# ChatGPT API Key\n# CHATGPT_API_KEY=sk-your-api-key\n\n# APP TIMEZONE - Set your application timezone - Default is "UTC"\nAPP_TIMEZONE="UTC"\n\n# APP ENV - Set your application environment - Default is "development" - Change this in production to "production"\nAPP_ENV=development';if(s.prisma){const s=`${'# Environment variables declared in this file are automatically made available to Prisma.\n# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema\n\n# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.\n# See the documentation for all the connection string options: https://pris.ly/d/connection-strings\n\nDATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public"'}\n\n${i}`;await createOrUpdateEnvFile(e,s)}else await createOrUpdateEnvFile(e,i);await createUpdateGitignoreFile(e,["vendor",".env","node_modules"])}async function getAnswer(e={}){var s,n,t,i,c,o,r,a,l,p,d,u,h;const g=[];e.projectName||g.push({type:"text",name:"projectName",message:"What is your project named?",initial:"my-app"}),e.backendOnly||g.push({type:"toggle",name:"backendOnly",message:"Would you like to create a backend-only project?",initial:!1,active:"Yes",inactive:"No"});const m=()=>{process.exit(0)},f=await prompts(g,{onCancel:m}),y=[];f.backendOnly||e.backendOnly?(e.swaggerDocs||y.push({type:"toggle",name:"swaggerDocs",message:`Would you like to use ${chalk.blue("Swagger Docs")}?`,initial:!1,active:"Yes",inactive:"No"}),e.websocket||y.push({type:"toggle",name:"websocket",message:`Would you like to use ${chalk.blue("Websocket")}?`,initial:!0,active:"Yes",inactive:"No"}),e.prisma||y.push({type:"toggle",name:"prisma",message:`Would you like to use ${chalk.blue("Prisma PHP ORM")}?`,initial:!0,active:"Yes",inactive:"No"}),e.docker||y.push({type:"toggle",name:"docker",message:`Would you like to use ${chalk.blue("Docker")}?`,initial:!1,active:"Yes",inactive:"No"})):(e.swaggerDocs||y.push({type:"toggle",name:"swaggerDocs",message:`Would you like to use ${chalk.blue("Swagger Docs")}?`,initial:!1,active:"Yes",inactive:"No"}),e.tailwindcss||y.push({type:"toggle",name:"tailwindcss",message:`Would you like to use ${chalk.blue("Tailwind CSS")}?`,initial:!0,active:"Yes",inactive:"No"}),e.websocket||y.push({type:"toggle",name:"websocket",message:`Would you like to use ${chalk.blue("Websocket")}?`,initial:!0,active:"Yes",inactive:"No"}),e.prisma||y.push({type:"toggle",name:"prisma",message:`Would you like to use ${chalk.blue("Prisma PHP ORM")}?`,initial:!0,active:"Yes",inactive:"No"}),e.docker||y.push({type:"toggle",name:"docker",message:`Would you like to use ${chalk.blue("Docker")}?`,initial:!1,active:"Yes",inactive:"No"}));const w=await prompts(y,{onCancel:m});return{projectName:f.projectName?String(f.projectName).trim().replace(/ /g,"-"):null!==(s=e.projectName)&&void 0!==s?s:"my-app",backendOnly:null!==(t=null!==(n=f.backendOnly)&&void 0!==n?n:e.backendOnly)&&void 0!==t&&t,swaggerDocs:null!==(c=null!==(i=w.swaggerDocs)&&void 0!==i?i:e.swaggerDocs)&&void 0!==c&&c,tailwindcss:null!==(r=null!==(o=w.tailwindcss)&&void 0!==o?o:e.tailwindcss)&&void 0!==r&&r,websocket:null!==(l=null!==(a=w.websocket)&&void 0!==a?a:e.websocket)&&void 0!==l&&l,prisma:null!==(d=null!==(p=w.prisma)&&void 0!==p?p:e.prisma)&&void 0!==d&&d,docker:null!==(h=null!==(u=w.docker)&&void 0!==u?u:e.docker)&&void 0!==h&&h}}async function installDependencies(e,s,n=!1){fs.existsSync(path.join(e,"package.json"))||execSync("npm init -y",{stdio:"inherit",cwd:e}),s.forEach((e=>{}));const t=`npm install ${n?"--save-dev":""} ${s.join(" ")}`;execSync(t,{stdio:"inherit",cwd:e})}async function uninstallDependencies(e,s,n=!1){s.forEach((e=>{}));const t=`npm uninstall ${n?"--save-dev":"--save"} ${s.join(" ")}`;execSync(t,{stdio:"inherit",cwd:e})}function fetchPackageVersion(e){return new Promise(((s,n)=>{https.get(`https://registry.npmjs.org/${e}`,(e=>{let t="";e.on("data",(e=>t+=e)),e.on("end",(()=>{try{const e=JSON.parse(t);s(e["dist-tags"].latest)}catch(e){n(new Error("Failed to parse JSON response"))}}))})).on("error",(e=>n(e)))}))}const readJsonFile=e=>{const s=fs.readFileSync(e,"utf8");return JSON.parse(s)};function compareVersions(e,s){const n=e.split(".").map(Number),t=s.split(".").map(Number);for(let e=0;e<n.length;e++){if(n[e]>t[e])return 1;if(n[e]<t[e])return-1}return 0}function getInstalledPackageVersion(e){try{const s=execSync(`npm list -g ${e} --depth=0`).toString().match(new RegExp(`${e}@(\\d+\\.\\d+\\.\\d+)`));return s?s[1]:null}catch(e){return null}}async function main(){var e,s,n,t,i,c,o,r,a;try{const l=process.argv.slice(2);let p=l[0],d=null;if(p){const a={projectName:p,backendOnly:l.includes("--backend-only"),swaggerDocs:l.includes("--swagger-docs"),tailwindcss:l.includes("--tailwindcss"),websocket:l.includes("--websocket"),prisma:l.includes("--prisma"),docker:l.includes("--docker")};if(d=await getAnswer(a),null===d)return;const u=process.cwd(),h=path.join(u,"prisma-php.json"),g=readJsonFile(h);let m=[];null===(e=g.excludeFiles)||void 0===e||e.map((e=>{const s=path.join(u,e);fs.existsSync(s)&&m.push(s.replace(/\\/g,"/"))})),updateAnswer={projectName:p,backendOnly:null!==(s=null==d?void 0:d.backendOnly)&&void 0!==s&&s,swaggerDocs:null!==(n=null==d?void 0:d.swaggerDocs)&&void 0!==n&&n,tailwindcss:null!==(t=null==d?void 0:d.tailwindcss)&&void 0!==t&&t,websocket:null!==(i=null==d?void 0:d.websocket)&&void 0!==i&&i,prisma:null!==(c=null==d?void 0:d.prisma)&&void 0!==c&&c,docker:null!==(o=null==d?void 0:d.docker)&&void 0!==o&&o,isUpdate:!0,excludeFiles:null!==(r=g.excludeFiles)&&void 0!==r?r:[],excludeFilePath:null!=m?m:[],filePath:u}}else d=await getAnswer();if(null===d)return;const u=await fetchPackageVersion("create-prisma-php-app"),h=getInstalledPackageVersion("create-prisma-php-app");h?-1===compareVersions(h,u)&&(execSync("npm uninstall -g create-prisma-php-app",{stdio:"inherit"}),execSync("npm install -g create-prisma-php-app",{stdio:"inherit"})):execSync("npm install -g create-prisma-php-app",{stdio:"inherit"});const g=await fetchPackageVersion("browser-sync"),m=getInstalledPackageVersion("browser-sync");m?-1===compareVersions(m,g)&&(execSync("npm uninstall -g browser-sync",{stdio:"inherit"}),execSync("npm install -g browser-sync",{stdio:"inherit"})):execSync("npm install -g browser-sync",{stdio:"inherit"}),p||fs.mkdirSync(d.projectName);const f=process.cwd();let y=p?f:path.join(f,d.projectName);p||process.chdir(d.projectName);const w=["typescript","@types/node","ts-node","http-proxy-middleware@^3.0.0","chalk","npm-run-all"];d.swaggerDocs&&w.push("swagger-jsdoc"),d.tailwindcss&&w.push("tailwindcss","autoprefixer","postcss","postcss-cli","cssnano"),d.websocket&&w.push("chokidar-cli"),d.prisma&&w.push("prisma","@prisma/client"),await installDependencies(y,w,!0),p||execSync("npx tsc --init",{stdio:"inherit"}),d.tailwindcss&&execSync("npx tailwindcss init -p",{stdio:"inherit"}),d.prisma&&(fs.existsSync(path.join(y,"prisma"))||execSync("npx prisma init",{stdio:"inherit"})),await createDirectoryStructure(y,d);const k=path.join(y,"public");if(fs.existsSync(k)||fs.mkdirSync(k),d.swaggerDocs){const e=path.join(y,"src","app","swagger-docs");fs.existsSync(e)||fs.mkdirSync(e,{recursive:!0}),execSync(`git clone https://github.com/TheSteelNinjaCode/prisma-php-swagger-docs.git ${e}`,{stdio:"inherit"})}if(null==updateAnswer?void 0:updateAnswer.isUpdate){const e=[];if(updateAnswer.backendOnly){nonBackendFiles.forEach((e=>{const s=path.join(y,"src","app",e);fs.existsSync(s)&&fs.unlinkSync(s)}));["js","css"].forEach((e=>{const s=path.join(y,"src","app",e);fs.existsSync(s)&&fs.rmSync(s,{recursive:!0,force:!0})}))}if(!updateAnswer.swaggerDocs){const s=path.join(y,"src","app","swagger-docs");fs.existsSync(s)&&fs.rmSync(s,{recursive:!0,force:!0});["swagger-setup.js"].forEach((e=>{const s=path.join(y,"settings",e);fs.existsSync(s)&&fs.unlinkSync(s)})),e.push("swagger-jsdoc")}if(!updateAnswer.tailwindcss){["postcss.config.js","tailwind.config.js"].forEach((e=>{const s=path.join(y,e);fs.existsSync(s)&&fs.unlinkSync(s)})),e.push("tailwindcss","autoprefixer","postcss","postcss-cli","cssnano")}if(updateAnswer.websocket||e.push("chokidar-cli"),updateAnswer.prisma||e.push("prisma","@prisma/client"),!updateAnswer.docker){[".dockerignore","docker-compose.yml","Dockerfile","apache.conf"].forEach((e=>{const s=path.join(y,e);fs.existsSync(s)&&fs.unlinkSync(s)}))}e.length>0&&await uninstallDependencies(y,e,!0)}const S=y.replace(/\\/g,"\\"),b=bsConfigUrls(S),j=d.prisma?"src/Lib/Prisma/Classes":"",v={projectName:d.projectName,projectRootPath:S,phpEnvironment:"XAMPP",phpRootPathExe:"C:\\xampp\\php\\php.exe",phpGenerateClassPath:j,bsTarget:b.bsTarget,bsPathRewrite:b.bsPathRewrite,backendOnly:d.backendOnly,swaggerDocs:d.swaggerDocs,tailwindcss:d.tailwindcss,websocket:d.websocket,prisma:d.prisma,docker:d.docker,version:u,excludeFiles:null!==(a=null==updateAnswer?void 0:updateAnswer.excludeFiles)&&void 0!==a?a:[]};fs.writeFileSync(path.join(y,"prisma-php.json"),JSON.stringify(v,null,2),{flag:"w"}),(null==updateAnswer?void 0:updateAnswer.isUpdate)?execSync("C:\\xampp\\php\\php.exe C:\\ProgramData\\ComposerSetup\\bin\\composer.phar update",{stdio:"inherit"}):execSync("C:\\xampp\\php\\php.exe C:\\ProgramData\\ComposerSetup\\bin\\composer.phar install",{stdio:"inherit"})}catch(e){process.exit(1)}}main();
@@ -5,4 +5,5 @@ module.exports = {
5
5
  notify: false,
6
6
  open: false,
7
7
  ghostMode: false,
8
+ codeSync: true, // Disable synchronization of code changes across clients
8
9
  };
@@ -0,0 +1,61 @@
1
+ import { readdirSync, statSync, existsSync, writeFileSync } from "fs";
2
+ import path, { dirname } from "path";
3
+ import { fileURLToPath } from "url";
4
+
5
+ // Define __dirname equivalent in ES modules
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = dirname(__filename);
8
+
9
+ const dirPath = path.join(__dirname, "..", "src", "app");
10
+ const jsonFilePath = path.join(__dirname, "files-list.json");
11
+
12
+ // Function to get all files in the directory
13
+ const getAllFiles = (dirPath) => {
14
+ const files = [];
15
+
16
+ // Check if directory exists before reading
17
+ if (!existsSync(dirPath)) {
18
+ console.error(`Directory not found: ${dirPath}`);
19
+ return files; // Return an empty array if the directory doesn't exist
20
+ }
21
+
22
+ const items = readdirSync(dirPath);
23
+ items.forEach((item) => {
24
+ const fullPath = path.join(dirPath, item);
25
+ if (statSync(fullPath).isDirectory()) {
26
+ files.push(...getAllFiles(fullPath)); // Recursive call for subdirectories
27
+ } else {
28
+ // Generate the relative path and ensure it starts with ./src
29
+ const relativePath = `.${path.sep}${path.relative(
30
+ path.join(__dirname, ".."),
31
+ fullPath
32
+ )}`;
33
+ // Replace only the root backslashes with forward slashes and leave inner ones
34
+ files.push(relativePath.replace(/\\/g, "\\").replace(/^\.\.\//, ""));
35
+ }
36
+ });
37
+
38
+ return files;
39
+ };
40
+
41
+ // Function to generate the files-list.json
42
+ const generateFileListJson = () => {
43
+ const files = getAllFiles(dirPath);
44
+
45
+ // If files exist, generate JSON file
46
+ if (files.length > 0) {
47
+ writeFileSync(jsonFilePath, JSON.stringify(files, null, 2));
48
+ console.log(
49
+ `File list has been saved to: settings/files-list.json`
50
+ );
51
+ } else {
52
+ console.error("No files found to save in the JSON file.");
53
+ }
54
+ };
55
+
56
+ // Main function
57
+ async function processDirectory() {
58
+ generateFileListJson();
59
+ }
60
+
61
+ processDirectory();
@@ -126,7 +126,7 @@ async function startDev() {
126
126
 
127
127
  if (!browserSyncReady) {
128
128
  browserSyncReady = true;
129
- // Start _dev after browserSync is ready
129
+ // Start npmRunAll after browserSync is ready
130
130
  startDevProcess();
131
131
  }
132
132
  } else if (REGEX_PATTERNS.BROWSERSYNC.test(line)) {
@@ -148,10 +148,10 @@ async function startDev() {
148
148
  console.error(`Failed to start browserSync process: ${err.message}`);
149
149
  });
150
150
 
151
- // Function to start _dev process
151
+ // Function to start npmRunAll process
152
152
  function startDevProcess() {
153
- console.log("Starting _dev...");
154
- devProcess = spawn("npm", ["run", "_dev"], { shell: true });
153
+ console.log("Starting npmRunAll...");
154
+ devProcess = spawn("npm", ["run", "npmRunAll"], { shell: true });
155
155
 
156
156
  devProcess.stdout.on("data", (data) => {
157
157
  process.stdout.write(data);
@@ -72,6 +72,19 @@ class Boom
72
72
  return new self(401, $message, $details);
73
73
  }
74
74
 
75
+ /**
76
+ * Factory method for 402 Payment Required.
77
+ *
78
+ * @param string $message Error message.
79
+ * @param array $details Additional error details.
80
+ *
81
+ * @return self
82
+ */
83
+ public static function paymentRequired(string $message = 'Payment Required', array $details = []): self
84
+ {
85
+ return new self(402, $message, $details);
86
+ }
87
+
75
88
  /**
76
89
  * Factory method for 403 Forbidden.
77
90
  *
@@ -98,6 +111,32 @@ class Boom
98
111
  return new self(404, $message, $details);
99
112
  }
100
113
 
114
+ /**
115
+ * Factory method for 405 Method Not Allowed.
116
+ *
117
+ * @param string $message Error message.
118
+ * @param array $details Additional error details.
119
+ *
120
+ * @return self
121
+ */
122
+ public static function methodNotAllowed(string $message = 'Method Not Allowed', array $details = []): self
123
+ {
124
+ return new self(405, $message, $details);
125
+ }
126
+
127
+ /**
128
+ * Factory method for 406 Not Acceptable.
129
+ *
130
+ * @param string $message Error message.
131
+ * @param array $details Additional error details.
132
+ *
133
+ * @return self
134
+ */
135
+ public static function notAcceptable(string $message = 'Not Acceptable', array $details = []): self
136
+ {
137
+ return new self(406, $message, $details);
138
+ }
139
+
101
140
  /**
102
141
  * Factory method for 500 Internal Server Error.
103
142
  *
@@ -6,7 +6,7 @@
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
7
  <meta name="pp-description" content="<?= htmlspecialchars($metadata['description']); ?>">
8
8
  <title><?= htmlspecialchars($metadata['title']); ?></title>
9
- <link rel="icon" href="<?= $baseUrl; ?>\favicon.ico" type="image/x-icon">
9
+ <link rel="icon" href="<?= $baseUrl; ?>/favicon.ico" type="image/x-icon">
10
10
  </head>
11
11
 
12
12
  <body>
@@ -1,11 +1,8 @@
1
1
  /** @type {import('tailwindcss').Config} */
2
- export default {
3
- content: [
4
- "./src/app/**/*.{html,js,php}"
5
- ],
2
+ module.exports = {
3
+ content: ["./src/**/*.{html,js,php}"],
6
4
  theme: {
7
5
  extend: {},
8
6
  },
9
7
  plugins: [],
10
- }
11
-
8
+ };
@@ -11,7 +11,7 @@
11
11
  // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
12
12
 
13
13
  /* Language and Environment */
14
- "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
14
+ "target": "ESNext" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
15
15
  // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
16
16
  // "jsx": "preserve", /* Specify what JSX code is generated. */
17
17
  // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
@@ -25,7 +25,7 @@
25
25
  // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
26
26
 
27
27
  /* Modules */
28
- "module": "commonjs", /* Specify what module code is generated. */
28
+ "module": "NodeNext" /* Specify what module code is generated. */,
29
29
  // "rootDir": "./", /* Specify the root folder within your source files. */
30
30
  // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
31
31
  // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
@@ -39,7 +39,7 @@
39
39
  // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
40
40
  // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
41
41
  // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
42
- // "resolveJsonModule": true, /* Enable importing .json files. */
42
+ "resolveJsonModule": true /* Enable importing .json files. */,
43
43
  // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
44
44
  // "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
45
45
 
@@ -77,12 +77,12 @@
77
77
  // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
78
78
  // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
79
79
  // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
80
- "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
80
+ "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
81
81
  // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
82
- "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
82
+ "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
83
83
 
84
84
  /* Type Checking */
85
- "strict": true, /* Enable all strict type-checking options. */
85
+ "strict": true /* Enable all strict type-checking options. */,
86
86
  // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
87
87
  // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
88
88
  // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
@@ -104,8 +104,8 @@
104
104
 
105
105
  /* Completeness */
106
106
  // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
107
- "skipLibCheck": true /* Skip type checking all .d.ts files. */
107
+ "skipLibCheck": true /* Skip type checking all .d.ts files. */
108
108
  },
109
- "include": ["src/**/*.ts"],
110
- "exclude": ["node_modules"]
109
+ "include": ["src/**/*.ts", "settings/**/*.ts"],
110
+ "exclude": ["node_modules", "vendor"]
111
111
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-prisma-php-app",
3
- "version": "1.22.0",
3
+ "version": "1.22.1",
4
4
  "description": "Prisma-PHP: A Revolutionary Library Bridging PHP with Prisma ORM",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",