@illumiarq/lumis 1.1.4 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/cli.js +80 -5
  2. package/dist/cli.js.map +1 -1
  3. package/dist/commands/app/cache-workflows.d.ts +7 -0
  4. package/dist/commands/app/cache-workflows.d.ts.map +1 -0
  5. package/dist/commands/app/cache-workflows.js +134 -0
  6. package/dist/commands/app/cache-workflows.js.map +1 -0
  7. package/dist/commands/app/command-runtime.d.ts +11 -0
  8. package/dist/commands/app/command-runtime.d.ts.map +1 -0
  9. package/dist/commands/app/command-runtime.js +41 -0
  10. package/dist/commands/app/command-runtime.js.map +1 -0
  11. package/dist/commands/app/config-cache.d.ts +3 -0
  12. package/dist/commands/app/config-cache.d.ts.map +1 -0
  13. package/dist/commands/app/config-cache.js +75 -0
  14. package/dist/commands/app/config-cache.js.map +1 -0
  15. package/dist/commands/app/config-publish.d.ts +2 -0
  16. package/dist/commands/app/config-publish.d.ts.map +1 -0
  17. package/dist/commands/app/config-publish.js +68 -0
  18. package/dist/commands/app/config-publish.js.map +1 -0
  19. package/dist/commands/app/config-stubs.d.ts +3 -0
  20. package/dist/commands/app/config-stubs.d.ts.map +1 -0
  21. package/dist/commands/app/config-stubs.js +154 -0
  22. package/dist/commands/app/config-stubs.js.map +1 -0
  23. package/dist/commands/app/database.d.ts +3 -0
  24. package/dist/commands/app/database.d.ts.map +1 -0
  25. package/dist/commands/app/database.js +219 -0
  26. package/dist/commands/app/database.js.map +1 -0
  27. package/dist/commands/app/docs.d.ts +7 -0
  28. package/dist/commands/app/docs.d.ts.map +1 -0
  29. package/dist/commands/app/docs.js +59 -0
  30. package/dist/commands/app/docs.js.map +1 -0
  31. package/dist/commands/app/routes.d.ts +5 -0
  32. package/dist/commands/app/routes.d.ts.map +1 -0
  33. package/dist/commands/app/routes.js +91 -0
  34. package/dist/commands/app/routes.js.map +1 -0
  35. package/dist/commands/app/security.d.ts +3 -0
  36. package/dist/commands/app/security.d.ts.map +1 -0
  37. package/dist/commands/app/security.js +15 -0
  38. package/dist/commands/app/security.js.map +1 -0
  39. package/dist/commands/app/worker-schedule.d.ts +5 -0
  40. package/dist/commands/app/worker-schedule.d.ts.map +1 -0
  41. package/dist/commands/app/worker-schedule.js +94 -0
  42. package/dist/commands/app/worker-schedule.js.map +1 -0
  43. package/dist/commands/app-commands.d.ts +7 -15
  44. package/dist/commands/app-commands.d.ts.map +1 -1
  45. package/dist/commands/app-commands.js +12 -642
  46. package/dist/commands/app-commands.js.map +1 -1
  47. package/package.json +2 -2
@@ -1,57 +1,22 @@
1
- import { spawnSync } from 'node:child_process';
2
- import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs';
1
+ import { existsSync, writeFileSync } from 'node:fs';
3
2
  import { join, relative } from 'node:path';
4
- import { fileURLToPath } from 'node:url';
5
3
  import { generateAuthModule, generateAuthUI, generateIAMModule, generateLangFile, generateUserModule, publishAuthStubs, publishIAMStubs, } from '@illumiarq/auth-starter';
6
- import { buildSearchIndex } from '@illumiarq/search';
7
- import { viewCache, viewClear } from '@illumiarq/veil';
8
4
  import { ui, writeError, writeLine } from '../console.js';
