@lastbrain/app 0.1.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.
Files changed (159) hide show
  1. package/README.md +256 -0
  2. package/dist/app-shell/(admin)/dashboard/page.d.ts +2 -0
  3. package/dist/app-shell/(admin)/dashboard/page.d.ts.map +1 -0
  4. package/dist/app-shell/(admin)/dashboard/page.js +5 -0
  5. package/dist/app-shell/(admin)/layout.d.ts +3 -0
  6. package/dist/app-shell/(admin)/layout.d.ts.map +1 -0
  7. package/dist/app-shell/(admin)/layout.js +5 -0
  8. package/dist/app-shell/(admin)/page.d.ts +2 -0
  9. package/dist/app-shell/(admin)/page.d.ts.map +1 -0
  10. package/dist/app-shell/(admin)/page.js +5 -0
  11. package/dist/app-shell/(auth)/dashboard/page.d.ts +2 -0
  12. package/dist/app-shell/(auth)/dashboard/page.d.ts.map +1 -0
  13. package/dist/app-shell/(auth)/dashboard/page.js +5 -0
  14. package/dist/app-shell/(auth)/layout.d.ts +3 -0
  15. package/dist/app-shell/(auth)/layout.d.ts.map +1 -0
  16. package/dist/app-shell/(auth)/layout.js +5 -0
  17. package/dist/app-shell/(auth)/page.d.ts +2 -0
  18. package/dist/app-shell/(auth)/page.d.ts.map +1 -0
  19. package/dist/app-shell/(auth)/page.js +5 -0
  20. package/dist/app-shell/(public)/page.d.ts +2 -0
  21. package/dist/app-shell/(public)/page.d.ts.map +1 -0
  22. package/dist/app-shell/(public)/page.js +5 -0
  23. package/dist/app-shell/layout.d.ts +3 -0
  24. package/dist/app-shell/layout.d.ts.map +1 -0
  25. package/dist/app-shell/layout.js +3 -0
  26. package/dist/app-shell/not-found.d.ts +2 -0
  27. package/dist/app-shell/not-found.d.ts.map +1 -0
  28. package/dist/app-shell/not-found.js +10 -0
  29. package/dist/auth/authHelpers.d.ts +7 -0
  30. package/dist/auth/authHelpers.d.ts.map +1 -0
  31. package/dist/auth/authHelpers.js +19 -0
  32. package/dist/auth/useAuthSession.d.ts +7 -0
  33. package/dist/auth/useAuthSession.d.ts.map +1 -0
  34. package/dist/auth/useAuthSession.js +43 -0
  35. package/dist/cli.d.ts +3 -0
  36. package/dist/cli.d.ts.map +1 -0
  37. package/dist/cli.js +88 -0
  38. package/dist/components/TableStructure.d.ts +8 -0
  39. package/dist/components/TableStructure.d.ts.map +1 -0
  40. package/dist/components/TableStructure.js +37 -0
  41. package/dist/index.d.ts +15 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +15 -0
  44. package/dist/layouts/AdminLayout.d.ts +3 -0
  45. package/dist/layouts/AdminLayout.d.ts.map +1 -0
  46. package/dist/layouts/AdminLayout.js +5 -0
  47. package/dist/layouts/AppProviders.d.ts +13 -0
  48. package/dist/layouts/AppProviders.d.ts.map +1 -0
  49. package/dist/layouts/AppProviders.js +35 -0
  50. package/dist/layouts/AuthLayout.d.ts +3 -0
  51. package/dist/layouts/AuthLayout.d.ts.map +1 -0
  52. package/dist/layouts/AuthLayout.js +5 -0
  53. package/dist/layouts/PublicLayout.d.ts +3 -0
  54. package/dist/layouts/PublicLayout.d.ts.map +1 -0
  55. package/dist/layouts/PublicLayout.js +5 -0
  56. package/dist/layouts/RootLayout.d.ts +3 -0
  57. package/dist/layouts/RootLayout.d.ts.map +1 -0
  58. package/dist/layouts/RootLayout.js +8 -0
  59. package/dist/module-build.d.ts +2 -0
  60. package/dist/module-build.d.ts.map +1 -0
  61. package/dist/module-build.js +50 -0
  62. package/dist/modules/index.d.ts +3 -0
  63. package/dist/modules/index.d.ts.map +1 -0
  64. package/dist/modules/index.js +2 -0
  65. package/dist/modules/module-loader.d.ts +5 -0
  66. package/dist/modules/module-loader.d.ts.map +1 -0
  67. package/dist/modules/module-loader.js +10 -0
  68. package/dist/scripts/db-init.d.ts +2 -0
  69. package/dist/scripts/db-init.d.ts.map +1 -0
  70. package/dist/scripts/db-init.js +281 -0
  71. package/dist/scripts/db-migrations-sync.d.ts +2 -0
  72. package/dist/scripts/db-migrations-sync.d.ts.map +1 -0
  73. package/dist/scripts/db-migrations-sync.js +55 -0
  74. package/dist/scripts/dev-sync.d.ts +2 -0
  75. package/dist/scripts/dev-sync.d.ts.map +1 -0
  76. package/dist/scripts/dev-sync.js +182 -0
  77. package/dist/scripts/init-app.d.ts +11 -0
  78. package/dist/scripts/init-app.d.ts.map +1 -0
  79. package/dist/scripts/init-app.js +846 -0
  80. package/dist/scripts/module-add.d.ts +13 -0
  81. package/dist/scripts/module-add.d.ts.map +1 -0
  82. package/dist/scripts/module-add.js +178 -0
  83. package/dist/scripts/module-build.d.ts +2 -0
  84. package/dist/scripts/module-build.d.ts.map +1 -0
  85. package/dist/scripts/module-build.js +413 -0
  86. package/dist/scripts/module-create.d.ts +5 -0
  87. package/dist/scripts/module-create.d.ts.map +1 -0
  88. package/dist/scripts/module-create.js +694 -0
  89. package/dist/scripts/module-list.d.ts +2 -0
  90. package/dist/scripts/module-list.d.ts.map +1 -0
  91. package/dist/scripts/module-list.js +31 -0
  92. package/dist/scripts/module-remove.d.ts +2 -0
  93. package/dist/scripts/module-remove.d.ts.map +1 -0
  94. package/dist/scripts/module-remove.js +282 -0
  95. package/dist/scripts/readme-build.d.ts +2 -0
  96. package/dist/scripts/readme-build.d.ts.map +1 -0
  97. package/dist/scripts/readme-build.js +39 -0
  98. package/dist/templates/AuthGuidePage.d.ts +2 -0
  99. package/dist/templates/AuthGuidePage.d.ts.map +1 -0
  100. package/dist/templates/AuthGuidePage.js +9 -0
  101. package/dist/templates/DefaultDoc.d.ts +2 -0
  102. package/dist/templates/DefaultDoc.d.ts.map +1 -0
  103. package/dist/templates/DefaultDoc.js +29 -0
  104. package/dist/templates/DocPage.d.ts +17 -0
  105. package/dist/templates/DocPage.d.ts.map +1 -0
  106. package/dist/templates/DocPage.js +165 -0
  107. package/dist/templates/DocsPageWithModules.d.ts +2 -0
  108. package/dist/templates/DocsPageWithModules.d.ts.map +1 -0
  109. package/dist/templates/DocsPageWithModules.js +8 -0
  110. package/dist/templates/HomePage.d.ts +6 -0
  111. package/dist/templates/HomePage.d.ts.map +1 -0
  112. package/dist/templates/HomePage.js +6 -0
  113. package/dist/templates/MigrationsGuidePage.d.ts +2 -0
  114. package/dist/templates/MigrationsGuidePage.d.ts.map +1 -0
  115. package/dist/templates/MigrationsGuidePage.js +11 -0
  116. package/dist/templates/ModuleGuidePage.d.ts +2 -0
  117. package/dist/templates/ModuleGuidePage.d.ts.map +1 -0
  118. package/dist/templates/ModuleGuidePage.js +14 -0
  119. package/dist/templates/SimpleDocPage.d.ts +2 -0
  120. package/dist/templates/SimpleDocPage.d.ts.map +1 -0
  121. package/dist/templates/SimpleDocPage.js +28 -0
  122. package/dist/templates/SimpleHomePage.d.ts +6 -0
  123. package/dist/templates/SimpleHomePage.d.ts.map +1 -0
  124. package/dist/templates/SimpleHomePage.js +7 -0
  125. package/dist/templates/env.example/.env.example +6 -0
  126. package/dist/templates/migrations/20201010100000_app_base.sql +228 -0
  127. package/dist/templates/migrations/20201010100000_init.sql +123 -0
  128. package/package.json +75 -0
  129. package/src/app-shell/(admin)/layout.tsx +13 -0
  130. package/src/app-shell/(auth)/layout.tsx +13 -0
  131. package/src/app-shell/(public)/page.tsx +11 -0
  132. package/src/app-shell/layout.tsx +5 -0
  133. package/src/app-shell/not-found.tsx +28 -0
  134. package/src/layouts/AdminLayout.tsx +7 -0
  135. package/src/layouts/AppProviders.tsx +61 -0
  136. package/src/layouts/AuthLayout.tsx +7 -0
  137. package/src/layouts/PublicLayout.tsx +7 -0
  138. package/src/layouts/RootLayout.tsx +27 -0
  139. package/src/scripts/README.md +262 -0
  140. package/src/scripts/db-init.ts +338 -0
  141. package/src/scripts/db-migrations-sync.ts +86 -0
  142. package/src/scripts/dev-sync.ts +218 -0
  143. package/src/scripts/init-app.ts +1011 -0
  144. package/src/scripts/module-add.ts +242 -0
  145. package/src/scripts/module-build.ts +502 -0
  146. package/src/scripts/module-create.ts +809 -0
  147. package/src/scripts/module-list.ts +37 -0
  148. package/src/scripts/module-remove.ts +367 -0
  149. package/src/scripts/readme-build.ts +60 -0
  150. package/src/templates/AuthGuidePage.tsx +68 -0
  151. package/src/templates/DefaultDoc.tsx +462 -0
  152. package/src/templates/DocPage.tsx +381 -0
  153. package/src/templates/DocsPageWithModules.tsx +22 -0
  154. package/src/templates/MigrationsGuidePage.tsx +61 -0
  155. package/src/templates/ModuleGuidePage.tsx +71 -0
  156. package/src/templates/SimpleDocPage.tsx +587 -0
  157. package/src/templates/SimpleHomePage.tsx +385 -0
  158. package/src/templates/env.example/.env.example +6 -0
  159. package/src/templates/migrations/20201010100000_app_base.sql +228 -0
