@lunora/cli 1.0.0-alpha.9 → 1.0.0-alpha.90

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 +358 -300
  5. package/dist/index.d.ts +358 -300
  6. package/dist/index.mjs +8 -9
  7. package/dist/packem_chunks/handler.mjs +15 -5
  8. package/dist/packem_chunks/handler10.mjs +8 -14
  9. package/dist/packem_chunks/handler11.mjs +20 -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 +94 -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 +8 -4
  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 +330 -12
  28. package/dist/packem_chunks/planDevCommand.mjs +762 -81
  29. package/dist/packem_chunks/runCodegenCommand.mjs +5 -4
  30. package/dist/packem_chunks/runDeployCommand.mjs +137 -22
  31. package/dist/packem_chunks/runInitCommand.mjs +1648 -188
  32. package/dist/packem_chunks/runMigrateGenerateCommand.mjs +94 -47
  33. package/dist/packem_chunks/runResetCommand.mjs +2 -2
  34. package/dist/packem_chunks/runRpcCommand.mjs +3 -2
  35. package/dist/packem_shared/{COMMANDS-Dh0bSERi.mjs → COMMANDS-DZ0NnuqI.mjs} +103 -27
  36. package/dist/packem_shared/{DEFAULT_IMPORT_BATCH_SIZE-Ck-2bU08.mjs → DEFAULT_IMPORT_BATCH_SIZE-D0VOTerB.mjs} +7 -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-CkkATMMx.mjs → commands-D5Yxt9VY.mjs} +199 -20
  42. package/dist/packem_shared/{createLogger-CHPNjFw2.mjs → createLogger-CIWSHrTL.mjs} +40 -8
  43. package/dist/packem_shared/{createRecordingSpawner-DxI3mebw.mjs → createRecordingSpawner-WuSn20kb.mjs} +23 -2
  44. package/dist/packem_shared/detect-package-manager-v4hHpQd0.mjs +62 -0
  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-D1hgfqFQ.mjs +4 -0
  51. package/dist/packem_shared/{storage-Bjo35hPa.mjs → storage-BXU4ax4O.mjs} +1 -1
  52. package/dist/packem_shared/tui-prompts-BjEN8XgP.mjs +658 -0
  53. package/dist/packem_shared/wrangler-secrets-Coni-mER.mjs +49 -0
  54. package/package.json +17 -14
  55. package/skills/lunora-quickstart/SKILL.md +26 -5
  56. package/dist/packem_shared/detect-package-manager-DYp7n3mJ.mjs +0 -61
  57. package/dist/packem_shared/parseArgs-YXFuKdEk.mjs +0 -56
  58. package/dist/packem_shared/runAddCommand-DepS9W9W.mjs +0 -4
  59. package/dist/packem_shared/tui-prompts-CA9lngSS.mjs +0 -269
@@ -1,4 +1,5 @@
1
- import { createLogger } from './createLogger-CHPNjFw2.mjs';
1
+ import { createLogger } from './createLogger-CIWSHrTL.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,15 +1,16 @@
1
1
  import { existsSync, readFileSync, writeFileSync, mkdirSync, mkdtempSync, rmSync } from 'node:fs';
2
- import { dirname, join } from '@visulima/path';
3
- import { DEV_VARS_FILE, parseDevVariableEntries } from '@lunora/config';
2
+ import { dirname, join, relative } from '@visulima/path';
3
+ import { DEV_VARS_FILE, parseDevVariableEntries, findWranglerFile, readWranglerJsonc } from '@lunora/config';
4
4
  import { modify, applyEdits, parse } from 'jsonc-parser';
5
5
  import { fileURLToPath } from 'node:url';
