create-prisma-php-app 1.8.20 → 1.9.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.
package/dist/.htaccess CHANGED
@@ -10,9 +10,20 @@ RewriteEngine On
10
10
  # Allow cross-origin requests (CORS) for all routes
11
11
  <IfModule mod_headers.c>
12
12
  Header set Access-Control-Allow-Origin "*"
13
- Header set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
13
+ Header set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS"
14
14
  Header set Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With"
15
15
  </IfModule>
16
16
 
17
+ # Exclude OPTIONS method from being redirected
18
+ RewriteCond %{REQUEST_METHOD} !OPTIONS
19
+
20
+ # Redirect all AJAX requests to bootstrap-ajax.php
21
+ RewriteCond %{HTTP:X-Requested-With} ^XMLHttpRequest$
22
+ RewriteRule ^(.*)$ bootstrap-ajax.php [L,QSA]
23
+
17
24
  # Redirect all non-file and non-directory requests not starting with src/app/ to src/app/layout.php
18
25
  RewriteRule !^src/app/ src/app/layout.php [QSA,L]
26
+
27
+ # Add this to ensure OPTIONS requests are handled correctly
28
+ RewriteCond %{REQUEST_METHOD} OPTIONS
29
+ RewriteRule ^(.*)$ $1 [R=200,L]
@@ -0,0 +1,141 @@
1
+ <?php
2
+
3
+ header('Content-Type: application/json');
4
+
5
+ if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
6
+ header('HTTP/1.1 200 OK');
7
+ exit;
8
+ }
9
+
10
+ if (empty($_SERVER["HTTP_X_REQUESTED_WITH"]) || $_SERVER["HTTP_X_REQUESTED_WITH"] != "XMLHttpRequest") {
11
+ header("HTTP/1.0 400 Bad Request");
12
+ echo json_encode(["error" => "Invalid request method or type."]);
13
+ exit;
14
+ }
15
+
16
+ // Determine the request method
17
+ $requestMethod = $_SERVER['REQUEST_METHOD'];
18
+ $allowedMethods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'];
19
+
20
+ if (!in_array($requestMethod, $allowedMethods)) {
21
+ echo "Method not allowed\n";
22
+ header("HTTP/1.1 405 Method Not Allowed");
23
+ exit;
24
+ }
25
+
26
+ $isGet = $requestMethod === 'GET';
27
+ $isPost = $requestMethod === 'POST';
28
+ $isPut = $requestMethod === 'PUT';
29
+ $isDelete = $requestMethod === 'DELETE';
30
+ $isHead = $requestMethod === 'HEAD';
31
+ $isOptions = $requestMethod === 'OPTIONS';
32
+ $contentType = $_SERVER['CONTENT_TYPE'] ?? '';
33
+ $params = [];
34
+
35
+ if (stripos($contentType, 'application/json') !== false) {
36
+ $jsonInput = file_get_contents('php://input');
37
+ if (!empty($jsonInput)) {
38
+ $data = json_decode($jsonInput, true);
39
+ if (json_last_error() === JSON_ERROR_NONE) {
40
+ $params = $data;
41
+ } else {
42
+ echo json_encode(['error' => 'Error: Invalid JSON body!']);
43
+ exit;
44
+ }
45
+ }
46
+ }
47
+
48
+ $scriptUrl = $_SERVER['REQUEST_URI'];
49
+ $scriptUrl = explode('?', $scriptUrl, 2)[0];
50
+ $uri = $_SERVER['SCRIPT_URL'] ?? uriExtractor($scriptUrl);
51
+
52
+ // Check for private directory access
53
+ if (strpos($uri, '/_') !== false || strpos($uri, '_') === 0) {
54
+ handleNotFound();
55
+ }
56
+
57
+ $baseDir = __DIR__;
58
+
59
+ require_once $baseDir . "/settings/paths.php";
60
+ require_once $baseDir . "/vendor/autoload.php";
61
+
62
+ $groupFolder = findGroupFolder($uri);
63
+ $filePath = $baseDir . '/src/app' . $groupFolder;
64
+
65
+ if (basename($filePath) === 'route.php' && file_exists($filePath)) {
66
+ require_once($filePath);
67
+ } else {
68
+ handleNotFound();
69
+ }
70
+
71
+ function uriExtractor(string $scriptUrl): string
72
+ {
73
+ $prismaPHPSettings = json_decode(file_get_contents("prisma-php.json"), true);
74
+ $projectName = $prismaPHPSettings['projectName'] ?? '';
75
+ if (empty($projectName)) {
76
+ return "/";
77
+ }
78
+
79
+ $escapedIdentifier = preg_quote($projectName, '/');
80
+ $pattern = "/(?:{$escapedIdentifier})(\/.+?)(?=\/{$escapedIdentifier}\/|$)/";
81
+
82
+ if (preg_match_all($pattern, $scriptUrl, $matches, PREG_SET_ORDER)) {
83
+ $lastMatch = end($matches);
84
+ if (!empty($lastMatch[1])) {
85
+ return $lastMatch[1];
86
+ }
87
+ }
88
+
89
+ return "/";
90
+ }
91
+
92
+ function handleNotFound()
93
+ {
94
+ header("HTTP/1.0 404 Not Found");
95
+ echo json_encode(['error' => 'Not Found', 'message' => 'The requested resource was not found.']);
96
+ exit;
97
+ }
98
+
99
+ function findGroupFolder($uri): string
100
+ {
101
+ $uriSegments = explode('/', $uri);
102
+ foreach ($uriSegments as $segment) {
103
+ if (!empty($segment)) {
104
+ if (isGroupIdentifier($segment)) {
105
+ return $segment;
106
+ }
107
+ }
108
+ }
109
+
110
+ $matchedGroupFolder = matchGroupFolder($uri);
111
+ if ($matchedGroupFolder) {
112
+ return $matchedGroupFolder;
113
+ } else {
114
+ return '';
115
+ }
116
+ }
117
+
118
+ function isGroupIdentifier($segment): bool
119
+ {
120
+ return preg_match('/^\(.*\)$/', $segment);
121
+ }
122
+
123
+ function matchGroupFolder($constructedPath): ?string
124
+ {
125
+ $routes = json_decode(file_get_contents(SETTINGS_PATH . "/files-list.json"), true);
126
+ $bestMatch = null;
127
+ $normalizedConstructedPath = ltrim(str_replace('\\', '/', $constructedPath), './');
128
+
129
+ $normalizedConstructedPath = "/$normalizedConstructedPath/route.php";
130
+
131
+ foreach ($routes as $route) {
132
+ $normalizedRoute = trim(str_replace('\\', '/', $route), '.');
133
+ $cleanedRoute = preg_replace('/\/\([^)]+\)/', '', $normalizedRoute);
134
+ if ($cleanedRoute === $normalizedConstructedPath) {
135
+ $bestMatch = $normalizedRoute;
136
+ break;
137
+ }
138
+ }
139
+
140
+ return $bestMatch;
141
+ }
@@ -7,7 +7,7 @@ if (session_status() == PHP_SESSION_NONE) {
7
7
  require_once __DIR__ . '/settings/paths.php';
8
8
  require_once __DIR__ . '/vendor/autoload.php';
9
9
 
10
- $metadata = include __DIR__ . '/src/app/metadata.php';
10
+ $metadata = require_once __DIR__ . '/src/app/metadata.php';
11
11
 
12
12
  function determineContentToInclude()
13
13
  {
@@ -40,13 +40,6 @@ function determineContentToInclude()
40
40
  }
41
41
  }
