create-meridian-app 0.1.24 → 0.1.26
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/{chunk-EE7FQVVB.js → chunk-FNX5ZWEV.js} +1 -1
- package/dist/cli.js +188 -120
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -17,7 +17,7 @@ function renderPackageJson(vars) {
|
|
|
17
17
|
scripts: {
|
|
18
18
|
dev: "meridian dev",
|
|
19
19
|
build: "meridian build",
|
|
20
|
-
start: "meridian
|
|
20
|
+
start: "meridian start",
|
|
21
21
|
"db:migrate": "meridian db:migrate",
|
|
22
22
|
"db:generate": "meridian db:generate",
|
|
23
23
|
...vars.seedDemo ? { "seed:demo": "node --import tsx/esm src/scripts/seed-demo.ts" } : {}
|
package/dist/cli.js
CHANGED
|
@@ -14,17 +14,18 @@ import {
|
|
|
14
14
|
toKebabCase,
|
|
15
15
|
toPascalCase,
|
|
16
16
|
writeFile
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-FNX5ZWEV.js";
|
|
18
18
|
|
|
19
19
|
// src/cli.ts
|
|
20
20
|
import { Command } from "commander";
|
|
21
|
-
import
|
|
21
|
+
import chalk14 from "chalk";
|
|
22
22
|
|
|
23
23
|
// src/commands/dev.ts
|
|
24
24
|
import path2 from "path";
|
|
25
25
|
import { existsSync as existsSync2 } from "fs";
|
|
26
26
|
import chalk2 from "chalk";
|
|
27
27
|
import { execa } from "execa";
|
|
28
|
+
import dotenv2 from "dotenv";
|
|
28
29
|
|
|
29
30
|
// src/commands/serve-dashboard.ts
|
|
30
31
|
import path from "path";
|
|
@@ -226,6 +227,7 @@ async function runDev() {
|
|
|
226
227
|
console.error(chalk2.red(" \u2716 Could not find meridian.config.ts. Are you inside a Meridian project?"));
|
|
227
228
|
process.exit(1);
|
|
228
229
|
}
|
|
230
|
+
dotenv2.config({ path: path2.join(rootDir, ".env") });
|
|
229
231
|
const mainTs = path2.join(rootDir, "src", "main.ts");
|
|
230
232
|
if (!existsSync2(mainTs)) {
|
|
231
233
|
console.error(chalk2.red(` \u2716 Entry point not found: ${mainTs}`));
|
|
@@ -234,9 +236,10 @@ async function runDev() {
|
|
|
234
236
|
const dashboardDist = path2.join(rootDir, "node_modules", "@meridianjs", "admin-dashboard", "dist");
|
|
235
237
|
const hasDashboard = existsSync2(dashboardDist);
|
|
236
238
|
const { apiPort, dashboardPort } = await readProjectPorts(rootDir);
|
|
239
|
+
const apiUrl = process.env.API_URL ?? `http://localhost:${apiPort}`;
|
|
237
240
|
let dashServer = null;
|
|
238
241
|
if (hasDashboard) {
|
|
239
|
-
dashServer = await startDashboardServer(dashboardDist, dashboardPort, apiPort);
|
|
242
|
+
dashServer = await startDashboardServer(dashboardDist, dashboardPort, apiPort, "localhost", null, apiUrl);
|
|
240
243
|
console.log(
|
|
241
244
|
chalk2.dim(" \u2192 API: ") + chalk2.cyan(`http://localhost:${apiPort}`) + chalk2.dim(" dashboard: ") + chalk2.cyan(`http://localhost:${dashboardPort}`)
|
|
242
245
|
);
|
|
@@ -271,39 +274,98 @@ async function runDev() {
|
|
|
271
274
|
process.exit(apiProc.exitCode ?? 0);
|
|
272
275
|
}
|
|
273
276
|
|
|
274
|
-
// src/commands/
|
|
277
|
+
// src/commands/start.ts
|
|
278
|
+
import path3 from "path";
|
|
279
|
+
import { existsSync as existsSync3 } from "fs";
|
|
275
280
|
import chalk3 from "chalk";
|
|
276
281
|
import { execa as execa2 } from "execa";
|
|
277
|
-
|
|
282
|
+
import dotenv3 from "dotenv";
|
|
283
|
+
async function runStart() {
|
|
278
284
|
const rootDir = findProjectRoot();
|
|
279
285
|
if (!rootDir) {
|
|
280
286
|
console.error(chalk3.red(" \u2716 Could not find meridian.config.ts. Are you inside a Meridian project?"));
|
|
281
287
|
process.exit(1);
|
|
282
288
|
}
|
|
283
|
-
|
|
289
|
+
dotenv3.config({ path: path3.join(rootDir, ".env") });
|
|
290
|
+
const mainTs = path3.join(rootDir, "src", "main.ts");
|
|
291
|
+
if (!existsSync3(mainTs)) {
|
|
292
|
+
console.error(chalk3.red(` \u2716 Entry point not found: ${mainTs}`));
|
|
293
|
+
process.exit(1);
|
|
294
|
+
}
|
|
295
|
+
const dashboardDist = path3.join(rootDir, "node_modules", "@meridianjs", "admin-dashboard", "dist");
|
|
296
|
+
const hasDashboard = existsSync3(dashboardDist);
|
|
297
|
+
const { apiPort, dashboardPort } = await readProjectPorts(rootDir);
|
|
298
|
+
const apiUrl = process.env.API_URL ?? `http://localhost:${apiPort}`;
|
|
299
|
+
let dashServer = null;
|
|
300
|
+
if (hasDashboard) {
|
|
301
|
+
dashServer = await startDashboardServer(dashboardDist, dashboardPort, apiPort, "localhost", null, apiUrl);
|
|
302
|
+
console.log(
|
|
303
|
+
chalk3.dim(" \u2192 API: ") + chalk3.cyan(`http://localhost:${apiPort}`) + chalk3.dim(" dashboard: ") + chalk3.cyan(`http://localhost:${dashboardPort}`)
|
|
304
|
+
);
|
|
305
|
+
} else {
|
|
306
|
+
console.log(chalk3.dim(` \u2192 API: http://localhost:${apiPort}`));
|
|
307
|
+
}
|
|
308
|
+
console.log();
|
|
309
|
+
const apiProc = execa2(
|
|
310
|
+
"node",
|
|
311
|
+
["--import", "tsx/esm", mainTs],
|
|
312
|
+
{
|
|
313
|
+
cwd: rootDir,
|
|
314
|
+
stdio: "inherit",
|
|
315
|
+
env: { ...process.env, NODE_ENV: "production", FORCE_COLOR: "1" }
|
|
316
|
+
}
|
|
317
|
+
);
|
|
318
|
+
const shutdown = (signal) => {
|
|
319
|
+
dashServer?.close();
|
|
320
|
+
apiProc.kill(signal);
|
|
321
|
+
process.exit(0);
|
|
322
|
+
};
|
|
323
|
+
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
324
|
+
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
325
|
+
await apiProc.catch((err) => {
|
|
326
|
+
if (err.signal === "SIGINT" || err.signal === "SIGTERM") {
|
|
327
|
+
dashServer?.close();
|
|
328
|
+
process.exit(0);
|
|
329
|
+
}
|
|
330
|
+
throw err;
|
|
331
|
+
});
|
|
332
|
+
dashServer?.close();
|
|
333
|
+
process.exit(apiProc.exitCode ?? 0);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// src/commands/build.ts
|
|
337
|
+
import chalk4 from "chalk";
|
|
338
|
+
import { execa as execa3 } from "execa";
|
|
339
|
+
async function runBuild() {
|
|
340
|
+
const rootDir = findProjectRoot();
|
|
341
|
+
if (!rootDir) {
|
|
342
|
+
console.error(chalk4.red(" \u2716 Could not find meridian.config.ts. Are you inside a Meridian project?"));
|
|
343
|
+
process.exit(1);
|
|
344
|
+
}
|
|
345
|
+
console.log(chalk4.dim(" \u2192 Type-checking project..."));
|
|
284
346
|
console.log();
|
|
285
|
-
const result = await
|
|
347
|
+
const result = await execa3("npx", ["tsc", "--noEmit"], {
|
|
286
348
|
cwd: rootDir,
|
|
287
349
|
stdio: "inherit"
|
|
288
350
|
}).catch((err) => err);
|
|
289
351
|
if (result.exitCode !== 0) {
|
|
290
352
|
console.log();
|
|
291
|
-
console.error(
|
|
353
|
+
console.error(chalk4.red(" \u2716 Type check failed"));
|
|
292
354
|
process.exit(result.exitCode ?? 1);
|
|
293
355
|
}
|
|
294
356
|
console.log();
|
|
295
|
-
console.log(
|
|
357
|
+
console.log(chalk4.green(" \u2713 Type check passed"));
|
|
296
358
|
}
|
|
297
359
|
|
|
298
360
|
// src/commands/db-migrate.ts
|
|
299
|
-
import
|
|
300
|
-
import
|
|
361
|
+
import path4 from "path";
|
|
362
|
+
import chalk5 from "chalk";
|
|
301
363
|
import ora from "ora";
|
|
302
|
-
import { execa as
|
|
364
|
+
import { execa as execa4 } from "execa";
|
|
303
365
|
async function runDbMigrate() {
|
|
304
366
|
const rootDir = findProjectRoot();
|
|
305
367
|
if (!rootDir) {
|
|
306
|
-
console.error(
|
|
368
|
+
console.error(chalk5.red(" \u2716 Could not find meridian.config.ts. Are you inside a Meridian project?"));
|
|
307
369
|
process.exit(1);
|
|
308
370
|
}
|
|
309
371
|
const spinner = ora("Synchronizing database schema...").start();
|
|
@@ -321,11 +383,11 @@ await app.stop()
|
|
|
321
383
|
process.exit(0)
|
|
322
384
|
`;
|
|
323
385
|
const { randomBytes } = await import("crypto");
|
|
324
|
-
const scriptPath =
|
|
386
|
+
const scriptPath = path4.join(rootDir, `.meridian-migrate-${randomBytes(8).toString("hex")}.mjs`);
|
|
325
387
|
const { writeFile: writeFile2, unlink } = await import("fs/promises");
|
|
326
388
|
await writeFile2(scriptPath, script, { encoding: "utf-8", mode: 384 });
|
|
327
389
|
try {
|
|
328
|
-
await
|
|
390
|
+
await execa4("node", ["--import", "tsx/esm", scriptPath], {
|
|
329
391
|
cwd: rootDir,
|
|
330
392
|
stdio: "pipe",
|
|
331
393
|
env: { ...process.env, NODE_ENV: "development" }
|
|
@@ -334,7 +396,7 @@ process.exit(0)
|
|
|
334
396
|
} catch (err) {
|
|
335
397
|
spinner.fail("Schema migration failed");
|
|
336
398
|
console.error();
|
|
337
|
-
if (err.stderr) console.error(
|
|
399
|
+
if (err.stderr) console.error(chalk5.red(err.stderr));
|
|
338
400
|
if (err.stdout) console.error(err.stdout);
|
|
339
401
|
process.exit(1);
|
|
340
402
|
} finally {
|
|
@@ -343,23 +405,23 @@ process.exit(0)
|
|
|
343
405
|
}
|
|
344
406
|
|
|
345
407
|
// src/commands/db-generate.ts
|
|
346
|
-
import
|
|
408
|
+
import path5 from "path";
|
|
347
409
|
import fs2 from "fs/promises";
|
|
348
|
-
import
|
|
410
|
+
import chalk6 from "chalk";
|
|
349
411
|
async function runDbGenerate(migrationName) {
|
|
350
412
|
const rootDir = findProjectRoot();
|
|
351
413
|
if (!rootDir) {
|
|
352
|
-
console.error(
|
|
414
|
+
console.error(chalk6.red(" \u2716 Could not find meridian.config.ts. Are you inside a Meridian project?"));
|
|
353
415
|
process.exit(1);
|
|
354
416
|
}
|
|
355
417
|
if (!migrationName) {
|
|
356
|
-
console.error(
|
|
418
|
+
console.error(chalk6.red(" \u2716 Migration name is required. Usage: meridian db:generate <name>"));
|
|
357
419
|
process.exit(1);
|
|
358
420
|
}
|
|
359
421
|
const safeName = migrationName.toLowerCase().replace(/[^a-z0-9_]/g, "_").replace(/_+/g, "_");
|
|
360
422
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:T.Z]/g, "").slice(0, 14);
|
|
361
423
|
const fileName = `${timestamp}_${safeName}.ts`;
|
|
362
|
-
const migrationsDir =
|
|
424
|
+
const migrationsDir = path5.join(rootDir, "src", "migrations");
|
|
363
425
|
await fs2.mkdir(migrationsDir, { recursive: true });
|
|
364
426
|
const content = `/**
|
|
365
427
|
* Migration: ${safeName}
|
|
@@ -378,87 +440,87 @@ export async function down(): Promise<void> {
|
|
|
378
440
|
// Write your rollback SQL here
|
|
379
441
|
}
|
|
380
442
|
`;
|
|
381
|
-
const filePath =
|
|
443
|
+
const filePath = path5.join(migrationsDir, fileName);
|
|
382
444
|
await fs2.writeFile(filePath, content, "utf-8");
|
|
383
|
-
console.log(
|
|
445
|
+
console.log(chalk6.green(` \u2713 Created migration: src/migrations/${fileName}`));
|
|
384
446
|
console.log();
|
|
385
|
-
console.log(
|
|
386
|
-
console.log(
|
|
447
|
+
console.log(chalk6.dim(" Note: Meridian auto-syncs schema on startup via updateSchema()."));
|
|
448
|
+
console.log(chalk6.dim(" Use this file for custom DDL that requires manual control."));
|
|
387
449
|
}
|
|
388
450
|
|
|
389
451
|
// src/commands/generate/module.ts
|
|
390
|
-
import
|
|
391
|
-
import { existsSync as
|
|
392
|
-
import
|
|
452
|
+
import path6 from "path";
|
|
453
|
+
import { existsSync as existsSync4 } from "fs";
|
|
454
|
+
import chalk7 from "chalk";
|
|
393
455
|
async function generateModule(name) {
|
|
394
456
|
const rootDir = findProjectRoot();
|
|
395
457
|
if (!rootDir) {
|
|
396
|
-
console.error(
|
|
458
|
+
console.error(chalk7.red(" \u2716 Could not find meridian.config.ts. Are you inside a Meridian project?"));
|
|
397
459
|
process.exit(1);
|
|
398
460
|
}
|
|
399
461
|
if (!name) {
|
|
400
|
-
console.error(
|
|
462
|
+
console.error(chalk7.red(" \u2716 Module name is required. Usage: meridian generate module <name>"));
|
|
401
463
|
process.exit(1);
|
|
402
464
|
}
|
|
403
465
|
const kebab = toKebabCase(name);
|
|
404
466
|
const pascal = toPascalCase(kebab);
|
|
405
|
-
const moduleDir =
|
|
406
|
-
if (
|
|
407
|
-
console.error(
|
|
467
|
+
const moduleDir = path6.join(rootDir, "src", "modules", kebab);
|
|
468
|
+
if (existsSync4(moduleDir)) {
|
|
469
|
+
console.error(chalk7.red(` \u2716 Module directory already exists: src/modules/${kebab}`));
|
|
408
470
|
process.exit(1);
|
|
409
471
|
}
|
|
410
|
-
await writeFile(
|
|
411
|
-
await writeFile(
|
|
412
|
-
await writeFile(
|
|
413
|
-
await writeFile(
|
|
414
|
-
console.log(
|
|
472
|
+
await writeFile(path6.join(moduleDir, "index.ts"), renderModuleIndex(kebab, pascal));
|
|
473
|
+
await writeFile(path6.join(moduleDir, `models/${kebab}.ts`), renderModuleModel(kebab, pascal));
|
|
474
|
+
await writeFile(path6.join(moduleDir, `loaders/default.ts`), renderModuleLoader(kebab, pascal));
|
|
475
|
+
await writeFile(path6.join(moduleDir, "service.ts"), renderModuleService(kebab, pascal));
|
|
476
|
+
console.log(chalk7.green(` \u2713 Generated module: src/modules/${kebab}/`));
|
|
415
477
|
console.log();
|
|
416
478
|
console.log(" Files created:");
|
|
417
|
-
console.log(
|
|
418
|
-
console.log(
|
|
419
|
-
console.log(
|
|
420
|
-
console.log(
|
|
479
|
+
console.log(chalk7.dim(` src/modules/${kebab}/index.ts`));
|
|
480
|
+
console.log(chalk7.dim(` src/modules/${kebab}/models/${kebab}.ts`));
|
|
481
|
+
console.log(chalk7.dim(` src/modules/${kebab}/loaders/default.ts`));
|
|
482
|
+
console.log(chalk7.dim(` src/modules/${kebab}/service.ts`));
|
|
421
483
|
console.log();
|
|
422
484
|
console.log(" Next steps:");
|
|
423
|
-
console.log(
|
|
424
|
-
console.log(
|
|
425
|
-
console.log(
|
|
485
|
+
console.log(chalk7.dim(` 1. Add the module to your meridian.config.ts:`));
|
|
486
|
+
console.log(chalk7.dim(` { resolve: "./src/modules/${kebab}" }`));
|
|
487
|
+
console.log(chalk7.dim(` 2. Run \`npm run db:migrate\` to sync the schema`));
|
|
426
488
|
}
|
|
427
489
|
|
|
428
490
|
// src/commands/generate/workflow.ts
|
|
429
|
-
import
|
|
430
|
-
import { existsSync as
|
|
431
|
-
import
|
|
491
|
+
import path7 from "path";
|
|
492
|
+
import { existsSync as existsSync5 } from "fs";
|
|
493
|
+
import chalk8 from "chalk";
|
|
432
494
|
async function generateWorkflow(name) {
|
|
433
495
|
const rootDir = findProjectRoot();
|
|
434
496
|
if (!rootDir) {
|
|
435
|
-
console.error(
|
|
497
|
+
console.error(chalk8.red(" \u2716 Could not find meridian.config.ts. Are you inside a Meridian project?"));
|
|
436
498
|
process.exit(1);
|
|
437
499
|
}
|
|
438
500
|
if (!name) {
|
|
439
|
-
console.error(
|
|
501
|
+
console.error(chalk8.red(" \u2716 Workflow name is required. Usage: meridian generate workflow <name>"));
|
|
440
502
|
process.exit(1);
|
|
441
503
|
}
|
|
442
504
|
const kebab = toKebabCase(name);
|
|
443
505
|
const pascal = toPascalCase(kebab);
|
|
444
|
-
const filePath =
|
|
445
|
-
if (
|
|
446
|
-
console.error(
|
|
506
|
+
const filePath = path7.join(rootDir, "src", "workflows", `${kebab}.ts`);
|
|
507
|
+
if (existsSync5(filePath)) {
|
|
508
|
+
console.error(chalk8.red(` \u2716 Workflow already exists: src/workflows/${kebab}.ts`));
|
|
447
509
|
process.exit(1);
|
|
448
510
|
}
|
|
449
511
|
const camel = pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
450
512
|
await writeFile(filePath, renderWorkflow(kebab, pascal));
|
|
451
|
-
console.log(
|
|
513
|
+
console.log(chalk8.green(` \u2713 Generated workflow: src/workflows/${kebab}.ts`));
|
|
452
514
|
console.log();
|
|
453
515
|
console.log(" Usage:");
|
|
454
|
-
console.log(
|
|
455
|
-
console.log(
|
|
516
|
+
console.log(chalk8.dim(` import { ${camel}Workflow } from "./workflows/${kebab}.js"`));
|
|
517
|
+
console.log(chalk8.dim(` const { result } = await ${camel}Workflow(req.scope).run({ input: {...} })`));
|
|
456
518
|
}
|
|
457
519
|
|
|
458
520
|
// src/commands/generate/plugin.ts
|
|
459
|
-
import
|
|
460
|
-
import { existsSync as
|
|
461
|
-
import
|
|
521
|
+
import path8 from "path";
|
|
522
|
+
import { existsSync as existsSync6 } from "fs";
|
|
523
|
+
import chalk9 from "chalk";
|
|
462
524
|
function renderPluginIndex(_name, pascalName) {
|
|
463
525
|
return `import { fileURLToPath } from "node:url"
|
|
464
526
|
import path from "node:path"
|
|
@@ -495,158 +557,158 @@ export const GET = async (req: any, res: Response) => {
|
|
|
495
557
|
async function generatePlugin(name) {
|
|
496
558
|
const rootDir = findProjectRoot();
|
|
497
559
|
if (!rootDir) {
|
|
498
|
-
console.error(
|
|
560
|
+
console.error(chalk9.red(" \u2716 Could not find meridian.config.ts. Are you inside a Meridian project?"));
|
|
499
561
|
process.exit(1);
|
|
500
562
|
}
|
|
501
563
|
if (!name) {
|
|
502
|
-
console.error(
|
|
564
|
+
console.error(chalk9.red(" \u2716 Plugin name is required. Usage: meridian generate plugin <name>"));
|
|
503
565
|
process.exit(1);
|
|
504
566
|
}
|
|
505
567
|
const kebab = toKebabCase(name);
|
|
506
568
|
const pascal = toPascalCase(kebab);
|
|
507
|
-
const pluginDir =
|
|
508
|
-
if (
|
|
509
|
-
console.error(
|
|
569
|
+
const pluginDir = path8.join(rootDir, "src", "plugins", kebab);
|
|
570
|
+
if (existsSync6(pluginDir)) {
|
|
571
|
+
console.error(chalk9.red(` \u2716 Plugin directory already exists: src/plugins/${kebab}`));
|
|
510
572
|
process.exit(1);
|
|
511
573
|
}
|
|
512
|
-
await writeFile(
|
|
574
|
+
await writeFile(path8.join(pluginDir, "index.ts"), renderPluginIndex(kebab, pascal));
|
|
513
575
|
await writeFile(
|
|
514
|
-
|
|
576
|
+
path8.join(pluginDir, "api", "admin", kebab, "route.ts"),
|
|
515
577
|
renderPluginRoute(kebab)
|
|
516
578
|
);
|
|
517
|
-
console.log(
|
|
579
|
+
console.log(chalk9.green(` \u2713 Generated plugin: src/plugins/${kebab}/`));
|
|
518
580
|
console.log();
|
|
519
581
|
console.log(" Files created:");
|
|
520
|
-
console.log(
|
|
521
|
-
console.log(
|
|
582
|
+
console.log(chalk9.dim(` src/plugins/${kebab}/index.ts`));
|
|
583
|
+
console.log(chalk9.dim(` src/plugins/${kebab}/api/admin/${kebab}/route.ts`));
|
|
522
584
|
console.log();
|
|
523
585
|
console.log(" Next steps:");
|
|
524
|
-
console.log(
|
|
525
|
-
console.log(
|
|
526
|
-
console.log(
|
|
586
|
+
console.log(chalk9.dim(` 1. Add the plugin to your meridian.config.ts:`));
|
|
587
|
+
console.log(chalk9.dim(` plugins: [{ resolve: "./src/plugins/${kebab}" }]`));
|
|
588
|
+
console.log(chalk9.dim(` 2. Start the dev server: \`npm run dev\``));
|
|
527
589
|
}
|
|
528
590
|
|
|
529
591
|
// src/commands/generate/subscriber.ts
|
|
530
|
-
import
|
|
531
|
-
import { existsSync as
|
|
532
|
-
import
|
|
592
|
+
import path9 from "path";
|
|
593
|
+
import { existsSync as existsSync7 } from "fs";
|
|
594
|
+
import chalk10 from "chalk";
|
|
533
595
|
async function generateSubscriber(eventName) {
|
|
534
596
|
const rootDir = findProjectRoot();
|
|
535
597
|
if (!rootDir) {
|
|
536
|
-
console.error(
|
|
598
|
+
console.error(chalk10.red(" \u2716 Could not find meridian.config.ts. Are you inside a Meridian project?"));
|
|
537
599
|
process.exit(1);
|
|
538
600
|
}
|
|
539
601
|
if (!eventName) {
|
|
540
|
-
console.error(
|
|
602
|
+
console.error(chalk10.red(" \u2716 Event name is required. Usage: meridian generate subscriber <event>"));
|
|
541
603
|
process.exit(1);
|
|
542
604
|
}
|
|
543
605
|
const fileName = toKebabCase(eventName.replace(/\./g, "-"));
|
|
544
|
-
const filePath =
|
|
545
|
-
if (
|
|
546
|
-
console.error(
|
|
606
|
+
const filePath = path9.join(rootDir, "src", "subscribers", `${fileName}.ts`);
|
|
607
|
+
if (existsSync7(filePath)) {
|
|
608
|
+
console.error(chalk10.red(` \u2716 Subscriber already exists: src/subscribers/${fileName}.ts`));
|
|
547
609
|
process.exit(1);
|
|
548
610
|
}
|
|
549
611
|
await writeFile(filePath, renderSubscriber(eventName));
|
|
550
|
-
console.log(
|
|
612
|
+
console.log(chalk10.green(` \u2713 Generated subscriber: src/subscribers/${fileName}.ts`));
|
|
551
613
|
console.log();
|
|
552
614
|
console.log(" Listens for event:");
|
|
553
|
-
console.log(
|
|
615
|
+
console.log(chalk10.dim(` "${eventName}"`));
|
|
554
616
|
console.log();
|
|
555
617
|
console.log(" The file is auto-loaded by the framework on next dev restart.");
|
|
556
618
|
}
|
|
557
619
|
|
|
558
620
|
// src/commands/generate/job.ts
|
|
559
|
-
import
|
|
560
|
-
import { existsSync as
|
|
561
|
-
import
|
|
621
|
+
import path10 from "path";
|
|
622
|
+
import { existsSync as existsSync8 } from "fs";
|
|
623
|
+
import chalk11 from "chalk";
|
|
562
624
|
async function generateJob(name, schedule) {
|
|
563
625
|
const rootDir = findProjectRoot();
|
|
564
626
|
if (!rootDir) {
|
|
565
|
-
console.error(
|
|
627
|
+
console.error(chalk11.red(" \u2716 Could not find meridian.config.ts. Are you inside a Meridian project?"));
|
|
566
628
|
process.exit(1);
|
|
567
629
|
}
|
|
568
630
|
if (!name) {
|
|
569
|
-
console.error(
|
|
631
|
+
console.error(chalk11.red(" \u2716 Job name is required. Usage: meridian generate job <name>"));
|
|
570
632
|
process.exit(1);
|
|
571
633
|
}
|
|
572
634
|
const kebab = toKebabCase(name);
|
|
573
|
-
const filePath =
|
|
574
|
-
if (
|
|
575
|
-
console.error(
|
|
635
|
+
const filePath = path10.join(rootDir, "src", "jobs", `${kebab}.ts`);
|
|
636
|
+
if (existsSync8(filePath)) {
|
|
637
|
+
console.error(chalk11.red(` \u2716 Job already exists: src/jobs/${kebab}.ts`));
|
|
576
638
|
process.exit(1);
|
|
577
639
|
}
|
|
578
640
|
await writeFile(filePath, renderJob(kebab, schedule));
|
|
579
|
-
console.log(
|
|
641
|
+
console.log(chalk11.green(` \u2713 Generated job: src/jobs/${kebab}.ts`));
|
|
580
642
|
console.log();
|
|
581
643
|
console.log(" Schedule:");
|
|
582
|
-
console.log(
|
|
644
|
+
console.log(chalk11.dim(` ${schedule}`));
|
|
583
645
|
console.log();
|
|
584
646
|
console.log(" The job is auto-loaded by the framework on next dev restart.");
|
|
585
647
|
}
|
|
586
648
|
|
|
587
649
|
// src/commands/generate/route.ts
|
|
588
|
-
import
|
|
589
|
-
import { existsSync as
|
|
590
|
-
import
|
|
650
|
+
import path11 from "path";
|
|
651
|
+
import { existsSync as existsSync9 } from "fs";
|
|
652
|
+
import chalk12 from "chalk";
|
|
591
653
|
var VALID_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
|
|
592
654
|
async function generateRoute(routePath, methods) {
|
|
593
655
|
const rootDir = findProjectRoot();
|
|
594
656
|
if (!rootDir) {
|
|
595
|
-
console.error(
|
|
657
|
+
console.error(chalk12.red(" \u2716 Could not find meridian.config.ts. Are you inside a Meridian project?"));
|
|
596
658
|
process.exit(1);
|
|
597
659
|
}
|
|
598
660
|
if (!routePath) {
|
|
599
|
-
console.error(
|
|
661
|
+
console.error(chalk12.red(" \u2716 Route path is required. Usage: meridian generate route <path>"));
|
|
600
662
|
process.exit(1);
|
|
601
663
|
}
|
|
602
664
|
const normalized = routePath.replace(/^\//, "");
|
|
603
|
-
const filePath =
|
|
604
|
-
const resolvedFile =
|
|
605
|
-
const resolvedBase =
|
|
606
|
-
if (!resolvedFile.startsWith(resolvedBase +
|
|
607
|
-
console.error(
|
|
665
|
+
const filePath = path11.join(rootDir, "src", "api", normalized, "route.ts");
|
|
666
|
+
const resolvedFile = path11.resolve(filePath);
|
|
667
|
+
const resolvedBase = path11.resolve(rootDir, "src", "api");
|
|
668
|
+
if (!resolvedFile.startsWith(resolvedBase + path11.sep)) {
|
|
669
|
+
console.error(chalk12.red(" \u2716 Invalid route path: must resolve within src/api/"));
|
|
608
670
|
process.exit(1);
|
|
609
671
|
}
|
|
610
|
-
if (
|
|
611
|
-
console.error(
|
|
672
|
+
if (existsSync9(filePath)) {
|
|
673
|
+
console.error(chalk12.red(` \u2716 Route already exists: src/api/${normalized}/route.ts`));
|
|
612
674
|
process.exit(1);
|
|
613
675
|
}
|
|
614
676
|
const normalizedMethods = methods.map((m) => m.toUpperCase());
|
|
615
677
|
const invalid = normalizedMethods.filter((m) => !VALID_METHODS.includes(m));
|
|
616
678
|
if (invalid.length > 0) {
|
|
617
|
-
console.error(
|
|
679
|
+
console.error(chalk12.red(` \u2716 Invalid HTTP methods: ${invalid.join(", ")}. Valid: ${VALID_METHODS.join(", ")}`));
|
|
618
680
|
process.exit(1);
|
|
619
681
|
}
|
|
620
682
|
await writeFile(filePath, renderRoute(normalizedMethods));
|
|
621
|
-
console.log(
|
|
683
|
+
console.log(chalk12.green(` \u2713 Generated route: src/api/${normalized}/route.ts`));
|
|
622
684
|
console.log();
|
|
623
685
|
console.log(" Handlers exported:");
|
|
624
686
|
for (const m of normalizedMethods) {
|
|
625
|
-
console.log(
|
|
687
|
+
console.log(chalk12.dim(` ${m} /${normalized}`));
|
|
626
688
|
}
|
|
627
689
|
console.log();
|
|
628
690
|
console.log(" The route is auto-loaded by the framework on next dev restart.");
|
|
629
691
|
}
|
|
630
692
|
|
|
631
693
|
// src/commands/user-create.ts
|
|
632
|
-
import
|
|
633
|
-
import
|
|
694
|
+
import path12 from "path";
|
|
695
|
+
import chalk13 from "chalk";
|
|
634
696
|
import ora2 from "ora";
|
|
635
697
|
import prompts from "prompts";
|
|
636
|
-
import { execa as
|
|
698
|
+
import { execa as execa5 } from "execa";
|
|
637
699
|
var ROLES = ["super-admin", "admin", "moderator", "member"];
|
|
638
700
|
async function runUserCreate(opts) {
|
|
639
701
|
const rootDir = findProjectRoot();
|
|
640
702
|
if (!rootDir) {
|
|
641
703
|
console.error(
|
|
642
|
-
|
|
704
|
+
chalk13.red(
|
|
643
705
|
" \u2716 Could not find meridian.config.ts. Are you inside a Meridian project?"
|
|
644
706
|
)
|
|
645
707
|
);
|
|
646
708
|
process.exit(1);
|
|
647
709
|
}
|
|
648
710
|
if (opts.role && !ROLES.includes(opts.role)) {
|
|
649
|
-
console.error(
|
|
711
|
+
console.error(chalk13.red(` \u2716 Invalid role "${opts.role}". Must be one of: ${ROLES.join(", ")}`));
|
|
650
712
|
process.exit(1);
|
|
651
713
|
}
|
|
652
714
|
const response = await prompts(
|
|
@@ -703,7 +765,7 @@ async function runUserCreate(opts) {
|
|
|
703
765
|
],
|
|
704
766
|
{
|
|
705
767
|
onCancel: () => {
|
|
706
|
-
console.log(
|
|
768
|
+
console.log(chalk13.yellow("\n Cancelled."));
|
|
707
769
|
process.exit(0);
|
|
708
770
|
}
|
|
709
771
|
}
|
|
@@ -744,11 +806,11 @@ await app.stop()
|
|
|
744
806
|
process.exit(output.success ? 0 : 1)
|
|
745
807
|
`;
|
|
746
808
|
const { randomBytes } = await import("crypto");
|
|
747
|
-
const scriptPath =
|
|
809
|
+
const scriptPath = path12.join(rootDir, `.meridian-user-create-${randomBytes(8).toString("hex")}.mjs`);
|
|
748
810
|
const { writeFile: writeFile2, unlink } = await import("fs/promises");
|
|
749
811
|
await writeFile2(scriptPath, script, { encoding: "utf-8", mode: 384 });
|
|
750
812
|
try {
|
|
751
|
-
const result = await
|
|
813
|
+
const result = await execa5("node", ["--import", "tsx/esm", scriptPath], {
|
|
752
814
|
cwd: rootDir,
|
|
753
815
|
stdio: "pipe",
|
|
754
816
|
env: { ...process.env, NODE_ENV: "development", MERIDIAN_USER_PASSWORD: password }
|
|
@@ -757,7 +819,7 @@ process.exit(output.success ? 0 : 1)
|
|
|
757
819
|
const output = JSON.parse(lines[lines.length - 1]);
|
|
758
820
|
if (output.success) {
|
|
759
821
|
spinner.succeed(
|
|
760
|
-
`User created: ${
|
|
822
|
+
`User created: ${chalk13.green(output.user.email)} (${chalk13.cyan(role)})`
|
|
761
823
|
);
|
|
762
824
|
} else {
|
|
763
825
|
spinner.fail(output.error ?? "Failed to create user");
|
|
@@ -769,12 +831,12 @@ process.exit(output.success ? 0 : 1)
|
|
|
769
831
|
try {
|
|
770
832
|
const lines = err.stdout.trim().split("\n");
|
|
771
833
|
const output = JSON.parse(lines[lines.length - 1]);
|
|
772
|
-
console.error(
|
|
834
|
+
console.error(chalk13.red(` ${output.error ?? err.stdout}`));
|
|
773
835
|
} catch {
|
|
774
|
-
console.error(
|
|
836
|
+
console.error(chalk13.red(` ${err.stdout}`));
|
|
775
837
|
}
|
|
776
838
|
}
|
|
777
|
-
if (err.stderr) console.error(
|
|
839
|
+
if (err.stderr) console.error(chalk13.red(err.stderr));
|
|
778
840
|
process.exit(1);
|
|
779
841
|
} finally {
|
|
780
842
|
await unlink(scriptPath).catch(() => null);
|
|
@@ -796,6 +858,12 @@ program.command("dev").description("Start the development server").action(() =>
|
|
|
796
858
|
process.exit(1);
|
|
797
859
|
});
|
|
798
860
|
});
|
|
861
|
+
program.command("start").description("Start the server in production mode").action(() => {
|
|
862
|
+
runStart().catch((err) => {
|
|
863
|
+
console.error(err);
|
|
864
|
+
process.exit(1);
|
|
865
|
+
});
|
|
866
|
+
});
|
|
799
867
|
program.command("build").description("Type-check the project").action(() => {
|
|
800
868
|
runBuild().catch((err) => {
|
|
801
869
|
console.error(err);
|
|
@@ -817,7 +885,7 @@ program.command("db:generate <name>").description("Generate a new migration file
|
|
|
817
885
|
program.command("serve-dashboard").description("Serve the admin dashboard as a static site").option("-p, --port <port>", "Port to serve on", "5174").action((options) => {
|
|
818
886
|
const port = Number(options.port);
|
|
819
887
|
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
820
|
-
console.error(
|
|
888
|
+
console.error(chalk14.red(` \u2716 Invalid port: ${options.port}`));
|
|
821
889
|
process.exit(1);
|
|
822
890
|
}
|
|
823
891
|
runServeDashboard(port).catch((err) => {
|
package/dist/index.js
CHANGED