@lastbrain/app 0.1.22 → 0.1.24

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.
@@ -1 +1 @@
1
- {"version":3,"file":"init-app.d.ts","sourceRoot":"","sources":["../../src/scripts/init-app.ts"],"names":[],"mappings":"AAWA,UAAU,cAAc;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,wBAAsB,OAAO,CAAC,OAAO,EAAE,cAAc,iBAqJpD"}
1
+ {"version":3,"file":"init-app.d.ts","sourceRoot":"","sources":["../../src/scripts/init-app.ts"],"names":[],"mappings":"AAWA,UAAU,cAAc;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,wBAAsB,OAAO,CAAC,OAAO,EAAE,cAAc,iBAwJpD"}
@@ -48,14 +48,16 @@ export async function initApp(options) {
48
48
  await createNextStructure(targetDir, force, useHeroUI, withAuth);
49
49
  // 4. Créer les fichiers de configuration
50
50
  await createConfigFiles(targetDir, force, useHeroUI);
51
- // 5. Créer .gitignore et .env.local.example
51
+ // 5. Créer le système de proxy storage
52
+ await createStorageProxy(targetDir, force);
53
+ // 6. Créer .gitignore et .env.local.example
52
54
  await createGitIgnore(targetDir, force);
53
55
  await createEnvExample(targetDir, force);
54
- // 6. Créer la structure Supabase avec migrations
56
+ // 7. Créer la structure Supabase avec migrations
55
57
  await createSupabaseStructure(targetDir, force);
56
- // 7. Ajouter les scripts NPM
58
+ // 8. Ajouter les scripts NPM
57
59
  await addScriptsToPackageJson(targetDir);
58
- // 8. Enregistrer les modules sélectionnés
60
+ // 9. Enregistrer les modules sélectionnés
59
61
  if (withAuth || selectedModules.length > 0) {
60
62
  await saveModulesConfig(targetDir, selectedModules, withAuth);
61
63
  }
@@ -146,34 +148,76 @@ async function ensurePackageJson(targetDir, projectName) {
146
148
  console.log(chalk.green("✓ package.json existe"));
147
149
  }
148
150
  }
