@lunora/cli 1.0.0-alpha.8 → 1.0.0-alpha.80

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 (59) hide show
  1. package/LICENSE.md +6 -0
  2. package/__assets__/package-og.svg +1 -1
  3. package/dist/bin.mjs +1 -1
  4. package/dist/index.d.mts +290 -117
  5. package/dist/index.d.ts +290 -117
  6. package/dist/index.mjs +8 -8
  7. package/dist/packem_chunks/handler.mjs +87 -6
  8. package/dist/packem_chunks/handler10.mjs +8 -14
  9. package/dist/packem_chunks/handler11.mjs +19 -189
  10. package/dist/packem_chunks/handler12.mjs +176 -115
  11. package/dist/packem_chunks/handler13.mjs +118 -52
  12. package/dist/packem_chunks/handler14.mjs +50 -43
  13. package/dist/packem_chunks/handler15.mjs +48 -67
  14. package/dist/packem_chunks/handler16.mjs +85 -37
  15. package/dist/packem_chunks/handler17.mjs +39 -100
  16. package/dist/packem_chunks/handler18.mjs +87 -152
  17. package/dist/packem_chunks/handler19.mjs +148 -67
  18. package/dist/packem_chunks/handler2.mjs +6 -4
  19. package/dist/packem_chunks/handler20.mjs +77 -75
  20. package/dist/packem_chunks/handler21.mjs +71 -288
  21. package/dist/packem_chunks/handler3.mjs +6 -3
  22. package/dist/packem_chunks/handler4.mjs +2 -2
  23. package/dist/packem_chunks/handler5.mjs +7 -4
  24. package/dist/packem_chunks/handler6.mjs +10 -7
  25. package/dist/packem_chunks/handler7.mjs +2 -2
  26. package/dist/packem_chunks/handler8.mjs +1 -1
  27. package/dist/packem_chunks/handler9.mjs +315 -12
  28. package/dist/packem_chunks/planDevCommand.mjs +758 -81
  29. package/dist/packem_chunks/runCodegenCommand.mjs +5 -4
  30. package/dist/packem_chunks/runDeployCommand.mjs +126 -20
  31. package/dist/packem_chunks/runInitCommand.mjs +1661 -179
  32. package/dist/packem_chunks/runMigrateGenerateCommand.mjs +5 -4
  33. package/dist/packem_chunks/runResetCommand.mjs +2 -2
  34. package/dist/packem_chunks/runRpcCommand.mjs +3 -2
  35. package/dist/packem_shared/{COMMANDS-Bn8luojF.mjs → COMMANDS-DW8lQmuW.mjs} +85 -23
  36. package/dist/packem_shared/{DEFAULT_IMPORT_BATCH_SIZE-Ck-2bU08.mjs → DEFAULT_IMPORT_BATCH_SIZE-CRQmmBZM.mjs} +3 -2
  37. package/dist/packem_shared/{api-spec-CtA6ilu4.mjs → api-spec-Bx0iKbxA.mjs} +3 -1
  38. package/dist/packem_shared/{buildRegistryIndex-BcYe607_.mjs → buildRegistryIndex-BS5ig822.mjs} +1 -1
  39. package/dist/packem_shared/codegen-error-DJG-ghs_.mjs +31 -0
  40. package/dist/packem_shared/{command-BDXcJCCJ.mjs → command-lYnl4QyF.mjs} +6 -1
  41. package/dist/packem_shared/{commands-DqsEzojt.mjs → commands-ClEvcz3V.mjs} +224 -18
  42. package/dist/packem_shared/{createLogger-CHPNjFw2.mjs → createLogger-CIWSHrTL.mjs} +40 -8
  43. package/dist/packem_shared/{createRecordingSpawner-DxI3mebw.mjs → createRecordingSpawner-Cw5Iu73G.mjs} +12 -2
  44. package/dist/packem_shared/{detect-package-manager-DYp7n3mJ.mjs → detect-package-manager-DvEthdCw.mjs} +26 -30
  45. package/dist/packem_shared/{insertSchemaExtension-BuzF6-t2.mjs → insertSchemaExtension-DAqbfr9Z.mjs} +15 -10
  46. package/dist/packem_shared/{open-url-Dfq6fAyT.mjs → open-url-4PBLY9X0.mjs} +3 -2
  47. package/dist/packem_shared/{output-format-7gyGR3h8.mjs → output-format-B4642rjE.mjs} +1 -1
  48. package/dist/packem_shared/{parseManifest--vZf2FY1.mjs → parseManifest-Dbp-Q2q3.mjs} +36 -9
  49. package/dist/packem_shared/prompt-cancelled-APzX1Im-.mjs +9 -0
  50. package/dist/packem_shared/runAddCommand-DGjFXGqa.mjs +4 -0
  51. package/dist/packem_shared/storage-DEXpJkXh.mjs +84 -0
  52. package/dist/packem_shared/tui-prompts-BjEN8XgP.mjs +658 -0
  53. package/dist/packem_shared/wrangler-secrets-Dq_Fkbm-.mjs +49 -0
  54. package/package.json +16 -13
  55. package/skills/lunora-quickstart/SKILL.md +25 -5
  56. package/skills/lunora-setup-storage/SKILL.md +7 -3
  57. package/dist/packem_shared/features-ocSSpZtS.mjs +0 -24
  58. package/dist/packem_shared/runAddCommand-G544_v6e.mjs +0 -4
  59. package/dist/packem_shared/tui-prompts-XHFxlOg5.mjs +0 -269