6
- import { b as tuiConfirm } from './tui-prompts-CA9lngSS.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
- const resolveDistTag = () => {
65
- const ref = resolveVersionRef(resolveCliVersion());
103
+ const resolveDistTag = (version = resolveCliVersion()) => {
104
+ const ref = resolveVersionRef(version);
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.replaceAll("/", "%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,90 @@ 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/server/index.tsx", "src/index.ts", "src/worker.ts"];
526
+ const readWranglerMain = (projectRoot) => {
527
+ const wranglerPath = findWranglerFile(projectRoot);
528
+ if (wranglerPath === void 0) {
529
+ return void 0;
530
+ }
531
+ const { parsed } = readWranglerJsonc(wranglerPath);
532
+ return typeof parsed?.main === "string" ? parsed.main : void 0;
533
+ };
534
+ const findWorkerEntry = (projectRoot) => {
535
+ const main = readWranglerMain(projectRoot);
536
+ const candidates = main === void 0 ? WORKER_ENTRY_FALLBACKS : [main, ...WORKER_ENTRY_FALLBACKS];
537
+ for (const candidate of candidates) {
538
+ const absolute = join(projectRoot, candidate);
539
+ if (!existsSync(absolute)) {
540
+ continue;
541
+ }
542
+ const content = readFileSync(absolute, "utf8");
543
+ if (!content.includes("createShardDO(")) {
544
+ if (candidate === main) {
545
+ break;
546
+ }
547
+ continue;
548
+ }
549
+ return { entryPath: absolute, main: candidate, source: content };
550
+ }
551
+ return void 0;
552
+ };
553
+ const computeRelativeSpecifier = (entryPath, projectRoot, moduleName) => {
554
+ const importPath = relative(dirname(entryPath), join(projectRoot, "lunora", moduleName)).replaceAll("\\", "/");
555
+ return importPath.startsWith(".") ? importPath : `./${importPath}`;
556
+ };
557
+ const logClassAFallback = (entrypointReexports, logger) => {
558
+ for (const reexport of entrypointReexports) {
559
+ const specifier = `./lunora/${reexport.module}.js`;
560
+ const instruction = `Add \`export * from "${specifier}"\` to your worker entry`;
561
+ const suffix = reexport.comment ? ` (${reexport.comment})` : "";
562
+ logger.warn(`${instruction}${suffix}`);
563
+ }
564
+ return 0;
565
+ };
566
+ const buildReexportLines = (entrypointReexports, entryPath, projectRoot, source) => {
567
+ const lines = [];
568
+ for (const reexport of entrypointReexports) {
569
+ const specifier = computeRelativeSpecifier(entryPath, projectRoot, reexport.module);
570
+ const escapedSpecifier = specifier.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
571
+ const existingRe = new RegExp(String.raw`export\s+\*\s+from\s+["']${escapedSpecifier}\.js["']`, "u");
572
+ if (existingRe.test(source)) {
573
+ continue;
574
+ }
575
+ if (reexport.comment) {
576
+ lines.push(`
577
+ // ${reexport.comment}`);
578
+ }
579
+ lines.push(`export * from "${specifier}.js";`);
580
+ }
581
+ return lines;
582
+ };
583
+ const applyEntrypointReexports = (entrypointReexports, projectRoot, logger, diff) => {
584
+ if (entrypointReexports.length === 0) {
585
+ return 0;
586
+ }
587
+ const entry = findWorkerEntry(projectRoot);
588
+ if (entry === void 0) {
589
+ return logClassAFallback(entrypointReexports, logger);
590
+ }
591
+ const linesToAppend = buildReexportLines(entrypointReexports, entry.entryPath, projectRoot, entry.source);
592
+ if (linesToAppend.length === 0) {
593
+ return 0;
594
+ }
595
+ if (diff) {
596
+ for (const line of linesToAppend) {
597
+ if (line !== "") {
598
+ logger.info(`~ entrypoint: ${line}`);
599
+ }
600
+ }
601
+ return linesToAppend.length;
602
+ }
603
+ const separator = entry.source.endsWith("\n") ? "" : "\n";
604
+ writeFileSync(entry.entryPath, `${entry.source}${separator}${linesToAppend.join("\n")}
605
+ `, "utf8");
606
+ logger.success(`wrote ${String(linesToAppend.length)} entrypoint re-export(s) to ${relative(projectRoot, entry.entryPath)}`);
607
+ return linesToAppend.length;
608
+ };
452
609
  const reconcileItems = (items, cwd, logger, reconcileOptions = {}) => {
453
610
  const written = [];
454
611
  const skipped = [];
@@ -461,6 +618,9 @@ const reconcileItems = (items, cwd, logger, reconcileOptions = {}) => {
461
618
  const outcome = reconcileFile(file, manifest.name, directory, cwd, logger, lock, reconcileOptions, useUmbrella);
462
619
  (outcome.kind === "written" ? written : skipped).push(outcome.path);
463
620
  }
621
+ if (manifest.entrypointReexports !== void 0) {
622
+ applyEntrypointReexports(manifest.entrypointReexports, cwd, logger, reconcileOptions.diff === true);
623
+ }
464
624
  if (reconcileOptions.diff) {
465
625
  continue;
466
626
  }
@@ -478,7 +638,8 @@ const DEFAULT_SOURCE_BASE = "gh:anolilab/lunora/registry";
478
638
  const VALID_ITEM_NAME = /^[A-Za-z0-9][\w-]*$/u;
479
639
  const assertSafeItemName = (name) => {
480
640
  if (!VALID_ITEM_NAME.test(name)) {
481
- throw new Error(
641
+ throw new LunoraError(
642
+ "INTERNAL",
482
643
  `invalid registry item name "${name}" — names must match ${VALID_ITEM_NAME.source} (letters, digits, "-", "_"; no path separators or "..")`
483
644
  );
484
645
  }
@@ -515,29 +676,39 @@ const fetchToStaging = async (remote, label, logger) => {
515
676
  throw error;
516
677
  }
517
678
  };
679
+ const remoteRefCache = /* @__PURE__ */ new WeakMap();
680
+ const resolveRemoteRef = async (options) => {
681
+ const cached = remoteRefCache.get(options);
682
+ if (cached !== void 0) {
683
+ return cached;
684
+ }
685
+ const pending = options.source !== void 0 && options.source.length > 0 ? Promise.resolve(resolveSourceRef(options.ref)) : resolvePinnedSourceRef(options.ref, options.logger);
686
+ remoteRefCache.set(options, pending);
687
+ return pending;
688
+ };
518
689
  const resolveItemDirectory = async (name, options) => {
519
690
  assertSafeItemName(name);
520
691
  if (options.from !== void 0) {
521
692
  const directory = join(options.from, name);
522
693
  if (!existsSync(directory)) {
523
- throw new Error(`registry item not found in local source: ${directory}`);
694
+ throw new LunoraError("INTERNAL", `registry item not found in local source: ${directory}`);
524
695
  }
525
696
  return { cleanup: () => {
526
697
  }, directory };
527
698
  }
528
699
  const base = options.source ?? DEFAULT_SOURCE_BASE;
529
- return fetchToStaging(`${base}/${name}#${resolveSourceRef(options.ref)}`, "item", options.logger);
700
+ return fetchToStaging(`${base}/${name}#${await resolveRemoteRef(options)}`, "item", options.logger);
530
701
  };
531
702
  const resolveRegistryRoot = async (options) => {
532
703
  if (options.from !== void 0) {
533
704
  if (!existsSync(options.from)) {
534
- throw new Error(`registry root not found: ${options.from}`);
705
+ throw new LunoraError("INTERNAL", `registry root not found: ${options.from}`);
535
706
  }
536
707
  return { cleanup: () => {
537
708
  }, root: options.from };
538
709
  }
539
710
  const base = options.source ?? DEFAULT_SOURCE_BASE;
540
- const { cleanup, directory } = await fetchToStaging(`${base}#${resolveSourceRef(options.ref)}`, "registry", options.logger);
711
+ const { cleanup, directory } = await fetchToStaging(`${base}#${await resolveRemoteRef(options)}`, "registry", options.logger);
541
712
  return { cleanup, root: directory };
542
713
  };
543
714
  const readManifest = (itemDirectory, name) => {
@@ -554,7 +725,7 @@ const resolvePlan = async (names, options) => {
554
725
  return;
555
726
  }
556
727
  if (inProgress.has(name)) {
557
- throw new Error(`cyclic registry dependency detected at "${name}"`);
728
+ throw new LunoraError("INTERNAL", `cyclic registry dependency detected at "${name}"`);
558
729
  }
559
730
  inProgress.add(name);
560
731
  const { cleanup, directory } = await resolveItemDirectory(name, options);
@@ -623,6 +794,11 @@ const printPlan = (logger, manifest) => {
623
794
  const valueSuffix = variable.secret ? " (secret)" : ` = ${JSON.stringify(variable.value ?? "")}`;
624
795
  logger.info(` env ${variable.name}${valueSuffix}`);
625
796
  }
797
+ for (const reexport of manifest.entrypointReexports ?? []) {
798
+ const specifier = `./lunora/${reexport.module}`;
799
+ const suffix = reexport.comment ? ` // ${reexport.comment}` : "";
800
+ logger.info(` entry ${specifier}${suffix}`);
801
+ }
626
802
  };
627
803
  const printJsonPlan = (items) => {
628
804
  const planSnapshot = items.map(({ manifest }) => {
@@ -634,6 +810,9 @@ const printJsonPlan = (items) => {
634
810
  }),
635
811
  deps: Object.keys(manifest.deps ?? {}),
636
812
  devDependencies: Object.keys(manifest.devDependencies ?? {}),
813
+ entrypointReexports: (manifest.entrypointReexports ?? []).map((reexport) => {
814
+ return { module: reexport.module, ...reexport.comment ? { comment: reexport.comment } : {} };
815
+ }),
637
816
  envVars: (manifest.envVars ?? []).map((variable) => {
638
817
  return { name: variable.name, ...variable.secret ? { secret: true } : { value: variable.value ?? "" } };
639
818
  }),
@@ -809,4 +988,4 @@ const runBuildIndexCommand = async (options) => {
809
988
  return empty;
810
989
  };
811
990
 
812
- export { runBuildIndexCommand as a, runRegistryViewCommand as b, resolveDistTag as c, resolveSourceRef as d, runListCommand as e, runAddCommand as r, setBindingField as s };
991
+ 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,24 @@
1
1
  import { spawn } from 'node:child_process';
2
2
 
3
+ const NEEDS_CMD_QUOTING = /[\s"%&<>^|]/u;
4
+ const BACKSLASH_RUN_BEFORE_QUOTE = /(\\*)"/gu;
5
+ const TRAILING_BACKSLASH_RUN = /(\\+)$/u;
6
+ const spawnShellCompat = (command, args, platform = process.platform) => {
7
+ if (platform !== "win32" || command === process.execPath) {
8
+ return { args: [...args], command, shell: false };
9
+ }
10
+ const quote = (value) => {
11
+ if (value === "") {
12
+ return `""`;
13
+ }
14
+ if (!NEEDS_CMD_QUOTING.test(value)) {
15
+ return value;
16
+ }
17
+ const escaped = value.replaceAll(BACKSLASH_RUN_BEFORE_QUOTE, String.raw`$1$1\"`).replace(TRAILING_BACKSLASH_RUN, "$1$1");
18
+ return `"${escaped}"`;
19
+ };
20
+ return { args: args.map((argument) => quote(argument)), command: quote(command), shell: true };
21
+ };
3
22
  const defaultSpawner = (descriptor) => new Promise((resolve, reject) => {
4
23
  const hasInput = typeof descriptor.input === "string";
5
24
  const wantCapture = descriptor.captureStdout === true;
@@ -9,9 +28,11 @@ const defaultSpawner = (descriptor) => new Promise((resolve, reject) => {
9
28
  } else if (descriptor.stdoutToStderr) {
10
29
  stdout = 2;
11
30
  }
12
- const child = spawn(descriptor.command, [...descriptor.args], {
31
+ const exec = spawnShellCompat(descriptor.command, descriptor.args);
32
+ const child = spawn(exec.command, exec.args, {
13
33
  cwd: descriptor.cwd ?? process.cwd(),
14
34
  env: descriptor.env ? { ...process.env, ...descriptor.env } : process.env,
35
+ shell: exec.shell,
15
36
  stdio: [hasInput ? "pipe" : "inherit", stdout, "inherit"]
16
37
  });
17
38
  let captured = "";
@@ -40,4 +61,4 @@ const createRecordingSpawner = (exitCode = 0) => {
40
61
  return { calls, spawner };
41
62
  };
42
63
 
43
- export { createRecordingSpawner, defaultSpawner };
64
+ export { createRecordingSpawner, defaultSpawner, spawnShellCompat };
@@ -0,0 +1,62 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { LunoraError } from '@lunora/errors';
3
+ import { findPackageManagerSync, identifyInitiatingPackageManager } from '@visulima/package/package-manager';
4
+
5
+ const INSTALL_PREFERENCE = ["pnpm", "bun", "yarn", "npm"];
6
+ const KNOWN_PACKAGE_MANAGERS = /* @__PURE__ */ new Set(["bun", "npm", "pnpm", "yarn"]);
7
+ const isKnownPackageManager = (name) => KNOWN_PACKAGE_MANAGERS.has(name);
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 detectPackageManager = (startDirectory) => {
20
+ try {
21
+ return findPackageManagerSync(startDirectory).packageManager;
22
+ } catch {
23
+ }
24
+ const initiating = identifyInitiatingPackageManager();
25
+ if (initiating !== void 0) {
26
+ const name = initiating.name === "cnpm" ? "npm" : initiating.name;
27
+ if (isKnownPackageManager(name)) {
28
+ return name;
29
+ }
30
+ }
31
+ const [installed] = detectInstalledManagers();
32
+ if (installed !== void 0) {
33
+ return installed;
34
+ }
35
+ throw new LunoraError(
36
+ "INTERNAL",
37
+ "Could not detect a package manager: no lock file or `packageManager` field was found, and none (pnpm, bun, yarn, npm) is installed on PATH."
38
+ );
39
+ };
40
+ const execArgsFor = (manager, command, args) => {
41
+ if (manager === "yarn") {
42
+ return { args: [command, ...args], command: "yarn" };
43
+ }
44
+ if (manager === "bun") {
45
+ return { args: ["x", command, ...args], command: "bun" };
46
+ }
47
+ if (manager === "npm") {
48
+ return { args: ["--", command, ...args], command: "npx" };
49
+ }
50
+ return { args: ["exec", command, ...args], command: "pnpm" };
51
+ };
52
+ const runScriptCommand = (manager, script) => {
53
+ if (manager === "npm") {
54
+ return `npm run ${script}`;
55
+ }
56
+ if (manager === "bun") {
57
+ return `bun run ${script}`;
58
+ }
59
+ return `${manager} ${script}`;
60
+ };
61
+
62
+ 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) => {