@lastbrain/app 2.0.1 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/version.d.ts +7 -0
- package/dist/config/version.d.ts.map +1 -0
- package/dist/config/version.js +25 -0
- package/dist/src/__tests__/module-registry.test.d.ts +2 -0
- package/dist/src/__tests__/module-registry.test.d.ts.map +1 -0
- package/dist/src/__tests__/module-registry.test.js +53 -0
- package/dist/src/app-shell/(admin)/layout.d.ts +4 -0
- package/dist/src/app-shell/(admin)/layout.d.ts.map +1 -0
- package/dist/src/app-shell/(admin)/layout.js +5 -0
- package/dist/src/app-shell/(auth)/layout.d.ts +4 -0
- package/dist/src/app-shell/(auth)/layout.d.ts.map +1 -0
- package/dist/src/app-shell/(auth)/layout.js +5 -0
- package/dist/src/app-shell/(public)/page.d.ts +2 -0
- package/dist/src/app-shell/(public)/page.d.ts.map +1 -0
- package/dist/src/app-shell/(public)/page.js +5 -0
- package/dist/src/app-shell/layout.d.ts +3 -0
- package/dist/src/app-shell/layout.d.ts.map +1 -0
- package/dist/src/app-shell/layout.js +3 -0
- package/dist/src/app-shell/not-found.d.ts +2 -0
- package/dist/src/app-shell/not-found.d.ts.map +1 -0
- package/dist/src/app-shell/not-found.js +10 -0
- package/dist/src/auth/authHelpers.d.ts +7 -0
- package/dist/src/auth/authHelpers.d.ts.map +1 -0
- package/dist/src/auth/authHelpers.js +19 -0
- package/dist/src/auth/useAuthSession.d.ts +7 -0
- package/dist/src/auth/useAuthSession.d.ts.map +1 -0
- package/dist/src/auth/useAuthSession.js +49 -0
- package/dist/src/cli.d.ts +3 -0
- package/dist/src/cli.d.ts.map +1 -0
- package/dist/src/cli.js +143 -0
- package/dist/src/components/NotificationContainer.d.ts +2 -0
- package/dist/src/components/NotificationContainer.d.ts.map +1 -0
- package/dist/src/components/NotificationContainer.js +8 -0
- package/dist/src/hooks/useNotifications.d.ts +30 -0
- package/dist/src/hooks/useNotifications.d.ts.map +1 -0
- package/dist/src/hooks/useNotifications.js +165 -0
- package/dist/src/index.d.ts +22 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +22 -0
- package/dist/src/layouts/AdminLayout.d.ts +4 -0
- package/dist/src/layouts/AdminLayout.d.ts.map +1 -0
- package/dist/src/layouts/AdminLayout.js +4 -0
- package/dist/src/layouts/AdminLayoutWithSidebar.d.ts +10 -0
- package/dist/src/layouts/AdminLayoutWithSidebar.d.ts.map +1 -0
- package/dist/src/layouts/AdminLayoutWithSidebar.js +62 -0
- package/dist/src/layouts/AppProviders.d.ts +27 -0
- package/dist/src/layouts/AppProviders.d.ts.map +1 -0
- package/dist/src/layouts/AppProviders.js +48 -0
- package/dist/src/layouts/AuthLayout.d.ts +4 -0
- package/dist/src/layouts/AuthLayout.d.ts.map +1 -0
- package/dist/src/layouts/AuthLayout.js +4 -0
- package/dist/src/layouts/AuthLayoutWithSidebar.d.ts +12 -0
- package/dist/src/layouts/AuthLayoutWithSidebar.d.ts.map +1 -0
- package/dist/src/layouts/AuthLayoutWithSidebar.js +60 -0
- package/dist/src/layouts/PublicLayout.d.ts +8 -0
- package/dist/src/layouts/PublicLayout.d.ts.map +1 -0
- package/dist/src/layouts/PublicLayout.js +6 -0
- package/dist/src/layouts/PublicLayoutWithSidebar.d.ts +9 -0
- package/dist/src/layouts/PublicLayoutWithSidebar.d.ts.map +1 -0
- package/dist/src/layouts/PublicLayoutWithSidebar.js +60 -0
- package/dist/src/layouts/RootLayout.d.ts +6 -0
- package/dist/src/layouts/RootLayout.d.ts.map +1 -0
- package/dist/src/layouts/RootLayout.js +9 -0
- package/dist/src/modules/module-loader.d.ts +5 -0
- package/dist/src/modules/module-loader.d.ts.map +1 -0
- package/dist/src/modules/module-loader.js +10 -0
- package/dist/src/scripts/db-init.d.ts +2 -0
- package/dist/src/scripts/db-init.d.ts.map +1 -0
- package/dist/src/scripts/db-init.js +300 -0
- package/dist/src/scripts/db-migrations-sync.d.ts +2 -0
- package/dist/src/scripts/db-migrations-sync.d.ts.map +1 -0
- package/dist/src/scripts/db-migrations-sync.js +84 -0
- package/dist/src/scripts/dev-sync.d.ts +2 -0
- package/dist/src/scripts/dev-sync.d.ts.map +1 -0
- package/dist/src/scripts/dev-sync.js +194 -0
- package/dist/src/scripts/init-app.d.ts +12 -0
- package/dist/src/scripts/init-app.d.ts.map +1 -0
- package/dist/src/scripts/init-app.js +2175 -0
- package/dist/src/scripts/module-add.d.ts +2 -0
- package/dist/src/scripts/module-add.d.ts.map +1 -0
- package/dist/src/scripts/module-add.js +232 -0
- package/dist/src/scripts/module-build.d.ts +2 -0
- package/dist/src/scripts/module-build.d.ts.map +1 -0
- package/dist/src/scripts/module-build.js +1280 -0
- package/dist/src/scripts/module-create.d.ts +28 -0
- package/dist/src/scripts/module-create.d.ts.map +1 -0
- package/dist/src/scripts/module-create.js +1429 -0
- package/dist/src/scripts/module-delete.d.ts +6 -0
- package/dist/src/scripts/module-delete.d.ts.map +1 -0
- package/dist/src/scripts/module-delete.js +147 -0
- package/dist/src/scripts/module-list.d.ts +2 -0
- package/dist/src/scripts/module-list.d.ts.map +1 -0
- package/dist/src/scripts/module-list.js +61 -0
- package/dist/src/scripts/module-remove.d.ts +2 -0
- package/dist/src/scripts/module-remove.d.ts.map +1 -0
- package/dist/src/scripts/module-remove.js +311 -0
- package/dist/src/scripts/readme-build.d.ts +2 -0
- package/dist/src/scripts/readme-build.d.ts.map +1 -0
- package/dist/src/scripts/readme-build.js +39 -0
- package/dist/src/scripts/script-runner.d.ts +5 -0
- package/dist/src/scripts/script-runner.d.ts.map +1 -0
- package/dist/src/scripts/script-runner.js +25 -0
- package/dist/src/templates/AuthGuidePage.d.ts +2 -0
- package/dist/src/templates/AuthGuidePage.d.ts.map +1 -0
- package/dist/src/templates/AuthGuidePage.js +9 -0
- package/dist/src/templates/DefaultDoc.d.ts +2 -0
- package/dist/src/templates/DefaultDoc.d.ts.map +1 -0
- package/dist/src/templates/DefaultDoc.js +240 -0
- package/dist/src/templates/DocPage.d.ts +17 -0
- package/dist/src/templates/DocPage.d.ts.map +1 -0
- package/dist/src/templates/DocPage.js +193 -0
- package/dist/src/templates/DocsPageWithModules.d.ts +2 -0
- package/dist/src/templates/DocsPageWithModules.d.ts.map +1 -0
- package/dist/src/templates/DocsPageWithModules.js +8 -0
- package/dist/src/templates/MigrationsGuidePage.d.ts +2 -0
- package/dist/src/templates/MigrationsGuidePage.d.ts.map +1 -0
- package/dist/src/templates/MigrationsGuidePage.js +11 -0
- package/dist/src/templates/ModuleGuidePage.d.ts +2 -0
- package/dist/src/templates/ModuleGuidePage.d.ts.map +1 -0
- package/dist/src/templates/ModuleGuidePage.js +14 -0
- package/dist/src/templates/SimpleDocPage.d.ts +2 -0
- package/dist/src/templates/SimpleDocPage.d.ts.map +1 -0
- package/dist/src/templates/SimpleDocPage.js +28 -0
- package/dist/src/templates/SimpleHomePage.d.ts +6 -0
- package/dist/src/templates/SimpleHomePage.d.ts.map +1 -0
- package/dist/src/templates/SimpleHomePage.js +7 -0
- package/dist/src/types/menu.d.ts +23 -0
- package/dist/src/types/menu.d.ts.map +1 -0
- package/dist/src/types/menu.js +1 -0
- package/package.json +4 -5
- package/src/scripts/init-app.ts +7 -76
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { spawn, spawnSync, } from "node:child_process";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
// Utiliser PROJECT_ROOT si défini (pour pnpm --filter), sinon process.cwd()
|
|
6
|
+
const projectRoot = process.env.PROJECT_ROOT || process.cwd();
|
|
7
|
+
const supabaseDir = path.join(projectRoot, "supabase");
|
|
8
|
+
const envTargets = [path.join(projectRoot, ".env.local")];
|
|
9
|
+
// Trouver le template dans le package @lastbrain/app
|
|
10
|
+
const packageAppDir = path
|
|
11
|
+
.dirname(fileURLToPath(import.meta.url))
|
|
12
|
+
.replace(/dist\/scripts$/, "src");
|
|
13
|
+
const envExampleTemplate = path.join(packageAppDir, "templates/env.example/.env.example");
|
|
14
|
+
function ensureDirectory(dir) {
|
|
15
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
16
|
+
}
|
|
17
|
+
function runSupabase(...args) {
|
|
18
|
+
const bin = path.join(projectRoot, "node_modules", ".bin", "supabase");
|
|
19
|
+
// Ensure PATH includes common binary locations
|
|
20
|
+
const PATH = process.env.PATH || "";
|
|
21
|
+
const enhancedPath = `/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin:${PATH}`;
|
|
22
|
+
// Check if first argument is a boolean (captureOutput flag)
|
|
23
|
+
const captureOutput = typeof args[0] === "boolean" ? args.shift() : false;
|
|
24
|
+
const stdio = captureOutput ? "pipe" : "inherit";
|
|
25
|
+
const runners = [
|
|
26
|
+
() => spawnSync("supabase", args, {
|
|
27
|
+
cwd: projectRoot,
|
|
28
|
+
stdio: stdio,
|
|
29
|
+
env: { ...process.env, PATH: enhancedPath },
|
|
30
|
+
shell: true,
|
|
31
|
+
}),
|
|
32
|
+
() => spawnSync(bin, args, {
|
|
33
|
+
cwd: projectRoot,
|
|
34
|
+
stdio: stdio,
|
|
35
|
+
env: { ...process.env, PATH: enhancedPath },
|
|
36
|
+
}),
|
|
37
|
+
() => spawnSync("pnpm", ["exec", "supabase", ...args], {
|
|
38
|
+
cwd: projectRoot,
|
|
39
|
+
stdio: stdio,
|
|
40
|
+
env: { ...process.env, PATH: enhancedPath },
|
|
41
|
+
shell: true,
|
|
42
|
+
}),
|
|
43
|
+
];
|
|
44
|
+
for (let i = 0; i < runners.length; i++) {
|
|
45
|
+
const result = runners[i]();
|
|
46
|
+
if (result.error) {
|
|
47
|
+
// Si c'est une erreur ENOENT (commande non trouvée), essayer la suivante
|
|
48
|
+
if (result.error.code === "ENOENT") {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
// Si c'est une autre erreur, la propager
|
|
52
|
+
throw result.error;
|
|
53
|
+
}
|
|
54
|
+
// Si le processus s'est terminé avec un code de sortie non nul, propager l'erreur
|
|
55
|
+
if (result.status !== 0) {
|
|
56
|
+
const error = new Error(`Command failed with exit code ${result.status}`);
|
|
57
|
+
error.status = result.status;
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
// Succès !
|
|
61
|
+
return result.stdout || null;
|
|
62
|
+
}
|
|
63
|
+
throw new Error("Unable to locate Supabase CLI (install it globally or via pnpm).");
|
|
64
|
+
}
|
|
65
|
+
function ensureSupabaseInit() {
|
|
66
|
+
const configPath = path.join(supabaseDir, "config.toml");
|
|
67
|
+
if (!fs.existsSync(configPath)) {
|
|
68
|
+
runSupabase("init");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function parseEnvFile(filePath) {
|
|
72
|
+
const data = fs.readFileSync(filePath, "utf-8");
|
|
73
|
+
return Object.fromEntries(data
|
|
74
|
+
.split("\n")
|
|
75
|
+
.map((line) => line.trim())
|
|
76
|
+
.filter((line) => line && !line.startsWith("#"))
|
|
77
|
+
.map((line) => {
|
|
78
|
+
const [key, ...value] = line.split("=");
|
|
79
|
+
return [key, value.join("=")];
|
|
80
|
+
}));
|
|
81
|
+
}
|
|
82
|
+
function ensureEnvFile(values) {
|
|
83
|
+
envTargets.forEach((target) => {
|
|
84
|
+
let existingVars = {};
|
|
85
|
+
// Lire le fichier .env.local existant pour préserver les variables personnalisées
|
|
86
|
+
if (fs.existsSync(target)) {
|
|
87
|
+
try {
|
|
88
|
+
const existingContent = fs.readFileSync(target, "utf-8");
|
|
89
|
+
existingVars = Object.fromEntries(existingContent
|
|
90
|
+
.split("\n")
|
|
91
|
+
.filter((line) => line.includes("=") && !line.startsWith("#"))
|
|
92
|
+
.map((line) => {
|
|
93
|
+
const [key, ...value] = line.split("=");
|
|
94
|
+
return [key, value.join("=")];
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
console.warn(`⚠️ Erreur lors de la lecture de ${target}:`, error);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Fusionner les nouvelles valeurs avec les existantes (nouvelles valeurs prioritaires)
|
|
102
|
+
const mergedValues = { ...existingVars, ...values };
|
|
103
|
+
const content = Object.entries(mergedValues)
|
|
104
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
105
|
+
.join("\n");
|
|
106
|
+
fs.writeFileSync(target, content);
|
|
107
|
+
console.log(`📝 Wrote env to ${target}`);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
function buildEnvPayload(creds) {
|
|
111
|
+
return {
|
|
112
|
+
NEXT_PUBLIC_SUPABASE_URL: creds.url ?? "",
|
|
113
|
+
NEXT_PUBLIC_SUPABASE_ANON_KEY: creds.anon ?? "",
|
|
114
|
+
SUPABASE_URL: creds.url ?? "",
|
|
115
|
+
SUPABASE_ANON_KEY: creds.anon ?? "",
|
|
116
|
+
SUPABASE_SERVICE_ROLE_KEY: creds.service ?? "",
|
|
117
|
+
SUPABASE_DB_URL: creds.db ?? "",
|
|
118
|
+
SUPABASE_JWT_SECRET: creds.jwt ?? "",
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function applyEnvToProcess(values) {
|
|
122
|
+
process.env.NEXT_PUBLIC_SUPABASE_URL = values.NEXT_PUBLIC_SUPABASE_URL;
|
|
123
|
+
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY =
|
|
124
|
+
values.NEXT_PUBLIC_SUPABASE_ANON_KEY;
|
|
125
|
+
process.env.SUPABASE_URL = values.SUPABASE_URL;
|
|
126
|
+
process.env.SUPABASE_ANON_KEY = values.SUPABASE_ANON_KEY;
|
|
127
|
+
process.env.SUPABASE_SERVICE_ROLE_KEY = values.SUPABASE_SERVICE_ROLE_KEY;
|
|
128
|
+
process.env.SUPABASE_DB_URL = values.SUPABASE_DB_URL;
|
|
129
|
+
process.env.SUPABASE_JWT_SECRET = values.SUPABASE_JWT_SECRET;
|
|
130
|
+
}
|
|
131
|
+
function copyEnvExample() {
|
|
132
|
+
if (!fs.existsSync(envExampleTemplate)) {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
const target = envTargets[0];
|
|
136
|
+
ensureDirectory(path.dirname(target));
|
|
137
|
+
fs.copyFileSync(envExampleTemplate, target);
|
|
138
|
+
return target;
|
|
139
|
+
}
|
|
140
|
+
function readSupabaseEnv() {
|
|
141
|
+
const candidate = path.join(supabaseDir, ".env");
|
|
142
|
+
if (fs.existsSync(candidate)) {
|
|
143
|
+
return parseEnvFile(candidate);
|
|
144
|
+
}
|
|
145
|
+
const candidateLocal = path.join(supabaseDir, ".env.local");
|
|
146
|
+
if (fs.existsSync(candidateLocal)) {
|
|
147
|
+
return parseEnvFile(candidateLocal);
|
|
148
|
+
}
|
|
149
|
+
return {};
|
|
150
|
+
}
|
|
151
|
+
function getSupabaseStatusEnv() {
|
|
152
|
+
try {
|
|
153
|
+
const result = runSupabase(true, "status", "-o", "env");
|
|
154
|
+
if (!result)
|
|
155
|
+
return null;
|
|
156
|
+
const envString = result.toString();
|
|
157
|
+
const lines = envString
|
|
158
|
+
.split("\n")
|
|
159
|
+
.map((line) => line.trim())
|
|
160
|
+
.filter((line) => line && !line.startsWith("#") && line.includes("="));
|
|
161
|
+
const env = {};
|
|
162
|
+
for (const line of lines) {
|
|
163
|
+
const equalIndex = line.indexOf("=");
|
|
164
|
+
if (equalIndex > 0) {
|
|
165
|
+
const key = line.substring(0, equalIndex);
|
|
166
|
+
const value = line
|
|
167
|
+
.substring(equalIndex + 1)
|
|
168
|
+
.replace(/^["']|["']$/g, "");
|
|
169
|
+
env[key] = value;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return Object.keys(env).length > 0 ? env : null;
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function buildCredsFromStatus(status) {
|
|
179
|
+
if (!status) {
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
url: status.API_URL ?? status.SUPABASE_URL ?? "",
|
|
184
|
+
anon: status.ANON_KEY ?? "",
|
|
185
|
+
service: status.SERVICE_ROLE_KEY ?? status.SECRET_KEY ?? "",
|
|
186
|
+
jwt: status.JWT_SECRET ?? "",
|
|
187
|
+
db: status.DB_URL ?? status.DATABASE_URL ?? "",
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
function findLocalKeysFallback() {
|
|
191
|
+
const env = readSupabaseEnv();
|
|
192
|
+
if (Object.keys(env).length === 0) {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
url: env.SUPABASE_URL ?? env.API_URL ?? null,
|
|
197
|
+
anon: env.SUPABASE_ANON_KEY ?? env.ANON_KEY ?? null,
|
|
198
|
+
service: env.SUPABASE_SERVICE_ROLE_KEY ?? env.SERVICE_ROLE_KEY ?? null,
|
|
199
|
+
jwt: env.JWT_SECRET ?? null,
|
|
200
|
+
db: env.SUPABASE_DB_URL ?? env.DB_URL ?? null,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
async function wait(ms) {
|
|
204
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
205
|
+
}
|
|
206
|
+
async function waitForStatus() {
|
|
207
|
+
const maxAttempts = 10;
|
|
208
|
+
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
209
|
+
const status = getSupabaseStatusEnv();
|
|
210
|
+
if (status && Object.keys(status).length > 0) {
|
|
211
|
+
return status;
|
|
212
|
+
}
|
|
213
|
+
await wait(1000);
|
|
214
|
+
}
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
async function gatherCredentials() {
|
|
218
|
+
const envStatus = await waitForStatus();
|
|
219
|
+
let creds = envStatus ? buildCredsFromStatus(envStatus) : null;
|
|
220
|
+
if (!creds || !creds.url || !creds.anon) {
|
|
221
|
+
const fallback = findLocalKeysFallback();
|
|
222
|
+
if (fallback) {
|
|
223
|
+
creds = {
|
|
224
|
+
url: fallback.url ?? "",
|
|
225
|
+
anon: fallback.anon ?? "",
|
|
226
|
+
service: fallback.service ?? "",
|
|
227
|
+
jwt: fallback.jwt ?? "",
|
|
228
|
+
db: fallback.db ?? "",
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return creds;
|
|
233
|
+
}
|
|
234
|
+
async function main() {
|
|
235
|
+
ensureSupabaseInit();
|
|
236
|
+
const PATH = process.env.PATH || "";
|
|
237
|
+
const enhancedPath = `/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin:${PATH}`;
|
|
238
|
+
const migrationResult = spawnSync("pnpm", ["--filter", "web", "db:migrations:sync"], {
|
|
239
|
+
cwd: projectRoot,
|
|
240
|
+
stdio: "inherit",
|
|
241
|
+
env: { ...process.env, PATH: enhancedPath },
|
|
242
|
+
shell: true,
|
|
243
|
+
});
|
|
244
|
+
if (migrationResult.error || migrationResult.status !== 0) {
|
|
245
|
+
console.warn("⚠️ Migration sync failed, continuing anyway...");
|
|
246
|
+
}
|
|
247
|
+
console.log("⚙️ Starting Supabase...");
|
|
248
|
+
try {
|
|
249
|
+
runSupabase("start");
|
|
250
|
+
}
|
|
251
|
+
catch {
|
|
252
|
+
console.warn("⚠️ Supabase start had issues, continuing...");
|
|
253
|
+
}
|
|
254
|
+
console.log("🔄 Resetting database...");
|
|
255
|
+
try {
|
|
256
|
+
runSupabase("db", "reset");
|
|
257
|
+
console.log("✅ Database reset complete.");
|
|
258
|
+
}
|
|
259
|
+
catch (error) {
|
|
260
|
+
console.error("⚠️ supabase db reset failed:", error);
|
|
261
|
+
throw error;
|
|
262
|
+
}
|
|
263
|
+
console.log("🛠 Waiting for services to restart...");
|
|
264
|
+
await wait(3000);
|
|
265
|
+
const creds = await gatherCredentials();
|
|
266
|
+
if (creds && creds.url && creds.anon) {
|
|
267
|
+
const payload = buildEnvPayload(creds);
|
|
268
|
+
ensureEnvFile(payload);
|
|
269
|
+
applyEnvToProcess(payload);
|
|
270
|
+
console.log("✅ Environment configured successfully!");
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
const example = copyEnvExample();
|
|
274
|
+
if (example) {
|
|
275
|
+
console.log("ℹ️ Copied .env.example into .env.local as fallback.");
|
|
276
|
+
}
|
|
277
|
+
console.warn("⚠️ Impossible de récupérer automatiquement les accès Supabase.");
|
|
278
|
+
}
|
|
279
|
+
if (process.argv.includes("--with-dev")) {
|
|
280
|
+
console.log("🚀 Launching Next dev server...");
|
|
281
|
+
const devProcess = spawn("pnpm", ["--filter", "web", "dev"], {
|
|
282
|
+
cwd: projectRoot,
|
|
283
|
+
stdio: "inherit",
|
|
284
|
+
});
|
|
285
|
+
devProcess.on("exit", (code) => {
|
|
286
|
+
console.log("🧹 Stopping Supabase");
|
|
287
|
+
try {
|
|
288
|
+
runSupabase("stop");
|
|
289
|
+
}
|
|
290
|
+
catch {
|
|
291
|
+
// Ignore errors when stopping
|
|
292
|
+
}
|
|
293
|
+
process.exit(code ?? 0);
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
main().catch((error) => {
|
|
298
|
+
console.error(error);
|
|
299
|
+
process.exit(1);
|
|
300
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db-migrations-sync.d.ts","sourceRoot":"","sources":["../../../src/scripts/db-migrations-sync.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
// Utiliser PROJECT_ROOT si défini (pour pnpm --filter), sinon process.cwd()
|
|
4
|
+
const projectRoot = process.env.PROJECT_ROOT || process.cwd();
|
|
5
|
+
const migrationsDir = path.join(projectRoot, "supabase/migrations");
|
|
6
|
+
function ensureDirectory(dir) {
|
|
7
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Copie les migrations d'un module vers supabase/migrations
|
|
11
|
+
*/
|
|
12
|
+
function copyModuleMigrations(moduleName, moduleDir) {
|
|
13
|
+
const sourceMigrationsDir = path.join(moduleDir, "supabase", "migrations");
|
|
14
|
+
if (!fs.existsSync(sourceMigrationsDir)) {
|
|
15
|
+
return 0;
|
|
16
|
+
}
|
|
17
|
+
const files = fs
|
|
18
|
+
.readdirSync(sourceMigrationsDir)
|
|
19
|
+
.filter((file) => file.endsWith(".sql"))
|
|
20
|
+
.sort();
|
|
21
|
+
if (files.length === 0) {
|
|
22
|
+
return 0;
|
|
23
|
+
}
|
|
24
|
+
ensureDirectory(migrationsDir);
|
|
25
|
+
let copiedCount = 0;
|
|
26
|
+
files.forEach((file) => {
|
|
27
|
+
const sourceFile = path.join(sourceMigrationsDir, file);
|
|
28
|
+
const targetFile = path.join(migrationsDir, file);
|
|
29
|
+
// Ne copier que si le fichier n'existe pas déjà
|
|
30
|
+
if (!fs.existsSync(targetFile)) {
|
|
31
|
+
fs.copyFileSync(sourceFile, targetFile);
|
|
32
|
+
console.log(`📜 Copied migration: ${file} from ${moduleName}`);
|
|
33
|
+
copiedCount++;
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
console.log(`⏭️ Migration already exists: ${file}`);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
return copiedCount;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Synchronise les migrations de tous les modules actifs
|
|
43
|
+
*/
|
|
44
|
+
function syncModuleMigrations() {
|
|
45
|
+
// Charger modules.json
|
|
46
|
+
const modulesJsonPath = path.join(projectRoot, ".lastbrain", "modules.json");
|
|
47
|
+
if (!fs.existsSync(modulesJsonPath)) {
|
|
48
|
+
console.error("❌ modules.json not found");
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const modulesData = JSON.parse(fs.readFileSync(modulesJsonPath, "utf-8"));
|
|
52
|
+
const modules = modulesData.modules || [];
|
|
53
|
+
// Filtrer les modules actifs
|
|
54
|
+
const activeModules = modules.filter((m) => m.active !== false);
|
|
55
|
+
console.log(`🔄 Syncing migrations from ${activeModules.length} active module(s)...\n`);
|
|
56
|
+
let totalCopied = 0;
|
|
57
|
+
activeModules.forEach((module) => {
|
|
58
|
+
const moduleName = module.package;
|
|
59
|
+
// Essayer plusieurs chemins possibles pour trouver le module
|
|
60
|
+
const possiblePaths = [
|
|
61
|
+
// Dans node_modules local de l'app
|
|
62
|
+
path.join(projectRoot, "node_modules", moduleName),
|
|
63
|
+
// Dans node_modules à la racine du workspace
|
|
64
|
+
path.join(projectRoot, "..", "..", "node_modules", moduleName),
|
|
65
|
+
// Directement dans packages/ (pour monorepo)
|
|
66
|
+
path.join(projectRoot, "..", "..", "packages", moduleName.replace("@lastbrain/", "").replace("@lastbrain-labs/", "")),
|
|
67
|
+
];
|
|
68
|
+
let moduleDir = null;
|
|
69
|
+
for (const possiblePath of possiblePaths) {
|
|
70
|
+
if (fs.existsSync(possiblePath)) {
|
|
71
|
+
moduleDir = possiblePath;
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (!moduleDir) {
|
|
76
|
+
console.warn(`⚠️ Module not found: ${moduleName}`);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const copied = copyModuleMigrations(moduleName, moduleDir);
|
|
80
|
+
totalCopied += copied;
|
|
81
|
+
});
|
|
82
|
+
console.log(`\n✅ Migration sync completed: ${totalCopied} file(s) copied`);
|
|
83
|
+
}
|
|
84
|
+
syncModuleMigrations();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev-sync.d.ts","sourceRoot":"","sources":["../../../src/scripts/dev-sync.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
const GENERATED_HEADER = "// GENERATED BY LASTBRAIN APP-SHELL\n";
|
|
5
|
+
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
const packageRoot = path.join(scriptDir, "..", "..");
|
|
7
|
+
const projectRoot = path.join(packageRoot, "..", "..");
|
|
8
|
+
const srcAppShell = path.join(projectRoot, "packages/app/src/app-shell");
|
|
9
|
+
const destAppShell = path.join(projectRoot, "apps/web/app");
|
|
10
|
+
const webPackage = path.join(projectRoot, "apps/web/package.json");
|
|
11
|
+
const scriptsToEnsure = {
|
|
12
|
+
dev: "next dev",
|
|
13
|
+
build: "next build",
|
|
14
|
+
"build:modules": "pnpm --filter @lastbrain/app module:build",
|
|
15
|
+
"db:migrations:sync": "pnpm --filter @lastbrain/app db:migrations:sync",
|
|
16
|
+
"db:init": "pnpm --filter @lastbrain/app db:init",
|
|
17
|
+
"readme:create": "pnpm --filter @lastbrain/app readme:create",
|
|
18
|
+
};
|
|
19
|
+
const dependenciesToEnsure = {
|
|
20
|
+
"@lastbrain/app": "workspace:*",
|
|
21
|
+
"@lastbrain/core": "workspace:*",
|
|
22
|
+
"@lastbrain/ui": "workspace:*",
|
|
23
|
+
"@lastbrain/module-auth": "workspace:*",
|
|
24
|
+
};
|
|
25
|
+
const gitignoreTemplate = path.join(projectRoot, "packages/app/src/templates/gitignore/.gitignore");
|
|
26
|
+
const consumerGitignore = path.join(projectRoot, "apps/web/.gitignore");
|
|
27
|
+
const envTemplate = path.join(projectRoot, "packages/app/src/templates/env.example/.env.example");
|
|
28
|
+
const consumerEnvExample = path.join(projectRoot, "apps/web/.env.example");
|
|
29
|
+
const consumerEnvLocal = path.join(projectRoot, "apps/web/.env.local");
|
|
30
|
+
function ensureDirectory(dir) {
|
|
31
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
32
|
+
}
|
|
33
|
+
function normalizeContent(data) {
|
|
34
|
+
const trimmed = data.startsWith(GENERATED_HEADER)
|
|
35
|
+
? data.slice(GENERATED_HEADER.length)
|
|
36
|
+
: data;
|
|
37
|
+
const sanitized = trimmed.trimStart();
|
|
38
|
+
return `${GENERATED_HEADER}\n${sanitized}`;
|
|
39
|
+
}
|
|
40
|
+
function syncFile(src, dest) {
|
|
41
|
+
const relativePath = path.relative(destAppShell, dest);
|
|
42
|
+
let content;
|
|
43
|
+
if (relativePath === "layout.tsx") {
|
|
44
|
+
content = `${GENERATED_HEADER}\nimport { RootLayout } from "@lastbrain/app";\n\nexport default RootLayout;\n`;
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
const data = fs.readFileSync(src, "utf-8");
|
|
48
|
+
content = normalizeContent(data);
|
|
49
|
+
}
|
|
50
|
+
ensureDirectory(path.dirname(dest));
|
|
51
|
+
fs.writeFileSync(dest, content);
|
|
52
|
+
return dest;
|
|
53
|
+
}
|
|
54
|
+
function _copyDirectory(srcDir, destDir) {
|
|
55
|
+
const stats = fs.statSync(srcDir);
|
|
56
|
+
if (stats.isDirectory()) {
|
|
57
|
+
ensureDirectory(destDir);
|
|
58
|
+
const entries = fs.readdirSync(srcDir, { withFileTypes: true });
|
|
59
|
+
for (const entry of entries) {
|
|
60
|
+
_copyDirectory(path.join(srcDir, entry.name), path.join(destDir, entry.name));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
else if (stats.isFile()) {
|
|
64
|
+
syncFile(srcDir, destDir);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function syncAppShell() {
|
|
68
|
+
if (!fs.existsSync(srcAppShell)) {
|
|
69
|
+
throw new Error(`Source app-shell not found at ${srcAppShell}`);
|
|
70
|
+
}
|
|
71
|
+
const copied = [];
|
|
72
|
+
const recurse = (src, dest) => {
|
|
73
|
+
const stats = fs.statSync(src);
|
|
74
|
+
if (stats.isDirectory()) {
|
|
75
|
+
ensureDirectory(dest);
|
|
76
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
77
|
+
for (const entry of entries) {
|
|
78
|
+
recurse(path.join(src, entry.name), path.join(dest, entry.name));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
syncFile(src, dest);
|
|
83
|
+
copied.push(dest);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
recurse(srcAppShell, destAppShell);
|
|
87
|
+
return copied;
|
|
88
|
+
}
|
|
89
|
+
function cleanupStaleGroupFiles() {
|
|
90
|
+
const staleFiles = [
|
|
91
|
+
path.join(destAppShell, "(auth)", "page.tsx"),
|
|
92
|
+
path.join(destAppShell, "(admin)", "page.tsx"),
|
|
93
|
+
];
|
|
94
|
+
staleFiles.forEach((file) => {
|
|
95
|
+
if (fs.existsSync(file)) {
|
|
96
|
+
fs.unlinkSync(file);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
const staleDirs = [
|
|
100
|
+
path.join(destAppShell, "(auth)", "dashboard"),
|
|
101
|
+
path.join(destAppShell, "(admin)", "dashboard"),
|
|
102
|
+
];
|
|
103
|
+
staleDirs.forEach((dir) => {
|
|
104
|
+
if (fs.existsSync(dir)) {
|
|
105
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
function mergeScripts(base, additions) {
|
|
110
|
+
return { ...base, ...additions };
|
|
111
|
+
}
|
|
112
|
+
function syncPackageJson() {
|
|
113
|
+
ensureDirectory(path.dirname(webPackage));
|
|
114
|
+
const defaultPkg = {
|
|
115
|
+
name: "web",
|
|
116
|
+
private: true,
|
|
117
|
+
version: "0.1.0",
|
|
118
|
+
type: "module",
|
|
119
|
+
};
|
|
120
|
+
const pkg = fs.existsSync(webPackage)
|
|
121
|
+
? JSON.parse(fs.readFileSync(webPackage, "utf-8"))
|
|
122
|
+
: defaultPkg;
|
|
123
|
+
pkg.scripts = mergeScripts(pkg.scripts ?? {}, scriptsToEnsure);
|
|
124
|
+
pkg.dependencies = { ...(pkg.dependencies ?? {}), ...dependenciesToEnsure };
|
|
125
|
+
fs.writeFileSync(webPackage, JSON.stringify(pkg, null, 2) + "\n");
|
|
126
|
+
return {
|
|
127
|
+
scripts: Object.keys(scriptsToEnsure),
|
|
128
|
+
dependencies: Object.keys(dependenciesToEnsure),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function syncEnvExample() {
|
|
132
|
+
if (!fs.existsSync(envTemplate)) {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
const shouldOverwrite = !fs.existsSync(consumerEnvExample) ||
|
|
136
|
+
fs
|
|
137
|
+
.readFileSync(consumerEnvExample, "utf-8")
|
|
138
|
+
.includes("GENERATED BY LASTBRAIN");
|
|
139
|
+
if (!shouldOverwrite) {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
fs.copyFileSync(envTemplate, consumerEnvExample);
|
|
143
|
+
return consumerEnvExample;
|
|
144
|
+
}
|
|
145
|
+
function ensureEnvLocal() {
|
|
146
|
+
if (fs.existsSync(consumerEnvLocal)) {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
if (!fs.existsSync(envTemplate)) {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
fs.copyFileSync(envTemplate, consumerEnvLocal);
|
|
153
|
+
return consumerEnvLocal;
|
|
154
|
+
}
|
|
155
|
+
function syncGitignore() {
|
|
156
|
+
if (!fs.existsSync(gitignoreTemplate)) {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
const shouldOverwrite = !fs.existsSync(consumerGitignore) ||
|
|
160
|
+
fs
|
|
161
|
+
.readFileSync(consumerGitignore, "utf-8")
|
|
162
|
+
.includes("GENERATED BY LASTBRAIN");
|
|
163
|
+
if (!shouldOverwrite) {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
fs.copyFileSync(gitignoreTemplate, consumerGitignore);
|
|
167
|
+
return consumerGitignore;
|
|
168
|
+
}
|
|
169
|
+
function main() {
|
|
170
|
+
ensureDirectory(destAppShell);
|
|
171
|
+
const copiedFiles = syncAppShell();
|
|
172
|
+
cleanupStaleGroupFiles();
|
|
173
|
+
const changes = syncPackageJson();
|
|
174
|
+
const gitignoreSynced = syncGitignore();
|
|
175
|
+
const envExampleSynced = syncEnvExample();
|
|
176
|
+
const envLocalCreated = ensureEnvLocal();
|
|
177
|
+
console.log("✅ apps/web synced with @lastbrain/app");
|
|
178
|
+
console.log(`Copied or updated files (${copiedFiles.length}):`);
|
|
179
|
+
copiedFiles.forEach((file) => console.log(` • ${path.relative(projectRoot, file)}`));
|
|
180
|
+
console.log("Scripts ensured in apps/web/package.json:");
|
|
181
|
+
changes.scripts.forEach((script) => console.log(` • ${script}`));
|
|
182
|
+
console.log("Dependencies ensured in apps/web/package.json:");
|
|
183
|
+
changes.dependencies.forEach((dep) => console.log(` • ${dep}`));
|
|
184
|
+
if (gitignoreSynced) {
|
|
185
|
+
console.log(`.gitignore ensured at ${path.relative(projectRoot, gitignoreSynced)}`);
|
|
186
|
+
}
|
|
187
|
+
if (envExampleSynced) {
|
|
188
|
+
console.log(`.env.example ensured at ${path.relative(projectRoot, envExampleSynced)}`);
|
|
189
|
+
}
|
|
190
|
+
if (envLocalCreated) {
|
|
191
|
+
console.log(`.env.local ensured at ${path.relative(projectRoot, envLocalCreated)}`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
main();
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
interface InitAppOptions {
|
|
2
|
+
targetDir: string;
|
|
3
|
+
force: boolean;
|
|
4
|
+
projectName: string;
|
|
5
|
+
useHeroUI: boolean;
|
|
6
|
+
withAuth?: boolean;
|
|
7
|
+
interactive?: boolean;
|
|
8
|
+
isMonorepoProject?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare function initApp(options: InitAppOptions): Promise<void>;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=init-app.d.ts.map
|
|
@@ -0,0 +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;IACtB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,wBAAsB,OAAO,CAAC,OAAO,EAAE,cAAc,iBAkSpD"}
|