@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.
- package/LICENSE.md +6 -0
- package/__assets__/package-og.svg +1 -1
- package/dist/bin.mjs +1 -1
- package/dist/index.d.mts +290 -117
- package/dist/index.d.ts +290 -117
- package/dist/index.mjs +8 -8
- package/dist/packem_chunks/handler.mjs +87 -6
- package/dist/packem_chunks/handler10.mjs +8 -14
- package/dist/packem_chunks/handler11.mjs +19 -189
- package/dist/packem_chunks/handler12.mjs +176 -115
- package/dist/packem_chunks/handler13.mjs +118 -52
- package/dist/packem_chunks/handler14.mjs +50 -43
- package/dist/packem_chunks/handler15.mjs +48 -67
- package/dist/packem_chunks/handler16.mjs +85 -37
- package/dist/packem_chunks/handler17.mjs +39 -100
- package/dist/packem_chunks/handler18.mjs +87 -152
- package/dist/packem_chunks/handler19.mjs +148 -67
- package/dist/packem_chunks/handler2.mjs +6 -4
- package/dist/packem_chunks/handler20.mjs +77 -75
- package/dist/packem_chunks/handler21.mjs +71 -288
- package/dist/packem_chunks/handler3.mjs +6 -3
- package/dist/packem_chunks/handler4.mjs +2 -2
- package/dist/packem_chunks/handler5.mjs +7 -4
- package/dist/packem_chunks/handler6.mjs +10 -7
- package/dist/packem_chunks/handler7.mjs +2 -2
- package/dist/packem_chunks/handler8.mjs +1 -1
- package/dist/packem_chunks/handler9.mjs +315 -12
- package/dist/packem_chunks/planDevCommand.mjs +758 -81
- package/dist/packem_chunks/runCodegenCommand.mjs +5 -4
- package/dist/packem_chunks/runDeployCommand.mjs +126 -20
- package/dist/packem_chunks/runInitCommand.mjs +1661 -179
- package/dist/packem_chunks/runMigrateGenerateCommand.mjs +5 -4
- package/dist/packem_chunks/runResetCommand.mjs +2 -2
- package/dist/packem_chunks/runRpcCommand.mjs +3 -2
- package/dist/packem_shared/{COMMANDS-Bn8luojF.mjs → COMMANDS-DW8lQmuW.mjs} +85 -23
- package/dist/packem_shared/{DEFAULT_IMPORT_BATCH_SIZE-Ck-2bU08.mjs → DEFAULT_IMPORT_BATCH_SIZE-CRQmmBZM.mjs} +3 -2
- package/dist/packem_shared/{api-spec-CtA6ilu4.mjs → api-spec-Bx0iKbxA.mjs} +3 -1
- package/dist/packem_shared/{buildRegistryIndex-BcYe607_.mjs → buildRegistryIndex-BS5ig822.mjs} +1 -1
- package/dist/packem_shared/codegen-error-DJG-ghs_.mjs +31 -0
- package/dist/packem_shared/{command-BDXcJCCJ.mjs → command-lYnl4QyF.mjs} +6 -1
- package/dist/packem_shared/{commands-DqsEzojt.mjs → commands-ClEvcz3V.mjs} +224 -18
- package/dist/packem_shared/{createLogger-CHPNjFw2.mjs → createLogger-CIWSHrTL.mjs} +40 -8
- package/dist/packem_shared/{createRecordingSpawner-DxI3mebw.mjs → createRecordingSpawner-Cw5Iu73G.mjs} +12 -2
- package/dist/packem_shared/{detect-package-manager-DYp7n3mJ.mjs → detect-package-manager-DvEthdCw.mjs} +26 -30
- package/dist/packem_shared/{insertSchemaExtension-BuzF6-t2.mjs → insertSchemaExtension-DAqbfr9Z.mjs} +15 -10
- package/dist/packem_shared/{open-url-Dfq6fAyT.mjs → open-url-4PBLY9X0.mjs} +3 -2
- package/dist/packem_shared/{output-format-7gyGR3h8.mjs → output-format-B4642rjE.mjs} +1 -1
- package/dist/packem_shared/{parseManifest--vZf2FY1.mjs → parseManifest-Dbp-Q2q3.mjs} +36 -9
- package/dist/packem_shared/prompt-cancelled-APzX1Im-.mjs +9 -0
- package/dist/packem_shared/runAddCommand-DGjFXGqa.mjs +4 -0
- package/dist/packem_shared/storage-DEXpJkXh.mjs +84 -0
- package/dist/packem_shared/tui-prompts-BjEN8XgP.mjs +658 -0
- package/dist/packem_shared/wrangler-secrets-Dq_Fkbm-.mjs +49 -0
- package/package.json +16 -13
- package/skills/lunora-quickstart/SKILL.md +25 -5
- package/skills/lunora-setup-storage/SKILL.md +7 -3
- package/dist/packem_shared/features-ocSSpZtS.mjs +0 -24
- package/dist/packem_shared/runAddCommand-G544_v6e.mjs +0 -4
- 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 {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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}#${
|
|
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
|
|
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}#${
|
|
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
|
|
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,
|
|
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
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
|
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 {
|
|
3
|
-
import {
|
|
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
|
|
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
|
-
|
|
32
|
-
return parseDeclaredManager(parsed.packageManager);
|
|
19
|
+
return findPackageManagerSync(startDirectory).packageManager;
|
|
33
20
|
} catch {
|
|
34
|
-
return void 0;
|
|
35
21
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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 };
|
package/dist/packem_shared/{insertSchemaExtension-BuzF6-t2.mjs → insertSchemaExtension-DAqbfr9Z.mjs}
RENAMED
|
@@ -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
|
|
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
|
|
33
|
+
throw new LunoraError("INTERNAL", `Invalid URL: ${url}`);
|
|
33
34
|
}
|
|
34
35
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
35
|
-
throw new
|
|
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);
|