create-prisma-php-app 4.1.3-rc → 4.1.3

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.
@@ -0,0 +1,246 @@
1
+ import { Plugin } from "vite";
2
+ import path from "path";
3
+ import { writeFileSync, mkdirSync, existsSync, readFileSync } from "fs";
4
+ import ts from "typescript";
5
+
6
+ export function generateGlobalTypes(): Plugin {
7
+ const dtsPath = path.resolve(process.cwd(), ".pp", "global-functions.d.ts");
8
+
9
+ return {
10
+ name: "generate-global-types",
11
+
12
+ buildStart() {
13
+ const mainPath = path.resolve(process.cwd(), "ts", "main.ts");
14
+
15
+ if (!existsSync(mainPath)) {
16
+ console.warn("⚠️ ts/main.ts not found, skipping type generation");
17
+ return;
18
+ }
19
+
20
+ const content = readFileSync(mainPath, "utf-8");
21
+ const globals = parseGlobalSingletons(content, mainPath);
22
+
23
+ if (globals.length === 0) {
24
+ console.warn("⚠️ No createGlobalSingleton calls found");
25
+ return;
26
+ }
27
+
28
+ generateDtsWithTypeChecker(globals, dtsPath, mainPath);
29
+ },
30
+ };
31
+ }
32
+ interface GlobalDeclaration {
33
+ name: string;
34
+ importPath: string;
35
+ exportName: string;
36
+ }
37
+
38
+ function parseGlobalSingletons(
39
+ content: string,
40
+ filePath: string
41
+ ): GlobalDeclaration[] {
42
+ const sf = ts.createSourceFile(
43
+ filePath,
44
+ content,
45
+ ts.ScriptTarget.Latest,
46
+ true
47
+ );
48
+
49
+ const globals: GlobalDeclaration[] = [];
50
+ const importMap = new Map<string, { path: string; originalName: string }>();
51
+
52
+ sf.statements.forEach((stmt) => {
53
+ if (ts.isImportDeclaration(stmt) && stmt.importClause) {
54
+ const moduleSpecifier = (stmt.moduleSpecifier as ts.StringLiteral).text;
55
+
56
+ if (stmt.importClause.namedBindings) {
57
+ if (ts.isNamedImports(stmt.importClause.namedBindings)) {
58
+ stmt.importClause.namedBindings.elements.forEach((element) => {
59
+ const localName = element.name.text;
60
+ const importedName = element.propertyName
61
+ ? element.propertyName.text
62
+ : localName;
63
+
64
+ importMap.set(localName, {
65
+ path: moduleSpecifier,
66
+ originalName: importedName,
67
+ });
68
+ });
69
+ }
70
+ }
71
+ }
72
+ });
73
+
74
+ function visit(node: ts.Node) {
75
+ if (
76
+ ts.isCallExpression(node) &&
77
+ ts.isIdentifier(node.expression) &&
78
+ node.expression.text === "createGlobalSingleton"
79
+ ) {
80
+ if (node.arguments.length >= 2) {
81
+ const nameArg = node.arguments[0];
82
+ const valueArg = node.arguments[1];
83
+
84
+ if (ts.isStringLiteral(nameArg) && ts.isIdentifier(valueArg)) {
85
+ const name = nameArg.text;
86
+ const variable = valueArg.text;
87
+ const importInfo = importMap.get(variable);
88
+
89
+ if (importInfo) {
90
+ globals.push({
91
+ name,
92
+ importPath: importInfo.path,
93
+ exportName: importInfo.originalName,
94
+ });
95
+ }
96
+ }
97
+ }
98
+ }
99
+
100
+ ts.forEachChild(node, visit);
101
+ }
102
+
103
+ visit(sf);
104
+ return globals;
105
+ }
106
+
107
+ function generateDtsWithTypeChecker(
108
+ globals: GlobalDeclaration[],
109
+ dtsPath: string,
110
+ mainPath: string
111
+ ) {
112
+ const configPath = ts.findConfigFile(
113
+ process.cwd(),
114
+ ts.sys.fileExists,
115
+ "tsconfig.json"
116
+ );
117
+
118
+ const { config } = configPath
119
+ ? ts.readConfigFile(configPath, ts.sys.readFile)
120
+ : { config: {} };
121
+
122
+ const { options } = ts.parseJsonConfigFileContent(
123
+ config,
124
+ ts.sys,
125
+ process.cwd()
126
+ );
127
+
128
+ const program = ts.createProgram([mainPath], options);
129
+ const checker = program.getTypeChecker();
130
+ const sourceFile = program.getSourceFile(mainPath);
131
+
132
+ if (!sourceFile) {
133
+ console.warn("⚠️ Could not load main.ts for type checking");
134
+ generateFallbackDts(globals, dtsPath);
135
+ return;
136
+ }
137
+
138
+ const signatures = new Map<string, string>();
139
+
140
+ const importMap = new Map<string, ts.ImportDeclaration>();
141
+ sourceFile.statements.forEach((stmt) => {
142
+ if (ts.isImportDeclaration(stmt) && stmt.importClause?.namedBindings) {
143
+ if (ts.isNamedImports(stmt.importClause.namedBindings)) {
144
+ stmt.importClause.namedBindings.elements.forEach((element) => {
145
+ importMap.set(element.name.text, stmt);
146
+ });
147
+ }
148
+ }
149
+ });
150
+
151
+ globals.forEach(({ name, exportName }) => {
152
+ try {
153
+ const importDecl = importMap.get(exportName);
154
+ if (!importDecl || !importDecl.importClause?.namedBindings) {
155
+ signatures.set(name, "(...args: any[]) => any");
156
+ return;
157
+ }
158
+
159
+ if (ts.isNamedImports(importDecl.importClause.namedBindings)) {
160
+ const importSpec = importDecl.importClause.namedBindings.elements.find(
161
+ (el) => el.name.text === exportName
162
+ );
163
+
164
+ if (importSpec) {
165
+ const symbol = checker.getSymbolAtLocation(importSpec.name);
166
+ if (symbol) {
167
+ const type = checker.getTypeOfSymbolAtLocation(
168
+ symbol,
169
+ importSpec.name
170
+ );
171
+ const signature = checker.typeToString(
172
+ type,
173
+ undefined,
174
+ ts.TypeFormatFlags.NoTruncation
175
+ );
176
+ signatures.set(name, signature);
177
+ return;
178
+ }
179
+ }
180
+ }
181
+
182
+ signatures.set(name, "(...args: any[]) => any");
183
+ } catch (error) {
184
+ console.warn(`⚠️ Failed to extract type for ${name}:`, error);
185
+ signatures.set(name, "(...args: any[]) => any");
186
+ }
187
+ });
188
+
189
+ const declarations = globals
190
+ .map(({ name }) => {
191
+ const sig = signatures.get(name) || "(...args: any[]) => any";
192
+ return ` const ${name}: ${sig};`;
193
+ })
194
+ .join("\n");
195
+
196
+ const windowDeclarations = globals
197
+ .map(({ name }) => ` ${name}: typeof globalThis.${name};`)
198
+ .join("\n");
199
+
200
+ const content = `// Auto-generated by Vite plugin
201
+ // Do not edit manually - regenerate with: npm run dev or npm run build
202
+ // Source: ts/main.ts
203
+
204
+ declare global {
205
+ ${declarations}
206
+
207
+ interface Window {
208
+ ${windowDeclarations}
209
+ }
210
+ }
211
+
212
+ export {};
213
+ `;
214
+
215
+ const dir = path.dirname(dtsPath);
216
+ if (!existsSync(dir)) {
217
+ mkdirSync(dir, { recursive: true });
218
+ }
219
+
220
+ writeFileSync(dtsPath, content, "utf-8");
221
+ console.log(`✅ Generated ${path.relative(process.cwd(), dtsPath)}`);
222
+ }
223
+
224
+ function generateFallbackDts(globals: GlobalDeclaration[], dtsPath: string) {
225
+ const declarations = globals
226
+ .map(({ name }) => ` const ${name}: (...args: any[]) => any;`)
227
+ .join("\n");
228
+
229
+ const windowDeclarations = globals
230
+ .map(({ name }) => ` ${name}: typeof globalThis.${name};`)
231
+ .join("\n");
232
+
233
+ const content = `// Auto-generated by Vite plugin
234
+ declare global {
235
+ ${declarations}
236
+
237
+ interface Window {
238
+ ${windowDeclarations}
239
+ }
240
+ }
241
+
242
+ export {};
243
+ `;
244
+
245
+ writeFileSync(dtsPath, content, "utf-8");
246
+ }
@@ -53,33 +53,20 @@ class Auth
53
53
  }