@@ -1,15 +1,16 @@
1
1
  import { existsSync, readFileSync, writeFileSync, mkdirSync, mkdtempSync, rmSync } from 'node:fs';
2
- import { dirname, join } from '@visulima/path';
2
+ import { dirname, join, relative } from '@visulima/path';
3
3
  import { DEV_VARS_FILE, parseDevVariableEntries } from '@lunora/config';
4
4
  import { modify, applyEdits, parse } from 'jsonc-parser';
5
5
  import { fileURLToPath } from 'node:url';
6
- import { a as tuiConfirm } from './tui-prompts-XHFxlOg5.mjs';
7
- import { collectCatalog, buildRegistryIndex } from './buildRegistryIndex-BcYe607_.mjs';
8
- import { insertSchemaExtension } from './insertSchemaExtension-BuzF6-t2.mjs';
6
+ import { LunoraError } from '@lunora/errors';
7
+ import { b as tuiConfirm } from './tui-prompts-BjEN8XgP.mjs';
8
+ import { collectCatalog, buildRegistryIndex } from './buildRegistryIndex-BS5ig822.mjs';
9
+ import { insertSchemaExtension } from './insertSchemaExtension-DAqbfr9Z.mjs';
9
10
  import { createHash } from 'node:crypto';
10
11
  import { tmpdir } from 'node:os';
11
12
  import { downloadTemplate } from 'giget';
12
- import parseManifest from './parseManifest--vZf2FY1.mjs';
13
+ import parseManifest from './parseManifest-Dbp-Q2q3.mjs';
13
14
 
14
15
  const DEFAULT_SOURCE_REF_FALLBACK = "alpha";
15
16
  const STABLE_BRANCH = "main";