42
42
 
43
- if (substr($uri, -4) == '.php') {
44
- $path = $baseDir . '/' . $uri;
45
- if (file_exists($path)) {
46
- $includePath = $path;
47
- }
48
- }
49
-
50
43
  $currentPath = $baseDir;
51
44
  $getGroupFolder = getGroupFolder($groupFolder);
52
45
  $modifiedUri = $uri;
@@ -153,10 +146,7 @@ function matchGroupFolder($constructedPath): ?string
153
146
  $routes = json_decode(file_get_contents(SETTINGS_PATH . "/files-list.json"), true);
154
147
  $bestMatch = null;
155
148
  $normalizedConstructedPath = ltrim(str_replace('\\', '/', $constructedPath), './');
156
-
157
- if (substr($normalizedConstructedPath, -4) !== '.php') {
158
- $normalizedConstructedPath = "/$normalizedConstructedPath/index.php";
159
- }
149
+ $normalizedConstructedPath = "/$normalizedConstructedPath/index.php";
160
150
 
161
151
  foreach ($routes as $route) {
162
152
  $normalizedRoute = trim(str_replace('\\', '/', $route), '.');
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;function configureBrowserSyncCommand(e,s){const t=s.PROJECT_ROOT_PATH.indexOf("\\htdocs\\");if(-1===t)return"";const n=s.PROJECT_ROOT_PATH.substring(0,t+"\\htdocs\\".length).replace(/\\/g,"\\\\"),i=s.PROJECT_ROOT_PATH.replace(new RegExp(`^${n}`),"").replace(/\\/g,"/");let c=`http://localhost/${i}`;c=c.endsWith("/")?c.slice(0,-1):c;const r=c.replace(/(?<!:)(\/\/+)/g,"/"),o=i.replace(/\/\/+/g,"/"),a=`\n const { createProxyMiddleware } = require("http-proxy-middleware");\n\n module.exports = {\n // First middleware: Set Cache-Control headers\n function (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 // Use the 'middleware' option to create a proxy that masks the deep URL.\n middleware: [\n // This middleware intercepts requests to the root and proxies them to the deep path.\n createProxyMiddleware("/", {\n target:\n "${r}/",\n changeOrigin: true,\n pathRewrite: {\n "^/": "/${o.startsWith("/")?o.substring(1):o}/", // Rewrite the path.\n },\n }),\n ],\n proxy: "http://localhost:3000", // Proxy the BrowserSync server.\n files: "src/**/*.*",\n notify: false,\n open: false,\n ghostMode: false,\n };`,l=path.join(e,"settings","bs-config.cjs");return fs.writeFileSync(l,a,"utf8"),"browser-sync start --config settings/bs-config.cjs"}async function updatePackageJson(e,s,t){const n=path.join(e,"package.json");if(checkExcludeFiles(n))return;const i=JSON.parse(fs.readFileSync(n,"utf8")),c=configureBrowserSyncCommand(e,s);i.scripts=Object.assign(Object.assign({},i.scripts),{postinstall:"prisma generate"});let r=[];t.tailwindcss&&(i.scripts=Object.assign(Object.assign({},i.scripts),{tailwind:"postcss ./src/app/css/tailwind.css -o ./src/app/css/styles.css --watch"}),r.push("tailwind")),t.websocket&&(i.scripts=Object.assign(Object.assign({},i.scripts),{websocket:"node ./settings/restartWebsocket.cjs"}),r.push("websocket"));const o=Object.assign({},i.scripts);r.length>0&&(o["browser-sync"]=c),o.dev=r.length>0?`npm-run-all --parallel browser-sync ${r.join(" ")}`:c,i.scripts=o,i.type="module",i.prisma={seed:"node prisma/seed.js"},fs.writeFileSync(n,JSON.stringify(i,null,2))}async function updateComposerJson(e,s){if(!s.websocket)return;const t=path.join(e,"composer.json");if(checkExcludeFiles(t))return;let n;if(fs.existsSync(t)){{const e=fs.readFileSync(t,"utf8");n=JSON.parse(e)}s.websocket&&(n.require=Object.assign(Object.assign({},n.require),{"cboden/ratchet":"^0.4.4"})),fs.writeFileSync(t,JSON.stringify(n,null,2))}}async function updateIndexJsForWebSocket(e,s){if(!s.websocket)return;const t=path.join(e,"src","app","js","index.js");if(checkExcludeFiles(t))return;let n=fs.readFileSync(t,"utf8");n+='\n// WebSocket initialization\nconst ws = new WebSocket("ws://localhost:8080");\n',fs.writeFileSync(t,n,"utf8")}async function createUpdateGitignoreFile(e,s){const t=path.join(e,".gitignore");if(checkExcludeFiles(t))return;let n="";fs.existsSync(t)&&(n=fs.readFileSync(t,"utf8")),s.forEach((e=>{n.includes(e)||(n+=`\n${e}`)})),n=n.trimStart(),fs.writeFileSync(t,n)}function copyRecursiveSync(e,s){const t=fs.existsSync(e),n=t&&fs.statSync(e);if(t&&n&&n.isDirectory())fs.mkdirSync(s,{recursive:!0}),fs.readdirSync(e).forEach((t=>{copyRecursiveSync(path.join(e,t),path.join(s,t))}));else{if(checkExcludeFiles(s))return;fs.copyFileSync(e,s,0)}}async function executeCopy(e,s){s.forEach((({srcDir:s,destDir:t})=>{copyRecursiveSync(path.join(__dirname,s),path.join(e,t))}))}function createOrUpdateTailwindConfig(e){const s=path.join(e,"tailwind.config.js");if(checkExcludeFiles(s))return;let t=fs.readFileSync(s,"utf8");const n=["./src/app/**/*.{html,js,php}"].map((e=>` "${e}"`)).join(",\n");t=t.replace(/content: \[\],/g,`content: [\n${n}\n],`),fs.writeFileSync(s,t,{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 t=path.join(e,"src","app","layout.php");if(!checkExcludeFiles(t))try{let e=fs.readFileSync(t,"utf8");const n='\n <link href="<?php echo $baseUrl; ?>css/index.css" rel="stylesheet">\n <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;800&display=swap" rel="stylesheet">\n <script src="<?php echo $baseUrl; ?>js/index.js"><\/script>',i=s?` <link href="<?php echo $baseUrl; ?>css/styles.css" rel="stylesheet"> ${n}`:` <script src="https://cdn.tailwindcss.com"><\/script> ${n}`;e=e.replace("</head>",`${i}\n</head>`),fs.writeFileSync(t,e,{flag:"w"})}catch(e){}}async function createOrUpdateEnvFile(e,s){const t=path.join(e,".env");if(checkExcludeFiles(t))return;let n=fs.existsSync(t)?fs.readFileSync(t,"utf8"):"";n.includes(s)||(n+=`${""!==n?"\n\n":""}${s}`,fs.writeFileSync(t,n,{flag:"w"}))}function checkExcludeFiles(e){var s,t;return!!(null==updateAnswer?void 0:updateAnswer.isUpdate)&&(null!==(t=null===(s=null==updateAnswer?void 0:updateAnswer.excludeFilePath)||void 0===s?void 0:s.includes(e.replace(/\\/g,"/")))&&void 0!==t&&t)}async function createDirectoryStructure(e,s,t){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:"/.env",dest:"/.env"},{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"}));n.forEach((({src:s,dest:t})=>{const n=path.join(__dirname,s),i=path.join(e,t);if(checkExcludeFiles(i))return;const c=fs.readFileSync(n,"utf8");fs.writeFileSync(i,c,{flag:"w"})})),await executeCopy(e,[{srcDir:"/settings",destDir:"/settings"},{srcDir:"/prisma",destDir:"/prisma"},{srcDir:"/src",destDir:"/src"}]),await updatePackageJson(e,t,s),await updateComposerJson(e,s),await updateIndexJsForWebSocket(e,s),s.tailwindcss?(createOrUpdateTailwindConfig(e),modifyLayoutPHP(e,!0),modifyPostcssConfig(e)):modifyLayoutPHP(e,!1);await createOrUpdateEnvFile(e,'# 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"'),await createUpdateGitignoreFile(e,["vendor"])}async function getAnswer(e={}){var s,t,n;const i=[];e.projectName||i.push({type:"text",name:"projectName",message:"What is your project named?",initial:"my-app"}),e.tailwindcss||i.push({type:"toggle",name:"tailwindcss",message:`Would you like to use ${chalk.blue("Tailwind CSS")}?`,initial:!0,active:"Yes",inactive:"No"}),e.websocket||i.push({type:"toggle",name:"websocket",message:`Would you like to use ${chalk.blue("Websocket")}?`,initial:!0,active:"Yes",inactive:"No"});const c=i,r=()=>{process.exit(0)};try{const i=await prompts(c,{onCancel:r});return 0===Object.keys(i).length?null:{projectName:i.projectName?String(i.projectName).trim().replace(/ /g,"-"):null!==(s=e.projectName)&&void 0!==s?s:"my-app",tailwindcss:null!==(t=i.tailwindcss)&&void 0!==t?t:e.tailwindcss,websocket:null!==(n=i.websocket)&&void 0!==n?n:e.websocket}}catch(e){return null}}async function installDependencies(e,s,t=!1){fs.existsSync(path.join(e,"package.json"))||execSync("npm init -y",{stdio:"inherit",cwd:e}),s.forEach((e=>{}));const n=`npm install ${t?"--save-dev":""} ${s.join(" ")}`;execSync(n,{stdio:"inherit",cwd:e})}async function uninstallDependencies(e,s,t=!1){s.forEach((e=>{}));const n=`npm uninstall ${t?"--save-dev":"--save"} ${s.join(" ")}`;execSync(n,{stdio:"inherit",cwd:e})}function fetchPackageVersion(e){return new Promise(((s,t)=>{https.get(`https://registry.npmjs.org/${e}`,(e=>{let n="";e.on("data",(e=>n+=e)),e.on("end",(()=>{try{const e=JSON.parse(n);s(e["dist-tags"].latest)}catch(e){t(new Error("Failed to parse JSON response"))}}))})).on("error",(e=>t(e)))}))}const readJsonFile=e=>{const s=fs.readFileSync(e,"utf8");return JSON.parse(s)};async function main(){var e,s,t,n,i;try{const c=process.argv.slice(2);let r=c[0],o=null;if(r){const i={projectName:r,tailwindcss:c.includes("--tailwindcss"),websocket:c.includes("--websocket")};if(o=await getAnswer(i),null===o)return;const a=process.cwd(),l=path.join(a,"prisma-php.json"),p=readJsonFile(l);let d=[];null===(e=p.excludeFiles)||void 0===e||e.map((e=>{const s=path.join(a,e);fs.existsSync(s)&&d.push(s.replace(/\\/g,"/"))})),updateAnswer={projectName:r,tailwindcss:null!==(s=null==o?void 0:o.tailwindcss)&&void 0!==s&&s,websocket:null!==(t=null==o?void 0:o.websocket)&&void 0!==t&&t,isUpdate:!0,excludeFiles:null!==(n=p.excludeFiles)&&void 0!==n?n:[],excludeFilePath:null!=d?d:[],filePath:a}}else o=await getAnswer();if(null===o)return;execSync("npm install -g create-prisma-php-app",{stdio:"inherit"}),execSync("npm install -g browser-sync",{stdio:"inherit"}),r||fs.mkdirSync(o.projectName);const a=process.cwd();let l=r?a:path.join(a,o.projectName);r||process.chdir(o.projectName);const p=["prisma","@prisma/client","typescript","@types/node","ts-node","http-proxy-middleware"];o.tailwindcss&&p.push("tailwindcss","autoprefixer","postcss","postcss-cli","cssnano"),o.websocket&&p.push("chokidar-cli"),(o.tailwindcss||o.websocket)&&p.push("npm-run-all"),await installDependencies(l,p,!0),r||(execSync("npx prisma init",{stdio:"inherit"}),execSync("npx tsc --init",{stdio:"inherit"})),o.tailwindcss&&execSync("npx tailwindcss init -p",{stdio:"inherit"});const d={PROJECT_NAME:o.projectName,PROJECT_ROOT_PATH:l.replace(/\\/g,"\\\\"),PHP_ROOT_PATH_EXE:"D:\\\\xampp\\\\php\\\\php.exe",PHP_GENERATE_CLASS_PATH:"src/Lib/Prisma/Classes"};await createDirectoryStructure(l,o,d);const u=path.join(l,"settings","project-settings.js"),f=`export const projectSettings = {\n PROJECT_NAME: "${o.projectName}",\n PROJECT_ROOT_PATH: "${l.replace(/\\/g,"\\\\")}",\n PHP_ROOT_PATH_EXE: "D:\\\\xampp\\\\php\\\\php.exe",\n PHP_GENERATE_CLASS_PATH: "src/Lib/Prisma/Classes",\n };`;fs.writeFileSync(u,f,{flag:"w"});const h=path.join(l,"public");if(fs.existsSync(h)||fs.mkdirSync(h),!o.tailwindcss){const e=path.join(l,"src","app","css");["tailwind.css","styles.css"].forEach((s=>{const t=path.join(e,s);fs.existsSync(t)&&fs.unlinkSync(t)}))}if(!o.websocket){const e=path.join(l,"src","lib","websocket");fs.existsSync(e)&&fs.rmSync(e,{recursive:!0,force:!0});const s=path.join(l,"settings");["restartWebsocket.cjs","restart_websocket.bat"].forEach((e=>{const t=path.join(s,e);fs.existsSync(t)&&fs.unlinkSync(t)}))}if(null==updateAnswer?void 0:updateAnswer.isUpdate){const e=[];if(!updateAnswer.tailwindcss){["postcss.config.js","tailwind.config.js"].forEach((e=>{const s=path.join(l,e);fs.existsSync(s)&&fs.unlinkSync(s)})),e.push("tailwindcss","autoprefixer","postcss","postcss-cli","cssnano")}updateAnswer.websocket||e.push("chokidar-cli"),e.length>0&&await uninstallDependencies(l,e,!0)}const w=await fetchPackageVersion("create-prisma-php-app"),y={projectName:o.projectName,tailwindcss:o.tailwindcss,websocket:o.websocket,version:w,excludeFiles:null!==(i=null==updateAnswer?void 0:updateAnswer.excludeFiles)&&void 0!==i?i:[]};fs.writeFileSync(path.join(l,"prisma-php.json"),JSON.stringify(y,null,2),{flag:"w"})}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;function bsConfigUrls(e){const s=e.PROJECT_ROOT_PATH.indexOf("\\htdocs\\");if(-1===s)return{bsTarget:"",bsPathRewrite:{}};const t=e.PROJECT_ROOT_PATH.substring(0,s+"\\htdocs\\".length).replace(/\\/g,"\\\\"),n=e.PROJECT_ROOT_PATH.replace(new RegExp(`^${t}`),"").replace(/\\/g,"/");let i=`http://localhost/${n}`;i=i.endsWith("/")?i.slice(0,-1):i;const c=i.replace(/(?<!:)(\/\/+)/g,"/"),r=n.replace(/\/\/+/g,"/");return{bsTarget:`${c}/`,bsPathRewrite:{"^/":`/${r.startsWith("/")?r.substring(1):r}/`}}}function configureBrowserSyncCommand(e,s){const t=path.join(e,"settings","bs-config.cjs");return fs.writeFileSync(t,'const { createProxyMiddleware } = require("http-proxy-middleware");\n const fs = require("fs");\n \n const jsonData = fs.readFileSync("prisma-php.json", "utf8");\n const config = JSON.parse(jsonData);\n \n module.exports = {\n // First middleware: Set Cache-Control headers\n function(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 // Use the \'middleware\' option to create a proxy that masks the deep URL.\n middleware: [\n // This middleware intercepts requests to the root and proxies them to the deep path.\n createProxyMiddleware("/", {\n target: config.bsTarget,\n changeOrigin: true,\n pathRewrite: config.bsPathRewrite,\n }),\n ],\n proxy: "http://localhost:3000", // Proxy the BrowserSync server.\n files: "src/**/*.*",\n notify: false,\n open: false,\n ghostMode: false,\n };',"utf8"),"browser-sync start --config settings/bs-config.cjs"}async function updatePackageJson(e,s,t){const n=path.join(e,"package.json");if(checkExcludeFiles(n))return;const i=JSON.parse(fs.readFileSync(n,"utf8")),c=configureBrowserSyncCommand(e,s);i.scripts=Object.assign(Object.assign({},i.scripts),{postinstall:"prisma generate",projectName:"node settings/project-name.cjs"});let r=[];t.tailwindcss&&(i.scripts=Object.assign(Object.assign({},i.scripts),{tailwind:"postcss ./src/app/css/tailwind.css -o ./src/app/css/styles.css --watch"}),r.push("tailwind")),t.websocket&&(i.scripts=Object.assign(Object.assign({},i.scripts),{websocket:"node ./settings/restartWebsocket.cjs"}),r.push("websocket"));const o=Object.assign({},i.scripts);r.length>0&&(o["browser-sync"]=c),o.dev=r.length>0?`npm-run-all --parallel projectName browser-sync ${r.join(" ")}`:`npm run projectName && ${c}`,i.scripts=o,i.type="module",i.prisma={seed:"node prisma/seed.js"},fs.writeFileSync(n,JSON.stringify(i,null,2))}async function updateComposerJson(e,s){if(!s.websocket)return;const t=path.join(e,"composer.json");if(checkExcludeFiles(t))return;let n;if(fs.existsSync(t)){{const e=fs.readFileSync(t,"utf8");n=JSON.parse(e)}s.websocket&&(n.require=Object.assign(Object.assign({},n.require),{"cboden/ratchet":"^0.4.4"})),fs.writeFileSync(t,JSON.stringify(n,null,2))}}async function updateIndexJsForWebSocket(e,s){if(!s.websocket)return;const t=path.join(e,"src","app","js","index.js");if(checkExcludeFiles(t))return;let n=fs.readFileSync(t,"utf8");n+='\n// WebSocket initialization\nconst ws = new WebSocket("ws://localhost:8080");\n',fs.writeFileSync(t,n,"utf8")}async function createUpdateGitignoreFile(e,s){const t=path.join(e,".gitignore");if(checkExcludeFiles(t))return;let n="";fs.existsSync(t)&&(n=fs.readFileSync(t,"utf8")),s.forEach((e=>{n.includes(e)||(n+=`\n${e}`)})),n=n.trimStart(),fs.writeFileSync(t,n)}function copyRecursiveSync(e,s){const t=fs.existsSync(e),n=t&&fs.statSync(e);if(t&&n&&n.isDirectory())fs.mkdirSync(s,{recursive:!0}),fs.readdirSync(e).forEach((t=>{copyRecursiveSync(path.join(e,t),path.join(s,t))}));else{if(checkExcludeFiles(s))return;fs.copyFileSync(e,s,0)}}async function executeCopy(e,s){s.forEach((({srcDir:s,destDir:t})=>{copyRecursiveSync(path.join(__dirname,s),path.join(e,t))}))}function createOrUpdateTailwindConfig(e){const s=path.join(e,"tailwind.config.js");if(checkExcludeFiles(s))return;let t=fs.readFileSync(s,"utf8");const n=["./src/app/**/*.{html,js,php}"].map((e=>` "${e}"`)).join(",\n");t=t.replace(/content: \[\],/g,`content: [\n${n}\n],`),fs.writeFileSync(s,t,{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 t=path.join(e,"src","app","layout.php");if(!checkExcludeFiles(t))try{let e=fs.readFileSync(t,"utf8");const n='\n <link href="<?php echo $baseUrl; ?>css/index.css" rel="stylesheet">\n <script src="<?php echo $baseUrl; ?>js/index.js"><\/script>\n <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;800&display=swap" rel="stylesheet">',i=s?` <link href="<?php echo $baseUrl; ?>css/styles.css" rel="stylesheet"> ${n}`:` <script src="https://cdn.tailwindcss.com"><\/script> ${n}`;e=e.replace("</head>",`${i}\n</head>`),fs.writeFileSync(t,e,{flag:"w"})}catch(e){}}async function createOrUpdateEnvFile(e,s){const t=path.join(e,".env");if(checkExcludeFiles(t))return;let n=fs.existsSync(t)?fs.readFileSync(t,"utf8"):"";n.includes(s)||(n+=`${""!==n?"\n\n":""}${s}`,fs.writeFileSync(t,n,{flag:"w"}))}function checkExcludeFiles(e){var s,t;return!!(null==updateAnswer?void 0:updateAnswer.isUpdate)&&(null!==(t=null===(s=null==updateAnswer?void 0:updateAnswer.excludeFilePath)||void 0===s?void 0:s.includes(e.replace(/\\/g,"/")))&&void 0!==t&&t)}async function createDirectoryStructure(e,s,t){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:"/.env",dest:"/.env"},{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"}));n.forEach((({src:s,dest:t})=>{const n=path.join(__dirname,s),i=path.join(e,t);if(checkExcludeFiles(i))return;const c=fs.readFileSync(n,"utf8");fs.writeFileSync(i,c,{flag:"w"})})),await executeCopy(e,[{srcDir:"/settings",destDir:"/settings"},{srcDir:"/prisma",destDir:"/prisma"},{srcDir:"/src",destDir:"/src"}]),await updatePackageJson(e,t,s),await updateComposerJson(e,s),await updateIndexJsForWebSocket(e,s),s.tailwindcss?(createOrUpdateTailwindConfig(e),modifyLayoutPHP(e,!0),modifyPostcssConfig(e)):modifyLayoutPHP(e,!1);await createOrUpdateEnvFile(e,'# 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"'),await createUpdateGitignoreFile(e,["vendor"])}async function getAnswer(e={}){var s,t,n;const i=[];e.projectName||i.push({type:"text",name:"projectName",message:"What is your project named?",initial:"my-app"}),e.tailwindcss||i.push({type:"toggle",name:"tailwindcss",message:`Would you like to use ${chalk.blue("Tailwind CSS")}?`,initial:!0,active:"Yes",inactive:"No"}),e.websocket||i.push({type:"toggle",name:"websocket",message:`Would you like to use ${chalk.blue("Websocket")}?`,initial:!0,active:"Yes",inactive:"No"});const c=i,r=()=>{process.exit(0)};try{const i=await prompts(c,{onCancel:r});return 0===Object.keys(i).length?null:{projectName:i.projectName?String(i.projectName).trim().replace(/ /g,"-"):null!==(s=e.projectName)&&void 0!==s?s:"my-app",tailwindcss:null!==(t=i.tailwindcss)&&void 0!==t?t:e.tailwindcss,websocket:null!==(n=i.websocket)&&void 0!==n?n:e.websocket}}catch(e){return null}}async function installDependencies(e,s,t=!1){fs.existsSync(path.join(e,"package.json"))||execSync("npm init -y",{stdio:"inherit",cwd:e}),s.forEach((e=>{}));const n=`npm install ${t?"--save-dev":""} ${s.join(" ")}`;execSync(n,{stdio:"inherit",cwd:e})}async function uninstallDependencies(e,s,t=!1){s.forEach((e=>{}));const n=`npm uninstall ${t?"--save-dev":"--save"} ${s.join(" ")}`;execSync(n,{stdio:"inherit",cwd:e})}function fetchPackageVersion(e){return new Promise(((s,t)=>{https.get(`https://registry.npmjs.org/${e}`,(e=>{let n="";e.on("data",(e=>n+=e)),e.on("end",(()=>{try{const e=JSON.parse(n);s(e["dist-tags"].latest)}catch(e){t(new Error("Failed to parse JSON response"))}}))})).on("error",(e=>t(e)))}))}const readJsonFile=e=>{const s=fs.readFileSync(e,"utf8");return JSON.parse(s)};async function main(){var e,s,t,n,i;try{const c=process.argv.slice(2);let r=c[0],o=null;if(r){const i={projectName:r,tailwindcss:c.includes("--tailwindcss"),websocket:c.includes("--websocket")};if(o=await getAnswer(i),null===o)return;const a=process.cwd(),l=path.join(a,"prisma-php.json"),p=readJsonFile(l);let d=[];null===(e=p.excludeFiles)||void 0===e||e.map((e=>{const s=path.join(a,e);fs.existsSync(s)&&d.push(s.replace(/\\/g,"/"))})),updateAnswer={projectName:r,tailwindcss:null!==(s=null==o?void 0:o.tailwindcss)&&void 0!==s&&s,websocket:null!==(t=null==o?void 0:o.websocket)&&void 0!==t&&t,isUpdate:!0,excludeFiles:null!==(n=p.excludeFiles)&&void 0!==n?n:[],excludeFilePath:null!=d?d:[],filePath:a}}else o=await getAnswer();if(null===o)return;execSync("npm install -g create-prisma-php-app",{stdio:"inherit"}),execSync("npm install -g browser-sync",{stdio:"inherit"}),r||fs.mkdirSync(o.projectName);const a=process.cwd();let l=r?a:path.join(a,o.projectName);r||process.chdir(o.projectName);const p=["prisma","@prisma/client","typescript","@types/node","ts-node","http-proxy-middleware@^2.0.6"];o.tailwindcss&&p.push("tailwindcss","autoprefixer","postcss","postcss-cli","cssnano"),o.websocket&&p.push("chokidar-cli"),(o.tailwindcss||o.websocket)&&p.push("npm-run-all"),await installDependencies(l,p,!0),r||(execSync("npx prisma init",{stdio:"inherit"}),execSync("npx tsc --init",{stdio:"inherit"})),o.tailwindcss&&execSync("npx tailwindcss init -p",{stdio:"inherit"});const d=l.replace(/\\/g,"\\"),u={PROJECT_NAME:o.projectName,PROJECT_ROOT_PATH:d,PHP_ROOT_PATH_EXE:"D:\\\\xampp\\\\php\\\\php.exe",PHP_GENERATE_CLASS_PATH:"src/Lib/Prisma/Classes"};await createDirectoryStructure(l,o,u);const f=path.join(l,"public");if(fs.existsSync(f)||fs.mkdirSync(f),!o.tailwindcss){const e=path.join(l,"src","app","css");["tailwind.css","styles.css"].forEach((s=>{const t=path.join(e,s);fs.existsSync(t)&&fs.unlinkSync(t)}))}if(!o.websocket){const e=path.join(l,"src","lib","websocket");fs.existsSync(e)&&fs.rmSync(e,{recursive:!0,force:!0});const s=path.join(l,"settings");["restartWebsocket.cjs","restart_websocket.bat"].forEach((e=>{const t=path.join(s,e);fs.existsSync(t)&&fs.unlinkSync(t)}))}if(null==updateAnswer?void 0:updateAnswer.isUpdate){const e=[];if(!updateAnswer.tailwindcss){["postcss.config.js","tailwind.config.js"].forEach((e=>{const s=path.join(l,e);fs.existsSync(s)&&fs.unlinkSync(s)})),e.push("tailwindcss","autoprefixer","postcss","postcss-cli","cssnano")}updateAnswer.websocket||e.push("chokidar-cli"),e.length>0&&await uninstallDependencies(l,e,!0)}const h=await fetchPackageVersion("create-prisma-php-app"),w=bsConfigUrls(u),y={projectName:o.projectName,projectRootPath:d,phpRootPathExe:"D:\\xampp\\php\\php.exe",phpGenerateClassPath:"src/Lib/Prisma/Classes",bsTarget:w.bsTarget,bsPathRewrite:w.bsPathRewrite,tailwindcss:o.tailwindcss,websocket:o.websocket,version:h,excludeFiles:null!==(i=null==updateAnswer?void 0:updateAnswer.excludeFiles)&&void 0!==i?i:[]};fs.writeFileSync(path.join(l,"prisma-php.json"),JSON.stringify(y,null,2),{flag:"w"})}catch(e){process.exit(1)}}main();
@@ -1,15 +1,15 @@
1
- import*as fs from"fs";import{exec}from"child_process";import path from"path";import{fileURLToPath,pathToFileURL}from"url";import CryptoJS from"crypto-js";const __filename=fileURLToPath(import.meta.url),__dirname=path.dirname(__filename),getSecretKey=()=>{const t=fs.readFileSync(`${__dirname}/key.enc`,"utf8");if(t.length<400)throw new Error("File content is less than 400 characters.");return t.substring(247,289)},decryptData=(t,r)=>CryptoJS.AES.decrypt(t,r).toString(CryptoJS.enc.Utf8);
1
+ import*as fs from"fs";import{exec}from"child_process";import path from"path";import{fileURLToPath}from"url";import CryptoJS from"crypto-js";const __filename=fileURLToPath(import.meta.url),__dirname=path.dirname(__filename),getSecretKey=()=>{const r=fs.readFileSync(`${__dirname}/key.enc`,"utf8");if(r.length<400)throw new Error("File content is less than 400 characters.");return r.substring(247,289)},decryptData=(r,t)=>CryptoJS.AES.decrypt(r,t).toString(CryptoJS.enc.Utf8);
2
2
  const executePHP = (command) => {
3
- exec(command, (error, stdout, stderr) => {
4
- if (error) {
5
- console.error(`Execution error: ${error}`);
6
- return;
7
- }
8
- if (stderr) {
9
- console.error(`Standard error: ${stderr}`);
10
- return;
11
- }
12
- console.log(`Standard output...\n${stdout}`);
13
- });
3
+ exec(command, (error, stdout, stderr) => {
4
+ if (error) {
5
+ console.error(`Execution error: ${error}`);
6
+ return;
7
+ }
8
+ if (stderr) {
9
+ console.error(`Standard error: ${stderr}`);
10
+ return;
11
+ }
12
+ console.log(`Standard output...\n${stdout}`);
13
+ });
14
14
  };
15
- const main=async()=>{try{const e=process.cwd(),t=pathToFileURL(path.join(e,"settings","project-settings.js")),n=(await import(t.href)).projectSettings,c=n.PHP_GENERATE_CLASS_PATH,i=`${__dirname}/index.php`,a=`${__dirname}/index.enc`,s=getSecretKey(),r=fs.readFileSync(a,{encoding:"utf8"}),_=decryptData(r,s);fs.writeFileSync(`${__dirname}/index.php`,_);const p=`${n.PHP_ROOT_PATH_EXE} ${i} ${c}`;executePHP(p)}catch(e){}};main().catch((e=>{}));
15
+ const main=async()=>{try{const e=process.cwd(),n=path.join(e,"prisma-php.json"),a=fs.readFileSync(n,{encoding:"utf8"}),c=JSON.parse(a),t=c.phpGenerateClassPath,i=`${__dirname}/index.php`,p=`${__dirname}/index.enc`,s=getSecretKey(),r=fs.readFileSync(p,{encoding:"utf8"}),d=decryptData(r,s);fs.writeFileSync(`${__dirname}/index.php`,d);const h=`${c.phpRootPathExe} ${i} ${t}`;executePHP(h)}catch(e){}};main().catch((e=>{}));
@@ -0,0 +1,56 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+
4
+ // Path to your JSON configuration file
5
+ const configFilePath = path.join(__dirname, "..", "prisma-php.json");
6
+
7
+ // Use the parent directory name as the new project name
8
+ const newProjectName = path.basename(path.join(__dirname, ".."));
9
+
10
+ // Function to update the project name and paths in the JSON config
11
+ function updateProjectNameInConfig(filePath, newProjectName) {
12
+ fs.readFile(filePath, "utf8", (err, data) => {
13
+ if (err) {
14
+ console.error("Error reading the JSON file:", err);
15
+ return;
16
+ }
17
+
18
+ let config = JSON.parse(data);
19
+
20
+ // Extract the old project name from the projectRootPath
21
+ const oldProjectNameMatch = /[^\\]*$/.exec(config.projectRootPath);
22
+ if (!oldProjectNameMatch) {
23
+ console.error(
24
+ "Could not extract the old project name from projectRootPath."
25
+ );
26
+ return;
27
+ }
28
+ const oldProjectName = oldProjectNameMatch[0];
29
+
30
+ // Update the projectName
31
+ config.projectName = newProjectName;
32
+
33
+ // Update paths containing the old project name
34
+ config.projectRootPath = config.projectRootPath.replace(
35
+ new RegExp(oldProjectName + "$"),
36
+ newProjectName
37
+ );
38
+ config.bsTarget = config.bsTarget.replace(oldProjectName, newProjectName);
39
+ config.bsPathRewrite["^/"] = config.bsPathRewrite["^/"].replace(
40
+ oldProjectName,
41
+ newProjectName
42
+ );
43
+
44
+ // Save the updated config back to the JSON file
45
+ fs.writeFile(filePath, JSON.stringify(config, null, 2), "utf8", (err) => {
46
+ if (err) {
47
+ console.error("Error writing the updated JSON file:", err);
48
+ return;
49
+ }
50
+ console.log("The project name and paths have been updated successfully.");
51
+ });
52
+ });
53
+ }
54
+
55
+ // Run the function with your config file path and the new project name
56
+ updateProjectNameInConfig(configFilePath, newProjectName);
@@ -1,10 +1,10 @@
1
1
  /**
2
- * Description placeholder
2
+ * Debounces a function to limit the rate at which it is called.
3
3
  *
4
- * @param {*} func
5
- * @param {*} wait
6
- * @param {*} immediate
7
- * @returns {(...args: {}) => void}
4
+ * @param {Function} func - The function to debounce.
5
+ * @param {number} wait - The number of milliseconds to wait before invoking the function.
6
+ * @param {boolean} immediate - Whether to invoke the function immediately on the leading edge.
7
+ * @returns {Function} - The debounced function.
8
8
  */
9
9
  function debounce(func, wait, immediate) {
10
10
  let timeout;
@@ -21,50 +21,171 @@ function debounce(func, wait, immediate) {
21
21
  }
22
22
 
23
23
  /**
24
- * Description placeholder
25
- *
26
- * @async
27
- * @param {{ className: any; methodName: any; params?: {}; }} param0
28
- * @param {*} param0.className
29
- * @param {*} param0.methodName
30
- * @param {{}} [param0.params={}]
31
- * @returns {unknown}
24
+ * Represents a HTTP request.
32
25
  */
33
- async function fetchApi({ className, methodName, params = {} }) {
34
- // Construct the request body with provided parameters
35
- let requestBody = {
36
- className: className,
37
- methodName: methodName,
38
- params: JSON.stringify(params),
39
- };
26
+ class RequestApi {
27
+ static instance = null;
40
28
 
41
- // Return the fetch promise to the caller
42
- const response = await fetch(`${baseUrl}api/api.php`, {
43
- method: "POST",
44
- headers: {
45
- "Content-Type": "application/x-www-form-urlencoded",
46
- "X-Requested-With": "XMLHttpRequest",
47
- },
48
- body: new URLSearchParams(requestBody),
49
- });
50
- if (!response.ok) {
51
- // If the response is not ok, reject the promise
52
- throw new Error("Network response was not ok");
53
- }
54
- const data = await response.json();
55
- if (data.error) {
56
- // If the API returned an error, reject the promise
57
- throw new Error(data.error);
58
- }
59
- return data;
60
- // Note: No catch here, let the caller handle any errors
29
+ /**
30
+ * The constructor is now private. To ensure it's not accessible from outside,
31
+ * you can throw an error if someone tries to instantiate it directly
32
+ * (though JavaScript does not have true private constructors).
33
+ */
34
+ constructor(baseURL = window.location.origin) {
35
+ this.baseURL = baseURL;
36
+ }
37
+
38
+ /**
39
+ * Static method to get instance of RequestApi.
40
+ *
41
+ * @param {string} [baseURL=window.location.origin] - The base URL for the request.
42
+ * @returns {RequestApi} The singleton instance of the RequestApi.
43
+ */
44
+ static getInstance(baseURL = window.location.origin) {
45
+ if (!RequestApi.instance) {
46
+ RequestApi.instance = new RequestApi(baseURL);
47
+ }
48
+ return RequestApi.instance;
49
+ }
50
+
51
+ /**
52
+ * Sends a HTTP request.
53
+ *
54
+ * @async
55
+ * @param {string} method - The HTTP method.
56
+ * @param {string} url - The URL to send the request to.
57
+ * @param {*} [data=null] - The data to send with the request.
58
+ * @param {Object} [headers={}] - The headers to include in the request.
59
+ * @returns {Promise<unknown>} - A promise that resolves to the response data.
60
+ */
61
+ async request(method, url, data = null, headers = {}) {
62
+ let fullUrl = `${this.baseURL}${url}`;
63
+ const options = {
64
+ method,
65
+ headers: {
66
+ "Content-Type": "application/json",
67
+ "X-Requested-With": "XMLHttpRequest",
68
+ ...headers,
69
+ },
70
+ };
71
+
72
+ if (data) {
73
+ if (method === "GET") {
74
+ const params = new URLSearchParams(data).toString();
75
+ fullUrl += `?${params}`;
76
+ } else {
77
+ options.body = JSON.stringify(data);
78
+ }
79
+ }
80
+
81
+ if (data && method !== "GET" && method !== "HEAD" && method !== "OPTIONS") {
82
+ options.body = JSON.stringify(data);
83
+ }
84
+
85
+ try {
86
+ const response = await fetch(fullUrl, options);
87
+ if (method === "HEAD") {
88
+ return response.headers;
89
+ }
90
+ const contentType = response.headers.get("content-type");
91
+ if (contentType && contentType.includes("application/json")) {
92
+ return await response.json();
93
+ } else {
94
+ return await response.text();
95
+ }
96
+ } catch (error) {
97
+ throw error;
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Sends a GET request.
103
+ *
104
+ * @param {string} url - The URL to send the request to.
105
+ * @param {*} [params] - The parameters to include in the request.
106
+ * @param {Object} [headers={}] - The headers to include in the request.
107
+ * @returns {Promise<unknown>} - A promise that resolves to the response data.
108
+ */
109
+ get(url, params, headers) {
110
+ return this.request("GET", url, params, headers);
111
+ }
112
+
113
+ /**
114
+ * Sends a POST request.
115
+ *
116
+ * @param {string} url - The URL to send the request to.
117
+ * @param {*} data - The data to send with the request.
118
+ * @param {Object} [headers={}] - The headers to include in the request.
119
+ * @returns {Promise<unknown>} - A promise that resolves to the response data.
120
+ */
121
+ post(url, data, headers) {
122
+ return this.request("POST", url, data, headers);
123
+ }
124
+
125
+ /**
126
+ * Sends a PUT request.
127
+ *
128
+ * @param {string} url - The URL to send the request to.
129
+ * @param {*} data - The data to send with the request.
130
+ * @param {Object} [headers={}] - The headers to include in the request.
131
+ * @returns {Promise<unknown>} - A promise that resolves to the response data.
132
+ */
133
+ put(url, data, headers) {
134
+ return this.request("PUT", url, data, headers);
135
+ }
136
+
137
+ /**
138
+ * Sends a DELETE request.
139
+ *
140
+ * @param {string} url - The URL to send the request to.
141
+ * @param {*} data - The data to send with the request.
142
+ * @param {Object} [headers={}] - The headers to include in the request.
143
+ * @returns {Promise<unknown>} - A promise that resolves to the response data.
144
+ */
145
+ delete(url, data, headers) {
146
+ return this.request("DELETE", url, data, headers);
147
+ }
148
+
149
+ /**
150
+ * Sends a PATCH request.
151
+ *
152
+ * @param {string} url - The URL to send the request to.
153
+ * @param {*} data - The data to send with the request.
154
+ * @param {Object} [headers={}] - The headers to include in the request.
155
+ * @returns {Promise<unknown>} - A promise that resolves to the response data.
156
+ */
157
+ patch(url, data, headers) {
158
+ return this.request("PATCH", url, data, headers);
159
+ }
160
+
161
+ /**
162
+ * Sends a HEAD request.
163
+ *
164
+ * @param {string} url - The URL to send the request to.
165
+ * @param {Object} [headers={}] - The headers to include in the request.
166
+ * @returns {Promise<unknown>} - A promise that resolves to the response headers.
167
+ */
168
+ head(url, headers) {
169
+ return this.request("HEAD", url, null, headers);
170
+ }
171
+
172
+ /**
173
+ * Sends an OPTIONS request.
174
+ *
175
+ * @param {string} url - The URL to send the request to.
176
+ * @param {Object} [headers={}] - The headers to include in the request.
177
+ * @returns {Promise<unknown>} - A promise that resolves to the options available.
178
+ */
179
+ options(url, headers) {
180
+ return this.request("OPTIONS", url, null, headers);
181
+ }
61
182
  }
62
183
 
63
184
  /**
64
- * Description placeholder
185
+ * Copies text to the clipboard.
65
186
  *
66
- * @param {*} text
67
- * @param {*} btnElement
187
+ * @param {string} text - The text to copy.
188
+ * @param {HTMLElement} btnElement - The button element that triggered the copy action.
68
189
  */
69
190
  function copyToClipboard(text, btnElement) {
70
191
  navigator.clipboard.writeText(text).then(
@@ -89,9 +210,9 @@ function copyToClipboard(text, btnElement) {
89
210
  }
90
211
 
91
212
  /**
92
- * Description placeholder
213
+ * Copies code to the clipboard.
93
214
  *
94
- * @param {*} btnElement
215
+ * @param {HTMLElement} btnElement - The button element that triggered the copy action.
95
216
  */
96
217
  function copyCode(btnElement) {
97
218
  // Assuming your code block is uniquely identifiable close to your button
@@ -105,25 +226,27 @@ function copyCode(btnElement) {
105
226
  }
106
227
 
107
228
  /**
108
- * Description placeholder
109
- *
110
- * @class StateManager
111
- * @typedef {StateManager}
229
+ * Manages the application state.
112
230
  */
113
231
  class StateManager {
114
232
  static instance = null;
115
233
 
234
+ /**
235
+ * Creates a new StateManager instance.
236
+ *
237
+ * @param {{}} [initialState={}] - The initial state.
238
+ */
116
239
  constructor(initialState = {}) {
117
240
  this.state = initialState;
118
241
  this.listeners = [];
119
242
  }
120
243
 
121
244
  /**
122
- * Description placeholder
245
+ * Gets the singleton instance of StateManager.
123
246
  *
124
247
  * @static
125
- * @param {{}} [initialState={}]
126
- * @returns {*}
248
+ * @param {{}} [initialState={}] - The initial state.
249
+ * @returns {StateManager} - The StateManager instance.
127
250
  */
128
251
  static getInstance(initialState = {}) {
129
252
  if (!StateManager.instance) {
@@ -134,10 +257,10 @@ class StateManager {
134
257
  }
135
258
 
136
259
  /**
137
- * Description placeholder
260
+ * Sets the state.
138
261
  *
139
- * @param {*} update
140
- * @param {boolean} [saveToStorage=false]
262
+ * @param {*} update - The state update.
263
+ * @param {boolean} [saveToStorage=false] - Whether to save the state to localStorage.
141
264
  */
142
265
  setState(update, saveToStorage = false) {
143
266
  this.state = { ...this.state, ...update };
@@ -148,10 +271,10 @@ class StateManager {
148
271
  }
149
272
 
150
273
  /**
151
- * Description placeholder
274
+ * Subscribes to state changes.
152
275
  *
153
- * @param {*} listener
154
- * @returns {() => any}
276
+ * @param {*} listener - The listener function.
277
+ * @returns {Function} - A function to unsubscribe the listener.
155
278
  */
156
279
  subscribe(listener) {
157
280
  this.listeners.push(listener);
@@ -160,10 +283,16 @@ class StateManager {
160
283
  (this.listeners = this.listeners.filter((l) => l !== listener));
161
284
  }
162
285
 
286
+ /**
287
+ * Saves the state to localStorage.
288
+ */
163
289
  saveState() {
164
290
  localStorage.setItem("appState", JSON.stringify(this.state));
165
291
  }
166
292
 
293
+ /**
294
+ * Loads the state from localStorage.
295
+ */
167
296
  loadState() {
168
297
  const state = localStorage.getItem("appState");
169
298
  if (state) {
@@ -173,9 +302,9 @@ class StateManager {
173
302
  }
174
303
 
175
304
  /**
176
- * Description placeholder
177
- * Reset the state to its initial value and optionally clear it from localStorage
178
- * @param {boolean} [clearFromStorage=false]
305
+ * Resets the state to its initial value.
306
+ *
307
+ * @param {boolean} [clearFromStorage=false] - Whether to clear the state from localStorage.
179
308
  */
180
309
  resetState(clearFromStorage = false) {
181
310
  this.state = {}; // Reset the state to an empty object or a default state if you prefer
@@ -186,5 +315,5 @@ class StateManager {
186
315
  }
187
316
  }
188
317
 
189
- // This creates the instance and automatically loads the state if available
190
- const store = StateManager.getInstance({});
318
+ const store = StateManager.getInstance();
319
+ const api = RequestApi.getInstance();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-prisma-php-app",
3
- "version": "1.8.20",
3
+ "version": "1.9.0",
4
4
  "description": "Prisma-PHP: A Revolutionary Library Bridging PHP with Prisma ORM",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -1,6 +0,0 @@
1
- export const projectSettings = {
2
- PROJECT_NAME: "",
3
- PROJECT_ROOT_PATH: "",
4
- PHP_ROOT_PATH_EXE: "D:\\xampp\\php\\php.exe",
5
- PHP_GENERATE_CLASS_PATH: "src/app/classes/prisma",
6
- };
@@ -1,98 +0,0 @@
1
- <?php
2
-
3
- // CORS headers
4
- header('Access-Control-Allow-Origin: *');
5
- header('Access-Control-Allow-Methods: POST, OPTIONS');
6
- header('Access-Control-Allow-Headers: Content-Type, X-Requested-With, Accept, Origin, Authorization, X-CSRF-Token');
7
- header('Content-Type: application/json');
8
-
9
- // Preflight request handling
10
- if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
11
- exit(0);
12
- }
13
-
14
- // Check for valid POST request with XMLHttpRequest
15
- if ($_SERVER["REQUEST_METHOD"] !== "POST" || empty($_SERVER["HTTP_X_REQUESTED_WITH"]) || $_SERVER["HTTP_X_REQUESTED_WITH"] != "XMLHttpRequest") {
16
- http_response_code(400); // Bad Request
17
- echo json_encode(["error" => "Invalid request method or type."]);
18
- exit;
19
- }
20
-
21
- require_once __DIR__ . "/../../../settings/paths.php";
22
- require_once __DIR__ . "/../../../vendor/autoload.php";
23
-
24
- use Lib\Prisma\Classes\Prisma;
25
-
26
- // Initialize variables
27
- $className = $methodName = $paramsJson = "";
28
- $params = null;
29
-
30
- // Determine request content type
31
- $contentType = $_SERVER['CONTENT_TYPE'] ?? '';
32
-
33
- // Handle JSON content type
34
- if (stripos($contentType, 'application/json') !== false) {
35
- $inputJSON = file_get_contents('php://input');
36
- $input = json_decode($inputJSON, true);
37
- if (json_last_error() === JSON_ERROR_NONE) {
38
- $className = $input['className'] ?? '';
39
- $methodName = $input['methodName'] ?? '';
40
- // Directly assign $params without double decoding
41
- $params = $input['params'] ?? []; // Initialize as empty array if not set
42
- } else {
43
- echo json_encode(['error' => 'Error: Invalid JSON body!']);
44
- exit;
45
- }
46
- } else {
47
- // Handle form-urlencoded data
48
- $className = $_POST["className"] ?? "";
49
- $methodName = $_POST["methodName"] ?? "";
50
- $paramsJson = $_POST["params"] ?? "";
51
- if (!empty($paramsJson)) {
52
- $params = json_decode($paramsJson, true);
53
- if (json_last_error() !== JSON_ERROR_NONE) {
54
- echo json_encode(['error' => 'Error: Invalid JSON in params!']);
55
- exit;
56
- }
57
- } else {
58
- $params = []; // Initialize as empty array if not set
59
- }
60
- }
61
-
62
- // Construct the full class name and check for class and property existence
63
- $fullClassName = "Lib\\Prisma\\Classes\\" . pascalCase($className);
64
- if (!class_exists($fullClassName) || !property_exists(Prisma::class, camelCase($className))) {
65
- echo json_encode(['error' => "Error: Class $fullClassName not found or property $className not found in Prisma class!"]);
66
- exit;
67
- }
68
-
69
- // Create an instance of the class
70
- $instance = (new Prisma())->$className;
71
-
72
- // Check for method existence
73
- if (!method_exists($instance, $methodName)) {
74
- echo json_encode(['error' => "Error: Method $methodName not found in class $fullClassName!"]);
75
- exit;
76
- }
77
-
78
- try {
79
- $result = $instance->$methodName($params);
80
- echo json_encode(['result' => $result instanceof \stdClass ? (array)$result : $result]);
81
- } catch (\ArgumentCountError | \Exception $e) {
82
- echo json_encode(['error' => "Error: " . $e->getMessage()]);
83
- }
84
-
85
- function camelCase($string)
86
- {
87
- $string = ucwords($string, "_");
88
- $string = str_replace("_", "", $string);
89
- $string = lcfirst($string);
90
- return $string;
91
- }
92
-
93
- function pascalCase($string)
94
- {
95
- $string = ucwords($string, "_");
96
- $string = str_replace("_", "", $string);
97
- return $string;
98
- }