54
54
 
55
55
  /**
56
- * Authenticates a user and generates a JWT (JSON Web Token) based on the specified user data
57
- * and token validity duration. The method first checks if the secret key is set, calculates
58
- * the token's expiration time, sets the necessary payload, and encodes it into a JWT.
59
- * If possible (HTTP headers not yet sent), it also sets cookies with the JWT for client-side storage.
56
+ * Authenticates a user and generates a JWT.
57
+ * Optionally redirects the user to a default or custom URL.
60
58
  *
61
- * @param mixed $data User data which can be a simple string or an instance of AuthRole.
62
- * If an instance of AuthRole is provided, its `value` property will be used as the role in the token.
63
- * @param string|null $tokenValidity Optional parameter specifying the duration the token is valid for (e.g., '10m', '1h').
64
- * If null, the default validity period set in the class property is used, which is 1 hour.
65
- * The format should be a number followed by a time unit ('s' for seconds, 'm' for minutes,
66
- * 'h' for hours, 'd' for days), and this is parsed to calculate the exact expiration time.
59
+ * @param mixed $data User data (string or AuthRole).
60
+ * @param string|null $tokenValidity Duration token is valid for (e.g., '1h'). Default is '1h'.
61
+ * @param bool|string $redirect
62
+ * - If `false` (default): No redirect occurs; returns the JWT.
63
+ * - If `true`: Redirects to `AuthConfig::DEFAULT_SIGNIN_REDIRECT`.
64
+ * - If `string`: Redirects to the specified URL (e.g., '/dashboard').
67
65
  *
68
66
  * @return string Returns the encoded JWT as a string.
69
- *
70
- * @throws InvalidArgumentException Thrown if the secret key is not set or if the duration format is invalid.
71
- *
72
- * Example:
73
- * $auth = Auth::getInstance();
74
- * $auth->setSecretKey('your_secret_key');
75
- * try {
76
- * $jwt = $auth->signIn('Admin', '1h');
77
- * echo "JWT: " . $jwt;
78
- * } catch (InvalidArgumentException $e) {
79
- * echo "Error: " . $e->getMessage();
80
- * }
67
+ * @throws InvalidArgumentException
81
68
  */
