@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.
Files changed (52) hide show
  1. package/__assets__/package-og.svg +1 -1
  2. package/dist/bin.mjs +1 -1
  3. package/dist/index.d.mts +243 -114
  4. package/dist/index.d.ts +243 -114
  5. package/dist/index.mjs +7 -7
  6. package/dist/packem_chunks/handler.mjs +89 -7
  7. package/dist/packem_chunks/handler10.mjs +8 -14
  8. package/dist/packem_chunks/handler11.mjs +19 -189
  9. package/dist/packem_chunks/handler12.mjs +176 -115
  10. package/dist/packem_chunks/handler13.mjs +118 -52
  11. package/dist/packem_chunks/handler14.mjs +50 -43
  12. package/dist/packem_chunks/handler15.mjs +46 -67
  13. package/dist/packem_chunks/handler16.mjs +73 -37
  14. package/dist/packem_chunks/handler17.mjs +38 -100
  15. package/dist/packem_chunks/handler18.mjs +87 -154
  16. package/dist/packem_chunks/handler19.mjs +148 -67
  17. package/dist/packem_chunks/handler2.mjs +2 -2
  18. package/dist/packem_chunks/handler20.mjs +71 -76
  19. package/dist/packem_chunks/handler21.mjs +71 -288
  20. package/dist/packem_chunks/handler3.mjs +1 -1
  21. package/dist/packem_chunks/handler4.mjs +1 -1
  22. package/dist/packem_chunks/handler5.mjs +2 -2
  23. package/dist/packem_chunks/handler6.mjs +2 -2
  24. package/dist/packem_chunks/handler7.mjs +1 -1
  25. package/dist/packem_chunks/handler8.mjs +1 -1
  26. package/dist/packem_chunks/handler9.mjs +311 -12
  27. package/dist/packem_chunks/planDevCommand.mjs +46 -49
  28. package/dist/packem_chunks/runCodegenCommand.mjs +2 -2
  29. package/dist/packem_chunks/runDeployCommand.mjs +105 -15
  30. package/dist/packem_chunks/runInitCommand.mjs +1980 -77
  31. package/dist/packem_chunks/runMigrateGenerateCommand.mjs +5 -5
  32. package/dist/packem_chunks/runResetCommand.mjs +4 -4
  33. package/dist/packem_chunks/runRpcCommand.mjs +1 -1
  34. package/dist/packem_shared/{COMMANDS-CHw4zOZ9.mjs → COMMANDS-B0ftFD_3.mjs} +47 -21
  35. package/dist/packem_shared/{command-BDXcJCCJ.mjs → command-D3lB_4Az.mjs} +6 -1
  36. package/dist/packem_shared/{commands-DIQ3nf0C.mjs → commands-B-gR09Z_.mjs} +124 -22
  37. package/dist/packem_shared/{createLogger-CHPNjFw2.mjs → createLogger-B40gPzQo.mjs} +9 -4
  38. package/dist/packem_shared/detect-package-manager-DYp7n3mJ.mjs +61 -0
  39. package/dist/packem_shared/{diffSnapshots-RR2ZE8Ya.mjs → diffSnapshots-BeDvvNiF.mjs} +1 -1
  40. package/dist/packem_shared/{insertSchemaExtension-BuzF6-t2.mjs → insertSchemaExtension-DAqbfr9Z.mjs} +15 -10
  41. package/dist/packem_shared/{output-format-7gyGR3h8.mjs → output-format-wUvAN6AL.mjs} +1 -1
  42. package/dist/packem_shared/prompt-cancelled-APzX1Im-.mjs +9 -0
  43. package/dist/packem_shared/runAddCommand-bnY6-HKb.mjs +4 -0
  44. package/dist/packem_shared/{schemaIrToSnapshot-aBTo7TM5.mjs → schemaIrToSnapshot-DdsljJT-.mjs} +1 -1
  45. package/dist/packem_shared/storage-BIsph-Vk.mjs +84 -0
  46. package/dist/packem_shared/tui-prompts-BjEN8XgP.mjs +658 -0
  47. package/dist/packem_shared/wrangler-secrets-P2_ZUR-k.mjs +47 -0
  48. package/package.json +12 -11
  49. package/skills/lunora-setup-storage/SKILL.md +7 -3
  50. package/dist/packem_shared/features-ocSSpZtS.mjs +0 -24
  51. package/dist/packem_shared/runAddCommand-3I3JFZUG.mjs +0 -4
  52. /package/dist/packem_shared/{defaultSpawner-DxI3mebw.mjs → createRecordingSpawner-DxI3mebw.mjs} +0 -0
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { CodegenOptions, SchemaIR } from '@lunora/codegen';
2
2
  import '@visulima/cerebro';
3
- import { ensureDevVariables, ensureDevVarsExample, materializeRemoteWranglerConfig } from '@lunora/config';
3
+ import { ensureDevVariables, ensureDevVarsExample, fillDevSecrets, materializeRemoteWranglerConfig } from '@lunora/config';
4
4
  export { REQUIRED_COMPATIBILITY_DATE, REQUIRED_FLAG, type WranglerProjectValidationOptions as WranglerValidationOptions, type WranglerValidationReport, type WranglerProjectValidationResult as WranglerValidationResult, validateWranglerProject as validateWrangler, validateWranglerConfig } from '@lunora/config';
5
5
  /** Every command name the CLI registers (drives the `CommandName` type + tests). */