9
- function readPackageJson(cwd) {
10
- const target = join(cwd, 'package.json');
11
- if (!existsSync(target)) {
12
- return {};
13
- }
14
- return JSON.parse(readFileSync(target, 'utf8'));
15
- }
16
- function bridgePath() {
17
- return fileURLToPath(new URL('../bridges/project-bridge.ts', import.meta.url));
18
- }
19
- function runBridge(args, cwd) {
20
- const result = spawnSync('pnpm', ['exec', 'tsx', bridgePath(), ...args], {
21
- cwd,
22
- encoding: 'utf8',
23
- env: { ...process.env, FORCE_COLOR: '1' },
24
- });
25
- return {
26
- status: result.status ?? 1,
27
- stdout: result.stdout ?? '',
28
- stderr: result.stderr ?? '',
29
- };
30
- }
31
- function runPnpm(args, cwd, stdio = 'inherit') {
32
- const result = spawnSync('pnpm', args, {
33
- cwd,
34
- stdio,
35
- encoding: stdio === 'pipe' ? 'utf8' : undefined,
36
- env: { ...process.env, FORCE_COLOR: '1' },
37
- });
38
- if (stdio === 'pipe') {
39
- if (result.stdout)
40
- process.stdout.write(result.stdout);
41
- if (result.stderr)
42
- process.stderr.write(result.stderr);
43
- }
44
- return result.status ?? 1;
45
- }
46
- function ensureParentDir(filePath) {
47
- mkdirSync(join(filePath, '..'), { recursive: true });
48
- }
5
+ import { runBridge } from './app/command-runtime.js';
6
+ import { cacheSearchIndex, cacheViews, clearOptimizationCaches, clearSearchIndex, clearViews, optimizeForProduction, } from './app/cache-workflows.js';
7
+ import { cacheConfig, clearConfigCache } from './app/config-cache.js';
8
+ import { pingDatabaseConnection, runDatabaseCommand } from './app/database.js';
9
+ import { publishConfig } from './app/config-publish.js';
10
+ import { cacheRoutes, checkRoutes, clearRouteCache, listRoutes } from './app/routes.js';
11
+ import { ensureParentDir, resolvePathWithinRoot } from './app/security.js';
12
+ import { scheduleList, scheduleRun, workerList, workerStart } from './app/worker-schedule.js';
13
+ export { cacheConfig, cacheRoutes, cacheSearchIndex, cacheViews, clearConfigCache, clearOptimizationCaches, checkRoutes, clearRouteCache, clearSearchIndex, clearViews, listRoutes, optimizeForProduction, pingDatabaseConnection, publishConfig, runDatabaseCommand, scheduleList, scheduleRun, workerList, workerStart, };
49
14
  function writeGeneratedFiles(cwd, files) {
50
15
  let created = 0;
51
16
  let skipped = 0;
52
17
  for (const file of files) {
53
- const target = join(cwd, file.path);
54
- mkdirSync(join(target, '..'), { recursive: true });
18
+ const target = resolvePathWithinRoot(cwd, file.path);
19
+ ensureParentDir(target);
55
20
  if (existsSync(target) && file.path !== 'lang/en.json') {
56
21
  skipped += 1;
57
22
  continue;
@@ -61,62 +26,6 @@ function writeGeneratedFiles(cwd, files) {
61
26
  }
62
27
  return { created, skipped };
63
28
  }
64
- function parseFrontmatter(raw) {
65
- if (!raw.startsWith('---\n')) {
66
- return { data: {}, body: raw };
67
- }
68
- const end = raw.indexOf('\n---\n', 4);
69
- if (end === -1) {
70
- return { data: {}, body: raw };
71
- }
72
- const header = raw.slice(4, end);
73
- const body = raw.slice(end + 5);
74
- const data = {};
75
- for (const line of header.split(/\r?\n/g)) {
76
- const separatorIndex = line.indexOf(':');
77
- if (separatorIndex <= 0)
78
- continue;
79
- const key = line.slice(0, separatorIndex).trim();
80
- const rawValue = line.slice(separatorIndex + 1).trim();
81
- if (rawValue === 'true') {
82
- data[key] = true;
83
- }
84
- else if (rawValue === 'false') {
85
- data[key] = false;
86
- }
87
- else if (/^\d+$/.test(rawValue)) {
88
- data[key] = Number(rawValue);
89
- }
90
- else {
91
- data[key] = rawValue.replace(/^"|"$/g, '');
92
- }
93
- }
94
- return { data, body };
95
- }
96
- function walkMarkdownFiles(dir, bucket = []) {
97
- if (!existsSync(dir)) {
98
- return bucket;
99
- }
100
- for (const entry of readdirSync(dir, { withFileTypes: true })) {
101
- const fullPath = join(dir, entry.name);
102
- if (entry.isDirectory()) {
103
- walkMarkdownFiles(fullPath, bucket);
104
- continue;
105
- }
106
- if (entry.name.endsWith('.md')) {
107
- bucket.push(fullPath);
108
- }
109
- }
110
- return bucket;
111
- }
112
- function findDocsRoots(cwd) {
113
- return [
114
- join(cwd, 'content', 'docs'),
115
- join(cwd, 'src', 'content', 'docs'),
116
- join(cwd, 'src', 'shared', 'database', 'content', 'docs'),
117
- join(cwd, 'storage', 'docs-cache'),
118
- ].filter((candidate, index, all) => existsSync(candidate) && all.indexOf(candidate) === index);
119
- }
120
29
  export function showResolvedConfig(configName, cwd = process.cwd()) {
121
30
  const result = runBridge(['config:show', cwd, configName], cwd);
122
31
  if (result.status !== 0) {
@@ -130,156 +39,6 @@ export function showResolvedConfig(configName, cwd = process.cwd()) {
130
39
  writeLine();
131
40
  return 0;
132
41
  }
133
- export function cacheRoutes(cwd = process.cwd()) {
134
- const routesRoot = join(cwd, 'src', 'modules');
135
- if (!existsSync(routesRoot)) {
136
- writeError(ui.fail('No src/modules directory found.'));
137
- return 1;
138
- }
139
- const routeFiles = walkMarkdownFiles(join(cwd, '__never__'));
140
- void routeFiles;
141
- const imports = readdirSync(routesRoot, { withFileTypes: true });
142
- const discovered = [];
143
- const stack = [routesRoot];
144
- while (stack.length > 0) {
145
- const current = stack.pop();
146
- for (const entry of readdirSync(current, { withFileTypes: true })) {
147
- const fullPath = join(current, entry.name);
148
- if (entry.isDirectory()) {
149
- stack.push(fullPath);
150
- continue;
151
- }
152
- if (/\/http\/routes\/.+\.(ts|js)$/.test(fullPath)) {
153
- discovered.push(fullPath);
154
- }
155
- }
156
- }
157
- discovered.sort((left, right) => left.localeCompare(right));
158
- const loaderPath = join(cwd, 'storage', 'framework', 'cache', 'routes.loader.ts');
159
- mkdirSync(join(loaderPath, '..'), { recursive: true });
160
- const importLines = discovered.map((filePath) => {
161
- const relativeImport = relative(join(cwd, 'storage', 'framework', 'cache'), filePath).replaceAll('\\', '/');
162
- return `import '../../../${relativeImport.replace(/^\.\.\//, '')}'`;
163
- });
164
- const content = [
165
- '// storage/framework/cache/routes.loader.ts',
166
- '// Auto-generated by `lumis route:cache` — do not edit manually.',
167
- '// Commit this file; re-run `lumis route:cache` whenever routes change.',
168
- '',
169
- ...importLines.map((line) => `${line};`),
170
- '',
171
- ].join('\n');
172
- writeFileSync(loaderPath, content, 'utf8');
173
- writeLine();
174
- writeLine(ui.section('Route Cache'));
175
- writeLine(` ${ui.ok(`Cached ${discovered.length} route files`)}`);
176
- writeLine(` ${ui.bullet(relative(cwd, loaderPath))}`);
177
- writeLine();
178
- return 0;
179
- }
180
- export function clearRouteCache(cwd = process.cwd()) {
181
- const loaderPath = join(cwd, 'storage', 'framework', 'cache', 'routes.loader.ts');
182
- if (existsSync(loaderPath)) {
183
- rmSync(loaderPath, { force: true });
184
- }
185
- writeLine();
186
- writeLine(ui.section('Route Clear'));
187
- writeLine(` ${ui.ok(`Removed ${relative(cwd, loaderPath)}`)}`);
188
- writeLine();
189
- return 0;
190
- }
191
- export function listRoutes(cwd = process.cwd()) {
192
- const result = runBridge(['route:list', cwd], cwd);
193
- if (result.status !== 0) {
194
- writeError(ui.fail(result.stderr.trim() || 'Route listing failed.'));
195
- return result.status;
196
- }
197
- const parsed = JSON.parse(result.stdout);
198
- writeLine();
199
- writeLine(ui.section(`Routes (${parsed.routes.length})`));
200
- for (const route of parsed.routes) {
201
- const meta = [route.name || '-', route.render || '-', route.middleware.join(', ') || '-'].join(' · ');
202
- writeLine(` ${ui.cyan(route.method.padEnd(6, ' '))} ${route.path} ${ui.dim(meta)}`);
203
- }
204
- writeLine();
205
- return 0;
206
- }
207
- export function checkRoutes(cwd = process.cwd()) {
208
- const result = runBridge(['route:check', cwd], cwd);
209
- if (result.status !== 0) {
210
- writeError(ui.fail(result.stderr.trim() || 'Route check failed.'));
211
- return result.status;
212
- }
213
- const parsed = JSON.parse(result.stdout);
214
- writeLine();
215
- writeLine(ui.section('Route Check'));
216
- writeLine(` ${ui.ok(`${parsed.routes.length} routes validated`)}`);
217
- writeLine();
218
- return 0;
219
- }
220
- export async function cacheViews(cwd = process.cwd()) {
221
- const result = await viewCache(cwd);
222
- writeLine();
223
- writeLine(ui.section('View Cache'));
224
- writeLine(` ${ui.ok(`Compiled ${result.compiled} views`)}`);
225
- for (const filePath of result.paths.slice(0, 5)) {
226
- writeLine(` ${ui.bullet(relative(cwd, filePath))}`);
227
- }
228
- if (result.paths.length > 5) {
229
- writeLine(` ${ui.dim(`…and ${result.paths.length - 5} more`)}`);
230
- }
231
- writeLine();
232
- return 0;
233
- }
234
- export async function clearViews(cwd = process.cwd()) {
235
- const result = await viewClear(cwd);
236
- writeLine();
237
- writeLine(ui.section('View Clear'));
238
- writeLine(` ${ui.ok(result.cleared ? `Cleared ${relative(cwd, result.dir)}` : `Nothing to clear at ${relative(cwd, result.dir)}`)}`);
239
- writeLine();
240
- return 0;
241
- }
242
- export function cacheSearchIndex(cwd = process.cwd()) {
243
- const docsRoots = findDocsRoots(cwd);
244
- const docs = docsRoots.flatMap((root) => walkMarkdownFiles(root)).sort();
245
- const pages = docs
246
- .map((filePath) => {
247
- const raw = readFileSync(filePath, 'utf8');
248
- const parsed = parseFrontmatter(raw);
249
- if (parsed.data['draft'] === true) {
250
- return null;
251
- }
252
- const rel = relative(cwd, filePath).replace(/\\/g, '/');
253
- return {
254
- slug: `/${rel.replace(/\.md$/i, '')}`,
255
- title: String(parsed.data['title'] ?? relative(cwd, filePath)),
256
- section: String(parsed.data['section'] ?? ''),
257
- description: String(parsed.data['description'] ?? ''),
258
- };
259
- })
260
- .filter((entry) => entry !== null);
261
- const index = buildSearchIndex(pages);
262
- const outputPath = join(cwd, 'bootstrap', 'cache', 'search.index.json');
263
- mkdirSync(join(outputPath, '..'), { recursive: true });
264
- writeFileSync(outputPath, `${JSON.stringify(index, null, 2)}\n`, 'utf8');
265
- writeLine();
266
- writeLine(ui.section('Search Index'));
267
- writeLine(` ${ui.ok(`Indexed ${pages.length} documents`)}`);
268
- writeLine(` ${ui.bullet(relative(cwd, outputPath))}`);
269
- writeLine();
270
- return 0;
271
- }
272
- export function clearSearchIndex(cwd = process.cwd()) {
273
- const outputPath = join(cwd, 'bootstrap', 'cache', 'search.index.json');
274
- if (existsSync(outputPath)) {
275
- rmSync(outputPath, { force: true });
276
- }
277
- writeLine();
278
- writeLine(ui.section('Search Clear'));
279
- writeLine(` ${ui.ok(`Removed ${relative(cwd, outputPath)}`)}`);
280
- writeLine();
281
- return 0;
282
- }
283
42
  export function publishStubs(options = {}, cwd = process.cwd()) {
284
43
  if (options.all || !options.iam) {
285
44
  publishAuthStubs(cwd);
@@ -315,393 +74,4 @@ export function installAuth(options = {}, cwd = process.cwd()) {
315
74
  writeLine();
316
75
  return 0;
317
76
  }
318
- export function runDatabaseCommand(commandName, args, cwd = process.cwd()) {
319
- const packageJson = readPackageJson(cwd);
320
- if (packageJson.scripts?.[commandName]) {
321
- return runPnpm(['run', commandName, ...args], cwd);
322
- }
323
- if (commandName === 'db:generate') {
324
- return runPnpm(['exec', 'drizzle-kit', 'generate', ...args], cwd);
325
- }
326
- if (commandName === 'db:migrate') {
327
- return runPnpm(['exec', 'drizzle-kit', 'migrate', ...args], cwd);
328
- }
329
- if (commandName === 'db:studio') {
330
- return runPnpm(['exec', 'drizzle-kit', 'studio', ...args], cwd);
331
- }
332
- if (commandName === 'db:seed') {
333
- return runDatabaseSeed(args, cwd);
334
- }
335
- if (commandName === 'db:fresh') {
336
- writeLine();
337
- writeLine(ui.section('DB Fresh'));
338
- writeLine(` ${ui.bullet('Pushing schema (--force)…')}`);
339
- const pushStatus = runPnpm(['exec', 'drizzle-kit', 'push', '--force', ...args], cwd, 'pipe');
340
- if (pushStatus !== 0)
341
- return pushStatus;
342
- writeLine(` ${ui.bullet('Running migrations…')}`);
343
- const migrateStatus = runPnpm(['exec', 'drizzle-kit', 'migrate'], cwd, 'pipe');
344
- if (migrateStatus !== 0)
345
- return migrateStatus;
346
- writeLine(` ${ui.bullet('Running seeds…')}`);
347
- const seedStatus = runDatabaseSeed([], cwd);
348
- if (seedStatus !== 0)
349
- return seedStatus;
350
- writeLine(` ${ui.ok('Database refreshed.')}`);
351
- writeLine();
352
- return 0;
353
- }
354
- if (commandName === 'db:reset') {
355
- writeLine();
356
- writeLine(ui.section('DB Reset'));
357
- writeLine(` ${ui.bullet('Dropping all tables (drizzle-kit drop)…')}`);
358
- const dropStatus = runPnpm(['exec', 'drizzle-kit', 'drop', '--force'], cwd, 'pipe');
359
- if (dropStatus !== 0) {
360
- writeLine(` ${ui.warn('drizzle-kit drop failed — trying push --force to reset schema…')}`);
361
- runPnpm(['exec', 'drizzle-kit', 'push', '--force'], cwd, 'pipe');
362
- }
363
- writeLine(` ${ui.bullet('Running migrations…')}`);
364
- const migrateStatus = runPnpm(['exec', 'drizzle-kit', 'migrate'], cwd, 'pipe');
365
- if (migrateStatus !== 0)
366
- return migrateStatus;
367
- writeLine(` ${ui.ok('Database reset.')}`);
368
- writeLine();
369
- return 0;
370
- }
371
- writeError(ui.fail(`No handler configured for ${commandName}. Add a package.json script named ${commandName}.`));
372
- return 1;
373
- }
374
- function runDatabaseSeed(args, cwd) {
375
- const packageJson = readPackageJson(cwd);
376
- // Honour package.json script override
377
- if (packageJson.scripts?.['db:seed']) {
378
- return runPnpm(['run', 'db:seed', ...args], cwd);
379
- }
380
- // Convention: src/shared/database/seeds/index.ts
381
- const candidates = [
382
- join(cwd, 'src', 'shared', 'database', 'seeds', 'index.ts'),
383
- join(cwd, 'database', 'seeds', 'index.ts'),
384
- join(cwd, 'seeds', 'index.ts'),
385
- ];
386
- const seedFile = candidates.find((f) => existsSync(f));
387
- if (!seedFile) {
388
- writeError(ui.fail('No seed file found. Create src/shared/database/seeds/index.ts or add a db:seed script to package.json.'));
389
- return 1;
390
- }
391
- writeLine();
392
- writeLine(ui.section('DB Seed'));
393
- writeLine(` ${ui.bullet(`Running ${relative(cwd, seedFile)}…`)}`);
394
- const status = runPnpm(['exec', 'tsx', seedFile, ...args], cwd);
395
- if (status === 0) {
396
- writeLine(` ${ui.ok('Seed complete.')}`);
397
- }
398
- writeLine();
399
- return status;
400
- }
401
- // ─── Config publish ───────────────────────────────────────────────────────────
402
- const CONFIG_STUBS = {
403
- mail: `import { env } from '../bootstrap/env.js';
404
-
405
- const mail = {
406
- driver: env.MAIL_DRIVER ?? 'stub',
407
-
408
- smtp: {
409
- host: env.SMTP_HOST ?? 'localhost',
410
- port: Number(env.SMTP_PORT ?? 1025),
411
- secure: env.SMTP_SECURE === 'true',
412
- user: env.SMTP_USER,
413
- pass: env.SMTP_PASS,
414
- },
415
-
416
- resend: {
417
- apiKey: env.RESEND_API_KEY ?? '',
418
- },
419
-
420
- from: {
421
- address: env.MAIL_FROM_ADDRESS ?? 'hello@example.com',
422
- name: env.MAIL_FROM_NAME ?? 'Example App',
423
- },
424
- } as const;
425
-
426
- export default mail;
427
- `,
428
- queue: `import { env } from '../bootstrap/env.js';
429
-
430
- const queue = {
431
- driver: env.QUEUE_DRIVER ?? 'stub',
432
-
433
- bullmq: {
434
- connection: {
435
- host: env.REDIS_HOST ?? '127.0.0.1',
436
- port: Number(env.REDIS_PORT ?? 6379),
437
- },
438
- defaultQueue: 'default',
439
- concurrency: Number(env.QUEUE_CONCURRENCY ?? 5),
440
- },
441
- } as const;
442
-
443
- export default queue;
444
- `,
445
- cache: `import { env } from '../bootstrap/env.js';
446
-
447
- const cache = {
448
- driver: env.CACHE_DRIVER ?? 'memory',
449
-
450
- ttl: Number(env.CACHE_TTL ?? 3600),
451
-
452
- redis: {
453
- host: env.REDIS_HOST ?? '127.0.0.1',
454
- port: Number(env.REDIS_PORT ?? 6379),
455
- password: env.REDIS_PASSWORD,
456
- db: Number(env.REDIS_DB ?? 0),
457
- keyPrefix: env.CACHE_PREFIX ?? 'cache:',
458
- },
459
- } as const;
460
-
461
- export default cache;
462
- `,
463
- storage: `import { env } from '../bootstrap/env.js';
464
-
465
- const storage = {
466
- disks: {
467
- default: {
468
- driver: env.STORAGE_DRIVER ?? 'local',
469
- root: env.STORAGE_ROOT ?? 'storage/app',
470
- },
471
-
472
- s3: {
473
- driver: 's3' as const,
474
- bucket: env.AWS_BUCKET ?? '',
475
- region: env.AWS_REGION ?? 'us-east-1',
476
- endpoint: env.AWS_ENDPOINT,
477
- accessKeyId: env.AWS_ACCESS_KEY_ID ?? '',
478
- secretAccessKey: env.AWS_SECRET_ACCESS_KEY ?? '',
479
- },
480
- },
481
- } as const;
482
-
483
- export default storage;
484
- `,
485
- session: `import { env } from '../bootstrap/env.js';
486
-
487
- const session = {
488
- driver: env.SESSION_DRIVER ?? 'cookie',
489
- secret: env.SESSION_SECRET ?? 'change-me-in-production',
490
- ttl: Number(env.SESSION_TTL ?? 86400),
491
- cookieName: 'lumiarq_session',
492
- secure: env.NODE_ENV === 'production',
493
- sameSite: 'lax' as const,
494
- } as const;
495
-
496
- export default session;
497
- `,
498
- security: `import { env } from '../bootstrap/env.js';
499
-
500
- const security = {
501
- jwtSecret: env.JWT_SECRET ?? 'change-me-in-production',
502
- jwtTtl: Number(env.JWT_TTL ?? 3600),
503
-
504
- cors: {
505
- allowOrigins: (env.CORS_ORIGINS ?? '*').split(',').map((s) => s.trim()),
506
- allowMethods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
507
- allowHeaders: ['Content-Type', 'Authorization'],
508
- maxAge: 86400,
509
- },
510
-
511
- rateLimit: {
512
- max: Number(env.RATE_LIMIT_MAX ?? 100),
513
- windowMs: Number(env.RATE_LIMIT_WINDOW ?? 60000),
514
- },
515
- } as const;
516
-
517
- export default security;
518
- `,
519
- logging: `import { env } from '../bootstrap/env.js';
520
-
521
- const logging = {
522
- level: (env.LOG_LEVEL ?? 'info') as 'debug' | 'info' | 'warn' | 'error',
523
- driver: (env.LOG_DRIVER ?? 'console') as 'console' | 'traze',
524
-
525
- traze: {
526
- endpoint: env.TRAZE_ENDPOINT ?? '',
527
- apiKey: env.TRAZE_API_KEY ?? '',
528
- service: env.TRAZE_SERVICE ?? 'lumiarq-app',
529
- },
530
- } as const;
531
-
532
- export default logging;
533
- `,
534
- auth: `import { env } from '../bootstrap/env.js';
535
-
536
- const auth = {
537
- guard: (env.AUTH_GUARD ?? 'jwt') as 'jwt' | 'session',
538
- secret: env.JWT_SECRET ?? 'change-me-in-production',
539
-
540
- jwt: {
541
- ttl: Number(env.JWT_TTL ?? 3600),
542
- refreshTtl: Number(env.JWT_REFRESH_TTL ?? 604800),
543
- algorithm: 'HS256' as const,
544
- },
545
-
546
- password: {
547
- rounds: Number(env.BCRYPT_ROUNDS ?? 12),
548
- },
549
- } as const;
550
-
551
- export default auth;
552
- `,
553
- };
554
- const KNOWN_CONFIG_NAMES = Object.keys(CONFIG_STUBS);
555
- export function publishConfig(configName, force = false, cwd = process.cwd()) {
556
- if (configName === 'list' || !configName) {
557
- writeLine();
558
- writeLine(ui.section('Available Configs'));
559
- for (const name of KNOWN_CONFIG_NAMES) {
560
- const exists = existsSync(join(cwd, 'config', `${name}.ts`));
561
- writeLine(` ${exists ? ui.ok(name) : ui.bullet(name)}${exists ? ui.dim(' (already published)') : ''}`);
562
- }
563
- writeLine();
564
- writeLine(` Run: ${ui.cyan('lumis publish config <name>')} to publish a config file.`);
565
- writeLine();
566
- return 0;
567
- }
568
- if (configName === 'all') {
569
- let published = 0;
570
- let skipped = 0;
571
- for (const name of KNOWN_CONFIG_NAMES) {
572
- const result = publishSingleConfig(name, force, cwd);
573
- if (result.action === 'created')
574
- published++;
575
- else
576
- skipped++;
577
- }
578
- writeLine();
579
- writeLine(ui.section('Config Publish'));
580
- writeLine(` ${ui.ok(`Published ${published} config files`)}`);
581
- if (skipped > 0)
582
- writeLine(` ${ui.warn(`Skipped ${skipped} already existing files (use --force to overwrite)`)}`);
583
- writeLine();
584
- return 0;
585
- }
586
- const stub = CONFIG_STUBS[configName];
587
- if (!stub) {
588
- writeError(ui.fail(`Unknown config: "${configName}". Available: ${KNOWN_CONFIG_NAMES.join(', ')}`));
589
- return 1;
590
- }
591
- const { action, filePath } = publishSingleConfig(configName, force, cwd);
592
- writeLine();
593
- writeLine(ui.section('Config Publish'));
594
- if (action === 'created') {
595
- writeLine(` ${ui.ok(`Published config/${configName}.ts`)}`);
596
- }
597
- else {
598
- writeLine(` ${ui.warn(`config/${configName}.ts already exists — use --force to overwrite`)}`);
599
- }
600
- writeLine(` ${ui.bullet(relative(cwd, filePath))}`);
601
- writeLine();
602
- return 0;
603
- }
604
- function publishSingleConfig(name, force, cwd) {
605
- const filePath = join(cwd, 'config', `${name}.ts`);
606
- const stub = CONFIG_STUBS[name];
607
- if (!stub)
608
- return { action: 'skipped', filePath };
609
- if (existsSync(filePath) && !force) {
610
- return { action: 'skipped', filePath };
611
- }
612
- ensureParentDir(filePath);
613
- writeFileSync(filePath, stub, 'utf8');
614
- return { action: 'created', filePath };
615
- }
616
- // ─── Worker commands ──────────────────────────────────────────────────────────
617
- export function workerStart(dev = false, cwd = process.cwd()) {
618
- const workerFile = join(cwd, 'bootstrap', 'worker.ts');
619
- const workerBuilt = join(cwd, '.arc', 'node', 'worker.js');
620
- if (dev || !existsSync(workerBuilt)) {
621
- if (!existsSync(workerFile)) {
622
- writeError(ui.fail('No bootstrap/worker.ts found. Run: lumis publish config queue (then create bootstrap/worker.ts)'));
623
- return 1;
624
- }
625
- writeLine();
626
- writeLine(ui.section('Worker Start (dev)'));
627
- writeLine(` ${ui.bullet(`Starting ${relative(cwd, workerFile)} via tsx…`)}`);
628
- writeLine(` ${ui.dim('Press Ctrl+C to stop.')}`);
629
- writeLine();
630
- return runPnpm(['exec', 'tsx', workerFile], cwd);
631
- }
632
- writeLine();
633
- writeLine(ui.section('Worker Start'));
634
- writeLine(` ${ui.bullet(`Starting ${relative(cwd, workerBuilt)}…`)}`);
635
- writeLine(` ${ui.dim('Press Ctrl+C to stop.')}`);
636
- writeLine();
637
- return runPnpm(['exec', 'node', workerBuilt], cwd);
638
- }
639
- export function workerList(cwd = process.cwd()) {
640
- const result = runBridge(['schedule:list', cwd], cwd);
641
- writeLine();
642
- writeLine(ui.section('Worker Status'));
643
- const workerFile = join(cwd, 'bootstrap', 'worker.ts');
644
- const workerBuilt = join(cwd, '.arc', 'node', 'worker.js');
645
- writeLine(` ${ui.bullet('bootstrap/worker.ts')} ${existsSync(workerFile) ? ui.ok('present') : ui.warn('missing')}`);
646
- writeLine(` ${ui.bullet('.arc/node/worker.js')} ${existsSync(workerBuilt) ? ui.ok('built') : ui.dim('not built')}`);
647
- writeLine();
648
- if (result.status === 0) {
649
- try {
650
- const parsed = JSON.parse(result.stdout);
651
- if (parsed.jobs.length > 0) {
652
- writeLine(` ${ui.bold('Scheduled Jobs')}`);
653
- for (const job of parsed.jobs) {
654
- const next = job.nextRun ? ui.dim(` next: ${job.nextRun}`) : '';
655
- writeLine(` ${ui.bullet(job.name)} ${ui.cyan(job.cron)}${next}`);
656
- }
657
- writeLine();
658
- }
659
- }
660
- catch {
661
- // ignore parse errors
662
- }
663
- }
664
- return 0;
665
- }
666
- // ─── Schedule commands ────────────────────────────────────────────────────────
667
- export function scheduleList(cwd = process.cwd()) {
668
- const result = runBridge(['schedule:list', cwd], cwd);
669
- if (result.status !== 0) {
670
- writeError(ui.fail(result.stderr.trim() || 'Failed to list scheduled jobs.'));
671
- return result.status;
672
- }
673
- const parsed = JSON.parse(result.stdout);
674
- writeLine();
675
- writeLine(ui.section(`Scheduled Jobs (${parsed.jobs.length})`));
676
- if (parsed.jobs.length === 0) {
677
- writeLine(` ${ui.dim('No scheduled jobs registered.')}`);
678
- writeLine(` ${ui.dim('Export a scheduler from bootstrap/providers.ts and register jobs in bootstrap/schedule.ts.')}`);
679
- }
680
- else {
681
- for (const job of parsed.jobs) {
682
- const desc = job.description ? ui.dim(` — ${job.description}`) : '';
683
- const next = job.nextRun ? ui.dim(` (next: ${job.nextRun})`) : '';
684
- writeLine(` ${ui.cyan(job.name.padEnd(30, ' '))} ${job.cron}${desc}${next}`);
685
- }
686
- }
687
- writeLine();
688
- return 0;
689
- }
690
- export function scheduleRun(jobName, cwd = process.cwd()) {
691
- if (!jobName) {
692
- writeError(ui.fail('Usage: lumis schedule:run <JobName>'));
693
- return 1;
694
- }
695
- writeLine();
696
- writeLine(ui.section('Schedule Run'));
697
- writeLine(` ${ui.bullet(`Running job: ${jobName}…`)}`);
698
- const result = runBridge(['schedule:run', cwd, jobName], cwd);
699
- if (result.status !== 0) {
700
- writeError(ui.fail(result.stderr.trim() || `Failed to run job "${jobName}".`));
701
- return result.status;
702
- }
703
- writeLine(` ${ui.ok(`Job "${jobName}" completed.`)}`);
704
- writeLine();
705
- return 0;
706
- }
707
77
  //# sourceMappingURL=app-commands.js.map