82
- public function signIn($data, ?string $tokenValidity = null): string
69
+ public function signIn($data, ?string $tokenValidity = null, bool|string $redirect = false): string
83
70
  {
84
71
  if (!$this->secretKey) {
85
72
  throw new InvalidArgumentException("Secret key is required for authentication.");
@@ -106,6 +93,12 @@ class Auth
106
93
  $this->setCookies($jwt, $expirationTime);
107
94
  }
108
95
 
96
+ if ($redirect === true) {
97
+ Request::redirect(AuthConfig::DEFAULT_SIGNIN_REDIRECT);
98
+ } elseif (is_string($redirect) && !empty($redirect)) {
99
+ Request::redirect($redirect);
100
+ }
101
+
109
102
  return $jwt;
110
103
  }
111
104
 
@@ -8,7 +8,7 @@
8
8
  <ol class="list-inside list-decimal text-sm/6 text-center sm:text-left">
9
9
  <li class="mb-2 tracking-[-.01em]">
10
10
  Get started by editing
11
- <code class="bg-text-foreground/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-semibold">
11
+ <code class="bg-text-foreground/[.05] dark:bg-white/6 px-1 py-0.5 rounded font-semibold">
12
12
  src/app/index.php
13
13
  </code>
14
14
  .
@@ -46,7 +46,7 @@
46
46
  </div>
