@lastbrain/app 0.1.24 → 0.1.25

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 (46) hide show
  1. package/dist/__tests__/module-registry.test.d.ts +2 -0
  2. package/dist/__tests__/module-registry.test.d.ts.map +1 -0
  3. package/dist/__tests__/module-registry.test.js +64 -0
  4. package/dist/app-shell/(admin)/layout.d.ts +3 -2
  5. package/dist/app-shell/(admin)/layout.d.ts.map +1 -1
  6. package/dist/app-shell/(admin)/layout.js +1 -1
  7. package/dist/app-shell/(auth)/layout.d.ts +3 -2
  8. package/dist/app-shell/(auth)/layout.d.ts.map +1 -1
  9. package/dist/app-shell/(auth)/layout.js +1 -1
  10. package/dist/cli.js +50 -0
  11. package/dist/layouts/AdminLayout.d.ts +3 -2
  12. package/dist/layouts/AdminLayout.d.ts.map +1 -1
  13. package/dist/layouts/AppProviders.d.ts +3 -2
  14. package/dist/layouts/AppProviders.d.ts.map +1 -1
  15. package/dist/layouts/AuthLayout.d.ts +3 -2
  16. package/dist/layouts/AuthLayout.d.ts.map +1 -1
  17. package/dist/layouts/PublicLayout.d.ts +3 -2
  18. package/dist/layouts/PublicLayout.d.ts.map +1 -1
  19. package/dist/layouts/RootLayout.d.ts +3 -2
  20. package/dist/layouts/RootLayout.d.ts.map +1 -1
  21. package/dist/scripts/db-init.js +1 -1
  22. package/dist/scripts/db-migrations-sync.js +5 -5
  23. package/dist/scripts/init-app.js +13 -10
  24. package/dist/scripts/module-add.d.ts.map +1 -1
  25. package/dist/scripts/module-add.js +1 -1
  26. package/dist/scripts/script-runner.d.ts +5 -0
  27. package/dist/scripts/script-runner.d.ts.map +1 -0
  28. package/dist/scripts/script-runner.js +25 -0
  29. package/dist/templates/DocPage.d.ts.map +1 -1
  30. package/dist/templates/DocPage.js +14 -14
  31. package/package.json +1 -1
  32. package/src/__tests__/module-registry.test.ts +74 -0
  33. package/src/app-shell/(admin)/layout.tsx +5 -3
  34. package/src/app-shell/(auth)/layout.tsx +5 -3
  35. package/src/cli.ts +50 -0
  36. package/src/layouts/AdminLayout.tsx +1 -3
  37. package/src/layouts/AppProviders.tsx +2 -4
  38. package/src/layouts/AuthLayout.tsx +1 -3
  39. package/src/layouts/PublicLayout.tsx +1 -3
  40. package/src/layouts/RootLayout.tsx +1 -2
  41. package/src/scripts/db-init.ts +2 -2
  42. package/src/scripts/db-migrations-sync.ts +1 -1
  43. package/src/scripts/init-app.ts +13 -10
  44. package/src/scripts/module-add.ts +1 -3
  45. package/src/scripts/script-runner.ts +28 -0
  46. package/src/templates/DocPage.tsx +74 -46
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=module-registry.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module-registry.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/module-registry.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,64 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { AVAILABLE_MODULES } from "../scripts/module-add";
3
+ describe("Module Registry", () => {
4
+ describe("AVAILABLE_MODULES", () => {
5
+ it("should have at least one module defined", () => {
6
+ expect(AVAILABLE_MODULES.length).toBeGreaterThan(0);
7
+ });
8
+ it("should have auth module defined", () => {
9
+ const authModule = AVAILABLE_MODULES.find((m) => m.name === "auth");
10
+ expect(authModule).toBeDefined();
11
+ expect(authModule?.package).toBe("@lastbrain/module-auth");
12
+ expect(authModule?.hasMigrations).toBe(true);
13
+ });
14
+ it("should have ai module defined", () => {
15
+ const aiModule = AVAILABLE_MODULES.find((m) => m.name === "ai");
16
+ expect(aiModule).toBeDefined();
17
+ expect(aiModule?.package).toBe("@lastbrain/module-ai");
18
+ expect(aiModule?.hasMigrations).toBe(true);
19
+ });
20
+ it("should have all required properties for each module", () => {
21
+ AVAILABLE_MODULES.forEach((module) => {
22
+ expect(module.name).toBeDefined();
23
+ expect(module.package).toBeDefined();
24
+ expect(module.displayName).toBeDefined();
25
+ expect(module.description).toBeDefined();
26
+ expect(typeof module.hasMigrations).toBe("boolean");
27
+ });
28
+ });
29
+ it("should have migrations paths when hasMigrations is true", () => {
30
+ AVAILABLE_MODULES.forEach((module) => {
31
+ if (module.hasMigrations) {
32
+ expect(module.migrationsPath).toBeDefined();
33
+ expect(module.migrationsDownPath).toBeDefined();
34
+ }
35
+ });
36
+ });
37
+ it("should have unique module names", () => {
38
+ const names = AVAILABLE_MODULES.map((m) => m.name);
39
+ const uniqueNames = new Set(names);
40
+ expect(names.length).toBe(uniqueNames.size);
41
+ });
42
+ it("should have unique package names", () => {
43
+ const packages = AVAILABLE_MODULES.map((m) => m.package);
44
+ const uniquePackages = new Set(packages);
45
+ expect(packages.length).toBe(uniquePackages.size);
46
+ });
47
+ it("should have display names with emoji", () => {
48
+ AVAILABLE_MODULES.forEach((module) => {
49
+ // Check if displayName contains at least one emoji (basic check)
50
+ expect(module.displayName).toMatch(/[\u{1F300}-\u{1F9FF}]/u);
51
+ });
52
+ });
53
+ it("should have non-empty descriptions", () => {
54
+ AVAILABLE_MODULES.forEach((module) => {
55
+ expect(module.description.length).toBeGreaterThan(0);
56
+ });
57
+ });
58
+ it("should have valid package names", () => {
59
+ AVAILABLE_MODULES.forEach((module) => {
60
+ expect(module.package).toMatch(/^@lastbrain\/module-/);
61
+ });
62
+ });
63
+ });
64
+ });
@@ -1,3 +1,4 @@
1
- import type { PropsWithChildren } from "react";
2
- export default function AdminShellLayout({ children }: PropsWithChildren<{}>): import("react/jsx-runtime").JSX.Element;
1
+ export default function AdminShellLayout({ children, }: {
2
+ children: React.ReactNode;
3
+ }): import("react/jsx-runtime").JSX.Element;
3
4
  //# sourceMappingURL=layout.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../src/app-shell/(admin)/layout.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAE/C,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,EAAE,QAAQ,EAAE,EAAE,iBAAiB,CAAC,EAAE,CAAC,2CAQ3E"}
