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