47
47
  </main>
48
48
 
49
- <footer class="row-start-3 flex gap-[24px] flex-wrap items-center justify-center">
49
+ <footer class="row-start-3 flex gap-6 flex-wrap items-center justify-center">
50
50
  <a
51
51
  class="flex items-center gap-2 hover:underline hover:underline-offset-4"
52
52
  href="https://prismaphp.tsnc.tech/docs?doc=learning-path"
@@ -1,7 +1,7 @@
1
- <div class="flex flex-col min-h-[100vh] items-center justify-center space-y-4 text-center">
1
+ <div class="flex flex-col min-h-screen items-center justify-center space-y-4 text-center">
2
2
  <div class="space-y-2">
3
3
  <h1 class="text-4xl font-bold tracking-tighter sm:text-5xl md:text-6xl">404 Not Found</h1>
4
- <p class="max-w-[600px] text-gray-500 md:text-xl/relaxed lg:text-base/relaxed xl:text-xl/relaxed dark:text-gray-400">
4
+ <p class="max-w-150 text-gray-500 md:text-xl/relaxed lg:text-base/relaxed xl:text-xl/relaxed dark:text-gray-400">
5
5
  Sorry, we couldn't find the page you're looking for.
6
6
  </p>
7
7
  </div>
@@ -106,6 +106,6 @@
106
106
  // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
107
107
  "skipLibCheck": true /* Skip type checking all .d.ts files. */
108
108
  },
109
- "include": ["**/*.ts"],
109
+ "include": ["**/*.ts", ".pp/**/*.d.ts"],
110
110
  "exclude": ["node_modules", "vendor"]
111
111
  }
@@ -2,6 +2,7 @@ import { defineConfig, Plugin } from "vite";
2
2
  import path from "path";
3
3
  import fg from "fast-glob";
4
4
  import { writeFileSync } from "fs";
5
+ import { generateGlobalTypes } from "./settings/vite-plugins/generate-global-types.js";
5
6
 
6
7
  const entries = Object.fromEntries(
7
8
  fg.sync("ts/**/*.ts", { ignore: ["**/*.test.ts"] }).map((f) => {
@@ -13,6 +14,8 @@ const entries = Object.fromEntries(
13
14
  const VITE_WATCH_EXCLUDE = [
14
15
  "public/js/**",
15
16
  "node_modules/**",
17
+ "vendor/**",
18
+ ".pp/**",
16
19
  ];
17
20
 
18
21
  function browserSyncNotify(): Plugin {
@@ -47,8 +50,12 @@ export default defineConfig(({ command, mode }) => ({
47
50
  },
48
51
  },
49
52
  },
50
- plugins:
51
- command === "build" && mode === "development" ? [browserSyncNotify()] : [],
53
+ plugins: [
54
+ generateGlobalTypes(),
55
+ ...(command === "build" && mode === "development"
56
+ ? [browserSyncNotify()]
57
+ : []),
58
+ ],
52
59
  esbuild: { legalComments: "none" },
53
60
  define: { "process.env.NODE_ENV": '"production"' },
54
61
  }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-prisma-php-app",
3
- "version": "4.1.3-rc",
3
+ "version": "4.1.3",
4
4
  "description": "Prisma-PHP: A Revolutionary Library Bridging PHP with Prisma ORM",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -32,15 +32,15 @@
32
32
  "author": "Jefferson Abraham Omier <thesteelninjacode@gmail.com>",
33
33
  "license": "MIT",
34
34
  "dependencies": {
35
- "chalk": "^5.3.0",
35
+ "chalk": "^5.6.2",
36
36
  "crypto-js": "^4.2.0",
37
37
  "prompts": "^2.4.2"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@types/crypto-js": "^4.2.2",
41
- "@types/node": "^20.11.7",
41
+ "@types/node": "^25.0.3",
42
42
  "@types/prompts": "^2.4.9",
43
43
  "ts-node": "^10.9.2",
44
- "typescript": "^5.3.3"
44
+ "typescript": "^5.9.3"
45
45
  }
46
46
  }