6
6
  declare const COMMANDS: readonly ["init", "add", "dev", "codegen", "build", "deploy", "containers", "prepare", "link", "deployments", "logs", "run", "insights", "reset", "migrate", "export", "import", "seed", "backup", "verify", "info", "doctor", "env", "analyze", "view", "docs", "registry", "rules"];
@@ -64,6 +64,12 @@ declare const createLogger: () => Logger;
64
64
  * the package barrel) stays side-effect-free.
65
65
  */
66
66
  declare const pail: PailLogger;
67
+ /**
68
+ * Emit a badged step line through the shared pail (the `init` flow's off-TTY
69
+ * fallback for the create-astro-style transcript). The `message` may contain
70
+ * newlines — `LunoraReporter` indents continuation lines under the badge so a
71
+ * dimmed answer sits below its question.
72
+ */
67
73
  interface CodegenCommandOptions {
68
74
  /** Which API spec(s) to emit. Defaults to codegen's `"openapi"` when omitted. */
69
75
  apiSpec?: ApiSpec;
@@ -220,6 +226,35 @@ declare const createRecordingSpawner: (exitCode?: number) => {
220
226
  calls: RecordedSpawn[];
221
227
  spawner: Spawner;
222
228
  };
229
+ interface SecretListRunnerResult {
230
+ code: number;
231
+ stderr: string;
232
+ stdout: string;
233
+ }
234
+ /** Runs an argv and resolves its captured output. Injected in tests. */
235
+ type SecretListRunner = (command: string, args: ReadonlyArray<string>, cwd: string) => Promise<SecretListRunnerResult>;
236
+ interface ListRemoteSecretsInputs {
237
+ cwd: string;
238
+ /** Cloudflare environment name (`--env`). */
239
+ env?: string;
240
+ /** Injected command runner; defaults to a real `wrangler secret list`. */
241
+ runner?: SecretListRunner;
242
+ /** Target a temporary-account deployment (`--temporary`). */
243
+ temporary?: boolean;
244
+ }
245
+ interface ListRemoteSecretsResult {
246
+ /** Diagnostic message when `ok` is false. */
247
+ error?: string;
248
+ /** Remote secret names (sorted), empty when none or on failure. */
249
+ names: ReadonlyArray<string>;
250
+ /** False when wrangler failed or its output could not be parsed. */
251
+ ok: boolean;
252
+ }
253
+ /**
254
+ * Parse `wrangler secret list --format json` output into a sorted name list.
255
+ * The payload is an array of `{ name, type }`; anything else yields `undefined`
256
+ * so the caller can report a parse failure rather than silently returning [].
257
+ */
223
258
  type FetchLike = (input: string, init?: {
224
259
  body?: string;
225
260
  headers?: Record<string, string>;
@@ -312,6 +347,10 @@ interface DeployCommandOptions {
312
347
  preview?: boolean;
313
348
  /** Railpack-availability probe injected in tests. Defaults to a real `railpack --version` + `BUILDKIT_HOST` check. */
314
349
  railpackAvailable?: DockerProbe;
350
+ /** Confirm prompt for the missing-secret offer; injected in tests. Defaults to the TTY prompt. */
351
+ secretConfirm?: (message: string) => Promise<boolean>;
352
+ /** Remote-secret lister for the missing-secret offer; injected in tests. Defaults to `wrangler secret list`. */
353
+ secretLister?: (inputs: ListRemoteSecretsInputs) => Promise<ListRemoteSecretsResult>;
315
354
  skipCodegen?: boolean;
316
355
  spawner?: Spawner;
317
356
  /**
@@ -423,6 +462,8 @@ interface DevCommandOptions {
423
462
  ensureEnv?: typeof ensureDevVariables;
424
463
  /** Injection seam for tests — defaults to the real `.dev.vars.example` package-aware scaffolder. */
425
464
  ensureExample?: typeof ensureDevVarsExample;
465
+ /** Injection seam for tests — defaults to the real empty-secret/admin-token filler. */
466
+ fillSecrets?: typeof fillDevSecrets;
426
467
  logger: Logger;
427
468
  /** Injection seam for tests — defaults to the real remote-config materializer. */
428
469
  materializeRemote?: typeof materializeRemoteWranglerConfig;
@@ -496,15 +537,145 @@ declare const runDevCommand: (options: DevCommandOptions) => Promise<{
496
537
  /** `lunora dev` handler (lazy-loaded via the command's `loader`). */
497
538
  /** Supported CI providers. */
498
539
  type CiProvider = "github" | "gitlab";
540
+ type PackageManager = "pnpm" | "npm" | "yarn" | "bun";
541
+ /** True when `manager` is on PATH — probed by running `&lt;manager> --version`. Injectable for tests. */
542
+ type PackageManagerProbe = (manager: PackageManager) => boolean;
543
+ /**
544
+ * The package managers actually installed on this machine, in preference order
545
+ * ({@link INSTALL_PREFERENCE} — pnpm > bun > yarn > npm). The first entry is the
546
+ * recommended default for the install prompt; the whole list is what the user
547
+ * picks from. Empty when none are found.
548
+ */
499
549
  /** A registry item a feature can install. */
500
550
  type FeatureItem = "auth" | "auth-auth0" | "auth-clerk" | "mail";
501
551
  /** The auth-provider choices offered for `add auth` / the init auth prompt. Each value is a registry item name. */
502
-
503
- /** A feature offered in the post-scaffold multi-select. `value` is the stack-feature key, not (yet) a registry item. */
504
- type StackFeature = "auth" | "email";
552
+ /** A single file the item scaffolds into the project. */
553
+ interface RegistryFile {
554
+ /** Source path inside the item dir (e.g. `schema.ts`). */
555
+ from: string;
556
+ /** Merge strategy. `create-or-skip` writes whole files; `schema-extension` AST-merges schema.ts. */
557
+ merge: "create-or-skip" | "schema-extension";
558
+ /** Destination relative to the project root (e.g. `lunora/ratelimit/index.ts`). */
559
+ to: string;
560
+ }
561
+ /** A wrangler.jsonc binding addition. `path` is the jsonc key path; `value` the value to set. */
562
+ interface RegistryBinding {
563
+ path: ReadonlyArray<string>;
564
+ value: unknown;
565
+ }
566
+ /**
567
+ * An environment variable an item needs. Scaffolded into `.dev.vars` (Workers'
568
+ * local-secrets file) on add — non-secrets get their `value`; secrets get an
569
+ * empty placeholder and a reminder to run `wrangler secret put` for production.
570
+ */
571
+ interface RegistryEnvVariable {
572
+ /** Human note on what the variable is for. */
573
+ description?: string;
574
+ /** The variable name (e.g. `RESEND_API_KEY`). */
575
+ name: string;
576
+ /** Mark as a secret: never write a value, only a placeholder, and remind about prod. Defaults to `true` when no `value` is given. */
577
+ secret?: boolean;
578
+ /** A default/example value for non-secret vars. */
579
+ value?: string;
580
+ }
581
+ /** The `registry.json` manifest shape. */
582
+ interface RegistryManifest {
583
+ /** wrangler.jsonc additions (best-effort structural edits). */
584
+ bindings?: ReadonlyArray<RegistryBinding>;
585
+ /** npm deps to add to the project package.json (name → version range). */
586
+ deps?: Readonly<Record<string, string>>;
587
+ description?: string;
588
+ /** npm devDependencies to add to the project package.json. */
589
+ devDependencies?: Readonly<Record<string, string>>;
590
+ /** Post-install guidance printed after the item is added (per-item next steps). */
591
+ docs?: string;
592
+ /** Environment variables the item needs; scaffolded into `.dev.vars`. */
593
+ envVars?: ReadonlyArray<RegistryEnvVariable>;
594
+ files: ReadonlyArray<RegistryFile>;
595
+ name: string;
596
+ /** Other registry items this one depends on (resolved transitively, deps first). */
597
+ requires?: ReadonlyArray<string>;
598
+ /** Short human-readable label (distinct from the longer `description`). */
599
+ title?: string;
600
+ }
601
+ interface AddCommandOptions {
602
+ /** Bypass the `--source` safety gate (matches init). */
603
+ allowUnsafeSource?: boolean;
604
+ /** `registry build --check`: verify the index is current instead of rewriting it. */
605
+ check?: boolean;
606
+ /** Inject a confirmer for non-interactive callers / tests. */
607
+ confirm?: (prompt: string) => Promise<boolean>;
608
+ cwd?: string;
609
+ /** Preview the file-level changes (a content diff) and write nothing. */
610
+ diff?: boolean;
611
+ /** Print the plan and stop without writing anything. */
612
+ dryRun?: boolean;
613
+ /** Local registry root (offline / tests). Expects per-item subdirs, each with a `registry.json`. */
614
+ from?: string;
615
+ /** Emit a JSON snapshot of the plan/result. */
616
+ json?: boolean;
617
+ /** `--list`: enumerate available items instead of adding. */
618
+ list?: boolean;
619
+ logger: Logger;
620
+ /** Item names to add (positional args). */
621
+ names: ReadonlyArray<string>;
622
+ /** `registry build` output path for the generated catalog (defaults to the root's `index.json`). */
623
+ out?: string;
624
+ /** Force-overwrite existing files (take the incoming copy) instead of skipping/conflicting. */
625
+ overwrite?: boolean;
626
+ /** Override the git ref (branch, tag, or commit) items are fetched from (default: version-derived); appended to the `source` base when that is set. Ignored when `from` is set. */
627
+ ref?: string;
628
+ /** Override the remote registry source base (default gh:anolilab/lunora/registry). */
629
+ source?: string;
630
+ /**
631
+ * Customize each resolved manifest after it is loaded but before the plan is
632
+ * printed / reconciled — used to inject user-chosen values into otherwise
633
+ * static manifests (e.g. the R2 `bucket_name` the init storage prompt asks
634
+ * for). Applied to every item; return the manifest unchanged to leave it as-is.
635
+ */
636
+ transformManifest?: (manifest: RegistryManifest) => RegistryManifest;
637
+ /** Skip the package.json mutation confirmation prompt. */
638
+ yes?: boolean;
639
+ }
640
+ interface AddCommandResult {
641
+ /** Bindings written to wrangler.jsonc. */
642
+ bindings: ReadonlyArray<string>;
643
+ code: number;
644
+ /** Deps added to package.json. */
645
+ deps: ReadonlyArray<string>;
646
+ /** Files skipped because they already existed. */
647
+ skipped: ReadonlyArray<string>;
648
+ /** Files written (absolute paths). */
649
+ written: ReadonlyArray<string>;
650
+ }
651
+ /** One resolved item: its parsed manifest plus the (possibly staged) directory it lives in. */
652
+ /**
653
+ * A feature offered in the post-scaffold multi-select. `auth`/`email` carry a
654
+ * sub-prompt or alias; every other value IS the registry item name applied
655
+ * directly (`storage` → the `storage` registry item, etc.).
656
+ */
657
+ type StackFeature = "auth" | "backup" | "crons" | "email" | "presence" | "ratelimit" | "storage";
658
+ /** Customize a resolved manifest before it is written (e.g. inject the chosen R2 bucket name). */
659
+ type OfferTransformManifest = (manifest: RegistryManifest) => RegistryManifest;
660
+ /**
661
+ * One feature ready to apply: the registry item name(s), an optional manifest
662
+ * transform, and a short `label` (the feature value) shown on the combined
663
+ * progress line. Built up-front by the collectors so every prompt is answered
664
+ * before any apply runs.
665
+ */
666
+ interface FeatureApply {
667
+ label: string;
668
+ names: ReadonlyArray<string>;
669
+ transformManifest?: OfferTransformManifest;
670
+ }
505
671
  interface OfferDeps {
506
- /** Apply one or more registry items into the new project; resolves `true` on success. */
507
- apply: (names: ReadonlyArray<FeatureItem>) => Promise<boolean>;
672
+ /**
673
+ * Apply the collected features into the new project in one batch — resolves
674
+ * `true` when every item succeeds. The CLI renders this as a single progress
675
+ * line whose label changes per feature; each plan's `transformManifest`
676
+ * customizes that item's manifest before it is written.
677
+ */
678
+ applyAll: (plans: ReadonlyArray<FeatureApply>) => Promise<boolean>;
508
679
  /** When `false`, skip all prompts and print the later-setup hint. */
509
680
  interactive: boolean;
510
681
  logger: Logger;
@@ -516,6 +687,14 @@ interface OfferDeps {
516
687
  }>, settings?: {
517
688
  defaults?: ReadonlyArray<StackFeature>;
518
689
  }) => Promise<StackFeature[]>;
690
+ /**
691
+ * Features chosen non-interactively (the `--add` flag). When set, the
692
+ * multi-select and every sub-prompt are skipped — each feature is applied with
693
+ * its shipped defaults (base registry item, placeholder bindings).
694
+ */
695
+ preselected?: ReadonlyArray<StackFeature>;
696
+ /** The new project's name — seeds smart defaults like the `project-uploads` bucket name. */
697
+ projectName: string;
519
698
  /** Single-select among the auth providers (TTY-backed in production). */
520
699
  select: (message: string, options: ReadonlyArray<{
521
700
  description?: string;
@@ -524,16 +703,32 @@ interface OfferDeps {
524
703
  }>, settings?: {
525
704
  default?: FeatureItem;
526
705
  }) => Promise<FeatureItem | undefined>;
706
+ /** Single-line text input (TTY-backed in production) — used for the storage bucket-name prompt. */
707
+ text: (message: string, settings?: {
708
+ default?: string;
709
+ placeholder?: string;
710
+ }) => Promise<string>;
527
711
  }
528
712
  /**
529
- * Offer the stack features (authentication, transactional email) in ONE
530
- * multi-select after a successful scaffold. When auth is picked, a follow-up
531
- * single-select chooses the provider (email+password / Clerk / Auth0); email
532
- * maps to the `mail` item. Picked items are applied in selection order.
533
- * Non-interactive: prints how to add them later and changes nothing.
713
+ * Offer the stack features (auth, email, storage, rate limiting, crons,
714
+ * presence, backups) in ONE multi-select after a successful scaffold. Auth,
715
+ * email, and storage run a follow-up prompt (provider / destination / bucket
716
+ * name); every other feature value is applied as its registry item directly.
717
+ *
718
+ * Every question is asked FIRST (in selection order), then the picked features
719
+ * are applied together via {@link OfferDeps.applyAll} — the CLI renders that as a
720
+ * single progress line whose label changes per feature, instead of one spinner
721
+ * per item. Non-interactive: prints how to add them later and changes nothing.
534
722
  */
535
- type Template = "astro" | "next" | "nuxt" | "standalone" | "sveltekit" | "tanstack-start-react" | "tanstack-start-solid" | "vite";
723
+ type Template = "analog" | "astro" | "next" | "nuxt" | "react-router" | "standalone" | "sveltekit" | "tanstack-start-react" | "tanstack-start-solid";
536
724
  interface InitCommandOptions {
725
+ /**
726
+ * Add features non-interactively after scaffolding (the `--add` flag): a
727
+ * comma-separated list of `auth | email | storage | ratelimit | crons |
728
+ * presence | backup`. Bypasses the interactive multi-select and sub-prompts —
729
+ * each named feature is applied with its shipped defaults.
730
+ */
731
+ add?: string;
537
732
  /**
538
733
  * When true, accept `--source` values that don't start with `gh:` /
539
734
  * `github:` / `https://` or that contain `..`. Defaults to false; the CLI
@@ -545,6 +740,12 @@ interface InitCommandOptions {
545
740
  ci?: CiProvider;
546
741
  cwd?: string;
547
742
  /**
743
+ * Walk the whole flow — prompts, task list, next-steps, mascot — but make no
744
+ * changes: skip the template fetch/copy, the feature applies, the dependency
745
+ * install, and `git init`. Each skipped action logs a `would …` line instead.
746
+ */
747
+ dryRun?: boolean;
748
+ /**
548
749
  * Local directory containing the template subdirs (e.g. `vite/`,
549
750
  * `standalone/`). When provided, skips the network fetch entirely.
550
751
  * Useful for offline runs, the clean-machine smoke test, and unit tests.
@@ -559,6 +760,15 @@ interface InitCommandOptions {
559
760
  */
560
761
  inPlace?: boolean;
561
762
  /**
763
+ * Inject the post-scaffold install offer's prompts (tests). When set, the
764
+ * offer runs regardless of TTY: `confirmInstall` drives the yes/no, and
765
+ * `selectManager` picks among the detected managers.
766
+ */
767
+ installPrompt?: {
768
+ confirmInstall: () => Promise<boolean>;
769
+ selectManager: (managers: ReadonlyArray<PackageManager>) => Promise<PackageManager>;
770
+ };
771
+ /**
562
772
  * Force the post-scaffold "add auth / email?" offer on (the `--interactive`
563
773
  * flag). When omitted, the offer runs only when stdin is a TTY. `--yes`
564
774
  * suppresses it regardless. Has no effect once {@link prompt} is injected.
@@ -567,11 +777,19 @@ interface InitCommandOptions {
567
777
  logger: Logger;
568
778
  name?: string;
569
779
  /**
780
+ * Local directory holding create-vite bases (one `template-&lt;id>/` subdir per
781
+ * framework). When set with `vite`, the overlay copies the base from disk
782
+ * instead of fetching `create-vite` over the network — offline mode + tests.
783
+ */
784
+ overlayBaseFrom?: string;
785
+ /** Probe for which package managers are installed (tests). Defaults to a real `&lt;pm> --version` check. */
786
+ packageManagerProbe?: PackageManagerProbe;
787
+ /**
570
788
  * Inject the offer's prompts (tests). When set, the offer is treated as
571
- * interactive regardless of TTY, and these drive the feature multi-select
572
- * and the auth-provider sub-select.
789
+ * interactive regardless of TTY, and these drive the feature multi-select,
790
+ * the auth-provider sub-select, and the storage bucket-name text input.
573
791
  */
574
- prompt?: Pick<OfferDeps, "multiSelect" | "select">;
792
+ prompt?: Pick<OfferDeps, "multiSelect" | "select" | "text">;
575
793
  /**
576
794
  * Override the git ref (branch, tag, or commit) the default template source
577
795
  * is fetched from. Takes precedence over the version-derived ref. Ignored
@@ -590,7 +808,16 @@ interface InitCommandOptions {
590
808
  * instead to skip the network.
591
809
  */
592
810
  source?: string;
811
+ /** Spawner for the post-scaffold dependency install (tests inject a recording stub). Defaults to a real subprocess. */
812
+ spawner?: Spawner;
593
813
  templateType?: Template;
814
+ /**
815
+ * Scaffold via the **create-vite overlay** for this framework (`react`,
816
+ * `vue`, `solid`, `svelte`, `vanilla`) instead of a bespoke template: fetch
817
+ * the official create-vite base and apply the Lunora layer on top. Takes
818
+ * precedence over `templateType`.
819
+ */
820
+ vite?: string;
594
821
  /** Suppress the offer entirely (the `--yes` flag): scaffold only, print the later-setup hint. */
595
822
  yes?: boolean;
596
823
  }
@@ -599,13 +826,8 @@ interface InitCommandResult {
599
826
  files: ReadonlyArray<string>;
600
827
  target: string;
601
828
  }
602
- /**
603
- * `lunora init` entry: scaffold (in-place or a new directory), then — on success
604
- * — offer to add auth + email via the registry. The offer never affects the
605
- * scaffold's exit code.
606
- */
607
829
  declare const runInitCommand: (options: InitCommandOptions) => Promise<InitCommandResult>;
608
- /** Narrow a raw `--template` value to a known {@link Template} (defaults to vite). */
830
+ /** Narrow a raw `--template` value to a known {@link Template}. */
609
831
  interface MigrateGenerateCommandOptions {
610
832
  cwd?: string;
611
833
  logger: Logger;
@@ -641,99 +863,6 @@ interface IndexItem extends CatalogItem {
641
863
  declare const buildRegistryIndex: (root: string) => {
642
864
  items: IndexItem[];
643
865
  };
644
- /** A single file the item scaffolds into the project. */
645
- interface RegistryFile {
646
- /** Source path inside the item dir (e.g. `schema.ts`). */
647
- from: string;
648
- /** Merge strategy. `create-or-skip` writes whole files; `schema-extension` AST-merges schema.ts. */
649
- merge: "create-or-skip" | "schema-extension";
650
- /** Destination relative to the project root (e.g. `lunora/ratelimit/index.ts`). */
651
- to: string;
652
- }
653
- /** A wrangler.jsonc binding addition. `path` is the jsonc key path; `value` the value to set. */
654
- interface RegistryBinding {
655
- path: ReadonlyArray<string>;
656
- value: unknown;
657
- }
658
- /**
659
- * An environment variable an item needs. Scaffolded into `.dev.vars` (Workers'
660
- * local-secrets file) on add — non-secrets get their `value`; secrets get an
661
- * empty placeholder and a reminder to run `wrangler secret put` for production.
662
- */
663
- interface RegistryEnvVariable {
664
- /** Human note on what the variable is for. */
665
- description?: string;
666
- /** The variable name (e.g. `RESEND_API_KEY`). */
667
- name: string;
668
- /** Mark as a secret: never write a value, only a placeholder, and remind about prod. Defaults to `true` when no `value` is given. */
669
- secret?: boolean;
670
- /** A default/example value for non-secret vars. */
671
- value?: string;
672
- }
673
- /** The `registry.json` manifest shape. */
674
- interface RegistryManifest {
675
- /** wrangler.jsonc additions (best-effort structural edits). */
676
- bindings?: ReadonlyArray<RegistryBinding>;
677
- /** npm deps to add to the project package.json (name → version range). */
678
- deps?: Readonly<Record<string, string>>;
679
- description?: string;
680
- /** npm devDependencies to add to the project package.json. */
681
- devDependencies?: Readonly<Record<string, string>>;
682
- /** Post-install guidance printed after the item is added (per-item next steps). */
683
- docs?: string;
684
- /** Environment variables the item needs; scaffolded into `.dev.vars`. */
685
- envVars?: ReadonlyArray<RegistryEnvVariable>;
686
- files: ReadonlyArray<RegistryFile>;
687
- name: string;
688
- /** Other registry items this one depends on (resolved transitively, deps first). */
689
- requires?: ReadonlyArray<string>;
690
- /** Short human-readable label (distinct from the longer `description`). */
691
- title?: string;
692
- }
693
- interface AddCommandOptions {
694
- /** Bypass the `--source` safety gate (matches init). */
695
- allowUnsafeSource?: boolean;
696
- /** `registry build --check`: verify the index is current instead of rewriting it. */
697
- check?: boolean;
698
- /** Inject a confirmer for non-interactive callers / tests. */
699
- confirm?: (prompt: string) => Promise<boolean>;
700
- cwd?: string;
701
- /** Preview the file-level changes (a content diff) and write nothing. */
702
- diff?: boolean;
703
- /** Print the plan and stop without writing anything. */
704
- dryRun?: boolean;
705
- /** Local registry root (offline / tests). Expects per-item subdirs, each with a `registry.json`. */
706
- from?: string;
707
- /** Emit a JSON snapshot of the plan/result. */
708
- json?: boolean;
709
- /** `--list`: enumerate available items instead of adding. */
710
- list?: boolean;
711
- logger: Logger;
712
- /** Item names to add (positional args). */
713
- names: ReadonlyArray<string>;
714
- /** `registry build` output path for the generated catalog (defaults to the root's `index.json`). */
715
- out?: string;
716
- /** Force-overwrite existing files (take the incoming copy) instead of skipping/conflicting. */
717
- overwrite?: boolean;
718
- /** Override the git ref (branch, tag, or commit) items are fetched from (default: version-derived); appended to the `source` base when that is set. Ignored when `from` is set. */
719
- ref?: string;
720
- /** Override the remote registry source base (default gh:anolilab/lunora/registry). */
721
- source?: string;
722
- /** Skip the package.json mutation confirmation prompt. */
723
- yes?: boolean;
724
- }
725
- interface AddCommandResult {
726
- /** Bindings written to wrangler.jsonc. */
727
- bindings: ReadonlyArray<string>;
728
- code: number;
729
- /** Deps added to package.json. */
730
- deps: ReadonlyArray<string>;
731
- /** Files skipped because they already existed. */
732
- skipped: ReadonlyArray<string>;
733
- /** Files written (absolute paths). */
734
- written: ReadonlyArray<string>;
735
- }
736
- /** One resolved item: its parsed manifest plus the (possibly staged) directory it lives in. */
737
866
  /** `lunora registry add` (one or more item names): scaffold items into the project. */
738
867
  declare const runAddCommand: (options: AddCommandOptions) => Promise<AddCommandResult>;
739
868
  /**
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export { COMMANDS, VERSION, runCli } from './packem_shared/COMMANDS-CHw4zOZ9.mjs';
1
+ export { COMMANDS, VERSION, runCli } from './packem_shared/COMMANDS-B0ftFD_3.mjs';
2
2
  export { runCodegenCommand } from './packem_chunks/runCodegenCommand.mjs';
3
3
  export { DEFAULT_IMPORT_BATCH_SIZE, runExportCommand, runImportCommand } from './packem_shared/DEFAULT_IMPORT_BATCH_SIZE-Ck-2bU08.mjs';
4
4
  export { runDeployCommand } from './packem_chunks/runDeployCommand.mjs';
@@ -8,12 +8,12 @@ export { runMigrateGenerateCommand } from './packem_chunks/runMigrateGenerateCom
8
8
  export { runResetCommand } from './packem_chunks/runResetCommand.mjs';
9
9
  export { runRpcCommand } from './packem_chunks/runRpcCommand.mjs';
10
10
  export { default as parseArgs } from './packem_shared/parseArgs-YXFuKdEk.mjs';
11
- export { insertSchemaExtension } from './packem_shared/insertSchemaExtension-BuzF6-t2.mjs';
12
- export { createLogger, pail } from './packem_shared/createLogger-CHPNjFw2.mjs';
13
- export { diffSnapshots, renderAddColumn, renderCreateIndex, renderCreateTable, renderDropIndex, renderDropTable, renderMigrationFile, validatorKindToSqlType } from './packem_shared/diffSnapshots-RR2ZE8Ya.mjs';
14
- export { default as schemaIrToSnapshot } from './packem_shared/schemaIrToSnapshot-aBTo7TM5.mjs';
15
- export { createRecordingSpawner, defaultSpawner } from './packem_shared/defaultSpawner-DxI3mebw.mjs';
11
+ export { insertSchemaExtension } from './packem_shared/insertSchemaExtension-DAqbfr9Z.mjs';
12
+ export { createLogger, pail } from './packem_shared/createLogger-B40gPzQo.mjs';
13
+ export { diffSnapshots, renderAddColumn, renderCreateIndex, renderCreateTable, renderDropIndex, renderDropTable, renderMigrationFile, validatorKindToSqlType } from './packem_shared/diffSnapshots-BeDvvNiF.mjs';
14
+ export { default as schemaIrToSnapshot } from './packem_shared/schemaIrToSnapshot-DdsljJT-.mjs';
15
+ export { createRecordingSpawner, defaultSpawner } from './packem_shared/createRecordingSpawner-DxI3mebw.mjs';
16
16
  export { default as parseManifest } from './packem_shared/parseManifest--vZf2FY1.mjs';
17
17
  export { REQUIRED_COMPATIBILITY_DATE, REQUIRED_FLAG, validateWranglerProject as validateWrangler, validateWranglerConfig } from '@lunora/config';
18
18
  export { buildRegistryIndex } from './packem_shared/buildRegistryIndex-BcYe607_.mjs';
19
- export { r as runAddCommand, a as runBuildIndexCommand, b as runRegistryViewCommand } from './packem_shared/commands-DIQ3nf0C.mjs';
19
+ export { r as runAddCommand, a as runBuildIndexCommand, b as runRegistryViewCommand } from './packem_shared/commands-B-gR09Z_.mjs';
@@ -1,9 +1,11 @@
1
1
  import { existsSync } from 'node:fs';
2
- import { findWranglerFile, promptSelect } from '@lunora/config';
3
- import { join } from '@visulima/path';
4
- import { d as defineHandler } from '../packem_shared/command-BDXcJCCJ.mjs';
5
- import { n as normalizeFeature, E as EMAIL_ITEM, D as DEFAULT_AUTH_ITEM, p as promptAuthProvider, A as AUTH_PROVIDER_OPTIONS } from '../packem_shared/features-ocSSpZtS.mjs';
6
- import { r as runAddCommand } from '../packem_shared/commands-DIQ3nf0C.mjs';
2
+ import { findWranglerFile } from '@lunora/config';
3
+ import { join, basename } from '@visulima/path';
4
+ import { d as defineHandler } from '../packem_shared/command-D3lB_4Az.mjs';
5
+ import { v as validateOutputFormat, i as isJsonFormat, p as printJson, l as loggerForFormat } from '../packem_shared/output-format-wUvAN6AL.mjs';
6
+ import { t as tuiText, a as tuiSelect } from '../packem_shared/tui-prompts-BjEN8XgP.mjs';
7
+ import { n as normalizeFeature, E as EMAIL_ITEM, s as sanitizeBucketName, d as deriveBucketName, p as promptBucketName, r as resolveTypedDestination, M as MAIL_DESTINATION_PROMPT, a as sanitizeDatabaseName, b as deriveDatabaseName, c as promptDatabaseName, D as DEFAULT_AUTH_ITEM, e as promptAuthProvider, A as AUTH_PROVIDER_OPTIONS, w as withStorageBucketName, f as withMailDestination, g as withAuthDatabaseName } from '../packem_shared/storage-BIsph-Vk.mjs';
8
+ import { r as runAddCommand } from '../packem_shared/commands-B-gR09Z_.mjs';
7
9
 
8
10
  const providerToItem = (provider) => {
9
11
  const value = provider.trim().toLowerCase();
@@ -24,9 +26,54 @@ const resolveAuthItem = async (options) => {
24
26
  if (options.yes === true) {
25
27
  return DEFAULT_AUTH_ITEM;
26
28
  }
27
- const select = options.promptSelect ?? ((message, choices, settings) => promptSelect(message, choices, settings));
29
+ const select = options.promptSelect ?? ((message, choices, settings) => tuiSelect(message, choices, settings));
28
30
  return promptAuthProvider(select);
29
31
  };
32
+ const textPrompt = (options) => options.promptText ?? ((message, settings) => tuiText(message, settings));
33
+ const resolveStorageBucketName = async (options) => {
34
+ const projectName = basename(options.cwd ?? process.cwd());
35
+ if (options.bucket !== void 0 && options.bucket !== "") {
36
+ const sanitized = sanitizeBucketName(options.bucket);
37
+ if (sanitized !== void 0) {
38
+ return sanitized;
39
+ }
40
+ const fallback = deriveBucketName(projectName);
41
+ options.logger.warn(`add: "${options.bucket}" isn't a valid R2 bucket name (lowercase alphanumeric + hyphens, 3–63 chars) — using "${fallback}".`);
42
+ return fallback;
43
+ }
44
+ if (options.yes === true) {
45
+ return deriveBucketName(projectName);
46
+ }
47
+ return promptBucketName(textPrompt(options), projectName);
48
+ };
49
+ const resolveMailDestination = async (options) => {
50
+ const warn = (message) => {
51
+ options.logger.warn(`add: ${message}`);
52
+ };
53
+ if (options.mailTo !== void 0 && options.mailTo !== "") {
54
+ return resolveTypedDestination(options.mailTo, warn);
55
+ }
56
+ if (options.yes === true) {
57
+ return void 0;
58
+ }
59
+ return resolveTypedDestination(await textPrompt(options)(MAIL_DESTINATION_PROMPT, { placeholder: "you@yourdomain.com" }), warn);
60
+ };
61
+ const resolveAuthDatabaseName = async (options) => {
62
+ const projectName = basename(options.cwd ?? process.cwd());
63
+ if (options.db !== void 0 && options.db !== "") {
64
+ const sanitized = sanitizeDatabaseName(options.db);
65
+ if (sanitized !== void 0) {
66
+ return sanitized;
67
+ }
68
+ const fallback = deriveDatabaseName(projectName);
69
+ options.logger.warn(`add: "${options.db}" isn't a usable D1 database name — using "${fallback}".`);
70
+ return fallback;
71
+ }
72
+ if (options.yes === true) {
73
+ return deriveDatabaseName(projectName);
74
+ }
75
+ return promptDatabaseName(textPrompt(options), projectName);
76
+ };
30
77
  const resolveFeatureItems = async (feature, options) => {
31
78
  if (feature.kind === "auth") {
32
79
  return [await resolveAuthItem(options)];
@@ -48,6 +95,28 @@ const runAddFeature = async (options) => {
48
95
  return { code: 1, items: [] };
49
96
  }
50
97
  const items = await resolveFeatureItems(feature, options);
98
+ const transforms = [];
99
+ if (items.includes("storage")) {
100
+ const bucketName = await resolveStorageBucketName(options);
101
+ transforms.push((manifest) => withStorageBucketName(manifest, bucketName));
102
+ }
103
+ if (items.includes("mail")) {
104
+ const destination = await resolveMailDestination(options);
105
+ if (destination !== void 0) {
106
+ transforms.push((manifest) => withMailDestination(manifest, destination));
107
+ }
108
+ }
109
+ if (items.some((name) => name === "auth" || name.startsWith("auth-"))) {
110
+ const databaseName = await resolveAuthDatabaseName(options);
111
+ transforms.push((manifest) => withAuthDatabaseName(manifest, databaseName));
112
+ }
113
+ const transformManifest = transforms.length > 0 ? (manifest) => {
114
+ let result2 = manifest;
115
+ for (const transform of transforms) {
116
+ result2 = transform(result2);
117
+ }
118
+ return result2;
119
+ } : void 0;
51
120
  const result = await runAddCommand({
52
121
  allowUnsafeSource: options.allowUnsafeSource,
53
122
  cwd,
@@ -56,22 +125,35 @@ const runAddFeature = async (options) => {
56
125
  names: [...items],
57
126
  ref: options.ref,
58
127
  source: options.source,
128
+ transformManifest,
59
129
  yes: true
60
130
  });
61
131
  return { code: result.code, items };
62
132
  };
63
133
  const execute = defineHandler(async ({ argument, cwd, logger, options }) => {
134
+ const formatError = validateOutputFormat("add", options.format);
135
+ if (formatError !== void 0) {
136
+ logger.error(formatError);
137
+ return { code: 1 };
138
+ }
139
+ const effectiveLogger = loggerForFormat(options.format, logger);
64
140
  const result = await runAddFeature({
65
141
  allowUnsafeSource: options.allowUnsafeSource === true,
142
+ bucket: options.bucket,
66
143
  cwd,
144
+ db: options.db,
67
145
  feature: argument[0],
68
146
  from: options.from,
69
- logger,
147
+ logger: effectiveLogger,
148
+ mailTo: options.mailTo,
70
149
  provider: options.provider,
71
150
  ref: options.ref,
72
151
  source: options.source,
73
152
  yes: options.yes === true
74
153
  });
154
+ if (isJsonFormat(options.format)) {
155
+ printJson({ code: result.code, items: result.items });
156
+ }
75
157
  return { code: result.code };
76
158
  });
77
159
 
@@ -1,22 +1,16 @@
1
- import { d as defineHandler } from '../packem_shared/command-BDXcJCCJ.mjs';
1
+ import { d as defineHandler } from '../packem_shared/command-D3lB_4Az.mjs';
2
2
  import { a as resolveProductionWorkerUrl } from '../packem_shared/resolve-target-qbsJ_5sF.mjs';
3
- import { runImportCommand } from '../packem_shared/DEFAULT_IMPORT_BATCH_SIZE-Ck-2bU08.mjs';
3
+ import { runExportCommand } from '../packem_shared/DEFAULT_IMPORT_BATCH_SIZE-Ck-2bU08.mjs';
4
4
 
5
- const execute = defineHandler(({ argument, cwd, logger, options }) => {
6
- const file = argument[0];
7
- if (!file) {
8
- logger.error("import requires a file. Usage: lunora import <path> [--table <name>]");
9
- return { code: 1 };
10
- }
11
- return runImportCommand({
12
- batchSize: options.batchSize,
13
- file,
5
+ const execute = defineHandler(
6
+ ({ argument, cwd, logger, options }) => runExportCommand({
14
7
  logger,
8
+ out: argument[0] ?? options.out,
15
9
  prod: options.prod === true,
16
- table: options.table,
10
+ tables: options.tables,
17
11
  token: options.token,
18
12
  url: resolveProductionWorkerUrl({ cwd, prod: options.prod === true, url: options.url })
19
- });
20
- });
13
+ })
14
+ );
21
15
 
22
16
  export { execute };