1
+ {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../src/app-shell/(admin)/layout.tsx"],"names":[],"mappings":"AAEA,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,EACvC,QAAQ,GACT,EAAE;IACD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,2CAQA"}
@@ -1,5 +1,5 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- export default function AdminShellLayout({ children }) {
3
+ export default function AdminShellLayout({ children, }) {
4
4
  return (_jsxs("div", { className: "mx-auto max-w-5xl px-4 py-16", children: [_jsx("h1", { className: "text-3xl font-bold", children: "Section Admin" }), _jsx("p", { className: "text-slate-600", children: "Gestion avanc\u00E9e des modules." }), children] }));
5
5
  }
@@ -1,3 +1,4 @@
1
- import type { PropsWithChildren } from "react";
2
- export default function AuthLayout({ children }: PropsWithChildren<{}>): import("react/jsx-runtime").JSX.Element;
1
+ export default function AuthLayout({ children, }: {
2
+ children: React.ReactNode;
3
+ }): import("react/jsx-runtime").JSX.Element;
3
4
  //# sourceMappingURL=layout.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../src/app-shell/(auth)/layout.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAE/C,MAAM,CAAC,OAAO,UAAU,UAAU,CAAC,EAAE,QAAQ,EAAE,EAAE,iBAAiB,CAAC,EAAE,CAAC,2CAQrE"}
1
+ {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../src/app-shell/(auth)/layout.tsx"],"names":[],"mappings":"AAEA,MAAM,CAAC,OAAO,UAAU,UAAU,CAAC,EACjC,QAAQ,GACT,EAAE;IACD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,2CAQA"}
@@ -1,5 +1,5 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- export default function AuthLayout({ children }) {
3
+ export default function AuthLayout({ children, }) {
4
4
  return (_jsxs("div", { className: "mx-auto max-w-5xl px-4 py-16", children: [_jsx("h1", { className: "text-3xl font-bold", children: "Section Auth" }), _jsx("p", { className: "text-slate-600", children: "Les pages authentifi\u00E9es regroup\u00E9es ici." }), children] }));
5
5
  }
package/dist/cli.js CHANGED
@@ -85,4 +85,54 @@ program
85
85
  process.exit(1);
86
86
  }
87
87
  });
88
+ // Commandes de build et maintenance
89
+ program
90
+ .command("module:build")
91
+ .description("Build les configurations de modules")
92
+ .action(async () => {
93
+ try {
94
+ const { runModuleBuild } = await import("./scripts/module-build.js");
95
+ await runModuleBuild();
96
+ }
97
+ catch (error) {
98
+ console.error("❌ Erreur lors du build des modules:", error);
99
+ process.exit(1);
100
+ }
101
+ });
102
+ program
103
+ .command("db:init")
104
+ .description("Initialise la base de données Supabase")
105
+ .action(async () => {
106
+ try {
107
+ await import("./scripts/db-init.js");
108
+ }
109
+ catch (error) {
110
+ console.error("❌ Erreur lors de l'initialisation de la DB:", error);
111
+ process.exit(1);
112
+ }
113
+ });
114
+ program
115
+ .command("db:migrations:sync")
116
+ .description("Synchronise les migrations de modules")
117
+ .action(async () => {
118
+ try {
119
+ await import("./scripts/db-migrations-sync.js");
120
+ }
121
+ catch (error) {
122
+ console.error("❌ Erreur lors de la sync des migrations:", error);
123
+ process.exit(1);
124
+ }
125
+ });
126
+ program
127
+ .command("readme:create")
128
+ .description("Génère le fichier README")
129
+ .action(async () => {
130
+ try {
131
+ await import("./scripts/readme-build.js");
132
+ }
133
+ catch (error) {
134
+ console.error("❌ Erreur lors de la création du README:", error);
135
+ process.exit(1);
136
+ }
137
+ });
88
138
  program.parse();
@@ -1,3 +1,4 @@
1
- import type { PropsWithChildren } from "react";
2
- export declare function AdminLayout({ children }: PropsWithChildren<{}>): import("react/jsx-runtime").JSX.Element;
1
+ export declare function AdminLayout({ children }: {
2
+ children: React.ReactNode;
3
+ }): import("react/jsx-runtime").JSX.Element;
3
4
  //# sourceMappingURL=AdminLayout.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AdminLayout.d.ts","sourceRoot":"","sources":["../../src/layouts/AdminLayout.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAE/C,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,EAAE,iBAAiB,CAAC,EAAE,CAAC,2CAE9D"}
1
+ {"version":3,"file":"AdminLayout.d.ts","sourceRoot":"","sources":["../../src/layouts/AdminLayout.tsx"],"names":[],"mappings":"AAEA,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,2CAEtE"}
@@ -1,4 +1,3 @@
1
- import type { PropsWithChildren } from "react";
2
1
  import type { User } from "@supabase/supabase-js";
3
2
  export declare function useModules(): import("@lastbrain/core").ModuleBuildConfig[];
4
3
  export declare function useNotifications(): {
@@ -9,5 +8,7 @@ export declare function useAuth(): {
9
8
  loading: boolean;
10
9
  isSuperAdmin: boolean;
11
10
  };
12
- export declare function AppProviders({ children }: PropsWithChildren<{}>): import("react/jsx-runtime").JSX.Element;
11
+ export declare function AppProviders({ children }: {
12
+ children: React.ReactNode;
13
+ }): import("react/jsx-runtime").JSX.Element;
13
14
  //# sourceMappingURL=AppProviders.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AppProviders.d.ts","sourceRoot":"","sources":["../../src/layouts/AppProviders.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAK/C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAelD,wBAAgB,UAAU,kDAEzB;AAED,wBAAgB,gBAAgB;cAf4B,MAAM,EAAE;EAiBnE;AAED,wBAAgB,OAAO;UAjBf,IAAI,GAAG,IAAI;aACR,OAAO;kBACF,OAAO;EAiBtB;AAED,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,EAAE,iBAAiB,CAAC,EAAE,CAAC,2CA0B/D"}
1
+ {"version":3,"file":"AppProviders.d.ts","sourceRoot":"","sources":["../../src/layouts/AppProviders.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAclD,wBAAgB,UAAU,kDAEzB;AAED,wBAAgB,gBAAgB;cAf4B,MAAM,EAAE;EAiBnE;AAED,wBAAgB,OAAO;UAjBf,IAAI,GAAG,IAAI;aACR,OAAO;kBACF,OAAO;EAiBtB;AAED,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,2CA0BvE"}
@@ -1,3 +1,4 @@
1
- import type { PropsWithChildren } from "react";
2
- export declare function AuthLayout({ children }: PropsWithChildren<{}>): import("react/jsx-runtime").JSX.Element;
1
+ export declare function AuthLayout({ children }: {
2
+ children: React.ReactNode;
3
+ }): import("react/jsx-runtime").JSX.Element;
3
4
  //# sourceMappingURL=AuthLayout.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AuthLayout.d.ts","sourceRoot":"","sources":["../../src/layouts/AuthLayout.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAE/C,wBAAgB,UAAU,CAAC,EAAE,QAAQ,EAAE,EAAE,iBAAiB,CAAC,EAAE,CAAC,2CAE7D"}
1
+ {"version":3,"file":"AuthLayout.d.ts","sourceRoot":"","sources":["../../src/layouts/AuthLayout.tsx"],"names":[],"mappings":"AAEA,wBAAgB,UAAU,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,2CAErE"}
@@ -1,3 +1,4 @@
1
- import type { PropsWithChildren } from "react";
2
- export declare function PublicLayout({ children }: PropsWithChildren<{}>): import("react/jsx-runtime").JSX.Element;
1
+ export declare function PublicLayout({ children }: {
2
+ children: React.ReactNode;
3
+ }): import("react/jsx-runtime").JSX.Element;
3
4
  //# sourceMappingURL=PublicLayout.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PublicLayout.d.ts","sourceRoot":"","sources":["../../src/layouts/PublicLayout.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAE/C,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,EAAE,iBAAiB,CAAC,EAAE,CAAC,2CAE/D"}
1
+ {"version":3,"file":"PublicLayout.d.ts","sourceRoot":"","sources":["../../src/layouts/PublicLayout.tsx"],"names":[],"mappings":"AAEA,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,2CAEvE"}
@@ -1,3 +1,4 @@
1
- import type { PropsWithChildren } from "react";
2
- export declare function RootLayout({ children }: PropsWithChildren<{}>): import("react/jsx-runtime").JSX.Element;
1
+ export declare function RootLayout({ children }: {
2
+ children: React.ReactNode;
3
+ }): import("react/jsx-runtime").JSX.Element;
3
4
  //# sourceMappingURL=RootLayout.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"RootLayout.d.ts","sourceRoot":"","sources":["../../src/layouts/RootLayout.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAK/C,wBAAgB,UAAU,CAAC,EAAE,QAAQ,EAAE,EAAE,iBAAiB,CAAC,EAAE,CAAC,2CAmB7D"}
1
+ {"version":3,"file":"RootLayout.d.ts","sourceRoot":"","sources":["../../src/layouts/RootLayout.tsx"],"names":[],"mappings":"AAMA,wBAAgB,UAAU,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,2CAmBrE"}
@@ -229,7 +229,7 @@ async function main() {
229
229
  try {
230
230
  runSupabase("start");
231
231
  }
232
- catch (error) {
232
+ catch (_error) {
233
233
  console.warn("⚠️ Supabase start had issues, continuing...");
234
234
  }
235
235
  console.log("🔄 Resetting database...");
@@ -7,11 +7,11 @@ const migrationsDir = path.join(projectRoot, "supabase/migrations");
7
7
  function ensureDirectory(dir) {
8
8
  fs.mkdirSync(dir, { recursive: true });
9
9
  }
10
- var CopyStrategy;
11
- (function (CopyStrategy) {
12
- CopyStrategy[CopyStrategy["overwrite"] = 0] = "overwrite";
13
- CopyStrategy[CopyStrategy["skip"] = 1] = "skip";
14
- })(CopyStrategy || (CopyStrategy = {}));
10
+ var _CopyStrategy;
11
+ (function (_CopyStrategy) {
12
+ _CopyStrategy[_CopyStrategy["overwrite"] = 0] = "overwrite";
13
+ _CopyStrategy[_CopyStrategy["skip"] = 1] = "skip";
14
+ })(_CopyStrategy || (_CopyStrategy = {}));
15
15
  function getModulePackageDir(moduleName) {
16
16
  return path.join(projectRoot, "node_modules", ...moduleName.split("/"));
17
17
  }
@@ -223,6 +223,7 @@ async function addDependencies(targetDir, useHeroUI, withAuth, selectedModules =
223
223
  // Ajouter les dépendances HeroUI si nécessaire
224
224
  if (useHeroUI) {
225
225
  // Tous les packages HeroUI nécessaires (car @lastbrain/ui les ré-exporte)
226
+ requiredDeps["@heroui/system"] = "^2.4.23";
226
227
  requiredDeps["@heroui/theme"] = "^2.4.23";
227
228
  requiredDeps["@heroui/accordion"] = "^2.2.24";
228
229
  requiredDeps["@heroui/alert"] = "^2.2.27";
@@ -257,7 +258,6 @@ async function addDependencies(targetDir, useHeroUI, withAuth, selectedModules =
257
258
  requiredDeps["@heroui/switch"] = "^2.2.24";
258
259
  requiredDeps["@heroui/table"] = "^2.2.27";
259
260
  requiredDeps["@heroui/tabs"] = "^2.2.24";
260
- requiredDeps["@heroui/system"] = "^2.4.23"; // Ajout pour HeroUIProvider
261
261
  requiredDeps["@heroui/toast"] = "^2.0.17";
262
262
  requiredDeps["@heroui/tooltip"] = "^2.2.24";
263
263
  requiredDeps["@heroui/user"] = "^2.2.22";
@@ -903,21 +903,24 @@ async function addScriptsToPackageJson(targetDir) {
903
903
  // Détecter si le projet cible est dans un workspace
904
904
  const targetIsInMonorepo = fs.existsSync(path.join(targetDir, "../../../packages/core/package.json")) ||
905
905
  fs.existsSync(path.join(targetDir, "../../packages/core/package.json"));
906
- let scriptsPrefix = "node node_modules/@lastbrain/app/dist/scripts/";
907
- if (targetIsInMonorepo) {
908
- // Dans un monorepo, utiliser pnpm exec pour résoudre correctement les workspace packages
909
- scriptsPrefix = "pnpm exec lastbrain ";
906
+ let scriptsPrefix = "lastbrain";
907
+ if (!targetIsInMonorepo) {
908
+ // Hors monorepo, utiliser le chemin direct vers le CLI
909
+ scriptsPrefix = "node node_modules/@lastbrain/app/dist/cli.js";
910
910
  }
911
911
  const scripts = {
912
+ predev: targetIsInMonorepo
913
+ ? "pnpm --filter @lastbrain/core build && pnpm --filter @lastbrain/ui build && pnpm --filter @lastbrain/module-auth build && pnpm --filter @lastbrain/module-ai build"
914
+ : "echo 'No prebuild needed outside monorepo'",
912
915
  dev: "next dev",
913
916
  build: "next build",
914
917
  start: "next start",
915
918
  lint: "next lint",
916
- lastbrain: "node node_modules/@lastbrain/app/dist/cli.js",
917
- "build:modules": "node node_modules/@lastbrain/app/dist/scripts/module-build.js",
918
- "db:migrations:sync": "node node_modules/@lastbrain/app/dist/scripts/db-migrations-sync.js",
919
- "db:init": "node node_modules/@lastbrain/app/dist/scripts/db-init.js",
920
- "readme:create": "node node_modules/@lastbrain/app/dist/scripts/readme-build.js",
919
+ lastbrain: targetIsInMonorepo ? "lastbrain" : "node node_modules/@lastbrain/app/dist/cli.js",
920
+ "build:modules": `${scriptsPrefix} module:build`,
921
+ "db:migrations:sync": `${scriptsPrefix} db:migrations:sync`,
922
+ "db:init": `${scriptsPrefix} db:init`,
923
+ "readme:create": `${scriptsPrefix} readme:create`,
921
924
  };
922
925
  pkg.scripts = { ...pkg.scripts, ...scripts };
923
926
  await fs.writeJson(pkgPath, pkg, { spaces: 2 });
@@ -1 +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"}
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,iBAsMpE"}
@@ -63,7 +63,7 @@ export async function addModule(moduleName, targetDir) {
63
63
  process.exit(1);
64
64
  }
65
65
  // 5. Copier les migrations du module
66
- let copiedMigrationFiles = [];
66
+ const copiedMigrationFiles = [];
67
67
  if (module.hasMigrations) {
68
68
  console.log(chalk.yellow("\n📋 Copie des migrations du module..."));
69
69
  // Trouver le chemin du module installé
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Exécute un script via node en ajustant le chemin
3
+ */
4
+ export declare function runScript(scriptName: string, args?: string[]): Promise<void>;
5
+ //# sourceMappingURL=script-runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"script-runner.d.ts","sourceRoot":"","sources":["../../src/scripts/script-runner.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,wBAAsB,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,GAAE,MAAM,EAAO,iBAqBtE"}
@@ -0,0 +1,25 @@
1
+ import { spawn } from "child_process";
2
+ import path from "path";
3
+ /**
4
+ * Exécute un script via node en ajustant le chemin
5
+ */
6
+ export async function runScript(scriptName, args = []) {
7
+ return new Promise((resolve, reject) => {
8
+ const scriptPath = path.join(__dirname, `${scriptName}.js`);
9
+ const child = spawn("node", [scriptPath, ...args], {
10
+ stdio: "inherit",
11
+ env: { ...process.env, PROJECT_ROOT: process.cwd() }
12
+ });
13
+ child.on("close", (code) => {
14
+ if (code === 0) {
15
+ resolve();
16
+ }
17
+ else {
18
+ reject(new Error(`Script ${scriptName} exited with code ${code}`));
19
+ }
20
+ });
21
+ child.on("error", (error) => {
22
+ reject(error);
23
+ });
24
+ });
25
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"DocPage.d.ts","sourceRoot":"","sources":["../../src/templates/DocPage.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA8B,MAAM,OAAO,CAAC;AA+BnD,UAAU,eAAe;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EACF,SAAS,GACT,WAAW,GACX,SAAS,GACT,QAAQ,GACR,SAAS,GACT,SAAS,GACT,SAAS,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,UAAU,YAAY;IACpB,OAAO,CAAC,EAAE,eAAe,EAAE,CAAC;IAC5B,cAAc,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAClC;AAED,wBAAgB,OAAO,CAAC,EAAE,OAAY,EAAE,cAAc,EAAE,EAAE,YAAY,2CA2UrE"}
1
+ {"version":3,"file":"DocPage.d.ts","sourceRoot":"","sources":["../../src/templates/DocPage.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA8B,MAAM,OAAO,CAAC;AA6FnD,UAAU,eAAe;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EACF,SAAS,GACT,WAAW,GACX,SAAS,GACT,QAAQ,GACR,SAAS,GACT,SAAS,GACT,SAAS,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,UAAU,YAAY;IACpB,OAAO,CAAC,EAAE,eAAe,EAAE,CAAC;IAC5B,cAAc,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAClC;AAED,wBAAgB,OAAO,CAAC,EAAE,OAAY,EAAE,cAAc,EAAE,EAAE,YAAY,2CAySrE"}
@@ -5,6 +5,19 @@ import { useState, useEffect } from "react";
5
5
  import { Card, CardBody, CardHeader, Listbox, ListboxItem, Chip, Button, Drawer, DrawerContent, DrawerHeader, DrawerBody, Snippet, } from "@lastbrain/ui";
6
6
  import { Menu, Home, Sparkles, Rocket, Building2, Package, Database, Palette, BookOpen, Link, Blocks, HardDrive, } from "lucide-react";
7
7
  import { DefaultDocumentation } from "./DefaultDoc.js";
8
+ const NavigationListbox = ({ navigationItems, selectedModule, scrollToSection, setSelectedModule, }) => (_jsx(Listbox, { "aria-label": "Navigation", selectionMode: "single", selectedKeys: selectedModule ? [selectedModule] : [], onSelectionChange: (keys) => {
9
+ const key = Array.from(keys)[0];
10
+ if (key) {
11
+ scrollToSection(key);
12
+ }
13
+ else {
14
+ setSelectedModule("");
15
+ window.scrollTo({ top: 0, behavior: "smooth" });
16
+ }
17
+ }, items: navigationItems, children: (item) => {
18
+ const IconComponent = item.icon;
19
+ return (_jsx(ListboxItem, { textValue: item.name, description: item.description, color: item.color, variant: "solid", endContent: item.number && (_jsx(Chip, { size: "sm", color: "primary", children: item.number ?? 0 })), className: `${selectedModule === item.id ? "bg-default-200/40" : ""}`, startContent: _jsx(IconComponent, { size: 18, className: "shrink-0" }), children: item.name }, item.id));
20
+ } }));
8
21
  export function DocPage({ modules = [], defaultContent }) {
9
22
  const [selectedModule, setSelectedModule] = useState("default");
10
23
  const [isDrawerOpen, setIsDrawerOpen] = useState(false);
@@ -150,20 +163,7 @@ export function DocPage({ modules = [], defaultContent }) {
150
163
  color: "primary",
151
164
  })),
152
165
  ];
153
- const NavigationListbox = () => (_jsx(Listbox, { "aria-label": "Navigation", selectionMode: "single", selectedKeys: selectedModule ? [selectedModule] : [], onSelectionChange: (keys) => {
154
- const key = Array.from(keys)[0];
155
- if (key) {
156
- scrollToSection(key);
157
- }
158
- else {
159
- setSelectedModule("");
160
- window.scrollTo({ top: 0, behavior: "smooth" });
161
- }
162
- }, items: navigationItems, children: (item) => {
163
- const IconComponent = item.icon;
164
- return (_jsx(ListboxItem, { textValue: item.name, description: item.description, color: item.color, variant: "solid", endContent: item.number && (_jsx(Chip, { size: "sm", color: "primary", children: item.number ?? 0 })), className: `${selectedModule === item.id ? "bg-default-200/40" : ""}`, startContent: _jsx(IconComponent, { size: 18, className: "shrink-0" }), children: item.name }, item.id));
165
- } }));
166
- return (_jsx("div", { className: "w-full pt-8 md:pt-12 pb-24 lg:pb-8", children: _jsxs("div", { className: "container mx-auto md:px-4 py-8", children: [_jsx("div", { className: "fixed w-full h-16 left-0 bottom-0 bg-background/20 backdrop-blur-lg z-50 lg:hidden p-2", children: _jsx("div", { className: "flex justify-center", children: _jsx(Button, { isIconOnly: true, variant: "solid", onPress: () => setIsDrawerOpen(true), children: _jsx(Menu, { size: 24 }) }) }) }), _jsx(Drawer, { isOpen: isDrawerOpen, onOpenChange: setIsDrawerOpen, placement: "left", children: _jsxs(DrawerContent, { children: [_jsx(DrawerHeader, { children: _jsx("h2", { className: "text-xl font-semibold", children: "Navigation" }) }), _jsx(DrawerBody, { children: _jsx(NavigationListbox, {}) })] }) }), _jsxs("div", { className: "flex gap-8", children: [_jsx("aside", { className: "hidden lg:block w-64 shrink-0 sticky top-18 self-start", children: _jsxs(Card, { children: [_jsx(CardHeader, { className: "pb-2", children: _jsx("h2", { className: "text-xl font-semibold", children: "Navigation" }) }), _jsx(CardBody, { children: _jsx(NavigationListbox, {}) })] }) }), _jsxs("main", { className: "flex-1 w-full min-w-0 space-y-8", children: [defaultContent ? (_jsx("div", { children: defaultContent })) : (_jsx(DefaultDocumentation, {})), modules.length > 0 && (_jsxs("div", { className: "space-y-6", children: [_jsxs(Card, { id: "section-modules", className: "scroll-mt-32", children: [_jsx(CardHeader, { children: _jsx("h2", { className: "text-2xl font-semibold", children: "Modules disponibles" }) }), _jsxs(CardBody, { children: [_jsx("p", { className: "text-slate-600 dark:text-slate-400 mb-4", children: "Voici la liste de tous les modules disponibles dans LastBrain. Les modules actifs sont utilis\u00E9s dans votre application." }), _jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: modules.map((module) => (_jsx(Card, { isPressable: module.available, onPress: () => module.available && scrollToSection(module.id), className: `${module.available
166
+ return (_jsx("div", { className: "w-full pt-8 md:pt-12 pb-24 lg:pb-8", children: _jsxs("div", { className: "container mx-auto md:px-4 py-8", children: [_jsx("div", { className: "fixed w-full h-16 left-0 bottom-0 bg-background/20 backdrop-blur-lg z-50 lg:hidden p-2", children: _jsx("div", { className: "flex justify-center", children: _jsx(Button, { isIconOnly: true, variant: "solid", onPress: () => setIsDrawerOpen(true), children: _jsx(Menu, { size: 24 }) }) }) }), _jsx(Drawer, { isOpen: isDrawerOpen, onOpenChange: setIsDrawerOpen, placement: "left", children: _jsxs(DrawerContent, { children: [_jsx(DrawerHeader, { children: _jsx("h2", { className: "text-xl font-semibold", children: "Navigation" }) }), _jsx(DrawerBody, { children: _jsx(NavigationListbox, { navigationItems: navigationItems, selectedModule: selectedModule, scrollToSection: scrollToSection, setSelectedModule: setSelectedModule }) })] }) }), _jsxs("div", { className: "flex gap-8", children: [_jsx("aside", { className: "hidden lg:block w-64 shrink-0 sticky top-18 self-start", children: _jsxs(Card, { children: [_jsx(CardHeader, { className: "pb-2", children: _jsx("h2", { className: "text-xl font-semibold", children: "Navigation" }) }), _jsx(CardBody, { children: _jsx(NavigationListbox, { navigationItems: navigationItems, selectedModule: selectedModule, scrollToSection: scrollToSection, setSelectedModule: setSelectedModule }) })] }) }), _jsxs("main", { className: "flex-1 w-full min-w-0 space-y-8", children: [defaultContent ? (_jsx("div", { children: defaultContent })) : (_jsx(DefaultDocumentation, {})), modules.length > 0 && (_jsxs("div", { className: "space-y-6", children: [_jsxs(Card, { id: "section-modules", className: "scroll-mt-32", children: [_jsx(CardHeader, { children: _jsx("h2", { className: "text-2xl font-semibold", children: "Modules disponibles" }) }), _jsxs(CardBody, { children: [_jsx("p", { className: "text-slate-600 dark:text-slate-400 mb-4", children: "Voici la liste de tous les modules disponibles dans LastBrain. Les modules actifs sont utilis\u00E9s dans votre application." }), _jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: modules.map((module) => (_jsx(Card, { isPressable: module.available, onPress: () => module.available && scrollToSection(module.id), className: `${module.available
167
167
  ? "cursor-pointer hover:shadow-lg"
168
168
  : "opacity-70"} transition-shadow`, children: _jsxs(CardBody, { children: [_jsxs("div", { className: "flex items-start justify-between mb-2", children: [_jsx("h3", { className: "text-lg font-semibold", children: module.name }), _jsx(Chip, { size: "sm", color: module.available ? "success" : "warning", variant: "flat", children: module.available ? "Actif" : "Inactif" })] }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: module.description }), !module.available && (_jsxs("div", { className: "flex justify-between items-center text-xs text-default-700 mt-2", children: [_jsx("span", { children: "Pour activer : " }), _jsx(Snippet, { hideSymbol: true, color: "primary", children: `pnpm lastbrain add-module ${module.id}` })] }))] }) }, module.id))) })] })] }), modules
169
169
  .filter((m) => m.available)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lastbrain/app",
3
- "version": "0.1.24",
3
+ "version": "0.1.25",
4
4
  "description": "Framework modulaire Next.js avec CLI et système de modules",
5
5
  "private": false,
6
6
  "type": "module",
@@ -0,0 +1,74 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { AVAILABLE_MODULES } from "../scripts/module-add";
3
+
4
+ describe("Module Registry", () => {
5
+ describe("AVAILABLE_MODULES", () => {
6
+ it("should have at least one module defined", () => {
7
+ expect(AVAILABLE_MODULES.length).toBeGreaterThan(0);
8
+ });
9
+
10
+ it("should have auth module defined", () => {
11
+ const authModule = AVAILABLE_MODULES.find((m) => m.name === "auth");
12
+ expect(authModule).toBeDefined();
13
+ expect(authModule?.package).toBe("@lastbrain/module-auth");
14
+ expect(authModule?.hasMigrations).toBe(true);
15
+ });
16
+
17
+ it("should have ai module defined", () => {
18
+ const aiModule = AVAILABLE_MODULES.find((m) => m.name === "ai");
19
+ expect(aiModule).toBeDefined();
20
+ expect(aiModule?.package).toBe("@lastbrain/module-ai");
21
+ expect(aiModule?.hasMigrations).toBe(true);
22
+ });
23
+
24
+ it("should have all required properties for each module", () => {
25
+ AVAILABLE_MODULES.forEach((module) => {
26
+ expect(module.name).toBeDefined();
27
+ expect(module.package).toBeDefined();
28
+ expect(module.displayName).toBeDefined();
29
+ expect(module.description).toBeDefined();
30
+ expect(typeof module.hasMigrations).toBe("boolean");
31
+ });
32
+ });
33
+
34
+ it("should have migrations paths when hasMigrations is true", () => {
35
+ AVAILABLE_MODULES.forEach((module) => {
36
+ if (module.hasMigrations) {
37
+ expect(module.migrationsPath).toBeDefined();
38
+ expect(module.migrationsDownPath).toBeDefined();
39
+ }
40
+ });
41
+ });
42
+
43
+ it("should have unique module names", () => {
44
+ const names = AVAILABLE_MODULES.map((m) => m.name);
45
+ const uniqueNames = new Set(names);
46
+ expect(names.length).toBe(uniqueNames.size);
47
+ });
48
+
49
+ it("should have unique package names", () => {
50
+ const packages = AVAILABLE_MODULES.map((m) => m.package);
51
+ const uniquePackages = new Set(packages);
52
+ expect(packages.length).toBe(uniquePackages.size);
53
+ });
54
+
55
+ it("should have display names with emoji", () => {
56
+ AVAILABLE_MODULES.forEach((module) => {
57
+ // Check if displayName contains at least one emoji (basic check)
58
+ expect(module.displayName).toMatch(/[\u{1F300}-\u{1F9FF}]/u);
59
+ });
60
+ });
61
+
62
+ it("should have non-empty descriptions", () => {
63
+ AVAILABLE_MODULES.forEach((module) => {
64
+ expect(module.description.length).toBeGreaterThan(0);
65
+ });
66
+ });
67
+
68
+ it("should have valid package names", () => {
69
+ AVAILABLE_MODULES.forEach((module) => {
70
+ expect(module.package).toMatch(/^@lastbrain\/module-/);
71
+ });
72
+ });
73
+ });
74
+ });
@@ -1,8 +1,10 @@
1
1
  "use client";
2
2
 
3
- import type { PropsWithChildren } from "react";
4
-
5
- export default function AdminShellLayout({ children }: PropsWithChildren<{}>) {
3
+ export default function AdminShellLayout({
4
+ children,
5
+ }: {
6
+ children: React.ReactNode;
7
+ }) {
6
8
  return (
7
9
  <div className="mx-auto max-w-5xl px-4 py-16">
8
10
  <h1 className="text-3xl font-bold">Section Admin</h1>
@@ -1,8 +1,10 @@
1
1
  "use client";
2
2
 
3
- import type { PropsWithChildren } from "react";
4
-
5
- export default function AuthLayout({ children }: PropsWithChildren<{}>) {
3
+ export default function AuthLayout({
4
+ children,
5
+ }: {
6
+ children: React.ReactNode;
7
+ }) {
6
8
  return (
7
9
  <div className="mx-auto max-w-5xl px-4 py-16">
8
10
  <h1 className="text-3xl font-bold">Section Auth</h1>
package/src/cli.ts CHANGED
@@ -93,4 +93,54 @@ program
93
93
  }
94
94
  });
95
95
 
96
+ // Commandes de build et maintenance
97
+ program
98
+ .command("module:build")
99
+ .description("Build les configurations de modules")
100
+ .action(async () => {
101
+ try {
102
+ const { runModuleBuild } = await import("./scripts/module-build.js");
103
+ await runModuleBuild();
104
+ } catch (error) {
105
+ console.error("❌ Erreur lors du build des modules:", error);
106
+ process.exit(1);
107
+ }
108
+ });
109
+
110
+ program
111
+ .command("db:init")
112
+ .description("Initialise la base de données Supabase")
113
+ .action(async () => {
114
+ try {
115
+ await import("./scripts/db-init.js");
116
+ } catch (error) {
117
+ console.error("❌ Erreur lors de l'initialisation de la DB:", error);
118
+ process.exit(1);
119
+ }
120
+ });
121
+
122
+ program
123
+ .command("db:migrations:sync")
124
+ .description("Synchronise les migrations de modules")
125
+ .action(async () => {
126
+ try {
127
+ await import("./scripts/db-migrations-sync.js");
128
+ } catch (error) {
129
+ console.error("❌ Erreur lors de la sync des migrations:", error);
130
+ process.exit(1);
131
+ }
132
+ });
133
+
134
+ program
135
+ .command("readme:create")
136
+ .description("Génère le fichier README")
137
+ .action(async () => {
138
+ try {
139
+ await import("./scripts/readme-build.js");
140
+ } catch (error) {
141
+ console.error("❌ Erreur lors de la création du README:", error);
142
+ process.exit(1);
143
+ }
144
+ });
145
+
96
146
  program.parse();
@@ -1,7 +1,5 @@
1
1
  "use client";
2
2
 
3
- import type { PropsWithChildren } from "react";
4
-
5
- export function AdminLayout({ children }: PropsWithChildren<{}>) {
3
+ export function AdminLayout({ children }: { children: React.ReactNode }) {
6
4
  return <div className="pt-18 px-2 md:px-5">{children}</div>;
7
5
  }
@@ -1,12 +1,10 @@
1
1
  "use client";
2
2
 
3
- import type { PropsWithChildren } from "react";
4
3
  import { createContext, useContext, useMemo } from "react";
5
4
  import { getModuleConfigs } from "../modules/module-loader.js";
6
- import { Header, ToastProvider } from "@lastbrain/ui";
5
+ import { ToastProvider } from "@lastbrain/ui";
7
6
  import { useAuthSession } from "../auth/useAuthSession.js";
8
7
  import type { User } from "@supabase/supabase-js";
9
- import { useRouter } from "next/navigation.js";
10
8
 
11
9
  const ModuleContext = createContext(getModuleConfigs());
12
10
  const NotificationContext = createContext({ messages: [] as string[] });
@@ -32,7 +30,7 @@ export function useAuth() {
32
30
  return useContext(AuthContext);
33
31
  }
34
32
 
35
- export function AppProviders({ children }: PropsWithChildren<{}>) {
33
+ export function AppProviders({ children }: { children: React.ReactNode }) {
36
34
  const modules = useMemo(() => getModuleConfigs(), []);
37
35
  const notifications = useMemo(() => ({ messages: [] as string[] }), []);
38
36
  const { user, loading, isSuperAdmin } = useAuthSession();
@@ -1,7 +1,5 @@
1
1
  "use client";
2
2
 
3
- import type { PropsWithChildren } from "react";
4
-
5
- export function AuthLayout({ children }: PropsWithChildren<{}>) {
3
+ export function AuthLayout({ children }: { children: React.ReactNode }) {
6
4
  return <div className="pt-18 px-2 md:px-5 ">{children}</div>;
7
5
  }
@@ -1,7 +1,5 @@
1
1
  "use client";
2
2
 
3
- import type { PropsWithChildren } from "react";
4
-
5
- export function PublicLayout({ children }: PropsWithChildren<{}>) {
3
+ export function PublicLayout({ children }: { children: React.ReactNode }) {
6
4
  return <section className=" px-4 ">{children}</section>;
7
5
  }
@@ -1,11 +1,10 @@
1
1
  "use client";
2
2
 
3
- import type { PropsWithChildren } from "react";
4
3
  import { ThemeProvider } from "next-themes";
5
4
  import { AppProviders } from "./AppProviders.js";
6
5
 
7
6
  // Note: L'app Next.js doit importer son propre globals.css dans son layout
8
- export function RootLayout({ children }: PropsWithChildren<{}>) {
7
+ export function RootLayout({ children }: { children: React.ReactNode }) {
9
8
  return (
10
9
  <html lang="fr" suppressHydrationWarning>
11
10
  <body className="min-h-screen">
@@ -1,4 +1,4 @@
1
- import { spawn, spawnSync, execSync, execFileSync } from "node:child_process";
1
+ import { spawn, spawnSync, execSync as _execSync, execFileSync as _execFileSync } from "node:child_process";
2
2
  import fs from "node:fs";
3
3
  import path from "node:path";
4
4
  import { fileURLToPath } from "node:url";
@@ -282,7 +282,7 @@ async function main() {
282
282
  console.log("⚙️ Starting Supabase...");
283
283
  try {
284
284
  runSupabase("start");
285
- } catch (error) {
285
+ } catch (_error) {
286
286
  console.warn("⚠️ Supabase start had issues, continuing...");
287
287
  }
288
288
 
@@ -13,7 +13,7 @@ function ensureDirectory(dir: string) {
13
13
  fs.mkdirSync(dir, { recursive: true });
14
14
  }
15
15
 
16
- enum CopyStrategy {
16
+ enum _CopyStrategy {
17
17
  overwrite,
18
18
  skip,
19
19
  }
@@ -292,6 +292,7 @@ async function addDependencies(
292
292
  // Ajouter les dépendances HeroUI si nécessaire
293
293
  if (useHeroUI) {
294
294
  // Tous les packages HeroUI nécessaires (car @lastbrain/ui les ré-exporte)
295
+ requiredDeps["@heroui/system"] = "^2.4.23";
295
296
  requiredDeps["@heroui/theme"] = "^2.4.23";
296
297
  requiredDeps["@heroui/accordion"] = "^2.2.24";
297
298
  requiredDeps["@heroui/alert"] = "^2.2.27";
@@ -326,7 +327,6 @@ async function addDependencies(
326
327
  requiredDeps["@heroui/switch"] = "^2.2.24";
327
328
  requiredDeps["@heroui/table"] = "^2.2.27";
328
329
  requiredDeps["@heroui/tabs"] = "^2.2.24";
329
- requiredDeps["@heroui/system"] = "^2.4.23"; // Ajout pour HeroUIProvider
330
330
  requiredDeps["@heroui/toast"] = "^2.0.17";
331
331
  requiredDeps["@heroui/tooltip"] = "^2.2.24";
332
332
  requiredDeps["@heroui/user"] = "^2.2.22";
@@ -1054,23 +1054,26 @@ async function addScriptsToPackageJson(targetDir: string) {
1054
1054
  const targetIsInMonorepo = fs.existsSync(path.join(targetDir, "../../../packages/core/package.json")) ||
1055
1055
  fs.existsSync(path.join(targetDir, "../../packages/core/package.json"));
1056
1056
 
1057
- let scriptsPrefix = "node node_modules/@lastbrain/app/dist/scripts/";
1057
+ let scriptsPrefix = "lastbrain";
1058
1058
 
1059
- if (targetIsInMonorepo) {
1060
- // Dans un monorepo, utiliser pnpm exec pour résoudre correctement les workspace packages
1061
- scriptsPrefix = "pnpm exec lastbrain ";
1059
+ if (!targetIsInMonorepo) {
1060
+ // Hors monorepo, utiliser le chemin direct vers le CLI
1061
+ scriptsPrefix = "node node_modules/@lastbrain/app/dist/cli.js";
1062
1062
  }
1063
1063
 
1064
1064
  const scripts = {
1065
+ predev: targetIsInMonorepo
1066
+ ? "pnpm --filter @lastbrain/core build && pnpm --filter @lastbrain/ui build && pnpm --filter @lastbrain/module-auth build && pnpm --filter @lastbrain/module-ai build"
1067
+ : "echo 'No prebuild needed outside monorepo'",
1065
1068
  dev: "next dev",
1066
1069
  build: "next build",
1067
1070
  start: "next start",
1068
1071
  lint: "next lint",
1069
- lastbrain: "node node_modules/@lastbrain/app/dist/cli.js",
1070
- "build:modules": "node node_modules/@lastbrain/app/dist/scripts/module-build.js",
1071
- "db:migrations:sync": "node node_modules/@lastbrain/app/dist/scripts/db-migrations-sync.js",
1072
- "db:init": "node node_modules/@lastbrain/app/dist/scripts/db-init.js",
1073
- "readme:create": "node node_modules/@lastbrain/app/dist/scripts/readme-build.js",
1072
+ lastbrain: targetIsInMonorepo ? "lastbrain" : "node node_modules/@lastbrain/app/dist/cli.js",
1073
+ "build:modules": `${scriptsPrefix} module:build`,
1074
+ "db:migrations:sync": `${scriptsPrefix} db:migrations:sync`,
1075
+ "db:init": `${scriptsPrefix} db:init`,
1076
+ "readme:create": `${scriptsPrefix} readme:create`,
1074
1077
  };
1075
1078
 
1076
1079
  pkg.scripts = { ...pkg.scripts, ...scripts };
@@ -87,9 +87,7 @@ export async function addModule(moduleName: string, targetDir: string) {
87
87
  }
88
88
 
89
89
  // 5. Copier les migrations du module
90
- let copiedMigrationFiles: string[] = [];
91
-
92
- if (module.hasMigrations) {
90
+ const copiedMigrationFiles: string[] = []; if (module.hasMigrations) {
93
91
  console.log(chalk.yellow("\n📋 Copie des migrations du module..."));
94
92
 
95
93
  // Trouver le chemin du module installé
@@ -0,0 +1,28 @@
1
+ import { spawn } from "child_process";
2
+ import path from "path";
3
+
4
+ /**
5
+ * Exécute un script via node en ajustant le chemin
6
+ */
7
+ export async function runScript(scriptName: string, args: string[] = []) {
8
+ return new Promise<void>((resolve, reject) => {
9
+ const scriptPath = path.join(__dirname, `${scriptName}.js`);
10
+
11
+ const child = spawn("node", [scriptPath, ...args], {
12
+ stdio: "inherit",
13
+ env: { ...process.env, PROJECT_ROOT: process.cwd() }
14
+ });
15
+
16
+ child.on("close", (code) => {
17
+ if (code === 0) {
18
+ resolve();
19
+ } else {
20
+ reject(new Error(`Script ${scriptName} exited with code ${code}`));
21
+ }
22
+ });
23
+
24
+ child.on("error", (error) => {
25
+ reject(error);
26
+ });
27
+ });
28
+ }
@@ -31,6 +31,68 @@ import {
31
31
  } from "lucide-react";
32
32
  import { DefaultDocumentation } from "./DefaultDoc.js";
33
33
 
34
+ // Composant NavigationListbox séparé pour éviter la recréation
35
+ interface NavigationListboxProps {
36
+ navigationItems: Array<{
37
+ id: string;
38
+ name: string;
39
+ description: string;
40
+ icon: any;
41
+ color?: string;
42
+ number?: number;
43
+ }>;
44
+ selectedModule: string;
45
+ scrollToSection: (id: string) => void;
46
+ setSelectedModule: (id: string) => void;
47
+ }
48
+
49
+ const NavigationListbox: React.FC<NavigationListboxProps> = ({
50
+ navigationItems,
51
+ selectedModule,
52
+ scrollToSection,
53
+ setSelectedModule,
54
+ }) => (
55
+ <Listbox
56
+ aria-label="Navigation"
57
+ selectionMode="single"
58
+ selectedKeys={selectedModule ? [selectedModule] : []}
59
+ onSelectionChange={(keys) => {
60
+ const key = Array.from(keys)[0] as string;
61
+ if (key) {
62
+ scrollToSection(key);
63
+ } else {
64
+ setSelectedModule("");
65
+ window.scrollTo({ top: 0, behavior: "smooth" });
66
+ }
67
+ }}
68
+ items={navigationItems}
69
+ >
70
+ {(item: any) => {
71
+ const IconComponent = item.icon;
72
+ return (
73
+ <ListboxItem
74
+ key={item.id}
75
+ textValue={item.name}
76
+ description={item.description}
77
+ color={item.color}
78
+ variant="solid"
79
+ endContent={
80
+ item.number && (
81
+ <Chip size="sm" color="primary">
82
+ {item.number ?? 0}
83
+ </Chip>
84
+ )
85
+ }
86
+ className={`${selectedModule === item.id ? "bg-default-200/40" : ""}`}
87
+ startContent={<IconComponent size={18} className="shrink-0" />}
88
+ >
89
+ {item.name}
90
+ </ListboxItem>
91
+ );
92
+ }}
93
+ </Listbox>
94
+ );
95
+
34
96
  interface ModuleDocConfig {
35
97
  id: string;
36
98
  name: string;
@@ -207,50 +269,6 @@ export function DocPage({ modules = [], defaultContent }: DocPageProps) {
207
269
  })),
208
270
  ];
209
271
 
210
- const NavigationListbox = () => (
211
- <Listbox
212
- aria-label="Navigation"
213
- selectionMode="single"
214
- selectedKeys={selectedModule ? [selectedModule] : []}
215
- onSelectionChange={(keys) => {
216
- const key = Array.from(keys)[0] as string;
217
- if (key) {
218
- scrollToSection(key);
219
- } else {
220
- setSelectedModule("");
221
- window.scrollTo({ top: 0, behavior: "smooth" });
222
- }
223
- }}
224
- items={navigationItems}
225
- >
226
- {(item: any) => {
227
- const IconComponent = item.icon;
228
- return (
229
- <ListboxItem
230
- key={item.id}
231
- textValue={item.name}
232
- description={item.description}
233
- color={item.color}
234
- variant="solid"
235
- endContent={
236
- item.number && (
237
- <Chip size="sm" color="primary">
238
- {item.number ?? 0}
239
- </Chip>
240
- )
241
- }
242
- className={`${
243
- selectedModule === item.id ? "bg-default-200/40" : ""
244
- }`}
245
- startContent={<IconComponent size={18} className="shrink-0" />}
246
- >
247
- {item.name}
248
- </ListboxItem>
249
- );
250
- }}
251
- </Listbox>
252
- );
253
-
254
272
  return (
255
273
  <div className="w-full pt-8 md:pt-12 pb-24 lg:pb-8">
256
274
  <div className="container mx-auto md:px-4 py-8">
@@ -278,7 +296,12 @@ export function DocPage({ modules = [], defaultContent }: DocPageProps) {
278
296
  <h2 className="text-xl font-semibold">Navigation</h2>
279
297
  </DrawerHeader>
280
298
  <DrawerBody>
281
- <NavigationListbox />
299
+ <NavigationListbox
300
+ navigationItems={navigationItems}
301
+ selectedModule={selectedModule}
302
+ scrollToSection={scrollToSection}
303
+ setSelectedModule={setSelectedModule}
304
+ />
282
305
  </DrawerBody>
283
306
  </DrawerContent>
284
307
  </Drawer>
@@ -291,7 +314,12 @@ export function DocPage({ modules = [], defaultContent }: DocPageProps) {
291
314
  <h2 className="text-xl font-semibold">Navigation</h2>
292
315
  </CardHeader>
293
316
  <CardBody>
294
- <NavigationListbox />
317
+ <NavigationListbox
318
+ navigationItems={navigationItems}
319
+ selectedModule={selectedModule}
320
+ scrollToSection={scrollToSection}
321
+ setSelectedModule={setSelectedModule}
322
+ />
295
323
  </CardBody>
296
324
  </Card>
297
325
  </aside>