@@ -0,0 +1,694 @@
1
+ import fs from "fs-extra";
2
+ import path from "path";
3
+ import chalk from "chalk";
4
+ import inquirer from "inquirer";
5
+ /**
6
+ * Parse une chaîne de pages séparées par des virgules
7
+ * Ex: "legal, privacy, terms" => ["legal", "privacy", "terms"]
8
+ */
9
+ function parsePagesList(input) {
10
+ if (!input || input.trim() === "")
11
+ return [];
12
+ return input
13
+ .split(",")
14
+ .map((p) => p.trim())
15
+ .filter((p) => p.length > 0);
16
+ }
17
+ /**
18
+ * Parse une chaîne de tables séparées par des virgules
19
+ * Ex: "settings, users" => ["settings", "users"]
20
+ */
21
+ function parseTablesList(input) {
22
+ if (!input || input.trim() === "")
23
+ return [];
24
+ return input
25
+ .split(",")
26
+ .map((t) => t.trim())
27
+ .filter((t) => t.length > 0);
28
+ }
29
+ /**
30
+ * Génère le contenu du package.json
31
+ */
32
+ function generatePackageJson(moduleName, slug) {
33
+ const buildConfigExport = `./${slug}.build.config`;
34
+ return JSON.stringify({
35
+ name: moduleName,
36
+ version: "0.1.0",
37
+ private: false,
38
+ type: "module",
39
+ main: "dist/index.js",
40
+ types: "dist/index.d.ts",
41
+ files: ["dist", "supabase"],
42
+ scripts: {
43
+ build: "tsc -p tsconfig.json",
44
+ dev: "tsc -p tsconfig.json --watch",
45
+ },
46
+ dependencies: {
47
+ "@lastbrain/core": "workspace:0.1.0",
48
+ "@lastbrain/ui": "workspace:0.1.0",
49
+ react: "^19.0.0",
50
+ "lucide-react": "^0.554.0",
51
+ "react-dom": "^19.0.0",
52
+ },
53
+ devDependencies: {
54
+ typescript: "^5.4.0",
55
+ },
56
+ exports: {
57
+ ".": {
58
+ types: "./dist/index.d.ts",
59
+ default: "./dist/index.js",
60
+ },
61
+ "./server": {
62
+ types: "./dist/server.d.ts",
63
+ default: "./dist/server.js",
64
+ },
65
+ [buildConfigExport]: {
66
+ types: `./dist/${slug}.build.config.d.ts`,
67
+ default: `./dist/${slug}.build.config.js`,
68
+ },
69
+ "./api/*": {
70
+ types: "./dist/api/*.d.ts",
71
+ default: "./dist/api/*.js",
72
+ },
73
+ },
74
+ sideEffects: false,
75
+ }, null, 2);
76
+ }
77
+ /**
78
+ * Génère le contenu du tsconfig.json
79
+ */
80
+ function generateTsConfig() {
81
+ return JSON.stringify({
82
+ extends: "../../tsconfig.base.json",
83
+ compilerOptions: {
84
+ outDir: "dist",
85
+ rootDir: "src",
86
+ declaration: true,
87
+ },
88
+ include: ["src"],
89
+ }, null, 2);
90
+ }
91
+ /**
92
+ * Génère le contenu du fichier build.config.ts
93
+ */
94
+ function generateBuildConfig(config) {
95
+ const { moduleName, pages, tables } = config;
96
+ // Générer la liste des pages
97
+ const pagesConfig = pages.map((page) => {
98
+ const componentName = page.name
99
+ .split("-")
100
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
101
+ .join("");
102
+ return ` {
103
+ section: "${page.section}",
104
+ path: "${page.path}",
105
+ componentExport: "${componentName}Page",
106
+ }`;
107
+ });
108
+ // Générer la liste des APIs
109
+ const apisConfig = [];
110
+ for (const table of tables) {
111
+ for (const section of table.sections) {
112
+ const methods = ["GET", "POST", "PUT", "DELETE"];
113
+ for (const method of methods) {
114
+ apisConfig.push(` {
115
+ method: "${method}",
116
+ path: "/api/${section}/${table.name}",
117
+ handlerExport: "${method}",
118
+ entryPoint: "api/${section}/${table.name}",
119
+ authRequired: ${section !== "public"},
120
+ }`);
121
+ }
122
+ }
123
+ }
124
+ // Générer les menus par section
125
+ const menuSections = [];
126
+ // Menu public
127
+ const publicPages = pages.filter(p => p.section === "public");
128
+ if (publicPages.length > 0) {
129
+ const publicMenuItems = publicPages.map((page, index) => {
130
+ const title = page.name
131
+ .split("-")
132
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
133
+ .join(" ");
134
+ return ` {
135
+ title: "${title}",
136
+ description: "Page ${title}",
137
+ icon: "FileText",
138
+ path: "${page.path}",
139
+ order: ${index + 1},
140
+ }`;
141
+ });
142
+ menuSections.push({ section: "public", items: publicMenuItems });
143
+ }
144
+ // Menu auth
145
+ const authPages = pages.filter(p => p.section === "auth");
146
+ if (authPages.length > 0) {
147
+ const authMenuItems = authPages.map((page, index) => {
148
+ const title = page.name
149
+ .split("-")
150
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
151
+ .join(" ");
152
+ return ` {
153
+ title: "${title}",
154
+ description: "Page ${title}",
155
+ icon: "FileText",
156
+ path: "${page.path}",
157
+ order: ${index + 1},
158
+ }`;
159
+ });
160
+ menuSections.push({ section: "auth", items: authMenuItems });
161
+ }
162
+ // Menu admin
163
+ const adminPages = pages.filter(p => p.section === "admin");
164
+ if (adminPages.length > 0) {
165
+ const adminMenuItems = adminPages.map((page, index) => {
166
+ const title = page.name
167
+ .split("-")
168
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
169
+ .join(" ");
170
+ return ` {
171
+ title: "${title}",
172
+ description: "Page ${title}",
173
+ icon: "Settings",
174
+ path: "${page.path}",
175
+ order: ${index + 1},
176
+ }`;
177
+ });
178
+ menuSections.push({ section: "admin", items: adminMenuItems });
179
+ }
180
+ // Générer la configuration menu
181
+ const menuConfig = menuSections.length > 0
182
+ ? `,
183
+ menu: {
184
+ ${menuSections.map(({ section, items }) => ` ${section}: [
185
+ ${items.join(",\n")}
186
+ ]`).join(",\n")}
187
+ }`
188
+ : "";
189
+ return `import type { ModuleBuildConfig } from "@lastbrain/core";
190
+
191
+ const buildConfig: ModuleBuildConfig = {
192
+ moduleName: "${moduleName}",
193
+ pages: [
194
+ ${pagesConfig.join(",\n")}
195
+ ],
196
+ apis: [
197
+ ${apisConfig.join(",\n")}
198
+ ],
199
+ migrations: {
200
+ enabled: true,
201
+ priority: 30,
202
+ path: "supabase/migrations",
203
+ files: ${tables.length > 0 ? `["001_${config.slug}_init.sql"]` : "[]"},
204
+ }${menuConfig},
205
+ };
206
+
207
+ export default buildConfig;
208
+ `;
209
+ }
210
+ /**
211
+ * Génère le contenu du fichier index.ts
212
+ */
213
+ function generateIndexTs(pages) {
214
+ const exports = pages.map((page) => {
215
+ const componentName = page.name
216
+ .split("-")
217
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
218
+ .join("");
219
+ const fileName = page.name
220
+ .split("-")
221
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
222
+ .join("");
223
+ return `export { ${componentName}Page } from "./web/${page.section}/${fileName}Page.js";`;
224
+ });
225
+ return `// Client Components
226
+ ${exports.join("\n")}
227
+
228
+ // Configuration de build
229
+ export { default as buildConfig } from "./build.config.js";
230
+ `;
231
+ }
232
+ /**
233
+ * Génère le contenu du fichier server.ts
234
+ */
235
+ function generateServerTs(tables) {
236
+ const exports = [];
237
+ for (const table of tables) {
238
+ for (const section of table.sections) {
239
+ exports.push(`export { GET, POST, PUT, DELETE } from "./api/${section}/${table.name}.js";`);
240
+ }
241
+ }
242
+ return `// Server-only exports (Route Handlers, Server Actions, etc.)
243
+ ${exports.join("\n")}
244
+ `;
245
+ }
246
+ /**
247
+ * Génère le contenu d'une page
248
+ */
249
+ function generatePageComponent(pageName, section) {
250
+ const componentName = pageName
251
+ .split("-")
252
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
253
+ .join("");
254
+ return `"use client";
255
+
256
+ import { Card, CardBody, CardHeader } from "@lastbrain/ui";
257
+
258
+ export function ${componentName}Page() {
259
+ return (
260
+ <div className="container mx-auto p-6">
261
+ <Card>
262
+ <CardHeader>
263
+ <h1 className="text-2xl font-bold">${componentName}</h1>
264
+ </CardHeader>
265
+ <CardBody>
266
+ <p className="text-default-600">
267
+ Contenu de la page ${pageName} (section: ${section})
268
+ </p>
269
+ </CardBody>
270
+ </Card>
271
+ </div>
272
+ );
273
+ }
274
+ `;
275
+ }
276
+ /**
277
+ * Génère le contenu d'une route API CRUD
278
+ */
279
+ function generateApiRoute(tableName, section) {
280
+ const authRequired = section !== "public";
281
+ return `import { getSupabaseServerClient } from "@lastbrain/core/server";
282
+
283
+ const jsonResponse = (payload: unknown, status = 200) => {
284
+ return new Response(JSON.stringify(payload), {
285
+ headers: {
286
+ "content-type": "application/json"
287
+ },
288
+ status
289
+ });
290
+ };
291
+
292
+ /**
293
+ * GET - Liste tous les enregistrements de ${tableName}
294
+ */
295
+ export async function GET(request: Request) {
296
+ const supabase = await getSupabaseServerClient();
297
+ ${authRequired ? `
298
+ const { data: { user }, error: authError } = await supabase.auth.getUser();
299
+ if (authError || !user) {
300
+ return jsonResponse({ error: "Non authentifié" }, 401);
301
+ }
302
+ ` : ""}
303
+
304
+ const { data, error } = await supabase
305
+ .from("${tableName}")
306
+ .select("*");
307
+
308
+ if (error) {
309
+ return jsonResponse({ error: error.message }, 400);
310
+ }
311
+
312
+ return jsonResponse({ data });
313
+ }
314
+
315
+ /**
316
+ * POST - Crée un nouvel enregistrement dans ${tableName}
317
+ */
318
+ export async function POST(request: Request) {
319
+ const supabase = await getSupabaseServerClient();
320
+ ${authRequired ? `
321
+ const { data: { user }, error: authError } = await supabase.auth.getUser();
322
+ if (authError || !user) {
323
+ return jsonResponse({ error: "Non authentifié" }, 401);
324
+ }
325
+ ` : ""}
326
+
327
+ const body = await request.json();
328
+
329
+ const { data, error } = await supabase
330
+ .from("${tableName}")
331
+ .insert(body)
332
+ .select()
333
+ .single();
334
+
335
+ if (error) {
336
+ return jsonResponse({ error: error.message }, 400);
337
+ }
338
+
339
+ return jsonResponse({ data }, 201);
340
+ }
341
+
342
+ /**
343
+ * PUT - Met Ă  jour un enregistrement dans ${tableName}
344
+ */
345
+ export async function PUT(request: Request) {
346
+ const supabase = await getSupabaseServerClient();
347
+ ${authRequired ? `
348
+ const { data: { user }, error: authError } = await supabase.auth.getUser();
349
+ if (authError || !user) {
350
+ return jsonResponse({ error: "Non authentifié" }, 401);
351
+ }
352
+ ` : ""}
353
+
354
+ const body = await request.json();
355
+ const { id, ...updateData } = body;
356
+
357
+ if (!id) {
358
+ return jsonResponse({ error: "ID requis pour la mise Ă  jour" }, 400);
359
+ }
360
+
361
+ const { data, error } = await supabase
362
+ .from("${tableName}")
363
+ .update(updateData)
364
+ .eq("id", id)
365
+ .select()
366
+ .single();
367
+
368
+ if (error) {
369
+ return jsonResponse({ error: error.message }, 400);
370
+ }
371
+
372
+ return jsonResponse({ data });
373
+ }
374
+
375
+ /**
376
+ * DELETE - Supprime un enregistrement de ${tableName}
377
+ */
378
+ export async function DELETE(request: Request) {
379
+ const supabase = await getSupabaseServerClient();
380
+ ${authRequired ? `
381
+ const { data: { user }, error: authError } = await supabase.auth.getUser();
382
+ if (authError || !user) {
383
+ return jsonResponse({ error: "Non authentifié" }, 401);
384
+ }
385
+ ` : ""}
386
+
387
+ const { searchParams } = new URL(request.url);
388
+ const id = searchParams.get("id");
389
+
390
+ if (!id) {
391
+ return jsonResponse({ error: "ID requis pour la suppression" }, 400);
392
+ }
393
+
394
+ const { error } = await supabase
395
+ .from("${tableName}")
396
+ .delete()
397
+ .eq("id", id);
398
+
399
+ if (error) {
400
+ return jsonResponse({ error: error.message }, 400);
401
+ }
402
+
403
+ return jsonResponse({ success: true });
404
+ }
405
+ `;
406
+ }
407
+ /**
408
+ * Génère le contenu d'un fichier de migration SQL
409
+ */
410
+ function generateMigration(tables, slug) {
411
+ const timestamp = new Date()
412
+ .toISOString()
413
+ .replace(/[-:]/g, "")
414
+ .split(".")[0]
415
+ .replace("T", "");
416
+ const tablesSQL = tables
417
+ .map((table) => {
418
+ return `-- ===========================================================================
419
+ -- Table: public.${table.name}
420
+ -- ===========================================================================
421
+ CREATE TABLE IF NOT EXISTS public.${table.name} (
422
+ id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
423
+ owner_id uuid NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
424
+ title text NOT NULL,
425
+ description text,
426
+ created_at timestamptz NOT NULL DEFAULT now(),
427
+ updated_at timestamptz NOT NULL DEFAULT now()
428
+ );
429
+
430
+ -- RLS
431
+ ALTER TABLE public.${table.name} ENABLE ROW LEVEL SECURITY;
432
+
433
+ -- Politique: Les utilisateurs peuvent voir leurs propres enregistrements
434
+ DROP POLICY IF EXISTS ${table.name}_owner_select ON public.${table.name};
435
+ CREATE POLICY ${table.name}_owner_select ON public.${table.name}
436
+ FOR SELECT TO authenticated
437
+ USING (owner_id = auth.uid());
438
+
439
+ -- Politique: Les utilisateurs peuvent créer leurs propres enregistrements
440
+ DROP POLICY IF EXISTS ${table.name}_owner_insert ON public.${table.name};
441
+ CREATE POLICY ${table.name}_owner_insert ON public.${table.name}
442
+ FOR INSERT TO authenticated
443
+ WITH CHECK (owner_id = auth.uid());
444
+
445
+ -- Politique: Les utilisateurs peuvent modifier leurs propres enregistrements
446
+ DROP POLICY IF EXISTS ${table.name}_owner_update ON public.${table.name};
447
+ CREATE POLICY ${table.name}_owner_update ON public.${table.name}
448
+ FOR UPDATE TO authenticated
449
+ USING (owner_id = auth.uid())
450
+ WITH CHECK (owner_id = auth.uid());
451
+
452
+ -- Politique: Les utilisateurs peuvent supprimer leurs propres enregistrements
453
+ DROP POLICY IF EXISTS ${table.name}_owner_delete ON public.${table.name};
454
+ CREATE POLICY ${table.name}_owner_delete ON public.${table.name}
455
+ FOR DELETE TO authenticated
456
+ USING (owner_id = auth.uid());
457
+
458
+ -- Trigger updated_at
459
+ DROP TRIGGER IF EXISTS set_${table.name}_updated_at ON public.${table.name};
460
+ CREATE TRIGGER set_${table.name}_updated_at
461
+ BEFORE UPDATE ON public.${table.name}
462
+ FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
463
+
464
+ -- Index
465
+ CREATE INDEX IF NOT EXISTS idx_${table.name}_owner_id ON public.${table.name}(owner_id);
466
+
467
+ -- Grants
468
+ GRANT SELECT, INSERT, UPDATE, DELETE ON public.${table.name} TO service_role;
469
+ `;
470
+ })
471
+ .join("\n\n");
472
+ return `-- ${slug} module initial migration
473
+ -- Auto-generated by module-create.ts
474
+ -- NOTE: uses helper function set_updated_at() from base migration
475
+
476
+ -- ===========================================================================
477
+ -- Helper: set_updated_at trigger function (if not already present)
478
+ -- ===========================================================================
479
+ CREATE OR REPLACE FUNCTION public.set_updated_at()
480
+ RETURNS trigger
481
+ LANGUAGE plpgsql
482
+ AS $$
483
+ BEGIN
484
+ NEW.updated_at := now();
485
+ RETURN NEW;
486
+ END;
487
+ $$;
488
+
489
+ ${tablesSQL}
490
+ `;
491
+ }
492
+ /**
493
+ * Crée la structure du module
494
+ */
495
+ async function createModuleStructure(config, rootDir) {
496
+ const moduleDir = path.join(rootDir, "packages", config.slug);
497
+ console.log(chalk.blue(`\n📦 Création du module ${config.slug}...\n`));
498
+ // Créer la structure de base
499
+ await fs.ensureDir(moduleDir);
500
+ await fs.ensureDir(path.join(moduleDir, "src"));
501
+ await fs.ensureDir(path.join(moduleDir, "src", "web"));
502
+ await fs.ensureDir(path.join(moduleDir, "src", "api"));
503
+ await fs.ensureDir(path.join(moduleDir, "supabase", "migrations"));
504
+ // Créer package.json
505
+ console.log(chalk.yellow(" đź“„ package.json"));
506
+ await fs.writeFile(path.join(moduleDir, "package.json"), generatePackageJson(config.moduleName, config.slug));
507
+ // Créer tsconfig.json
508
+ console.log(chalk.yellow(" đź“„ tsconfig.json"));
509
+ await fs.writeFile(path.join(moduleDir, "tsconfig.json"), generateTsConfig());
510
+ // Créer {slug}.build.config.ts
511
+ const buildConfigFileName = `${config.slug}.build.config.ts`;
512
+ console.log(chalk.yellow(` đź“„ src/${buildConfigFileName}`));
513
+ await fs.writeFile(path.join(moduleDir, "src", buildConfigFileName), generateBuildConfig(config));
514
+ // Créer index.ts
515
+ console.log(chalk.yellow(" đź“„ src/index.ts"));
516
+ await fs.writeFile(path.join(moduleDir, "src", "index.ts"), generateIndexTs(config.pages));
517
+ // Créer server.ts
518
+ console.log(chalk.yellow(" đź“„ src/server.ts"));
519
+ await fs.writeFile(path.join(moduleDir, "src", "server.ts"), generateServerTs(config.tables));
520
+ // Créer les pages
521
+ console.log(chalk.blue("\n📄 Création des pages..."));
522
+ for (const page of config.pages) {
523
+ const pagePath = path.join(moduleDir, "src", "web", page.section);
524
+ await fs.ensureDir(pagePath);
525
+ const componentName = page.name
526
+ .split("-")
527
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
528
+ .join("");
529
+ const fileName = `${componentName}Page.tsx`;
530
+ console.log(chalk.yellow(` đź“„ src/web/${page.section}/${fileName}`));
531
+ await fs.writeFile(path.join(pagePath, fileName), generatePageComponent(page.name, page.section));
532
+ }
533
+ // Créer les routes API
534
+ if (config.tables.length > 0) {
535
+ console.log(chalk.blue("\n🔌 Création des routes API..."));
536
+ for (const table of config.tables) {
537
+ for (const section of table.sections) {
538
+ const apiPath = path.join(moduleDir, "src", "api", section);
539
+ await fs.ensureDir(apiPath);
540
+ const fileName = `${table.name}.ts`;
541
+ console.log(chalk.yellow(` đź“„ src/api/${section}/${fileName}`));
542
+ await fs.writeFile(path.join(apiPath, fileName), generateApiRoute(table.name, section));
543
+ }
544
+ }
545
+ }
546
+ // Créer les migrations
547
+ if (config.tables.length > 0) {
548
+ console.log(chalk.blue("\n🗄️ Création des migrations..."));
549
+ const timestamp = new Date()
550
+ .toISOString()
551
+ .replace(/[-:]/g, "")
552
+ .split(".")[0]
553
+ .replace("T", "");
554
+ const migrationFileName = `${timestamp}_${config.slug}_init.sql`;
555
+ console.log(chalk.yellow(` đź“„ supabase/migrations/${migrationFileName}`));
556
+ await fs.writeFile(path.join(moduleDir, "supabase", "migrations", migrationFileName), generateMigration(config.tables, config.slug));
557
+ }
558
+ console.log(chalk.green(`\n✅ Module ${config.slug} créé avec succès!\n`));
559
+ console.log(chalk.gray(`đź“‚ Emplacement: ${moduleDir}\n`));
560
+ console.log(chalk.blue("Prochaines étapes:"));
561
+ console.log(chalk.gray(` 1. cd ${moduleDir}`));
562
+ console.log(chalk.gray(` 2. pnpm install`));
563
+ console.log(chalk.gray(` 3. pnpm build`));
564
+ console.log(chalk.gray(` 4. Ajouter le module Ă  votre app avec: pnpm lastbrain add ${config.slug.replace("module-", "")}\n`));
565
+ }
566
+ /**
567
+ * Point d'entrée du script
568
+ */
569
+ export async function createModule() {
570
+ console.log(chalk.blue("\n🚀 Création d'un nouveau module LastBrain\n"));
571
+ const answers = await inquirer.prompt([
572
+ {
573
+ type: "input",
574
+ name: "slug",
575
+ message: "Nom du module (sera préfixé par 'module-'):",
576
+ validate: (input) => {
577
+ if (!input || input.trim() === "") {
578
+ return "Le nom du module est requis";
579
+ }
580
+ if (!/^[a-z0-9-]+$/.test(input)) {
581
+ return "Le nom doit contenir uniquement des lettres minuscules, chiffres et tirets";
582
+ }
583
+ return true;
584
+ },
585
+ filter: (input) => input.trim().toLowerCase(),
586
+ },
587
+ {
588
+ type: "input",
589
+ name: "pagesPublic",
590
+ message: "Pages publiques (séparées par des virgules, ex: legal, privacy, terms):",
591
+ default: "",
592
+ },
593
+ {
594
+ type: "input",
595
+ name: "pagesAuth",
596
+ message: "Pages authentifiées (séparées par des virgules, ex: dashboard, profile):",
597
+ default: "",
598
+ },
599
+ {
600
+ type: "input",
601
+ name: "pagesAdmin",
602
+ message: "Pages admin (séparées par des virgules, ex: settings, users):",
603
+ default: "",
604
+ },
605
+ {
606
+ type: "input",
607
+ name: "tables",
608
+ message: "Tables (séparées par des virgules, ex: settings, notifications):",
609
+ default: "",
610
+ },
611
+ ]);
612
+ // Construire la configuration du module
613
+ const slug = `module-${answers.slug}`;
614
+ const moduleName = `@lastbrain/${slug}`;
615
+ const pages = [];
616
+ // Pages publiques
617
+ const publicPages = parsePagesList(answers.pagesPublic);
618
+ for (const pageName of publicPages) {
619
+ pages.push({
620
+ section: "public",
621
+ path: `/${pageName}`,
622
+ name: pageName,
623
+ });
624
+ }
625
+ // Pages auth
626
+ const authPages = parsePagesList(answers.pagesAuth);
627
+ for (const pageName of authPages) {
628
+ pages.push({
629
+ section: "auth",
630
+ path: `/${pageName}`,
631
+ name: pageName,
632
+ });
633
+ }
634
+ // Pages admin
635
+ const adminPages = parsePagesList(answers.pagesAdmin);
636
+ for (const pageName of adminPages) {
637
+ pages.push({
638
+ section: "admin",
639
+ path: `/${pageName}`,
640
+ name: pageName,
641
+ });
642
+ }
643
+ // Tables
644
+ const tableNames = parseTablesList(answers.tables);
645
+ const tables = [];
646
+ for (const tableName of tableNames) {
647
+ // Déterminer dans quelles sections créer les APIs pour cette table
648
+ const sections = [];
649
+ if (publicPages.length > 0)
650
+ sections.push("public");
651
+ if (authPages.length > 0)
652
+ sections.push("auth");
653
+ if (adminPages.length > 0)
654
+ sections.push("admin");
655
+ // Si aucune page n'est définie, créer au moins les APIs auth
656
+ if (sections.length === 0)
657
+ sections.push("auth");
658
+ tables.push({
659
+ name: tableName,
660
+ sections,
661
+ });
662
+ }
663
+ const config = {
664
+ slug,
665
+ moduleName,
666
+ pages,
667
+ tables,
668
+ };
669
+ // Trouver le répertoire racine du workspace (chercher pnpm-workspace.yaml)
670
+ let rootDir = process.cwd();
671
+ let attempts = 0;
672
+ const maxAttempts = 5;
673
+ while (attempts < maxAttempts) {
674
+ const workspaceFile = path.join(rootDir, "pnpm-workspace.yaml");
675
+ if (fs.existsSync(workspaceFile)) {
676
+ break;
677
+ }
678
+ rootDir = path.resolve(rootDir, "..");
679
+ attempts++;
680
+ }
681
+ if (attempts === maxAttempts) {
682
+ console.error(chalk.red("❌ Impossible de trouver le répertoire racine du workspace (pnpm-workspace.yaml non trouvé)"));
683
+ process.exit(1);
684
+ }
685
+ // Créer le module
686
+ await createModuleStructure(config, rootDir);
687
+ }
688
+ // Exécuter le script si appelé directement
689
+ if (import.meta.url === `file://${process.argv[1]}`) {
690
+ createModule().catch((error) => {
691
+ console.error(chalk.red("❌ Erreur:"), error);
692
+ process.exit(1);
693
+ });
694
+ }
@@ -0,0 +1,2 @@
1
+ export declare function listModules(targetDir: string): Promise<void>;
2
+ //# sourceMappingURL=module-list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module-list.d.ts","sourceRoot":"","sources":["../../src/scripts/module-list.ts"],"names":[],"mappings":"AAKA,wBAAsB,WAAW,CAAC,SAAS,EAAE,MAAM,iBA+BlD"}