@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,13 @@
1
+ interface ModuleDefinition {
2
+ name: string;
3
+ package: string;
4
+ displayName: string;
5
+ description: string;
6
+ hasMigrations: boolean;
7
+ migrationsPath?: string;
8
+ migrationsDownPath?: string;
9
+ }
10
+ export declare const AVAILABLE_MODULES: ModuleDefinition[];
11
+ export declare function addModule(moduleName: string, targetDir: string): Promise<void>;
12
+ export {};
13
+ //# sourceMappingURL=module-add.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module-add.d.ts","sourceRoot":"","sources":["../../src/scripts/module-add.ts"],"names":[],"mappings":"AAMA,UAAU,gBAAgB;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,OAAO,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAGD,eAAO,MAAM,iBAAiB,EAAE,gBAAgB,EAsB/C,CAAC;AAEF,wBAAsB,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,iBAwMpE"}
@@ -0,0 +1,178 @@
1
+ import fs from "fs-extra";
2
+ import path from "path";
3
+ import chalk from "chalk";
4
+ import { execSync } from "child_process";
5
+ import inquirer from "inquirer";
6
+ // Registre des modules disponibles
7
+ export const AVAILABLE_MODULES = [
8
+ {
9
+ name: "auth",
10
+ package: "@lastbrain/module-auth",
11
+ displayName: "🔐 Authentication",
12
+ description: "Système d'authentification complet (signin, signup, sessions)",
13
+ hasMigrations: true,
14
+ migrationsPath: "supabase/migrations",
15
+ migrationsDownPath: "supabase/migrations-down",
16
+ },
17
+ {
18
+ name: "ai",
19
+ package: "@lastbrain/module-ai",
20
+ displayName: "🤖 AI Generation",
21
+ description: "Génération de texte et d'images avec gestion de tokens",
22
+ hasMigrations: true,
23
+ migrationsPath: "supabase/migrations",
24
+ migrationsDownPath: "supabase/migrations-down",
25
+ },
26
+ // Ajouter d'autres modules ici au fur et à mesure
27
+ ];
28
+ export async function addModule(moduleName, targetDir) {
29
+ console.log(chalk.blue(`\n🔧 Ajout du module: ${moduleName}\n`));
30
+ const module = AVAILABLE_MODULES.find((m) => m.name === moduleName);
31
+ if (!module) {
32
+ console.error(chalk.red(`❌ Module "${moduleName}" non trouvé. Modules disponibles:`));
33
+ AVAILABLE_MODULES.forEach((m) => {
34
+ console.log(chalk.gray(` - ${m.name}: ${m.description}`));
35
+ });
36
+ process.exit(1);
37
+ }
38
+ // 1. Vérifier qu'on est dans un projet LastBrain
39
+ const pkgPath = path.join(targetDir, "package.json");
40
+ if (!fs.existsSync(pkgPath)) {
41
+ console.error(chalk.red("❌ package.json non trouvé"));
42
+ process.exit(1);
43
+ }
44
+ const pkg = await fs.readJson(pkgPath);
45
+ // Vérifier si le module n'est pas déjà installé
46
+ if (pkg.dependencies?.[module.package] ||
47
+ pkg.devDependencies?.[module.package]) {
48
+ console.log(chalk.yellow(`⚠️ Module ${module.package} déjà installé`));
49
+ return;
50
+ }
51
+ // 2. Ajouter la dépendance au package.json
52
+ console.log(chalk.yellow(`📦 Ajout de ${module.package} aux dépendances...`));
53
+ pkg.dependencies = pkg.dependencies || {};
54
+ pkg.dependencies[module.package] = "workspace:*";
55
+ await fs.writeJson(pkgPath, pkg, { spaces: 2 });
56
+ // 3. Installer les dépendances
57
+ console.log(chalk.yellow("📥 Installation des dépendances..."));
58
+ try {
59
+ execSync("pnpm install", { cwd: targetDir, stdio: "inherit" });
60
+ }
61
+ catch (error) {
62
+ console.error(chalk.red("❌ Erreur lors de l'installation"));
63
+ process.exit(1);
64
+ }
65
+ // 5. Copier les migrations du module
66
+ let copiedMigrationFiles = [];
67
+ if (module.hasMigrations) {
68
+ console.log(chalk.yellow("\n📋 Copie des migrations du module..."));
69
+ // Trouver le chemin du module installé
70
+ const modulePackagePath = path.join(targetDir, "node_modules", ...module.package.split("/"));
71
+ const moduleMigrationsDir = path.join(modulePackagePath, module.migrationsPath || "supabase/migrations");
72
+ const projectMigrationsDir = path.join(targetDir, "supabase", "migrations");
73
+ await fs.ensureDir(projectMigrationsDir);
74
+ if (fs.existsSync(moduleMigrationsDir)) {
75
+ const migrationFiles = fs.readdirSync(moduleMigrationsDir);
76
+ for (const file of migrationFiles) {
77
+ if (file.endsWith(".sql")) {
78
+ const sourcePath = path.join(moduleMigrationsDir, file);
79
+ const destPath = path.join(projectMigrationsDir, file);
80
+ await fs.copyFile(sourcePath, destPath);
81
+ copiedMigrationFiles.push(file);
82
+ console.log(chalk.green(` ✓ ${file}`));
83
+ }
84
+ }
85
+ // 6. Demander comment appliquer les migrations
86
+ console.log(chalk.yellow("\n🗄️ Application des migrations à la base de données\n"));
87
+ console.log("Choisissez une option:");
88
+ console.log(chalk.cyan(" 1) 🔄 Reset complet (supabase db reset) - Recommandé en dev"));
89
+ console.log(chalk.cyan(" 2) ⬆️ Push uniquement les nouvelles migrations (supabase migration up)"));
90
+ console.log(chalk.cyan(" 3) ⏭️ Ignorer pour l'instant"));
91
+ console.log("");
92
+ const { migrationAction } = await inquirer.prompt([
93
+ {
94
+ type: "rawlist",
95
+ name: "migrationAction",
96
+ message: "Comment voulez-vous appliquer les migrations ?",
97
+ choices: [
98
+ {
99
+ name: "🔄 Reset complet (supabase db reset) - Recommandé en dev",
100
+ value: "reset",
101
+ },
102
+ {
103
+ name: "⬆️ Push uniquement les nouvelles migrations (supabase migration up)",
104
+ value: "push",
105
+ },
106
+ {
107
+ name: "⏭️ Ignorer pour l'instant",
108
+ value: "skip",
109
+ },
110
+ ],
111
+ },
112
+ ]);
113
+ if (migrationAction === "reset") {
114
+ console.log(chalk.yellow("\n🔄 Reset de la base de données..."));
115
+ try {
116
+ execSync("supabase db reset", { cwd: targetDir, stdio: "inherit" });
117
+ console.log(chalk.green("✓ Base de données réinitialisée"));
118
+ }
119
+ catch (error) {
120
+ console.error(chalk.red("❌ Erreur lors du reset"));
121
+ }
122
+ }
123
+ else if (migrationAction === "push") {
124
+ console.log(chalk.yellow("\n⬆️ Application des nouvelles migrations..."));
125
+ try {
126
+ execSync("supabase migration up", { cwd: targetDir, stdio: "inherit" });
127
+ console.log(chalk.green("✓ Migrations appliquées"));
128
+ }
129
+ catch (error) {
130
+ console.error(chalk.red("❌ Erreur lors de l'application des migrations"));
131
+ }
132
+ }
133
+ else {
134
+ console.log(chalk.gray("\n⚠️ N'oubliez pas d'appliquer les migrations avec:\n supabase db reset ou supabase migration up\n"));
135
+ }
136
+ }
137
+ else {
138
+ console.log(chalk.gray(" Aucune migration trouvée pour ce module"));
139
+ }
140
+ }
141
+ // 4. Enregistrer le module dans la configuration
142
+ const modulesConfigPath = path.join(targetDir, ".lastbrain", "modules.json");
143
+ await fs.ensureDir(path.dirname(modulesConfigPath));
144
+ let modulesConfig = { modules: [] };
145
+ if (fs.existsSync(modulesConfigPath)) {
146
+ modulesConfig = await fs.readJson(modulesConfigPath);
147
+ }
148
+ // Initialiser tous les modules disponibles s'ils n'existent pas
149
+ for (const availableModule of AVAILABLE_MODULES) {
150
+ const exists = modulesConfig.modules.find(m => m.package === availableModule.package);
151
+ if (!exists) {
152
+ modulesConfig.modules.push({
153
+ package: availableModule.package,
154
+ active: false,
155
+ migrations: [],
156
+ });
157
+ }
158
+ }
159
+ const existingModuleIndex = modulesConfig.modules.findIndex((m) => m.package === module.package);
160
+ const migrationsList = copiedMigrationFiles;
161
+ if (existingModuleIndex >= 0) {
162
+ modulesConfig.modules[existingModuleIndex] = {
163
+ package: module.package,
164
+ active: true,
165
+ migrations: migrationsList,
166
+ };
167
+ }
168
+ else {
169
+ modulesConfig.modules.push({
170
+ package: module.package,
171
+ active: true,
172
+ migrations: migrationsList,
173
+ });
174
+ }
175
+ await fs.writeJson(modulesConfigPath, modulesConfig, { spaces: 2 });
176
+ console.log(chalk.green(`\n✅ Module ${module.displayName} ajouté avec succès!\n`));
177
+ console.log(chalk.gray("Le serveur de développement redémarrera automatiquement.\n"));
178
+ }
@@ -0,0 +1,2 @@
1
+ export declare function runModuleBuild(): Promise<void>;
2
+ //# sourceMappingURL=module-build.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module-build.d.ts","sourceRoot":"","sources":["../../src/scripts/module-build.ts"],"names":[],"mappings":"AAseA,wBAAsB,cAAc,kBAanC"}
@@ -0,0 +1,413 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { createRequire } from "node:module";
4
+ // Utiliser PROJECT_ROOT si défini (pour pnpm --filter), sinon process.cwd()
5
+ const projectRoot = process.env.PROJECT_ROOT || process.cwd();
6
+ const appDirectory = path.join(projectRoot, "app");
7
+ // Créer un require dans le contexte de l'application pour résoudre les modules installés dans l'app
8
+ const projectRequire = createRequire(path.join(projectRoot, "package.json"));
9
+ // Charger les modules depuis modules.json
10
+ async function loadModuleConfigs() {
11
+ const moduleConfigs = [];
12
+ const modulesJsonPath = path.join(projectRoot, ".lastbrain", "modules.json");
13
+ if (!fs.existsSync(modulesJsonPath)) {
14
+ return moduleConfigs;
15
+ }
16
+ try {
17
+ const modulesData = JSON.parse(fs.readFileSync(modulesJsonPath, "utf-8"));
18
+ const modules = modulesData.modules || [];
19
+ for (const module of modules) {
20
+ // Ne charger que les modules actifs
21
+ if (module.active === false) {
22
+ continue;
23
+ }
24
+ const packageName = module.package;
25
+ try {
26
+ const moduleSuffix = packageName.replace("@lastbrain/module-", "");
27
+ const possibleConfigNames = [
28
+ `${moduleSuffix}.build.config`,
29
+ "build.config",
30
+ ];
31
+ let loaded = false;
32
+ for (const configName of possibleConfigNames) {
33
+ try {
34
+ // Résoudre le chemin du module depuis l'application
35
+ const modulePath = projectRequire.resolve(`${packageName}/${configName}`);
36
+ // Convertir en URL file:// pour l'import dynamique
37
+ const moduleUrl = `file://${modulePath}`;
38
+ const moduleImport = await import(moduleUrl);
39
+ if (moduleImport.default) {
40
+ moduleConfigs.push(moduleImport.default);
41
+ loaded = true;
42
+ break;
43
+ }
44
+ }
45
+ catch (err) {
46
+ // Essayer le nom suivant
47
+ }
48
+ }
49
+ if (!loaded) {
50
+ console.warn(`⚠️ Could not load build config for ${packageName}`);
51
+ }
52
+ }
53
+ catch (error) {
54
+ console.warn(`⚠️ Failed to load module ${packageName}:`, error);
55
+ }
56
+ }
57
+ }
58
+ catch (error) {
59
+ console.error("❌ Error loading modules.json:", error);
60
+ }
61
+ return moduleConfigs;
62
+ }
63
+ const sectionDirectoryMap = {
64
+ public: ["(public)"],
65
+ auth: ["auth"],
66
+ admin: ["admin"],
67
+ user: ["auth", "user"],
68
+ };
69
+ const sectionLayoutMap = {
70
+ auth: "AuthLayout",
71
+ admin: "AdminLayout",
72
+ "(public)": "PublicLayout",
73
+ };
74
+ const generatedSections = new Set();
75
+ const navigation = {
76
+ public: [],
77
+ auth: [],
78
+ admin: [],
79
+ };
80
+ const userMenu = [];
81
+ function ensureDirectory(dir) {
82
+ fs.mkdirSync(dir, { recursive: true });
83
+ }
84
+ function ensureSectionLayout(sectionPath) {
85
+ const sectionDir = path.join(appDirectory, ...sectionPath);
86
+ const sectionKey = sectionPath[0];
87
+ // Éviter de générer plusieurs fois le même layout
88
+ if (generatedSections.has(sectionKey)) {
89
+ return;
90
+ }
91
+ ensureDirectory(sectionDir);
92
+ const layoutName = sectionLayoutMap[sectionKey];
93
+ if (layoutName) {
94
+ const layoutPath = path.join(sectionDir, "layout.tsx");
95
+ if (!fs.existsSync(layoutPath)) {
96
+ const layoutContent = `import { ${layoutName} } from "@lastbrain/app";
97
+
98
+ export default function SectionLayout({ children }: { children: React.ReactNode }) {
99
+ return <${layoutName}>{children}</${layoutName}>;
100
+ }
101
+ `;
102
+ fs.writeFileSync(layoutPath, layoutContent);
103
+ console.log(`📐 Generated section layout: ${layoutPath}`);
104
+ }
105
+ }
106
+ generatedSections.add(sectionKey);
107
+ }
108
+ function toPascalCase(value) {
109
+ return value
110
+ .split(/[^a-zA-Z0-9]+/)
111
+ .filter(Boolean)
112
+ .map((segment) => segment[0].toUpperCase() + segment.slice(1))
113
+ .join("");
114
+ }
115
+ function buildPage(moduleConfig, page) {
116
+ const segments = page.path.replace(/^\/+/, "").split("/").filter(Boolean);
117
+ const sectionPath = sectionDirectoryMap[page.section] ?? ["(public)"];
118
+ // Générer le layout de section si nécessaire
119
+ ensureSectionLayout(sectionPath);
120
+ const routeDir = path.join(appDirectory, ...sectionPath, ...segments);
121
+ const filePath = path.join(routeDir, "page.tsx");
122
+ ensureDirectory(routeDir);
123
+ const wrapperSuffix = segments.length
124
+ ? toPascalCase(segments.join("-"))
125
+ : "Root";
126
+ const wrapperName = `${page.componentExport}${wrapperSuffix}Route`;
127
+ // Vérifier si la route a des paramètres dynamiques (segments avec [])
128
+ const hasDynamicParams = segments.some(seg => seg.startsWith('[') && seg.endsWith(']'));
129
+ // Pour les pages publiques (signin, signup, etc.), utiliser dynamic import sans SSR
130
+ // pour éviter les erreurs d'hydratation avec les IDs HeroUI/React Aria
131
+ const isPublicAuthPage = page.section === "public" &&
132
+ (page.path.includes("signin") || page.path.includes("signup") || page.path.includes("reset-password"));
133
+ let content;
134
+ if (isPublicAuthPage) {
135
+ content = `"use client";
136
+
137
+ import dynamic from "next/dynamic";
138
+
139
+ const ${page.componentExport} = dynamic(
140
+ () => import("${moduleConfig.moduleName}").then((mod) => ({ default: mod.${page.componentExport} })),
141
+ { ssr: false }
142
+ );
143
+
144
+ export default function ${wrapperName}${hasDynamicParams ? '(props: any)' : '()'} {
145
+ return <${page.componentExport} ${hasDynamicParams ? '{...props}' : ''} />;
146
+ }
147
+ `;
148
+ }
149
+ else {
150
+ content = `import { ${page.componentExport} } from "${moduleConfig.moduleName}";
151
+
152
+ export default function ${wrapperName}${hasDynamicParams ? '(props: any)' : '()'} {
153
+ return <${page.componentExport} ${hasDynamicParams ? '{...props}' : ''} />;
154
+ }
155
+ `;
156
+ }
157
+ fs.writeFileSync(filePath, content);
158
+ console.log(`⭐ Generated page: ${filePath}`);
159
+ const entry = {
160
+ label: `Module:${moduleConfig.moduleName} ${page.componentExport}`,
161
+ path: page.path,
162
+ module: moduleConfig.moduleName,
163
+ section: page.section,
164
+ };
165
+ if (page.section === "user") {
166
+ userMenu.push(entry);
167
+ }
168
+ else if (page.section) {
169
+ navigation[page.section]?.push(entry);
170
+ }
171
+ }
172
+ function buildApi(moduleConfig, api) {
173
+ const segments = api.path.replace(/^\/+/, "").split("/").filter(Boolean);
174
+ const sanitizedSegments = segments[0] === "api" ? segments.slice(1) : segments;
175
+ const routeDir = path.join(appDirectory, "api", ...sanitizedSegments);
176
+ const filePath = path.join(routeDir, "route.ts");
177
+ ensureDirectory(routeDir);
178
+ const handler = `${moduleConfig.moduleName}/${api.entryPoint}`;
179
+ const content = `export { ${api.handlerExport} } from "${handler}";
180
+ `;
181
+ fs.writeFileSync(filePath, content);
182
+ console.log(`🔌 Generated API route: ${filePath}`);
183
+ }
184
+ function dumpNavigation() {
185
+ const navPath = path.join(appDirectory, "navigation.generated.ts");
186
+ const content = `export type MenuEntry = { label: string; path: string; module: string; section: string };
187
+
188
+ export const navigation = ${JSON.stringify(navigation, null, 2)};
189
+
190
+ export const userMenu = ${JSON.stringify(userMenu, null, 2)};
191
+ `;
192
+ fs.writeFileSync(navPath, content);
193
+ console.log(`🧭 Generated navigation metadata: ${navPath}`);
194
+ }
195
+ /**
196
+ * Génère le fichier config/menu.ts à partir des configurations de menu des modules
197
+ */
198
+ function generateMenuConfig(moduleConfigs) {
199
+ const configDir = path.join(projectRoot, "config");
200
+ ensureDirectory(configDir);
201
+ const menuPath = path.join(configDir, "menu.ts");
202
+ // Collecter les menus de tous les modules
203
+ const publicMenus = [];
204
+ const authMenus = [];
205
+ const adminMenus = [];
206
+ const accountMenus = [];
207
+ moduleConfigs.forEach((moduleConfig) => {
208
+ if (moduleConfig.menu) {
209
+ if (moduleConfig.menu.public) {
210
+ publicMenus.push(...moduleConfig.menu.public);
211
+ }
212
+ if (moduleConfig.menu.auth) {
213
+ authMenus.push(...moduleConfig.menu.auth);
214
+ }
215
+ if (moduleConfig.menu.admin) {
216
+ adminMenus.push(...moduleConfig.menu.admin);
217
+ }
218
+ if (moduleConfig.menu.account) {
219
+ accountMenus.push(...moduleConfig.menu.account);
220
+ }
221
+ }
222
+ });
223
+ // Trier par ordre
224
+ const sortByOrder = (a, b) => {
225
+ return (a.order ?? 999) - (b.order ?? 999);
226
+ };
227
+ publicMenus.sort(sortByOrder);
228
+ authMenus.sort(sortByOrder);
229
+ adminMenus.sort(sortByOrder);
230
+ accountMenus.sort(sortByOrder);
231
+ // Générer le contenu du fichier
232
+ const content = `// Auto-generated menu configuration
233
+ // Generated from module build configs
234
+
235
+ export interface MenuItem {
236
+ title: string;
237
+ description?: string;
238
+ icon?: string;
239
+ path: string;
240
+ order?: number;
241
+ }
242
+
243
+ export interface MenuConfig {
244
+ public: MenuItem[];
245
+ auth: MenuItem[];
246
+ admin: MenuItem[];
247
+ account: MenuItem[];
248
+ }
249
+
250
+ export const menuConfig: MenuConfig = {
251
+ public: ${JSON.stringify(publicMenus, null, 2)},
252
+ auth: ${JSON.stringify(authMenus, null, 2)},
253
+ admin: ${JSON.stringify(adminMenus, null, 2)},
254
+ account: ${JSON.stringify(accountMenus, null, 2)},
255
+ };
256
+ `;
257
+ fs.writeFileSync(menuPath, content);
258
+ console.log(`🍔 Generated menu configuration: ${menuPath}`);
259
+ }
260
+ function copyModuleMigrations(moduleConfigs) {
261
+ const supabaseMigrationsDir = path.join(projectRoot, "supabase", "migrations");
262
+ // S'assurer que le dossier migrations existe
263
+ if (!fs.existsSync(supabaseMigrationsDir)) {
264
+ ensureDirectory(supabaseMigrationsDir);
265
+ }
266
+ const modulesJsonPath = path.join(projectRoot, ".lastbrain", "modules.json");
267
+ // Charger modules.json
268
+ let modulesConfig = { modules: [] };
269
+ if (fs.existsSync(modulesJsonPath)) {
270
+ modulesConfig = JSON.parse(fs.readFileSync(modulesJsonPath, "utf-8"));
271
+ }
272
+ moduleConfigs.forEach((moduleConfig) => {
273
+ try {
274
+ const moduleName = moduleConfig.moduleName;
275
+ const copiedMigrations = [];
276
+ // Essayer plusieurs chemins possibles pour trouver le module
277
+ const possiblePaths = [
278
+ // Dans node_modules local de l'app
279
+ path.join(projectRoot, "node_modules", moduleName),
280
+ // Dans node_modules à la racine du workspace
281
+ path.join(projectRoot, "..", "..", "node_modules", moduleName),
282
+ // Directement dans packages/ (pour monorepo)
283
+ path.join(projectRoot, "..", "..", "packages", moduleName.replace("@lastbrain/", "")),
284
+ ];
285
+ let moduleBasePath = null;
286
+ for (const possiblePath of possiblePaths) {
287
+ if (fs.existsSync(possiblePath)) {
288
+ moduleBasePath = possiblePath;
289
+ break;
290
+ }
291
+ }
292
+ if (!moduleBasePath) {
293
+ return;
294
+ }
295
+ const moduleMigrationsDir = path.join(moduleBasePath, "supabase", "migrations");
296
+ if (fs.existsSync(moduleMigrationsDir)) {
297
+ const migrationFiles = fs.readdirSync(moduleMigrationsDir);
298
+ migrationFiles.forEach((file) => {
299
+ if (file.endsWith(".sql")) {
300
+ const sourcePath = path.join(moduleMigrationsDir, file);
301
+ const destPath = path.join(supabaseMigrationsDir, file);
302
+ // Copier seulement si le fichier n'existe pas déjà
303
+ if (!fs.existsSync(destPath)) {
304
+ fs.copyFileSync(sourcePath, destPath);
305
+ console.log(`📦 Copied migration: ${file} from ${moduleName}`);
306
+ }
307
+ copiedMigrations.push(file);
308
+ }
309
+ });
310
+ }
311
+ // Mettre à jour modules.json avec les migrations
312
+ if (copiedMigrations.length > 0) {
313
+ const moduleIndex = modulesConfig.modules.findIndex((m) => m.package === moduleName);
314
+ if (moduleIndex >= 0) {
315
+ modulesConfig.modules[moduleIndex].migrations = copiedMigrations;
316
+ }
317
+ else {
318
+ modulesConfig.modules.push({
319
+ package: moduleName,
320
+ migrations: copiedMigrations,
321
+ });
322
+ }
323
+ }
324
+ }
325
+ catch (error) {
326
+ console.warn(`⚠️ Could not copy migrations from ${moduleConfig.moduleName}:`, error);
327
+ }
328
+ });
329
+ // Sauvegarder modules.json avec les migrations mises à jour
330
+ ensureDirectory(path.dirname(modulesJsonPath));
331
+ fs.writeFileSync(modulesJsonPath, JSON.stringify(modulesConfig, null, 2));
332
+ }
333
+ function generateDocsPage(moduleConfigs) {
334
+ const docsDir = path.join(appDirectory, "docs");
335
+ ensureDirectory(docsDir);
336
+ const docsPagePath = path.join(docsDir, "page.tsx");
337
+ // Charger tous les modules depuis modules.json (actifs ET inactifs)
338
+ const modulesJsonPath = path.join(projectRoot, ".lastbrain", "modules.json");
339
+ let allModules = [];
340
+ if (fs.existsSync(modulesJsonPath)) {
341
+ try {
342
+ const modulesData = JSON.parse(fs.readFileSync(modulesJsonPath, "utf-8"));
343
+ allModules = modulesData.modules || [];
344
+ }
345
+ catch (error) {
346
+ console.error("❌ Error reading modules.json:", error);
347
+ }
348
+ }
349
+ // Générer les imports des composants Doc de chaque module
350
+ const docImports = [];
351
+ const moduleConfigurations = [];
352
+ allModules.forEach((moduleEntry) => {
353
+ const moduleName = moduleEntry.package;
354
+ const moduleId = moduleName.replace("@lastbrain/module-", "");
355
+ const docComponentName = `${toPascalCase(moduleId)}ModuleDoc`;
356
+ // Trouver la config du module pour obtenir la description
357
+ const moduleConfig = moduleConfigs.find(mc => mc.moduleName === moduleName);
358
+ const description = moduleConfig ? getModuleDescription(moduleConfig) : "Module non configuré";
359
+ // Importer le composant Doc seulement pour les modules actifs
360
+ if (moduleEntry.active) {
361
+ docImports.push(`import { ${docComponentName} } from "${moduleName}";`);
362
+ }
363
+ const config = {
364
+ id: moduleId,
365
+ name: `Module ${moduleId.charAt(0).toUpperCase() + moduleId.slice(1)}`,
366
+ description: description,
367
+ component: docComponentName,
368
+ active: moduleEntry.active,
369
+ };
370
+ moduleConfigurations.push(` {
371
+ id: "${config.id}",
372
+ name: "${config.name}",
373
+ description: "${config.description}",
374
+ ${config.active ? `content: <${config.component} />,` : 'content: null,'}
375
+ available: ${config.active},
376
+ }`);
377
+ });
378
+ const docsContent = `// Auto-generated docs page with module documentation
379
+ import React from "react";
380
+ import { DocPage } from "@lastbrain/app";
381
+ ${docImports.join('\n')}
382
+
383
+ export default function DocsPage() {
384
+ const modules = [
385
+ ${moduleConfigurations.join(',\n')}
386
+ ];
387
+
388
+ return <DocPage modules={modules} />;
389
+ }
390
+ `;
391
+ fs.writeFileSync(docsPagePath, docsContent);
392
+ console.log(`📚 Generated docs page: ${docsPagePath}`);
393
+ }
394
+ function getModuleDescription(moduleConfig) {
395
+ // Essayer de déduire la description depuis les pages ou retourner une description par défaut
396
+ if (moduleConfig.pages.length > 0) {
397
+ return `${moduleConfig.pages.length} page(s), ${moduleConfig.apis.length} API(s)`;
398
+ }
399
+ return "Module documentation";
400
+ }
401
+ export async function runModuleBuild() {
402
+ ensureDirectory(appDirectory);
403
+ const moduleConfigs = await loadModuleConfigs();
404
+ moduleConfigs.forEach((moduleConfig) => {
405
+ moduleConfig.pages.forEach((page) => buildPage(moduleConfig, page));
406
+ moduleConfig.apis.forEach((api) => buildApi(moduleConfig, api));
407
+ });
408
+ dumpNavigation();
409
+ generateMenuConfig(moduleConfigs);
410
+ generateDocsPage(moduleConfigs);
411
+ copyModuleMigrations(moduleConfigs);
412
+ }
413
+ runModuleBuild();
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Point d'entrée du script
3
+ */
4
+ export declare function createModule(): Promise<void>;
5
+ //# sourceMappingURL=module-create.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module-create.d.ts","sourceRoot":"","sources":["../../src/scripts/module-create.ts"],"names":[],"mappings":"AAupBA;;GAEG;AACH,wBAAsB,YAAY,kBAsIjC"}