@@ -54,17 +55,88 @@ const resolveVersionRef = (version) => {
54
55
  const resolveSourceRef = (ref) => {
55
56
  if (ref !== void 0 && ref.length > 0) {
56
57
  if (!isSafeRef(ref)) {
57
- throw new Error(`invalid --ref "${ref}" — a ref may contain letters, digits, ".", "_", "-", "/", "@" and must not contain "..".`);
58
+ throw new LunoraError("INTERNAL", `invalid --ref "${ref}" — a ref may contain letters, digits, ".", "_", "-", "/", "@" and must not contain "..".`);
58
59
  }
59
60
  return ref;
60
61
  }
61
62
  return resolveVersionRef(resolveCliVersion());
62
63
  };
64
+ const SOURCE_REPO = "anolilab/lunora";
65
+ const COMMIT_SHA = /^[0-9a-f]{40}$/iu;
66
+ const SEMVER_BODY = String.raw`\d+\.\d+\.\d+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)+)?(?:\+[0-9A-Za-z.-]+)?`;
67
+ const LEADING_VERSION_TAG = new RegExp(String.raw`^v?${SEMVER_BODY}$`, "u");
68
+ const PACKAGE_VERSION_TAG = new RegExp(String.raw`^(?:@[\w.-]+\/)?[\w.-]+@${SEMVER_BODY}$`, "u");
69
+ const isImmutableRef = (ref) => COMMIT_SHA.test(ref) || LEADING_VERSION_TAG.test(ref) || PACKAGE_VERSION_TAG.test(ref);
70
+ const githubAuthHeaders = () => {
71
+ const token = process.env["GITHUB_TOKEN"] ?? process.env["GH_TOKEN"];
72
+ return token !== void 0 && token.length > 0 ? { authorization: `Bearer ${token}` } : {};
73
+ };
74
+ const fetchBranchSha = async (branch) => {
75
+ try {
76
+ const response = await fetch(`https://api.github.com/repos/${SOURCE_REPO}/commits/${encodeURIComponent(branch)}`, {
77
+ headers: { accept: "application/vnd.github+json", "user-agent": "lunora-cli", ...githubAuthHeaders() },
78
+ signal: AbortSignal.timeout(1e4)
79
+ });
80
+ if (!response.ok) {
81
+ return void 0;
82
+ }
83
+ const body = await response.json();
84
+ return typeof body.sha === "string" && COMMIT_SHA.test(body.sha) ? body.sha : void 0;
85
+ } catch {
86
+ return void 0;
87
+ }
88
+ };
89
+ const resolvePinnedSourceRef = async (ref, logger) => {
90
+ const resolved = resolveSourceRef(ref);
91
+ if (isImmutableRef(resolved)) {
92
+ return resolved;
93
+ }
94
+ const sha = await fetchBranchSha(resolved);
95
+ if (sha === void 0) {
96
+ logger.warn(`could not pin ${SOURCE_REPO}#${resolved} to a commit — fetching the UNPINNED branch (set GITHUB_TOKEN if rate-limited).`);
97
+ return resolved;
98
+ }
99
+ logger.info(`pinned ${SOURCE_REPO}#${resolved} → ${sha}`);
100
+ return sha;
101
+ };
63
102
  const STABLE_DIST_TAG = "latest";
64
103
  const resolveDistTag = () => {
65
104
  const ref = resolveVersionRef(resolveCliVersion());
66
105
  return ref === STABLE_BRANCH ? STABLE_DIST_TAG : ref;
67
106
  };
107
+ const DEFAULT_REGISTRY = "https://registry.npmjs.org";
108
+ const registryBase = () => {
109
+ const configured = process.env["npm_config_registry"];
110
+ const base = configured !== void 0 && configured.length > 0 ? configured : DEFAULT_REGISTRY;
111
+ return base.endsWith("/") ? base.slice(0, -1) : base;
112
+ };
113
+ const resolveTagVersion = async (packageName, tag) => {
114
+ try {
115
+ const response = await fetch(`${registryBase()}/${packageName.replace("/", "%2F")}`, {
116
+ headers: { accept: "application/vnd.npm.install-v1+json" },
117
+ signal: AbortSignal.timeout(1e4)
118
+ });
119
+ if (!response.ok) {
120
+ return void 0;
121
+ }
122
+ const packument = await response.json();
123
+ return packument["dist-tags"]?.[tag];
124
+ } catch {
125
+ return void 0;
126
+ }
127
+ };
128
+ const resolveTagVersions = async (names, tag) => {
129
+ const resolved = /* @__PURE__ */ new Map();
130
+ await Promise.all(
131
+ [...new Set(names)].map(async (name) => {
132
+ const version = await resolveTagVersion(name, tag);
133
+ if (version !== void 0) {
134
+ resolved.set(name, version);
135
+ }
136
+ })
137
+ );
138
+ return resolved;
139
+ };
68
140
 
69
141
  const resolveDepRange = (range) => {
70
142
  if (!range.startsWith("workspace:")) {
@@ -76,8 +148,8 @@ const resolveDepRange = (range) => {
76
148
  }
77
149
  return rest;
78
150
  };
79
- const UMBRELLA_REEXPORTED_DEPS = /* @__PURE__ */ new Set(["@lunora/client", "@lunora/do", "@lunora/runtime", "@lunora/server", "@lunora/values"]);
80
- const UMBRELLA_IMPORT_RE = /(['"])@lunora\/(client|do|runtime|server|values)(\/[^'"]*)?\1/gu;
151
+ const UMBRELLA_REEXPORTED_DEPS = /* @__PURE__ */ new Set(["@lunora/client", "@lunora/do", "@lunora/ratelimit", "@lunora/runtime", "@lunora/server", "@lunora/values"]);
152
+ const UMBRELLA_IMPORT_RE = /(['"])@lunora\/(client|do|ratelimit|runtime|server|values)(\/[^'"]*)?\1/gu;
81
153
  const projectUsesUmbrella = (projectRoot) => {
82
154
  const packageJsonPath = join(projectRoot, "package.json");
83
155
  if (!existsSync(packageJsonPath)) {
@@ -386,11 +458,12 @@ export const schema = defineSchema({});
386
458
  return { kind: "skipped", path: schemaPath };
387
459
  }
388
460
  if (result.reason === "invalid-identifier") {
389
- throw new Error(
461
+ throw new LunoraError(
462
+ "INTERNAL",
390
463
  `schema-extension item "${itemKey}" is not a valid JS identifier — it is spliced into lunora/schema.ts as \`import { ${itemKey} }\` / \`.extend(${itemKey}.extension)\`. Rename the item to a valid identifier (no leading digit, no "-").`
391
464
  );
392
465
  }
393
- throw new Error(`schema-extension merge failed for "${itemKey}": ${result.reason}`);
466
+ throw new LunoraError("INTERNAL", `schema-extension merge failed for "${itemKey}": ${result.reason}`);
394
467
  };
395
468
  const previewWholeFile = (file, current, incoming, exists, logger) => {
396
469
  const lines = renderDiff(current, incoming);
@@ -449,6 +522,93 @@ const reconcileFile = (file, itemKey, itemDirectory, projectRoot, logger, lock,
449
522
  }
450
523
  return reconcileWholeFile(file, itemKey, itemDirectory, projectRoot, logger, lock, reconcileOptions, useUmbrella);
451
524
  };
525
+ const WORKER_ENTRY_FALLBACKS = ["src/server.ts", "src/server/index.ts", "src/index.ts", "src/worker.ts"];
526
+ const WRANGLER_MAIN_RE = /"main"\s*:\s*"([^"]+)"/u;
527
+ const readWranglerMain = (projectRoot) => {
528
+ for (const file of ["wrangler.jsonc", "wrangler.json"]) {
529
+ const path = join(projectRoot, file);
530
+ if (!existsSync(path)) {
531
+ continue;
532
+ }
533
+ const match = WRANGLER_MAIN_RE.exec(readFileSync(path, "utf8"));
534
+ if (match?.[1]) {
535
+ return match[1];
536
+ }
537
+ }
538
+ return void 0;
539
+ };
540
+ const findWorkerEntry = (projectRoot) => {
541
+ const main = readWranglerMain(projectRoot);
542
+ const candidates = main === void 0 ? WORKER_ENTRY_FALLBACKS : [main, ...WORKER_ENTRY_FALLBACKS];
543
+ for (const candidate of candidates) {
544
+ const absolute = join(projectRoot, candidate);
545
+ if (!existsSync(absolute)) {
546
+ continue;
547
+ }
548
+ const content = readFileSync(absolute, "utf8");
549
+ if (!content.includes("createShardDO(")) {
550
+ break;
551
+ }
552
+ return { entryPath: absolute, main: candidate, source: content };
553
+ }
554
+ return void 0;
555
+ };
556
+ const computeRelativeSpecifier = (entryPath, projectRoot, moduleName) => {
557
+ const importPath = relative(dirname(entryPath), join(projectRoot, "lunora", moduleName)).replaceAll("\\", "/");
558
+ return importPath.startsWith(".") ? importPath : `./${importPath}`;
559
+ };
560
+ const logClassAFallback = (entrypointReexports, logger) => {
561
+ for (const reexport of entrypointReexports) {
562
+ const specifier = `./lunora/${reexport.module}.js`;
563
+ const instruction = `Add \`export * from "${specifier}"\` to your worker entry`;
564
+ const suffix = reexport.comment ? ` (${reexport.comment})` : "";
565
+ logger.warn(`${instruction}${suffix}`);
566
+ }
567
+ return 0;
568
+ };
569
+ const buildReexportLines = (entrypointReexports, entryPath, projectRoot, source) => {
570
+ const lines = [];
571
+ for (const reexport of entrypointReexports) {
572
+ const specifier = computeRelativeSpecifier(entryPath, projectRoot, reexport.module);
573
+ const escapedSpecifier = specifier.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
574
+ const existingRe = new RegExp(String.raw`export\s+\*\s+from\s+["']${escapedSpecifier}\.js["']`, "u");
575
+ if (existingRe.test(source)) {
576
+ continue;
577
+ }
578
+ if (reexport.comment) {
579
+ lines.push(`
580
+ // ${reexport.comment}`);
581
+ }
582
+ lines.push(`export * from "${specifier}.js";`);
583
+ }
584
+ return lines;
585
+ };
586
+ const applyEntrypointReexports = (entrypointReexports, projectRoot, logger, diff) => {
587
+ if (entrypointReexports.length === 0) {
588
+ return 0;
589
+ }
590
+ const entry = findWorkerEntry(projectRoot);
591
+ if (entry === void 0) {
592
+ return logClassAFallback(entrypointReexports, logger);
593
+ }
594
+ const linesToAppend = buildReexportLines(entrypointReexports, entry.entryPath, projectRoot, entry.source);
595
+ if (linesToAppend.length === 0) {
596
+ return 0;
597
+ }
598
+ if (diff) {
599
+ for (const line of linesToAppend) {
600
+ if (line !== "") {
601
+ logger.info(`~ entrypoint: ${line}`);
602
+ }
603
+ }
604
+ return linesToAppend.length;
605
+ }
606
+ const separator = entry.source.endsWith("\n") ? "" : "\n";
607
+ writeFileSync(entry.entryPath, `${entry.source}${separator}${linesToAppend.join("\n")}
608
+ `, "utf8");
609
+ logger.success(`wrote ${String(linesToAppend.length)} entrypoint re-export(s) to ${relative(projectRoot, entry.entryPath)}`);
610
+ return linesToAppend.length;
611
+ };
452
612
  const reconcileItems = (items, cwd, logger, reconcileOptions = {}) => {
453
613
  const written = [];
454
614
  const skipped = [];
@@ -461,6 +621,9 @@ const reconcileItems = (items, cwd, logger, reconcileOptions = {}) => {
461
621
  const outcome = reconcileFile(file, manifest.name, directory, cwd, logger, lock, reconcileOptions, useUmbrella);
462
622
  (outcome.kind === "written" ? written : skipped).push(outcome.path);
463
623
  }
624
+ if (manifest.entrypointReexports !== void 0) {
625
+ applyEntrypointReexports(manifest.entrypointReexports, cwd, logger, reconcileOptions.diff === true);
626
+ }
464
627
  if (reconcileOptions.diff) {
465
628
  continue;
466
629
  }
@@ -478,7 +641,8 @@ const DEFAULT_SOURCE_BASE = "gh:anolilab/lunora/registry";
478
641
  const VALID_ITEM_NAME = /^[A-Za-z0-9][\w-]*$/u;
479
642
  const assertSafeItemName = (name) => {
480
643
  if (!VALID_ITEM_NAME.test(name)) {
481
- throw new Error(
644
+ throw new LunoraError(
645
+ "INTERNAL",
482
646
  `invalid registry item name "${name}" — names must match ${VALID_ITEM_NAME.source} (letters, digits, "-", "_"; no path separators or "..")`
483
647
  );
484
648
  }
@@ -515,29 +679,39 @@ const fetchToStaging = async (remote, label, logger) => {
515
679
  throw error;
516
680
  }
517
681
  };
682
+ const remoteRefCache = /* @__PURE__ */ new WeakMap();
683
+ const resolveRemoteRef = async (options) => {
684
+ const cached = remoteRefCache.get(options);
685
+ if (cached !== void 0) {
686
+ return cached;
687
+ }
688
+ const pending = options.source !== void 0 && options.source.length > 0 ? Promise.resolve(resolveSourceRef(options.ref)) : resolvePinnedSourceRef(options.ref, options.logger);
689
+ remoteRefCache.set(options, pending);
690
+ return pending;
691
+ };
518
692
  const resolveItemDirectory = async (name, options) => {
519
693
  assertSafeItemName(name);
520
694
  if (options.from !== void 0) {
521
695
  const directory = join(options.from, name);
522
696
  if (!existsSync(directory)) {
523
- throw new Error(`registry item not found in local source: ${directory}`);
697
+ throw new LunoraError("INTERNAL", `registry item not found in local source: ${directory}`);
524
698
  }
525
699
  return { cleanup: () => {
526
700
  }, directory };
527
701
  }
528
702
  const base = options.source ?? DEFAULT_SOURCE_BASE;
529
- return fetchToStaging(`${base}/${name}#${resolveSourceRef(options.ref)}`, "item", options.logger);
703
+ return fetchToStaging(`${base}/${name}#${await resolveRemoteRef(options)}`, "item", options.logger);
530
704
  };
531
705
  const resolveRegistryRoot = async (options) => {
532
706
  if (options.from !== void 0) {
533
707
  if (!existsSync(options.from)) {
534
- throw new Error(`registry root not found: ${options.from}`);
708
+ throw new LunoraError("INTERNAL", `registry root not found: ${options.from}`);
535
709
  }
536
710
  return { cleanup: () => {
537
711
  }, root: options.from };
538
712
  }
539
713
  const base = options.source ?? DEFAULT_SOURCE_BASE;
540
- const { cleanup, directory } = await fetchToStaging(`${base}#${resolveSourceRef(options.ref)}`, "registry", options.logger);
714
+ const { cleanup, directory } = await fetchToStaging(`${base}#${await resolveRemoteRef(options)}`, "registry", options.logger);
541
715
  return { cleanup, root: directory };
542
716
  };
543
717
  const readManifest = (itemDirectory, name) => {
@@ -554,7 +728,7 @@ const resolvePlan = async (names, options) => {
554
728
  return;
555
729
  }
556
730
  if (inProgress.has(name)) {
557
- throw new Error(`cyclic registry dependency detected at "${name}"`);
731
+ throw new LunoraError("INTERNAL", `cyclic registry dependency detected at "${name}"`);
558
732
  }
559
733
  inProgress.add(name);
560
734
  const { cleanup, directory } = await resolveItemDirectory(name, options);
@@ -583,6 +757,26 @@ const resolvePlan = async (names, options) => {
583
757
  const emptyResult = () => {
584
758
  return { bindings: [], code: 0, deps: [], skipped: [], written: [] };
585
759
  };
760
+ const setBindingField = (manifest, section, match, field, fieldValue) => {
761
+ if (!manifest.bindings) {
762
+ return manifest;
763
+ }
764
+ return {
765
+ ...manifest,
766
+ bindings: manifest.bindings.map((binding) => {
767
+ if (binding.path[0] !== section || !Array.isArray(binding.value)) {
768
+ return binding;
769
+ }
770
+ const entries = binding.value;
771
+ return {
772
+ ...binding,
773
+ value: entries.map(
774
+ (entry) => typeof entry === "object" && entry !== null && entry[match.key] === match.value ? { ...entry, [field]: fieldValue } : entry
775
+ )
776
+ };
777
+ })
778
+ };
779
+ };
586
780
 
587
781
  const printPlan = (logger, manifest) => {
588
782
  const label = manifest.title ?? manifest.description;
@@ -603,6 +797,11 @@ const printPlan = (logger, manifest) => {
603
797
  const valueSuffix = variable.secret ? " (secret)" : ` = ${JSON.stringify(variable.value ?? "")}`;
604
798
  logger.info(` env ${variable.name}${valueSuffix}`);
605
799
  }
800
+ for (const reexport of manifest.entrypointReexports ?? []) {
801
+ const specifier = `./lunora/${reexport.module}`;
802
+ const suffix = reexport.comment ? ` // ${reexport.comment}` : "";
803
+ logger.info(` entry ${specifier}${suffix}`);
804
+ }
606
805
  };
607
806
  const printJsonPlan = (items) => {
608
807
  const planSnapshot = items.map(({ manifest }) => {
@@ -614,6 +813,9 @@ const printJsonPlan = (items) => {
614
813
  }),
615
814
  deps: Object.keys(manifest.deps ?? {}),
616
815
  devDependencies: Object.keys(manifest.devDependencies ?? {}),
816
+ entrypointReexports: (manifest.entrypointReexports ?? []).map((reexport) => {
817
+ return { module: reexport.module, ...reexport.comment ? { comment: reexport.comment } : {} };
818
+ }),
617
819
  envVars: (manifest.envVars ?? []).map((variable) => {
618
820
  return { name: variable.name, ...variable.secret ? { secret: true } : { value: variable.value ?? "" } };
619
821
  }),
@@ -688,8 +890,12 @@ const runAddCommand = async (options) => {
688
890
  }
689
891
  let cleanups = [];
690
892
  try {
691
- const { cleanups: planCleanups, items } = await resolvePlan(options.names, options);
893
+ const { cleanups: planCleanups, items: resolvedItems } = await resolvePlan(options.names, options);
692
894
  cleanups = planCleanups;
895
+ const { transformManifest } = options;
896
+ const items = transformManifest ? resolvedItems.map((item) => {
897
+ return { ...item, manifest: transformManifest(item.manifest) };
898
+ }) : resolvedItems;
693
899
  for (const { manifest } of items) {
694
900
  printPlan(options.logger, manifest);
695
901
  }
@@ -785,4 +991,4 @@ const runBuildIndexCommand = async (options) => {
785
991
  return empty;
786
992
  };
787
993
 
788
- export { runBuildIndexCommand as a, runRegistryViewCommand as b, resolveDistTag as c, resolveSourceRef as d, runListCommand as e, runAddCommand as r };
994
+ export { runBuildIndexCommand as a, runRegistryViewCommand as b, resolveTagVersions as c, resolveSourceRef as d, resolvePinnedSourceRef as e, resolveDistTag as f, runListCommand as g, runAddCommand as r, setBindingField as s };
@@ -1,22 +1,51 @@
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
+ let sharedPail;
6
+ let jsonForced = false;
7
+ let configuredReporters;
5
8
  const wantJson = () => {
9
+ if (jsonForced) {
10
+ return true;
11
+ }
6
12
  const flag = process.env.LUNORA_LOG_JSON;
7
13
  return flag === "1" || flag === "true";
8
14
  };
9
- const buildReporter = () => {
10
- const Reporter = wantJson() ? JsonReporter : PrettyReporter;
11
- return new Reporter();
15
+ const constructReporter = (Reporter) => new Reporter();
16
+ const buildReporters = () => {
17
+ if (jsonForced) {
18
+ return [constructReporter(JsonReporter)];
19
+ }
20
+ if (configuredReporters !== void 0 && configuredReporters.length > 0) {
21
+ return configuredReporters;
22
+ }
23
+ return [constructReporter(wantJson() ? JsonReporter : LunoraReporter)];
12
24
  };
13
- let sharedPail;
25
+ const forceJsonLogging = () => {
26
+ if (jsonForced) {
27
+ return;
28
+ }
29
+ jsonForced = true;
30
+ sharedPail = void 0;
31
+ };
32
+ const logHandlers = {
33
+ compose: (...reporters) => reporters,
34
+ console: () => constructReporter(LunoraReporter),
35
+ json: () => constructReporter(JsonReporter)
36
+ };
37
+ const configureLogHandlers = (reporters) => {
38
+ configuredReporters = Array.isArray(reporters) ? reporters : [reporters];
39
+ sharedPail = void 0;
40
+ };
41
+ const STEP_LOG_TYPES = Object.fromEntries(STEP_BADGE_NAMES.map((name) => [name, { label: name, logLevel: "informational" }]));
14
42
  const getPail = () => {
15
43
  sharedPail ??= createPail({
16
- reporters: [buildReporter()],
44
+ reporters: buildReporters(),
17
45
  scope: ["lunora"],
18
46
  stderr: process.stderr,
19
- stdout: process.stdout
47
+ stdout: process.stdout,
48
+ types: STEP_LOG_TYPES
20
49
  });
21
50
  return sharedPail;
22
51
  };
@@ -69,5 +98,8 @@ const pail = /* @__PURE__ */ new Proxy({}, {
69
98
  return typeof value === "function" ? value.bind(instance) : value;
70
99
  }
71
100
  });
101
+ const logStep = (type, message) => {
102
+ getPail()[type](message);
103
+ };
72
104
 
73
- export { createLogger, createStderrLogger, getPail, pail };
105
+ export { configureLogHandlers, createLogger, createStderrLogger, forceJsonLogging, getPail, logHandlers, logStep, pail };
@@ -1,5 +1,13 @@
1
1
  import { spawn } from 'node:child_process';
2
2
 
3
+ const NEEDS_CMD_QUOTING = /\s/;
4
+ const spawnShellCompat = (command, args, platform = process.platform) => {
5
+ if (platform !== "win32" || command === process.execPath) {
6
+ return { args: [...args], command, shell: false };
7
+ }
8
+ const quote = (value) => NEEDS_CMD_QUOTING.test(value) ? `"${value}"` : value;
9
+ return { args: args.map((argument) => quote(argument)), command: quote(command), shell: true };
10
+ };
3
11
  const defaultSpawner = (descriptor) => new Promise((resolve, reject) => {
4
12
  const hasInput = typeof descriptor.input === "string";
5
13
  const wantCapture = descriptor.captureStdout === true;
@@ -9,9 +17,11 @@ const defaultSpawner = (descriptor) => new Promise((resolve, reject) => {
9
17
  } else if (descriptor.stdoutToStderr) {
10
18
  stdout = 2;
11
19
  }
12
- const child = spawn(descriptor.command, [...descriptor.args], {
20
+ const exec = spawnShellCompat(descriptor.command, descriptor.args);
21
+ const child = spawn(exec.command, exec.args, {
13
22
  cwd: descriptor.cwd ?? process.cwd(),
14
23
  env: descriptor.env ? { ...process.env, ...descriptor.env } : process.env,
24
+ shell: exec.shell,
15
25
  stdio: [hasInput ? "pipe" : "inherit", stdout, "inherit"]
16
26
  });
17
27
  let captured = "";
@@ -40,4 +50,4 @@ const createRecordingSpawner = (exitCode = 0) => {
40
50
  return { calls, spawner };
41
51
  };
42
52
 
43
- export { createRecordingSpawner, defaultSpawner };
53
+ export { createRecordingSpawner, defaultSpawner, spawnShellCompat };
@@ -1,9 +1,7 @@
1
1
  import { spawnSync } from 'node:child_process';
2
- import { existsSync, readFileSync } from 'node:fs';
3
- import { dirname, join } from '@visulima/path';
2
+ import { LunoraError } from '@lunora/errors';
3
+ import { findPackageManagerSync, identifyInitiatingPackageManager } from '@visulima/package/package-manager';
4
4
 
5
- const FALLBACK = "pnpm";
6
- const KNOWN_MANAGERS = ["pnpm", "yarn", "npm", "bun"];
7
5
  const INSTALL_PREFERENCE = ["pnpm", "bun", "yarn", "npm"];
8
6
  const isManagerInstalled = (manager) => {
9
7
  try {
@@ -16,34 +14,23 @@ const detectInstalledManagers = (probe = isManagerInstalled) => INSTALL_PREFEREN
16
14
  const installArgsFor = (manager) => {
17
15
  return { args: ["install"], command: manager };
18
16
  };
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
- }
17
+ const detectPackageManager = (startDirectory) => {
30
18
  try {
31
- const parsed = JSON.parse(readFileSync(candidate, "utf8"));
32
- return parseDeclaredManager(parsed.packageManager);
19
+ return findPackageManagerSync(startDirectory).packageManager;
33
20
  } catch {
34
- return void 0;
35
21
  }
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;
22
+ const initiating = identifyInitiatingPackageManager();
23
+ if (initiating !== void 0) {
24
+ return initiating.name === "cnpm" ? "npm" : initiating.name;
25
+ }
26
+ const [installed] = detectInstalledManagers();
27
+ if (installed !== void 0) {
28
+ return installed;
29
+ }
30
+ throw new LunoraError(
31
+ "INTERNAL",
32
+ "Could not detect a package manager: no lock file or `packageManager` field was found, and none (pnpm, bun, yarn, npm) is installed on PATH."
33
+ );
47
34
  };
48
35
  const execArgsFor = (manager, command, args) => {
49
36
  if (manager === "yarn") {
@@ -57,5 +44,14 @@ const execArgsFor = (manager, command, args) => {
57
44
  }
58
45
  return { args: ["exec", command, ...args], command: "pnpm" };
59
46
  };
47
+ const runScriptCommand = (manager, script) => {
48
+ if (manager === "npm") {
49
+ return `npm run ${script}`;
50
+ }
51
+ if (manager === "bun") {
52
+ return `bun run ${script}`;
53
+ }
54
+ return `${manager} ${script}`;
55
+ };
60
56
 
61
- export { detectInstalledManagers as a, detectPackageManager as d, execArgsFor as e, installArgsFor as i };
57
+ export { detectInstalledManagers as a, detectPackageManager as d, execArgsFor as e, installArgsFor as i, runScriptCommand as r };
@@ -1,4 +1,4 @@
1
- import { Project, SyntaxKind } from 'ts-morph';
1
+ import { Project, SyntaxKind, Node } from 'ts-morph';
2
2
 
3
3
  const VALID_JS_IDENTIFIER = /^[A-Za-z_$][\w$]*$/u;
4
4
  const startMarker = (key) => `// lunora:add:${key}:start`;
@@ -12,6 +12,19 @@ const findDefineSchemaCall = (callExpressions) => {
12
12
  }
13
13
  return void 0;
14
14
  };
15
+ const outermostChainExpression = (defineSchemaCall) => {
16
+ let node = defineSchemaCall;
17
+ for (let parent = node.getParent(); parent !== void 0; parent = node.getParent()) {
18
+ if (Node.isPropertyAccessExpression(parent) && parent.getExpression() === node) {
19
+ node = parent;
20
+ } else if (Node.isCallExpression(parent) && parent.getExpression() === node) {
21
+ node = parent;
22
+ } else {
23
+ break;
24
+ }
25
+ }
26
+ return node;
27
+ };
15
28
  const insertSchemaExtension = (source, key) => {
16
29
  if (!VALID_JS_IDENTIFIER.test(key)) {
17
30
  return { ok: false, reason: "invalid-identifier" };
@@ -33,15 +46,7 @@ const insertSchemaExtension = (source, key) => {
33
46
  if (tablesArgument?.getKind() !== SyntaxKind.ObjectLiteralExpression) {
34
47
  return { ok: false, reason: "non-object-argument" };
35
48
  }
36
- const variableDeclaration = defineSchemaCall.getFirstAncestorByKind(SyntaxKind.VariableDeclaration);
37
- if (!variableDeclaration) {
38
- return { ok: false, reason: "no-define-schema" };
39
- }
40
- const initializer = variableDeclaration.getInitializer();
41
- if (!initializer) {
42
- return { ok: false, reason: "no-define-schema" };
43
- }
44
- const insertAt = initializer.getEnd();
49
+ const insertAt = outermostChainExpression(defineSchemaCall).getEnd();
45
50
  const chainText = `
46
51
  ${startMarker(key)}
47
52
  .extend(${key}.extension)
@@ -1,5 +1,6 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import { platform } from 'node:os';
3
+ import { LunoraError } from '@lunora/errors';
3
4
 
4
5
  const platformCommand = () => {
5
6
  const os = platform();
@@ -29,10 +30,10 @@ const openUrl = async (url, options = {}) => {
29
30
  try {
30
31
  parsed = new URL(url);
31
32
  } catch {
32
- throw new Error(`Invalid URL: ${url}`);
33
+ throw new LunoraError("INTERNAL", `Invalid URL: ${url}`);
33
34
  }
34
35
  if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
35
- throw new Error(`Refusing to open non-http(s) URL: ${url}`);
36
+ throw new LunoraError("INTERNAL", `Refusing to open non-http(s) URL: ${url}`);
36
37
  }
37
38
  const opener = options.opener ?? platformOpener;
38
39
  await opener(url);
@@ -1,4 +1,4 @@
1
- import { createStderrLogger } from './createLogger-CHPNjFw2.mjs';
1
+ import { createStderrLogger } from './createLogger-CIWSHrTL.mjs';
2
2
 
3
3
  const OUTPUT_FORMATS = /* @__PURE__ */ new Set(["json", "pretty"]);
4
4
  const validateOutputFormat = (command, format) => {