@lunora/cli 1.0.0-alpha.3 → 1.0.0-alpha.30
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/__assets__/package-og.svg +1 -1
- package/dist/bin.mjs +1 -1
- package/dist/index.d.mts +243 -114
- package/dist/index.d.ts +243 -114
- package/dist/index.mjs +7 -7
- package/dist/packem_chunks/handler.mjs +89 -7
- package/dist/packem_chunks/handler10.mjs +8 -14
- package/dist/packem_chunks/handler11.mjs +19 -189
- package/dist/packem_chunks/handler12.mjs +176 -115
- package/dist/packem_chunks/handler13.mjs +118 -52
- package/dist/packem_chunks/handler14.mjs +50 -43
- package/dist/packem_chunks/handler15.mjs +46 -67
- package/dist/packem_chunks/handler16.mjs +73 -37
- package/dist/packem_chunks/handler17.mjs +38 -100
- package/dist/packem_chunks/handler18.mjs +87 -154
- package/dist/packem_chunks/handler19.mjs +148 -67
- package/dist/packem_chunks/handler2.mjs +2 -2
- package/dist/packem_chunks/handler20.mjs +71 -76
- package/dist/packem_chunks/handler21.mjs +71 -288
- package/dist/packem_chunks/handler3.mjs +1 -1
- package/dist/packem_chunks/handler4.mjs +1 -1
- package/dist/packem_chunks/handler5.mjs +2 -2
- package/dist/packem_chunks/handler6.mjs +2 -2
- package/dist/packem_chunks/handler7.mjs +1 -1
- package/dist/packem_chunks/handler8.mjs +1 -1
- package/dist/packem_chunks/handler9.mjs +311 -12
- package/dist/packem_chunks/planDevCommand.mjs +46 -49
- package/dist/packem_chunks/runCodegenCommand.mjs +2 -2
- package/dist/packem_chunks/runDeployCommand.mjs +105 -15
- package/dist/packem_chunks/runInitCommand.mjs +1980 -77
- package/dist/packem_chunks/runMigrateGenerateCommand.mjs +5 -5
- package/dist/packem_chunks/runResetCommand.mjs +4 -4
- package/dist/packem_chunks/runRpcCommand.mjs +1 -1
- package/dist/packem_shared/{COMMANDS-CHw4zOZ9.mjs → COMMANDS-B0ftFD_3.mjs} +47 -21
- package/dist/packem_shared/{command-BDXcJCCJ.mjs → command-D3lB_4Az.mjs} +6 -1
- package/dist/packem_shared/{commands-DIQ3nf0C.mjs → commands-B-gR09Z_.mjs} +124 -22
- package/dist/packem_shared/{createLogger-CHPNjFw2.mjs → createLogger-B40gPzQo.mjs} +9 -4
- package/dist/packem_shared/detect-package-manager-DYp7n3mJ.mjs +61 -0
- package/dist/packem_shared/{diffSnapshots-RR2ZE8Ya.mjs → diffSnapshots-BeDvvNiF.mjs} +1 -1
- package/dist/packem_shared/{insertSchemaExtension-BuzF6-t2.mjs → insertSchemaExtension-DAqbfr9Z.mjs} +15 -10
- package/dist/packem_shared/{output-format-7gyGR3h8.mjs → output-format-wUvAN6AL.mjs} +1 -1
- package/dist/packem_shared/prompt-cancelled-APzX1Im-.mjs +9 -0
- package/dist/packem_shared/runAddCommand-bnY6-HKb.mjs +4 -0
- package/dist/packem_shared/{schemaIrToSnapshot-aBTo7TM5.mjs → schemaIrToSnapshot-DdsljJT-.mjs} +1 -1
- package/dist/packem_shared/storage-BIsph-Vk.mjs +84 -0
- package/dist/packem_shared/tui-prompts-BjEN8XgP.mjs +658 -0
- package/dist/packem_shared/wrangler-secrets-P2_ZUR-k.mjs +47 -0
- package/package.json +12 -11
- package/skills/lunora-setup-storage/SKILL.md +7 -3
- package/dist/packem_shared/features-ocSSpZtS.mjs +0 -24
- package/dist/packem_shared/runAddCommand-3I3JFZUG.mjs +0 -4
- /package/dist/packem_shared/{defaultSpawner-DxI3mebw.mjs → createRecordingSpawner-DxI3mebw.mjs} +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, writeFileSync,
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync, mkdtempSync, rmSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { tmpdir } from 'node:os';
|
|
3
|
-
import {
|
|
3
|
+
import { discoverSchema, discoverMigrations } from '@lunora/codegen';
|
|
4
4
|
import { join } from '@visulima/path';
|
|
5
5
|
import { Project } from 'ts-morph';
|
|
6
6
|
import { r as resolveAdminBaseUrl } from '../packem_shared/admin-url-4UzT-CI4.mjs';
|
|
7
|
-
import { d as defineHandler } from '../packem_shared/command-
|
|
8
|
-
import { diffSnapshots, renderMigrationFile } from '../packem_shared/diffSnapshots-
|
|
7
|
+
import { d as defineHandler } from '../packem_shared/command-D3lB_4Az.mjs';
|
|
8
|
+
import { diffSnapshots, renderMigrationFile } from '../packem_shared/diffSnapshots-BeDvvNiF.mjs';
|
|
9
9
|
import { a as resolveProductionWorkerUrl } from '../packem_shared/resolve-target-qbsJ_5sF.mjs';
|
|
10
|
-
import schemaIrToSnapshot from '../packem_shared/schemaIrToSnapshot-
|
|
10
|
+
import schemaIrToSnapshot from '../packem_shared/schemaIrToSnapshot-DdsljJT-.mjs';
|
|
11
11
|
import { runExportCommand, runImportCommand } from '../packem_shared/DEFAULT_IMPORT_BATCH_SIZE-Ck-2bU08.mjs';
|
|
12
12
|
|
|
13
13
|
const SNAPSHOT_FILENAME = ".snapshot.json";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, rmSync } from 'node:fs';
|
|
2
|
-
import { promptYesNo } from '@lunora/config';
|
|
3
2
|
import { join } from '@visulima/path';
|
|
4
|
-
import { d as defineHandler } from '../packem_shared/command-
|
|
3
|
+
import { d as defineHandler } from '../packem_shared/command-D3lB_4Az.mjs';
|
|
4
|
+
import { b as tuiConfirm } from '../packem_shared/tui-prompts-BjEN8XgP.mjs';
|
|
5
5
|
|
|
6
6
|
const runResetCommand = async (options) => {
|
|
7
7
|
const cwd = options.cwd ?? process.cwd();
|
|
@@ -15,8 +15,8 @@ const runResetCommand = async (options) => {
|
|
|
15
15
|
options.logger.error("reset: stdin is not a TTY — re-run with --yes to confirm deleting .wrangler/state");
|
|
16
16
|
return { code: 1, removed: [] };
|
|
17
17
|
}
|
|
18
|
-
const confirmer = options.confirm ??
|
|
19
|
-
const confirmed = await confirmer("This will delete .wrangler/state. Continue?
|
|
18
|
+
const confirmer = options.confirm ?? tuiConfirm;
|
|
19
|
+
const confirmed = await confirmer("This will delete .wrangler/state. Continue?");
|
|
20
20
|
if (!confirmed) {
|
|
21
21
|
options.logger.info("reset: aborted");
|
|
22
22
|
return { code: 1, removed: [] };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { d as defineHandler } from '../packem_shared/command-
|
|
1
|
+
import { d as defineHandler } from '../packem_shared/command-D3lB_4Az.mjs';
|
|
2
2
|
import { r as resolveWorkerUrl } from '../packem_shared/resolve-target-qbsJ_5sF.mjs';
|
|
3
3
|
|
|
4
4
|
const parseArgsJson = (raw) => {
|
|
@@ -3,7 +3,7 @@ import { createCerebro } from '@visulima/cerebro';
|
|
|
3
3
|
import completionCommand from '@visulima/cerebro/command/completion';
|
|
4
4
|
import versionCommand from '@visulima/cerebro/command/version';
|
|
5
5
|
import { A as API_SPEC_HELP } from './api-spec-CtA6ilu4.mjs';
|
|
6
|
-
import { createLogger } from './createLogger-
|
|
6
|
+
import { createLogger } from './createLogger-B40gPzQo.mjs';
|
|
7
7
|
import { tmpdir } from 'node:os';
|
|
8
8
|
import { join } from 'node:path';
|
|
9
9
|
|
|
@@ -14,7 +14,8 @@ const addCommand = {
|
|
|
14
14
|
["lunora add auth", "Add authentication (asks which provider)"],
|
|
15
15
|
["lunora add auth --provider clerk", "Add Clerk auth without prompting"],
|
|
16
16
|
["lunora add email", "Add transactional email (Cloudflare Email Workers + dev mail catcher)"],
|
|
17
|
-
["lunora add storage", "Add the R2 storage registry item"],
|
|
17
|
+
["lunora add storage", "Add the R2 storage registry item (asks for the bucket name)"],
|
|
18
|
+
["lunora add storage --bucket my-app-uploads", "Add storage with a bucket name, no prompt"],
|
|
18
19
|
["lunora add crons", "Add the scheduled-jobs registry item"],
|
|
19
20
|
["lunora add storage --ref alpha", "Add an item from the alpha branch's registry"]
|
|
20
21
|
],
|
|
@@ -25,7 +26,10 @@ const addCommand = {
|
|
|
25
26
|
name: "add",
|
|
26
27
|
options: [
|
|
27
28
|
{ description: "auth: provider to use without prompting (auth | clerk | auth0)", name: "provider", type: String },
|
|
28
|
-
{ description: "
|
|
29
|
+
{ description: "auth: D1 database name to use without prompting (lowercase alphanumeric + hyphens)", name: "db", type: String },
|
|
30
|
+
{ description: "storage: R2 bucket name to use without prompting (lowercase alphanumeric + hyphens)", name: "bucket", type: String },
|
|
31
|
+
{ description: "email: verified destination address to use without prompting", name: "mail-to", type: String },
|
|
32
|
+
{ description: "Skip prompts (auth provider, DB name, bucket name, mail destination) and use the defaults", name: "yes", type: Boolean },
|
|
29
33
|
{ description: "Local registry root (offline; expects <name>/ subdirs)", name: "from", type: String },
|
|
30
34
|
{ description: "Override the remote registry source base (e.g. gh:owner/repo/registry)", name: "source", type: String },
|
|
31
35
|
{
|
|
@@ -33,7 +37,8 @@ const addCommand = {
|
|
|
33
37
|
name: "ref",
|
|
34
38
|
type: String
|
|
35
39
|
},
|
|
36
|
-
{ description: "Permit --source values outside gh:/github:/https://", name: "allow-unsafe-source", type: Boolean }
|
|
40
|
+
{ description: "Permit --source values outside gh:/github:/https://", name: "allow-unsafe-source", type: Boolean },
|
|
41
|
+
{ description: "Output format: pretty (default) or json", name: "format", type: String }
|
|
37
42
|
]
|
|
38
43
|
};
|
|
39
44
|
|
|
@@ -256,21 +261,24 @@ const doctorCommand = {
|
|
|
256
261
|
};
|
|
257
262
|
|
|
258
263
|
const envCommand = {
|
|
259
|
-
argument: { description: "list | get <KEY> | set <KEY> <VALUE> | unset <KEY> | push | diff | doctor", name: "subcommand", type: String },
|
|
260
|
-
description: "Manage .dev.vars and sync secrets via wrangler (list | get | set | unset | push | diff | doctor)",
|
|
264
|
+
argument: { description: "list | get <KEY> | set <KEY> <VALUE> | unset <KEY> | generate [KEY] | push | diff | doctor", name: "subcommand", type: String },
|
|
265
|
+
description: "Manage .dev.vars and sync secrets via wrangler (list | get | set | unset | generate | push | diff | doctor)",
|
|
261
266
|
examples: [
|
|
262
267
|
["lunora env list", "List .dev.vars keys"],
|
|
263
268
|
["lunora env set API_KEY secret", "Set a local variable"],
|
|
269
|
+
["lunora env generate", "Generate strong values for the project's secrets (print KEY=value)"],
|
|
270
|
+
["lunora env generate AUTH_SECRET --set", "Generate one secret and write it to .dev.vars"],
|
|
264
271
|
["lunora env push --yes", "Upload secrets to Cloudflare"],
|
|
265
272
|
["lunora env diff", "Compare local .dev.vars keys against Cloudflare"]
|
|
266
273
|
],
|
|
267
274
|
group: "Data",
|
|
268
|
-
loader: () => import('../packem_chunks/
|
|
275
|
+
loader: () => import('../packem_chunks/handler9.mjs').then((m) => {
|
|
269
276
|
return { default: m.execute };
|
|
270
277
|
}),
|
|
271
278
|
name: "env",
|
|
272
279
|
options: [
|
|
273
280
|
{ description: "Target production for `push` (passes --env production to wrangler)", name: "prod", type: Boolean },
|
|
281
|
+
{ description: "For `generate` — write the generated secrets into .dev.vars instead of printing them", name: "set", type: Boolean },
|
|
274
282
|
{
|
|
275
283
|
description: "Push secrets to a temporary-account deployment when unauthenticated (wrangler secret put --temporary). Errors if you're already authenticated.",
|
|
276
284
|
name: "temporary",
|
|
@@ -288,7 +296,7 @@ const exportCommand = {
|
|
|
288
296
|
["lunora export --tables messages,users", "Export only specific tables"]
|
|
289
297
|
],
|
|
290
298
|
group: "Data",
|
|
291
|
-
loader: () => import('../packem_chunks/
|
|
299
|
+
loader: () => import('../packem_chunks/handler10.mjs').then((m) => {
|
|
292
300
|
return { default: m.execute };
|
|
293
301
|
}),
|
|
294
302
|
name: "export",
|
|
@@ -310,7 +318,7 @@ const importCommand = {
|
|
|
310
318
|
description: "Bulk-insert rows from an NDJSON file via the worker's admin endpoint",
|
|
311
319
|
examples: [["lunora import backup.ndjson", "Bulk-insert rows from an NDJSON file"]],
|
|
312
320
|
group: "Data",
|
|
313
|
-
loader: () => import('../packem_chunks/
|
|
321
|
+
loader: () => import('../packem_chunks/handler11.mjs').then((m) => {
|
|
314
322
|
return { default: m.execute };
|
|
315
323
|
}),
|
|
316
324
|
name: "import",
|
|
@@ -334,7 +342,7 @@ const infoCommand = {
|
|
|
334
342
|
["lunora info --json", "Emit a JSON snapshot"]
|
|
335
343
|
],
|
|
336
344
|
group: "Project",
|
|
337
|
-
loader: () => import('../packem_chunks/
|
|
345
|
+
loader: () => import('../packem_chunks/handler12.mjs').then((m) => {
|
|
338
346
|
return { default: m.execute };
|
|
339
347
|
}),
|
|
340
348
|
name: "info",
|
|
@@ -361,11 +369,19 @@ const initCommand = {
|
|
|
361
369
|
options: [
|
|
362
370
|
{
|
|
363
371
|
alias: "t",
|
|
364
|
-
|
|
365
|
-
|
|
372
|
+
// No default: when omitted, an interactive run shows the framework
|
|
373
|
+
// picker (default React overlay) and a non-interactive run errors.
|
|
374
|
+
// For React/Vue/Solid/Svelte SPAs use `--vite <framework>` (overlay);
|
|
375
|
+
// `-t` selects a bespoke template.
|
|
376
|
+
description: "Bespoke template (standalone | astro | nuxt | sveltekit | tanstack-start-react | tanstack-start-solid). For an SPA use --vite react|vue|solid|svelte.",
|
|
366
377
|
name: "template",
|
|
367
378
|
type: String
|
|
368
379
|
},
|
|
380
|
+
{
|
|
381
|
+
description: "Scaffold via the create-vite overlay for a framework (react | vue | solid | svelte | vanilla) — official create-vite base + Lunora layer",
|
|
382
|
+
name: "vite",
|
|
383
|
+
type: String
|
|
384
|
+
},
|
|
369
385
|
{
|
|
370
386
|
description: "Local templates root to copy from (offline-friendly; expects <type>/ subdirs)",
|
|
371
387
|
name: "from",
|
|
@@ -407,6 +423,16 @@ const initCommand = {
|
|
|
407
423
|
description: "Also scaffold a CI deploy pipeline: github (.github/workflows/deploy.yml) or gitlab (.gitlab-ci.yml)",
|
|
408
424
|
name: "ci",
|
|
409
425
|
type: String
|
|
426
|
+
},
|
|
427
|
+
{
|
|
428
|
+
description: "Add features non-interactively after scaffolding (comma-separated): auth | email | storage | ratelimit | crons | presence | backup",
|
|
429
|
+
name: "add",
|
|
430
|
+
type: String
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
description: "Walk through every step (prompts + output) without writing files, installing, or running git",
|
|
434
|
+
name: "dry-run",
|
|
435
|
+
type: Boolean
|
|
410
436
|
}
|
|
411
437
|
]
|
|
412
438
|
};
|
|
@@ -420,7 +446,7 @@ const insightsCommand = {
|
|
|
420
446
|
["lunora insights --prod --url https://app.example.com --token $LUNORA_ADMIN_TOKEN", "Report against production"]
|
|
421
447
|
],
|
|
422
448
|
group: "Develop",
|
|
423
|
-
loader: () => import('../packem_chunks/
|
|
449
|
+
loader: () => import('../packem_chunks/handler13.mjs').then((m) => {
|
|
424
450
|
return { default: m.execute };
|
|
425
451
|
}),
|
|
426
452
|
name: "insights",
|
|
@@ -442,7 +468,7 @@ const linkCommand = {
|
|
|
442
468
|
["lunora link --remove", "Remove the link"]
|
|
443
469
|
],
|
|
444
470
|
group: "Deploy",
|
|
445
|
-
loader: () => import('../packem_chunks/
|
|
471
|
+
loader: () => import('../packem_chunks/handler14.mjs').then((m) => {
|
|
446
472
|
return { default: m.execute };
|
|
447
473
|
}),
|
|
448
474
|
name: "link",
|
|
@@ -458,7 +484,7 @@ const logsCommand = {
|
|
|
458
484
|
argument: { description: "Worker name (defaults to the name in wrangler config)", name: "worker", type: String },
|
|
459
485
|
description: "Stream live logs from a deployed Lunora Worker",
|
|
460
486
|
group: "Deploy",
|
|
461
|
-
loader: () => import('../packem_chunks/
|
|
487
|
+
loader: () => import('../packem_chunks/handler15.mjs').then((m) => {
|
|
462
488
|
return { default: m.execute };
|
|
463
489
|
}),
|
|
464
490
|
name: "logs",
|
|
@@ -518,7 +544,7 @@ const prepareCommand = {
|
|
|
518
544
|
description: "Run codegen + binding reconcile + wrangler validation (no Vite) — for CI",
|
|
519
545
|
examples: [["lunora prepare", "Codegen + binding reconcile + validate (CI, before deploy)"]],
|
|
520
546
|
group: "Deploy",
|
|
521
|
-
loader: () => import('../packem_chunks/
|
|
547
|
+
loader: () => import('../packem_chunks/handler16.mjs').then((m) => {
|
|
522
548
|
return { default: m.execute };
|
|
523
549
|
}),
|
|
524
550
|
name: "prepare",
|
|
@@ -542,7 +568,7 @@ const registryCommand = {
|
|
|
542
568
|
["lunora registry build --check", "Verify the committed catalog is current"]
|
|
543
569
|
],
|
|
544
570
|
group: "Project",
|
|
545
|
-
loader: () => import('../packem_chunks/
|
|
571
|
+
loader: () => import('../packem_chunks/handler17.mjs').then((m) => {
|
|
546
572
|
return { default: m.execute };
|
|
547
573
|
}),
|
|
548
574
|
name: "registry",
|
|
@@ -592,7 +618,7 @@ const rulesCommand = {
|
|
|
592
618
|
["lunora rules check --strict", "Exit non-zero when rules are missing (CI gate)"]
|
|
593
619
|
],
|
|
594
620
|
group: "Project",
|
|
595
|
-
loader: () => import('../packem_chunks/
|
|
621
|
+
loader: () => import('../packem_chunks/handler18.mjs').then((m) => {
|
|
596
622
|
return { default: m.execute };
|
|
597
623
|
}),
|
|
598
624
|
name: "rules",
|
|
@@ -630,7 +656,7 @@ const seedCommand = {
|
|
|
630
656
|
["lunora seed --seed 7 --dry-run", "Print the NDJSON for seed 7 without inserting"]
|
|
631
657
|
],
|
|
632
658
|
group: "Data",
|
|
633
|
-
loader: () => import('../packem_chunks/
|
|
659
|
+
loader: () => import('../packem_chunks/handler19.mjs').then((m) => {
|
|
634
660
|
return { default: m.execute };
|
|
635
661
|
}),
|
|
636
662
|
name: "seed",
|
|
@@ -659,7 +685,7 @@ const verifyCommand = {
|
|
|
659
685
|
["lunora verify --no-typecheck", "Skip the TypeScript type-check"]
|
|
660
686
|
],
|
|
661
687
|
group: "Deploy",
|
|
662
|
-
loader: () => import('../packem_chunks/
|
|
688
|
+
loader: () => import('../packem_chunks/handler20.mjs').then((m) => {
|
|
663
689
|
return { default: m.execute };
|
|
664
690
|
}),
|
|
665
691
|
name: "verify",
|
|
@@ -678,7 +704,7 @@ const viewCommand = {
|
|
|
678
704
|
["lunora view --remote", "Open the deployed studio"]
|
|
679
705
|
],
|
|
680
706
|
group: "Project",
|
|
681
|
-
loader: () => import('../packem_chunks/
|
|
707
|
+
loader: () => import('../packem_chunks/handler21.mjs').then((m) => {
|
|
682
708
|
return { default: m.execute };
|
|
683
709
|
}),
|
|
684
710
|
name: "view",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { createLogger } from './createLogger-
|
|
1
|
+
import { createLogger } from './createLogger-B40gPzQo.mjs';
|
|
2
|
+
import { P as PromptCancelledError, a as PROMPT_CANCEL_EXIT_CODE } from './prompt-cancelled-APzX1Im-.mjs';
|
|
2
3
|
|
|
3
4
|
const defineHandler = (body) => async (toolbox) => {
|
|
4
5
|
const logger = createLogger();
|
|
@@ -6,6 +7,10 @@ const defineHandler = (body) => async (toolbox) => {
|
|
|
6
7
|
const { code } = await body({ argument: toolbox.argument, cwd: toolbox.process.cwd, logger, options: toolbox.options });
|
|
7
8
|
toolbox.process.exit(code);
|
|
8
9
|
} catch (error) {
|
|
10
|
+
if (error instanceof PromptCancelledError) {
|
|
11
|
+
toolbox.process.exit(PROMPT_CANCEL_EXIT_CODE);
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
9
14
|
logger.error(error instanceof Error ? error.message : String(error));
|
|
10
15
|
toolbox.process.exit(1);
|
|
11
16
|
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { existsSync, readFileSync, writeFileSync, mkdirSync, mkdtempSync, rmSync } from 'node:fs';
|
|
2
2
|
import { dirname, join } from '@visulima/path';
|
|
3
|
-
import { DEV_VARS_FILE, parseDevVariableEntries
|
|
3
|
+
import { DEV_VARS_FILE, parseDevVariableEntries } from '@lunora/config';
|
|
4
4
|
import { modify, applyEdits, parse } from 'jsonc-parser';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { b as tuiConfirm } from './tui-prompts-BjEN8XgP.mjs';
|
|
5
7
|
import { collectCatalog, buildRegistryIndex } from './buildRegistryIndex-BcYe607_.mjs';
|
|
6
|
-
import { insertSchemaExtension } from './insertSchemaExtension-
|
|
8
|
+
import { insertSchemaExtension } from './insertSchemaExtension-DAqbfr9Z.mjs';
|
|
7
9
|
import { createHash } from 'node:crypto';
|
|
8
10
|
import { tmpdir } from 'node:os';
|
|
9
11
|
import { downloadTemplate } from 'giget';
|
|
10
|
-
import { fileURLToPath } from 'node:url';
|
|
11
12
|
import parseManifest from './parseManifest--vZf2FY1.mjs';
|
|
12
13
|
|
|
13
14
|
const DEFAULT_SOURCE_REF_FALLBACK = "alpha";
|
|
@@ -59,8 +60,71 @@ const resolveSourceRef = (ref) => {
|
|
|
59
60
|
}
|
|
60
61
|
return resolveVersionRef(resolveCliVersion());
|
|
61
62
|
};
|
|
63
|
+
const STABLE_DIST_TAG = "latest";
|
|
64
|
+
const resolveDistTag = () => {
|
|
65
|
+
const ref = resolveVersionRef(resolveCliVersion());
|
|
66
|
+
return ref === STABLE_BRANCH ? STABLE_DIST_TAG : ref;
|
|
67
|
+
};
|
|
68
|
+
const DEFAULT_REGISTRY = "https://registry.npmjs.org";
|
|
69
|
+
const registryBase = () => {
|
|
70
|
+
const configured = process.env["npm_config_registry"];
|
|
71
|
+
const base = configured !== void 0 && configured.length > 0 ? configured : DEFAULT_REGISTRY;
|
|
72
|
+
return base.endsWith("/") ? base.slice(0, -1) : base;
|
|
73
|
+
};
|
|
74
|
+
const resolveTagVersion = async (packageName, tag) => {
|
|
75
|
+
try {
|
|
76
|
+
const response = await fetch(`${registryBase()}/${packageName.replace("/", "%2F")}`, {
|
|
77
|
+
headers: { accept: "application/vnd.npm.install-v1+json" },
|
|
78
|
+
signal: AbortSignal.timeout(1e4)
|
|
79
|
+
});
|
|
80
|
+
if (!response.ok) {
|
|
81
|
+
return void 0;
|
|
82
|
+
}
|
|
83
|
+
const packument = await response.json();
|
|
84
|
+
return packument["dist-tags"]?.[tag];
|
|
85
|
+
} catch {
|
|
86
|
+
return void 0;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
const resolveTagVersions = async (names, tag) => {
|
|
90
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
91
|
+
await Promise.all(
|
|
92
|
+
[...new Set(names)].map(async (name) => {
|
|
93
|
+
const version = await resolveTagVersion(name, tag);
|
|
94
|
+
if (version !== void 0) {
|
|
95
|
+
resolved.set(name, version);
|
|
96
|
+
}
|
|
97
|
+
})
|
|
98
|
+
);
|
|
99
|
+
return resolved;
|
|
100
|
+
};
|
|
62
101
|
|
|
63
|
-
const
|
|
102
|
+
const resolveDepRange = (range) => {
|
|
103
|
+
if (!range.startsWith("workspace:")) {
|
|
104
|
+
return range;
|
|
105
|
+
}
|
|
106
|
+
const rest = range.slice("workspace:".length);
|
|
107
|
+
if (rest === "" || rest === "*" || rest === "^" || rest === "~") {
|
|
108
|
+
return resolveDistTag();
|
|
109
|
+
}
|
|
110
|
+
return rest;
|
|
111
|
+
};
|
|
112
|
+
const UMBRELLA_REEXPORTED_DEPS = /* @__PURE__ */ new Set(["@lunora/client", "@lunora/do", "@lunora/runtime", "@lunora/server", "@lunora/values"]);
|
|
113
|
+
const UMBRELLA_IMPORT_RE = /(['"])@lunora\/(client|do|runtime|server|values)(\/[^'"]*)?\1/gu;
|
|
114
|
+
const projectUsesUmbrella = (projectRoot) => {
|
|
115
|
+
const packageJsonPath = join(projectRoot, "package.json");
|
|
116
|
+
if (!existsSync(packageJsonPath)) {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
const parsed = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
121
|
+
return parsed.dependencies?.lunorash !== void 0 || parsed.devDependencies?.lunorash !== void 0;
|
|
122
|
+
} catch {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
const rewriteUmbrellaImports = (source) => source.replaceAll(UMBRELLA_IMPORT_RE, (_match, quote, base, subpath) => `${quote}lunorash/${base}${subpath ?? ""}${quote}`);
|
|
127
|
+
const applyDeps = (deps, projectRoot, logger, section = "dependencies", useUmbrella = false) => {
|
|
64
128
|
const entries = Object.entries(deps);
|
|
65
129
|
if (entries.length === 0) {
|
|
66
130
|
return [];
|
|
@@ -74,11 +138,15 @@ const applyDeps = (deps, projectRoot, logger, section = "dependencies") => {
|
|
|
74
138
|
const parsed = JSON.parse(text);
|
|
75
139
|
const added = [];
|
|
76
140
|
for (const [name, range] of entries) {
|
|
141
|
+
if (useUmbrella && UMBRELLA_REEXPORTED_DEPS.has(name)) {
|
|
142
|
+
logger.info(`dep provided by the lunorash umbrella, skipping: ${name}`);
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
77
145
|
if (parsed.dependencies?.[name] !== void 0 || parsed.devDependencies?.[name] !== void 0) {
|
|
78
146
|
logger.info(`dep already present: ${name}`);
|
|
79
147
|
continue;
|
|
80
148
|
}
|
|
81
|
-
const edits = modify(text, [section, name], range, {
|
|
149
|
+
const edits = modify(text, [section, name], resolveDepRange(range), {
|
|
82
150
|
formattingOptions: { insertSpaces: true, tabSize: 4 }
|
|
83
151
|
});
|
|
84
152
|
text = applyEdits(text, edits);
|
|
@@ -199,14 +267,14 @@ const applyBindings = (bindings, projectRoot, logger) => {
|
|
|
199
267
|
}
|
|
200
268
|
return applied;
|
|
201
269
|
};
|
|
202
|
-
const applyItemResources = (manifest, cwd, logger) => {
|
|
270
|
+
const applyItemResources = (manifest, cwd, logger, useUmbrella = false) => {
|
|
203
271
|
const deps = [];
|
|
204
272
|
const bindings = [];
|
|
205
273
|
if (manifest.deps) {
|
|
206
|
-
deps.push(...applyDeps(manifest.deps, cwd, logger));
|
|
274
|
+
deps.push(...applyDeps(manifest.deps, cwd, logger, "dependencies", useUmbrella));
|
|
207
275
|
}
|
|
208
276
|
if (manifest.devDependencies) {
|
|
209
|
-
deps.push(...applyDeps(manifest.devDependencies, cwd, logger, "devDependencies"));
|
|
277
|
+
deps.push(...applyDeps(manifest.devDependencies, cwd, logger, "devDependencies", useUmbrella));
|
|
210
278
|
}
|
|
211
279
|
if (manifest.bindings) {
|
|
212
280
|
bindings.push(...applyBindings(manifest.bindings, cwd, logger));
|
|
@@ -238,8 +306,8 @@ const confirmDepMutation = async (items, options) => {
|
|
|
238
306
|
options.logger.error(`add: stdin is not a TTY and the requested items ${reasonText} — re-run with --yes to confirm`);
|
|
239
307
|
return false;
|
|
240
308
|
}
|
|
241
|
-
const confirmer = options.confirm ??
|
|
242
|
-
const confirmed = await confirmer(`The requested items ${reasonText}. Continue
|
|
309
|
+
const confirmer = options.confirm ?? tuiConfirm;
|
|
310
|
+
const confirmed = await confirmer(`The requested items ${reasonText}. Continue?`);
|
|
243
311
|
if (!confirmed) {
|
|
244
312
|
options.logger.info("add: aborted");
|
|
245
313
|
}
|
|
@@ -318,7 +386,12 @@ const renderDiff = (oldText, newText) => {
|
|
|
318
386
|
return out;
|
|
319
387
|
};
|
|
320
388
|
|
|
321
|
-
const
|
|
389
|
+
const CODE_FILE_RE = /\.[cm]?[jt]sx?$/u;
|
|
390
|
+
const readItemFile = (itemDirectory, file, useUmbrella) => {
|
|
391
|
+
const source = readFileSync(join(itemDirectory, file.from), "utf8");
|
|
392
|
+
return useUmbrella && CODE_FILE_RE.test(file.to) ? rewriteUmbrellaImports(source) : source;
|
|
393
|
+
};
|
|
394
|
+
const reconcileSchemaExtension = (file, itemKey, itemDirectory, projectRoot, logger, diff, useUmbrella) => {
|
|
322
395
|
const schemaPath = join(projectRoot, "lunora", "schema.ts");
|
|
323
396
|
if (diff) {
|
|
324
397
|
logger.info(`~ would merge .extend(${itemKey}.extension) into lunora/schema.ts (and create ${file.to} if absent)`);
|
|
@@ -327,9 +400,13 @@ const reconcileSchemaExtension = (file, itemKey, itemDirectory, projectRoot, log
|
|
|
327
400
|
const destinationPath = join(projectRoot, file.to);
|
|
328
401
|
if (!existsSync(destinationPath)) {
|
|
329
402
|
mkdirSync(dirname(destinationPath), { recursive: true });
|
|
330
|
-
writeFileSync(destinationPath,
|
|
403
|
+
writeFileSync(destinationPath, readItemFile(itemDirectory, file, useUmbrella), "utf8");
|
|
331
404
|
}
|
|
332
|
-
const
|
|
405
|
+
const baseModule = useUmbrella ? "lunorash/server" : "@lunora/server";
|
|
406
|
+
const existingSchema = existsSync(schemaPath) ? readFileSync(schemaPath, "utf8") : `import { defineSchema } from "${baseModule}";
|
|
407
|
+
|
|
408
|
+
export const schema = defineSchema({});
|
|
409
|
+
`;
|
|
333
410
|
const result = insertSchemaExtension(existingSchema, itemKey);
|
|
334
411
|
if (result.ok) {
|
|
335
412
|
mkdirSync(dirname(schemaPath), { recursive: true });
|
|
@@ -359,9 +436,9 @@ const previewWholeFile = (file, current, incoming, exists, logger) => {
|
|
|
359
436
|
logger.info(` ${line}`);
|
|
360
437
|
}
|
|
361
438
|
};
|
|
362
|
-
const reconcileWholeFile = (file, itemKey, itemDirectory, projectRoot, logger, lock, reconcileOptions) => {
|
|
439
|
+
const reconcileWholeFile = (file, itemKey, itemDirectory, projectRoot, logger, lock, reconcileOptions, useUmbrella) => {
|
|
363
440
|
const destinationPath = join(projectRoot, file.to);
|
|
364
|
-
const incoming =
|
|
441
|
+
const incoming = readItemFile(itemDirectory, file, useUmbrella);
|
|
365
442
|
const exists = existsSync(destinationPath);
|
|
366
443
|
const current = exists ? readFileSync(destinationPath, "utf8") : "";
|
|
367
444
|
const write = (message) => {
|
|
@@ -399,11 +476,11 @@ const reconcileWholeFile = (file, itemKey, itemDirectory, projectRoot, logger, l
|
|
|
399
476
|
logger.warn(`conflict: ${file.to} has local edits and an upstream update — wrote ${file.to}.new (use --overwrite to take theirs)`);
|
|
400
477
|
return { kind: "skipped", path: destinationPath };
|
|
401
478
|
};
|
|
402
|
-
const reconcileFile = (file, itemKey, itemDirectory, projectRoot, logger, lock, reconcileOptions = {}) => {
|
|
479
|
+
const reconcileFile = (file, itemKey, itemDirectory, projectRoot, logger, lock, reconcileOptions = {}, useUmbrella = false) => {
|
|
403
480
|
if (file.merge === "schema-extension") {
|
|
404
|
-
return reconcileSchemaExtension(file, itemKey, itemDirectory, projectRoot, logger, reconcileOptions.diff === true);
|
|
481
|
+
return reconcileSchemaExtension(file, itemKey, itemDirectory, projectRoot, logger, reconcileOptions.diff === true, useUmbrella);
|
|
405
482
|
}
|
|
406
|
-
return reconcileWholeFile(file, itemKey, itemDirectory, projectRoot, logger, lock, reconcileOptions);
|
|
483
|
+
return reconcileWholeFile(file, itemKey, itemDirectory, projectRoot, logger, lock, reconcileOptions, useUmbrella);
|
|
407
484
|
};
|
|
408
485
|
const reconcileItems = (items, cwd, logger, reconcileOptions = {}) => {
|
|
409
486
|
const written = [];
|
|
@@ -411,15 +488,16 @@ const reconcileItems = (items, cwd, logger, reconcileOptions = {}) => {
|
|
|
411
488
|
const depsAdded = [];
|
|
412
489
|
const bindingsApplied = [];
|
|
413
490
|
const lock = readLock(cwd);
|
|
491
|
+
const useUmbrella = projectUsesUmbrella(cwd);
|
|
414
492
|
for (const { directory, manifest } of items) {
|
|
415
493
|
for (const file of manifest.files) {
|
|
416
|
-
const outcome = reconcileFile(file, manifest.name, directory, cwd, logger, lock, reconcileOptions);
|
|
494
|
+
const outcome = reconcileFile(file, manifest.name, directory, cwd, logger, lock, reconcileOptions, useUmbrella);
|
|
417
495
|
(outcome.kind === "written" ? written : skipped).push(outcome.path);
|
|
418
496
|
}
|
|
419
497
|
if (reconcileOptions.diff) {
|
|
420
498
|
continue;
|
|
421
499
|
}
|
|
422
|
-
const applied = applyItemResources(manifest, cwd, logger);
|
|
500
|
+
const applied = applyItemResources(manifest, cwd, logger, useUmbrella);
|
|
423
501
|
depsAdded.push(...applied.deps);
|
|
424
502
|
bindingsApplied.push(...applied.bindings);
|
|
425
503
|
}
|
|
@@ -538,6 +616,26 @@ const resolvePlan = async (names, options) => {
|
|
|
538
616
|
const emptyResult = () => {
|
|
539
617
|
return { bindings: [], code: 0, deps: [], skipped: [], written: [] };
|
|
540
618
|
};
|
|
619
|
+
const setBindingField = (manifest, section, match, field, fieldValue) => {
|
|
620
|
+
if (!manifest.bindings) {
|
|
621
|
+
return manifest;
|
|
622
|
+
}
|
|
623
|
+
return {
|
|
624
|
+
...manifest,
|
|
625
|
+
bindings: manifest.bindings.map((binding) => {
|
|
626
|
+
if (binding.path[0] !== section || !Array.isArray(binding.value)) {
|
|
627
|
+
return binding;
|
|
628
|
+
}
|
|
629
|
+
const entries = binding.value;
|
|
630
|
+
return {
|
|
631
|
+
...binding,
|
|
632
|
+
value: entries.map(
|
|
633
|
+
(entry) => typeof entry === "object" && entry !== null && entry[match.key] === match.value ? { ...entry, [field]: fieldValue } : entry
|
|
634
|
+
)
|
|
635
|
+
};
|
|
636
|
+
})
|
|
637
|
+
};
|
|
638
|
+
};
|
|
541
639
|
|
|
542
640
|
const printPlan = (logger, manifest) => {
|
|
543
641
|
const label = manifest.title ?? manifest.description;
|
|
@@ -643,8 +741,12 @@ const runAddCommand = async (options) => {
|
|
|
643
741
|
}
|
|
644
742
|
let cleanups = [];
|
|
645
743
|
try {
|
|
646
|
-
const { cleanups: planCleanups, items } = await resolvePlan(options.names, options);
|
|
744
|
+
const { cleanups: planCleanups, items: resolvedItems } = await resolvePlan(options.names, options);
|
|
647
745
|
cleanups = planCleanups;
|
|
746
|
+
const { transformManifest } = options;
|
|
747
|
+
const items = transformManifest ? resolvedItems.map((item) => {
|
|
748
|
+
return { ...item, manifest: transformManifest(item.manifest) };
|
|
749
|
+
}) : resolvedItems;
|
|
648
750
|
for (const { manifest } of items) {
|
|
649
751
|
printPlan(options.logger, manifest);
|
|
650
752
|
}
|
|
@@ -740,4 +842,4 @@ const runBuildIndexCommand = async (options) => {
|
|
|
740
842
|
return empty;
|
|
741
843
|
};
|
|
742
844
|
|
|
743
|
-
export { runBuildIndexCommand as a, runRegistryViewCommand as b,
|
|
845
|
+
export { runBuildIndexCommand as a, runRegistryViewCommand as b, resolveTagVersions as c, resolveSourceRef as d, resolveDistTag as e, runListCommand as f, runAddCommand as r, setBindingField as s };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { STEP_BADGE_NAMES, LunoraReporter } from '@lunora/config';
|
|
1
2
|
import { JsonReporter } from '@visulima/pail/reporter/json';
|
|
2
|
-
import { PrettyReporter } from '@visulima/pail/reporter/pretty';
|
|
3
3
|
import { createPail } from '@visulima/pail/server';
|
|
4
4
|
|
|
5
5
|
const wantJson = () => {
|
|
@@ -7,16 +7,18 @@ const wantJson = () => {
|
|
|
7
7
|
return flag === "1" || flag === "true";
|
|
8
8
|
};
|
|
9
9
|
const buildReporter = () => {
|
|
10
|
-
const Reporter = wantJson() ? JsonReporter :
|
|
10
|
+
const Reporter = wantJson() ? JsonReporter : LunoraReporter;
|
|
11
11
|
return new Reporter();
|
|
12
12
|
};
|
|
13
|
+
const STEP_LOG_TYPES = Object.fromEntries(STEP_BADGE_NAMES.map((name) => [name, { label: name, logLevel: "informational" }]));
|
|
13
14
|
let sharedPail;
|
|
14
15
|
const getPail = () => {
|
|
15
16
|
sharedPail ??= createPail({
|
|
16
17
|
reporters: [buildReporter()],
|
|
17
18
|
scope: ["lunora"],
|
|
18
19
|
stderr: process.stderr,
|
|
19
|
-
stdout: process.stdout
|
|
20
|
+
stdout: process.stdout,
|
|
21
|
+
types: STEP_LOG_TYPES
|
|
20
22
|
});
|
|
21
23
|
return sharedPail;
|
|
22
24
|
};
|
|
@@ -69,5 +71,8 @@ const pail = /* @__PURE__ */ new Proxy({}, {
|
|
|
69
71
|
return typeof value === "function" ? value.bind(instance) : value;
|
|
70
72
|
}
|
|
71
73
|
});
|
|
74
|
+
const logStep = (type, message) => {
|
|
75
|
+
getPail()[type](message);
|
|
76
|
+
};
|
|
72
77
|
|
|
73
|
-
export { createLogger, createStderrLogger, getPail, pail };
|
|
78
|
+
export { createLogger, createStderrLogger, getPail, logStep, pail };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { dirname, join } from '@visulima/path';
|
|
4
|
+
|
|
5
|
+
const FALLBACK = "pnpm";
|
|
6
|
+
const KNOWN_MANAGERS = ["pnpm", "yarn", "npm", "bun"];
|
|
7
|
+
const INSTALL_PREFERENCE = ["pnpm", "bun", "yarn", "npm"];
|
|
8
|
+
const isManagerInstalled = (manager) => {
|
|
9
|
+
try {
|
|
10
|
+
return spawnSync(manager, ["--version"], { stdio: "ignore", timeout: 5e3 }).status === 0;
|
|
11
|
+
} catch {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
const detectInstalledManagers = (probe = isManagerInstalled) => INSTALL_PREFERENCE.filter((manager) => probe(manager));
|
|
16
|
+
const installArgsFor = (manager) => {
|
|
17
|
+
return { args: ["install"], command: manager };
|
|
18
|
+
};
|
|
19
|
+
const parseDeclaredManager = (declared) => {
|
|
20
|
+
if (typeof declared !== "string") {
|
|
21
|
+
return void 0;
|
|
22
|
+
}
|
|
23
|
+
return KNOWN_MANAGERS.find((manager) => declared.startsWith(`${manager}@`));
|
|
24
|
+
};
|
|
25
|
+
const readDeclaredManager = (directory) => {
|
|
26
|
+
const candidate = join(directory, "package.json");
|
|
27
|
+
if (!existsSync(candidate)) {
|
|
28
|
+
return void 0;
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
const parsed = JSON.parse(readFileSync(candidate, "utf8"));
|
|
32
|
+
return parseDeclaredManager(parsed.packageManager);
|
|
33
|
+
} catch {
|
|
34
|
+
return void 0;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
const detectPackageManager = (startDirectory) => {
|
|
38
|
+
let directory = startDirectory;
|
|
39
|
+
while (directory && directory !== dirname(directory)) {
|
|
40
|
+
const declared = readDeclaredManager(directory);
|
|
41
|
+
if (declared !== void 0) {
|
|
42
|
+
return declared;
|
|
43
|
+
}
|
|
44
|
+
directory = dirname(directory);
|
|
45
|
+
}
|
|
46
|
+
return FALLBACK;
|
|
47
|
+
};
|
|
48
|
+
const execArgsFor = (manager, command, args) => {
|
|
49
|
+
if (manager === "yarn") {
|
|
50
|
+
return { args: [command, ...args], command: "yarn" };
|
|
51
|
+
}
|
|
52
|
+
if (manager === "bun") {
|
|
53
|
+
return { args: ["x", command, ...args], command: "bun" };
|
|
54
|
+
}
|
|
55
|
+
if (manager === "npm") {
|
|
56
|
+
return { args: ["--", command, ...args], command: "npx" };
|
|
57
|
+
}
|
|
58
|
+
return { args: ["exec", command, ...args], command: "pnpm" };
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export { detectInstalledManagers as a, detectPackageManager as d, execArgsFor as e, installArgsFor as i };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { quoteIdentifier,
|
|
1
|
+
import { quoteIdentifier, sqlAffinityForKind, frameworkColumnDdl, columnRef, physicalIndexName } from '@lunora/d1/dialect';
|
|
2
2
|
|
|
3
3
|
const validatorKindToSqlType = (kind) => sqlAffinityForKind(kind);
|
|
4
4
|
const renderColumnDefinition = (name, column) => {
|