151
+ /**
152
+ * Récupère les versions actuelles des packages LastBrain depuis npm ou le monorepo
153
+ */
154
+ function getLastBrainVersions(targetDir) {
155
+ const targetIsInMonorepo = fs.existsSync(path.join(targetDir, "../../../packages/core/package.json")) ||
156
+ fs.existsSync(path.join(targetDir, "../../packages/core/package.json"));
157
+ if (targetIsInMonorepo) {
158
+ // Dans le monorepo, utiliser workspace:*
159
+ return {
160
+ app: "workspace:*",
161
+ core: "workspace:*",
162
+ ui: "workspace:*",
163
+ moduleAuth: "workspace:*",
164
+ };
165
+ }
166
+ // Hors monorepo, lire les versions depuis les package.json locaux (plus fiable que "latest")
167
+ try {
168
+ // Essayer de lire depuis node_modules/@lastbrain (si init-app a été installé)
169
+ const appPkgPath = path.join(__dirname, "../../package.json");
170
+ if (fs.existsSync(appPkgPath)) {
171
+ const appPkg = JSON.parse(fs.readFileSync(appPkgPath, "utf-8"));
172
+ const appVersion = `^${appPkg.version}`;
173
+ // Lire les versions des dépendances de @lastbrain/app
174
+ const coreDep = appPkg.dependencies?.["@lastbrain/core"];
175
+ const uiDep = appPkg.dependencies?.["@lastbrain/ui"];
176
+ const authDep = appPkg.dependencies?.["@lastbrain/module-auth"];
177
+ return {
178
+ app: appVersion,
179
+ core: coreDep || appVersion,
180
+ ui: uiDep || appVersion,
181
+ moduleAuth: authDep || appVersion,
182
+ };
183
+ }
184
+ }
185
+ catch (error) {
186
+ console.warn(chalk.yellow("⚠️ Impossible de lire les versions locales, utilisation de 'latest'"));
187
+ }
188
+ // Fallback: utiliser "latest"
189
+ return {
190
+ app: "latest",
191
+ core: "latest",
192
+ ui: "latest",
193
+ moduleAuth: "latest",
194
+ };
195
+ }
149
196
  async function addDependencies(targetDir, useHeroUI, withAuth, selectedModules = []) {
150
197
  const pkgPath = path.join(targetDir, "package.json");
151
198
  const pkg = await fs.readJson(pkgPath);
152
199
  console.log(chalk.yellow("\n📦 Configuration des dépendances..."));
153
- // Détecter si on est dans le monorepo (développement local)
154
- // Vérifier si le projet cible est à l'intérieur du monorepo
155
- const targetIsInMonorepo = fs.existsSync(path.join(targetDir, "../../../packages/core/package.json")) ||
156
- fs.existsSync(path.join(targetDir, "../../packages/core/package.json"));
157
- const latestVersion = targetIsInMonorepo ? "workspace:*" : "latest";
200
+ const versions = getLastBrainVersions(targetDir);
158
201
  // Dependencies
159
202
  const requiredDeps = {
160
203
  next: "^15.0.3",
161
204
  react: "^18.3.1",
162
205
  "react-dom": "^18.3.1",
163
- "@lastbrain/app": latestVersion,
164
- "@lastbrain/core": latestVersion,
165
- "@lastbrain/ui": latestVersion,
206
+ "@lastbrain/app": versions.app,
207
+ "@lastbrain/core": versions.core,
208
+ "@lastbrain/ui": versions.ui,
166
209
  "next-themes": "^0.4.6",
167
210
  };
168
211
  // Ajouter module-auth si demandé
169
212
  if (withAuth) {
170
- requiredDeps["@lastbrain/module-auth"] = latestVersion;
213
+ requiredDeps["@lastbrain/module-auth"] = versions.moduleAuth;
171
214
  }
172
215
  // Ajouter les autres modules sélectionnés
173
216
  for (const moduleName of selectedModules) {
174
217
  const moduleInfo = AVAILABLE_MODULES.find(m => m.name === moduleName);
175
218
  if (moduleInfo && moduleInfo.package !== "@lastbrain/module-auth") {
176
- requiredDeps[moduleInfo.package] = latestVersion;
219
+ // Pour les autres modules, utiliser "latest" ou la version depuis le package
220
+ requiredDeps[moduleInfo.package] = versions.app; // Utiliser la même version que app
177
221
  }
178
222
  }
179
223
  // Ajouter les dépendances HeroUI si nécessaire
@@ -213,6 +257,7 @@ async function addDependencies(targetDir, useHeroUI, withAuth, selectedModules =
213
257
  requiredDeps["@heroui/switch"] = "^2.2.24";
214
258
  requiredDeps["@heroui/table"] = "^2.2.27";
215
259
  requiredDeps["@heroui/tabs"] = "^2.2.24";
260
+ requiredDeps["@heroui/system"] = "^2.4.23"; // Ajout pour HeroUIProvider
216
261
  requiredDeps["@heroui/toast"] = "^2.0.17";
217
262
  requiredDeps["@heroui/tooltip"] = "^2.2.24";
218
263
  requiredDeps["@heroui/user"] = "^2.2.22";
@@ -868,21 +913,11 @@ async function addScriptsToPackageJson(targetDir) {
868
913
  build: "next build",
869
914
  start: "next start",
870
915
  lint: "next lint",
871
- lastbrain: targetIsInMonorepo
872
- ? "pnpm exec lastbrain"
873
- : "node node_modules/@lastbrain/app/dist/cli.js",
874
- "build:modules": targetIsInMonorepo
875
- ? "pnpm exec lastbrain module:build"
876
- : "node node_modules/@lastbrain/app/dist/scripts/module-build.js",
877
- "db:migrations:sync": targetIsInMonorepo
878
- ? "pnpm exec lastbrain db:migrations:sync"
879
- : "node node_modules/@lastbrain/app/dist/scripts/db-migrations-sync.js",
880
- "db:init": targetIsInMonorepo
881
- ? "pnpm exec lastbrain db:init"
882
- : "node node_modules/@lastbrain/app/dist/scripts/db-init.js",
883
- "readme:create": targetIsInMonorepo
884
- ? "pnpm exec lastbrain readme:create"
885
- : "node node_modules/@lastbrain/app/dist/scripts/readme-build.js",
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",
886
921
  };
887
922
  pkg.scripts = { ...pkg.scripts, ...scripts };
888
923
  await fs.writeJson(pkgPath, pkg, { spaces: 2 });
@@ -915,3 +950,339 @@ async function saveModulesConfig(targetDir, selectedModules, withAuth) {
915
950
  await fs.writeJson(modulesConfigPath, { modules }, { spaces: 2 });
916
951
  console.log(chalk.green("✓ Configuration des modules sauvegardée"));
917
952
  }
953
+ async function createStorageProxy(targetDir, force) {
954
+ console.log(chalk.yellow("\n🗂️ Création du système de proxy storage..."));
955
+ // Créer le dossier lib
956
+ const libDir = path.join(targetDir, "lib");
957
+ await fs.ensureDir(libDir);
958
+ // 1. Créer lib/bucket-config.ts
959
+ const bucketConfigPath = path.join(libDir, "bucket-config.ts");
960
+ if (!fs.existsSync(bucketConfigPath) || force) {
961
+ const bucketConfigContent = `/**
962
+ * Storage configuration for buckets and access control
963
+ */
964
+
965
+ export interface BucketConfig {
966
+ name: string;
967
+ isPublic: boolean;
968
+ description: string;
969
+ allowedFileTypes?: string[];
970
+ maxFileSize?: number; // in bytes
971
+ customAccessControl?: (userId: string, filePath: string) => boolean;
972
+ }
973
+
974
+ export const BUCKET_CONFIGS: Record<string, BucketConfig> = {
975
+ avatar: {
976
+ name: "avatar",
977
+ isPublic: true,
978
+ description: "User profile pictures and avatars",
979
+ allowedFileTypes: ["image/jpeg", "image/png", "image/webp", "image/gif"],
980
+ maxFileSize: 10 * 1024 * 1024, // 10MB
981
+ },
982
+ app: {
983
+ name: "app",
984
+ isPublic: false,
985
+ description: "Private user files and documents",
986
+ maxFileSize: 100 * 1024 * 1024, // 100MB
987
+ customAccessControl: (userId: string, filePath: string) => {
988
+ // Users can only access files in their own folder (app/{userId}/...)
989
+ return filePath.startsWith(\`\${userId}/\`);
990
+ },
991
+ },
992
+ // Example for future buckets:
993
+ // public: {
994
+ // name: "public",
995
+ // isPublic: true,
996
+ // description: "Publicly accessible files like logos, banners",
997
+ // allowedFileTypes: ["image/jpeg", "image/png", "image/webp", "application/pdf"],
998
+ // maxFileSize: 50 * 1024 * 1024, // 50MB
999
+ // },
1000
+ // documents: {
1001
+ // name: "documents",
1002
+ // isPublic: false,
1003
+ // description: "Private documents requiring authentication",
1004
+ // allowedFileTypes: ["application/pdf", "application/msword", "text/plain"],
1005
+ // maxFileSize: 25 * 1024 * 1024, // 25MB
1006
+ // },
1007
+ };
1008
+
1009
+ /**
1010
+ * Get bucket configuration
1011
+ */
1012
+ export function getBucketConfig(bucketName: string): BucketConfig | null {
1013
+ return BUCKET_CONFIGS[bucketName] || null;
1014
+ }
1015
+
1016
+ /**
1017
+ * Check if bucket is public
1018
+ */
1019
+ export function isPublicBucket(bucketName: string): boolean {
1020
+ const config = getBucketConfig(bucketName);
1021
+ return config?.isPublic ?? false;
1022
+ }
1023
+
1024
+ /**
1025
+ * Check if user has access to a specific file
1026
+ */
1027
+ export function hasFileAccess(bucketName: string, userId: string, filePath: string): boolean {
1028
+ const config = getBucketConfig(bucketName);
1029
+ if (!config) return false;
1030
+
1031
+ // Public buckets are accessible to everyone
1032
+ if (config.isPublic) return true;
1033
+
1034
+ // Private buckets require authentication
1035
+ if (!userId) return false;
1036
+
1037
+ // Apply custom access control if defined
1038
+ if (config.customAccessControl) {
1039
+ return config.customAccessControl(userId, filePath);
1040
+ }
1041
+
1042
+ return true;
1043
+ }
1044
+
1045
+ /**
1046
+ * Validate file type for bucket
1047
+ */
1048
+ export function isValidFileType(bucketName: string, contentType: string): boolean {
1049
+ const config = getBucketConfig(bucketName);
1050
+ if (!config || !config.allowedFileTypes) return true;
1051
+
1052
+ return config.allowedFileTypes.includes(contentType);
1053
+ }
1054
+
1055
+ /**
1056
+ * Check if file size is within bucket limits
1057
+ */
1058
+ export function isValidFileSize(bucketName: string, fileSize: number): boolean {
1059
+ const config = getBucketConfig(bucketName);
1060
+ if (!config || !config.maxFileSize) return true;
1061
+
1062
+ return fileSize <= config.maxFileSize;
1063
+ }`;
1064
+ await fs.writeFile(bucketConfigPath, bucketConfigContent);
1065
+ console.log(chalk.green("✓ lib/bucket-config.ts créé"));
1066
+ }
1067
+ // 2. Créer lib/storage.ts
1068
+ const storagePath = path.join(libDir, "storage.ts");
1069
+ if (!fs.existsSync(storagePath) || force) {
1070
+ const storageContent = `/**
1071
+ * Build storage proxy URL for files in Supabase buckets
1072
+ *
1073
+ * @param bucket - The bucket name (e.g., "avatar", "app")
1074
+ * @param path - The file path within the bucket
1075
+ * @returns Proxied URL (e.g., "/api/storage/avatar/user_128_123456.webp")
1076
+ */
1077
+ export function buildStorageUrl(bucket: string, path: string): string {
1078
+ // Remove leading slash if present
1079
+ const cleanPath = path.startsWith("/") ? path.slice(1) : path;
1080
+
1081
+ // Remove bucket prefix from path if present (e.g., "avatar/file.jpg" -> "file.jpg")
1082
+ const pathWithoutBucket = cleanPath.startsWith(bucket + "/")
1083
+ ? cleanPath.slice(bucket.length + 1)
1084
+ : cleanPath;
1085
+
1086
+ return \`/api/storage/\${bucket}/\${pathWithoutBucket}\`;
1087
+ }
1088
+
1089
+ /**
1090
+ * Extract bucket and path from a storage URL
1091
+ *
1092
+ * @param url - Storage URL (can be proxied URL or Supabase public URL)
1093
+ * @returns Object with bucket and path, or null if not a valid storage URL
1094
+ */
1095
+ export function parseStorageUrl(url: string): { bucket: string; path: string } | null {
1096
+ // Handle proxy URLs like "/api/storage/avatar/file.jpg"
1097
+ const proxyMatch = url.match(/^\\/api\\/storage\\/([^\\/]+)\\/(.+)$/);
1098
+ if (proxyMatch) {
1099
+ return {
1100
+ bucket: proxyMatch[1],
1101
+ path: proxyMatch[2]
1102
+ };
1103
+ }
1104
+
1105
+ // Handle Supabase public URLs
1106
+ const supabaseMatch = url.match(/\\/storage\\/v1\\/object\\/public\\/([^\\/]+)\\/(.+)$/);
1107
+ if (supabaseMatch) {
1108
+ return {
1109
+ bucket: supabaseMatch[1],
1110
+ path: supabaseMatch[2]
1111
+ };
1112
+ }
1113
+
1114
+ return null;
1115
+ }
1116
+
1117
+ /**
1118
+ * Convert a Supabase storage path to proxy URL
1119
+ *
1120
+ * @param storagePath - Path like "avatar/file.jpg" or "app/user/file.pdf"
1121
+ * @returns Proxied URL
1122
+ */
1123
+ export function storagePathToProxyUrl(storagePath: string): string {
1124
+ const parts = storagePath.split("/");
1125
+ if (parts.length < 2) {
1126
+ throw new Error("Invalid storage path format");
1127
+ }
1128
+
1129
+ const bucket = parts[0];
1130
+ const path = parts.slice(1).join("/");
1131
+
1132
+ return buildStorageUrl(bucket, path);
1133
+ }
1134
+
1135
+ /**
1136
+ * List of public buckets that don't require authentication
1137
+ */
1138
+ export const PUBLIC_BUCKETS = ["avatar"];
1139
+
1140
+ /**
1141
+ * List of private buckets that require authentication
1142
+ */
1143
+ export const PRIVATE_BUCKETS = ["app"];
1144
+
1145
+ /**
1146
+ * Check if a bucket is public
1147
+ */
1148
+ export function isPublicBucket(bucket: string): boolean {
1149
+ return PUBLIC_BUCKETS.includes(bucket);
1150
+ }
1151
+
1152
+ /**
1153
+ * Check if a bucket is private
1154
+ */
1155
+ export function isPrivateBucket(bucket: string): boolean {
1156
+ return PRIVATE_BUCKETS.includes(bucket);
1157
+ }`;
1158
+ await fs.writeFile(storagePath, storageContent);
1159
+ console.log(chalk.green("✓ lib/storage.ts créé"));
1160
+ }
1161
+ // 3. Créer app/api/storage/[bucket]/[...path]/route.ts
1162
+ const apiStorageDir = path.join(targetDir, "app", "api", "storage", "[bucket]", "[...path]");
1163
+ await fs.ensureDir(apiStorageDir);
1164
+ const routePath = path.join(apiStorageDir, "route.ts");
1165
+ if (!fs.existsSync(routePath) || force) {
1166
+ const routeContent = `import { NextRequest, NextResponse } from "next/server";
1167
+ import { getSupabaseServerClient } from "@lastbrain/core/server";
1168
+ import { getBucketConfig, hasFileAccess } from "@/lib/bucket-config";
1169
+
1170
+ /**
1171
+ * GET /api/storage/[bucket]/[...path]
1172
+ * Proxy for Supabase Storage files with clean URLs and access control
1173
+ *
1174
+ * Examples:
1175
+ * - /api/storage/avatar/user_128_123456.webp
1176
+ * - /api/storage/app/user/documents/file.pdf
1177
+ */
1178
+ export async function GET(
1179
+ request: NextRequest,
1180
+ props: { params: Promise<{ bucket: string; path: string[] }> }
1181
+ ) {
1182
+ try {
1183
+ const { bucket, path } = await props.params;
1184
+ const filePath = path.join("/");
1185
+
1186
+ // Check if bucket exists in our configuration
1187
+ const bucketConfig = getBucketConfig(bucket);
1188
+ if (!bucketConfig) {
1189
+ return new NextResponse("Bucket not allowed", { status: 403 });
1190
+ }
1191
+
1192
+ const supabase = await getSupabaseServerClient();
1193
+ let userId: string | null = null;
1194
+
1195
+ // Get user for private buckets or custom access control
1196
+ if (!bucketConfig.isPublic) {
1197
+ const { data: { user }, error: authError } = await supabase.auth.getUser();
1198
+
1199
+ if (authError || !user) {
1200
+ return new NextResponse("Unauthorized", { status: 401 });
1201
+ }
1202
+
1203
+ userId = user.id;
1204
+ }
1205
+
1206
+ // Check file access permissions
1207
+ if (!hasFileAccess(bucket, userId || "", filePath)) {
1208
+ return new NextResponse("Forbidden - Access denied to this file", { status: 403 });
1209
+ }
1210
+
1211
+ // Get file from Supabase Storage
1212
+ const { data: file, error } = await supabase.storage
1213
+ .from(bucket)
1214
+ .download(filePath);
1215
+
1216
+ if (error) {
1217
+ console.error("Storage download error:", error);
1218
+ if (error.message.includes("not found")) {
1219
+ return new NextResponse("File not found", { status: 404 });
1220
+ }
1221
+ return new NextResponse("Storage error", { status: 500 });
1222
+ }
1223
+
1224
+ if (!file) {
1225
+ return new NextResponse("File not found", { status: 404 });
1226
+ }
1227
+
1228
+ // Convert blob to array buffer
1229
+ const arrayBuffer = await file.arrayBuffer();
1230
+
1231
+ // Determine content type from file extension
1232
+ const getContentType = (filename: string): string => {
1233
+ const ext = filename.toLowerCase().split(".").pop();
1234
+ const mimeTypes: Record<string, string> = {
1235
+ // Images
1236
+ jpg: "image/jpeg",
1237
+ jpeg: "image/jpeg",
1238
+ png: "image/png",
1239
+ gif: "image/gif",
1240
+ webp: "image/webp",
1241
+ svg: "image/svg+xml",
1242
+ // Documents
1243
+ pdf: "application/pdf",
1244
+ doc: "application/msword",
1245
+ docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
1246
+ xls: "application/vnd.ms-excel",
1247
+ xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
1248
+ // Text
1249
+ txt: "text/plain",
1250
+ csv: "text/csv",
1251
+ // Videos
1252
+ mp4: "video/mp4",
1253
+ avi: "video/x-msvideo",
1254
+ mov: "video/quicktime",
1255
+ // Audio
1256
+ mp3: "audio/mpeg",
1257
+ wav: "audio/wav",
1258
+ // Archives
1259
+ zip: "application/zip",
1260
+ rar: "application/x-rar-compressed",
1261
+ };
1262
+ return mimeTypes[ext || ""] || "application/octet-stream";
1263
+ };
1264
+
1265
+ const contentType = getContentType(filePath);
1266
+
1267
+ // Create response with proper headers
1268
+ const response = new NextResponse(arrayBuffer, {
1269
+ status: 200,
1270
+ headers: {
1271
+ "Content-Type": contentType,
1272
+ "Cache-Control": "public, max-age=31536000, immutable", // Cache for 1 year
1273
+ "Content-Length": arrayBuffer.byteLength.toString(),
1274
+ },
1275
+ });
1276
+
1277
+ return response;
1278
+
1279
+ } catch (error) {
1280
+ console.error("Storage proxy error:", error);
1281
+ return new NextResponse("Internal server error", { status: 500 });
1282
+ }
1283
+ }`;
1284
+ await fs.writeFile(routePath, routeContent);
1285
+ console.log(chalk.green("✓ app/api/storage/[bucket]/[...path]/route.ts créé"));
1286
+ }
1287
+ console.log(chalk.green("✓ Système de proxy storage configuré"));
1288
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"module-create.d.ts","sourceRoot":"","sources":["../../src/scripts/module-create.ts"],"names":[],"mappings":"AAupBA;;GAEG;AACH,wBAAsB,YAAY,kBAsIjC"}
1
+ {"version":3,"file":"module-create.d.ts","sourceRoot":"","sources":["../../src/scripts/module-create.ts"],"names":[],"mappings":"AAirBA;;GAEG;AACH,wBAAsB,YAAY,kBAsIjC"}
@@ -26,10 +26,31 @@ function parseTablesList(input) {
26
26
  .map((t) => t.trim())
27
27
  .filter((t) => t.length > 0);
28
28
  }
29
+ /**
30
+ * Récupère les versions actuelles des packages LastBrain
31
+ */
32
+ function getLastBrainPackageVersions(rootDir) {
33
+ try {
34
+ const corePackageJson = JSON.parse(fs.readFileSync(path.join(rootDir, "packages", "core", "package.json"), "utf-8"));
35
+ const uiPackageJson = JSON.parse(fs.readFileSync(path.join(rootDir, "packages", "ui", "package.json"), "utf-8"));
36
+ return {
37
+ core: `^${corePackageJson.version}`,
38
+ ui: `^${uiPackageJson.version}`,
39
+ };
40
+ }
41
+ catch (error) {
42
+ console.warn(chalk.yellow("⚠️ Impossible de lire les versions des packages, utilisation des versions par défaut"));
43
+ return {
44
+ core: "^0.1.0",
45
+ ui: "^0.1.4",
46
+ };
47
+ }
48
+ }
29
49
  /**
30
50
  * Génère le contenu du package.json
31
51
  */
32
- function generatePackageJson(moduleName, slug) {
52
+ function generatePackageJson(moduleName, slug, rootDir) {
53
+ const versions = getLastBrainPackageVersions(rootDir);
33
54
  const buildConfigExport = `./${slug}.build.config`;
34
55
  return JSON.stringify({
35
56
  name: moduleName,
@@ -38,14 +59,14 @@ function generatePackageJson(moduleName, slug) {
38
59
  type: "module",
39
60
  main: "dist/index.js",
40
61
  types: "dist/index.d.ts",
41
- files: ["dist", "supabase"],
62
+ files: ["dist", "src", "supabase"],
42
63
  scripts: {
43
64
  build: "tsc -p tsconfig.json",
44
65
  dev: "tsc -p tsconfig.json --watch",
45
66
  },
46
67
  dependencies: {
47
- "@lastbrain/core": "workspace:0.1.0",
48
- "@lastbrain/ui": "workspace:0.1.0",
68
+ "@lastbrain/core": versions.core,
69
+ "@lastbrain/ui": versions.ui,
49
70
  react: "^19.0.0",
50
71
  "lucide-react": "^0.554.0",
51
72
  "react-dom": "^19.0.0",
@@ -503,7 +524,7 @@ async function createModuleStructure(config, rootDir) {
503
524
  await fs.ensureDir(path.join(moduleDir, "supabase", "migrations"));
504
525
  // Créer package.json
505
526
  console.log(chalk.yellow(" 📄 package.json"));
506
- await fs.writeFile(path.join(moduleDir, "package.json"), generatePackageJson(config.moduleName, config.slug));
527
+ await fs.writeFile(path.join(moduleDir, "package.json"), generatePackageJson(config.moduleName, config.slug, rootDir));
507
528
  // Créer tsconfig.json
508
529
  console.log(chalk.yellow(" 📄 tsconfig.json"));
509
530
  await fs.writeFile(path.join(moduleDir, "tsconfig.json"), generateTsConfig());