@crustjs/core 0.3.0 → 0.3.2
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/dist/context-Cud9frNp.d.ts +2059 -0
- package/dist/index.d.ts +3 -1287
- package/dist/index.js +2 -1
- package/dist/{invocation-CIvHShiW.js → invocation-B17RvTLb.js} +5 -216
- package/dist/tooling.d.ts +2 -1
- package/dist/tooling.js +2 -1
- package/package.json +4 -2
- package/dist/artifacts-D8Jlug-a.d.ts +0 -870
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { S as CrustError, _ as defineContext, a as runInvocation, b as normalizeArg, d as installExtensionContexts, f as validateCommandSections, g as contextSources, h as defineExtension, i as resolveTypedPath, m as registerFlag, n as executeInvocation, o as snapshotCommand, p as createCommandNode, r as prepareInvocation, u as cloneCommandNode, v as definingOf, x as normalizeFlag, y as validateContextAvailability } from "./invocation-B17RvTLb.js";
|
|
2
|
+
import { resolveArtifactDir } from "@crustjs/utils/artifacts";
|
|
2
3
|
//#region src/identity.ts
|
|
3
4
|
/** Mint an Extension identity from any non-blank, trimmed string. */
|
|
4
5
|
function defineExtensionId(id) {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
2
|
import { dirname, join, posix, resolve, win32 } from "node:path";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
6
|
-
import { Writable } from "node:stream";
|
|
3
|
+
import { BUILD_OUT_DIR_ENV } from "@crustjs/utils/artifacts";
|
|
4
|
+
import { withAmbientTerminalIO } from "@crustjs/utils/terminal";
|
|
7
5
|
import { parseArgs } from "node:util";
|
|
8
6
|
import { isPromise } from "node:util/types";
|
|
7
|
+
import { coerceBooleanString, tryCoerceNumber } from "@crustjs/utils/primitive";
|
|
9
8
|
import { homedir } from "node:os";
|
|
9
|
+
import { normalizeStandardIssues } from "@crustjs/utils/schema";
|
|
10
10
|
//#region src/errors.ts
|
|
11
11
|
/**
|
|
12
12
|
* A typed error for runtime recipe, Extension, Context, documentation, argv, and validation failures.
|
|
@@ -614,156 +614,6 @@ function installExtensionContexts(node, extensions, reRegisteredIds) {
|
|
|
614
614
|
return cloned;
|
|
615
615
|
}
|
|
616
616
|
//#endregion
|
|
617
|
-
//#region ../utils/src/source.ts
|
|
618
|
-
/**
|
|
619
|
-
* Find the nearest directory containing `package.json` by walking up the
|
|
620
|
-
* filesystem from `startPath`.
|
|
621
|
-
*
|
|
622
|
-
* If `startPath` points at an existing file, its parent directory is used as
|
|
623
|
-
* the starting point. The directory walk uses `path.resolve()` (lexical), not
|
|
624
|
-
* `fs.realpath()`, so a symlink's parent chain is walked rather than the
|
|
625
|
-
* symlink target's parent chain.
|
|
626
|
-
*
|
|
627
|
-
* @param startPath - Directory or file path to start walking from.
|
|
628
|
-
* @returns The absolute path of the nearest enclosing directory containing
|
|
629
|
-
* `package.json`, or `null` if the filesystem root is reached first.
|
|
630
|
-
*
|
|
631
|
-
* @internal Shared by {@link resolveSourceDir} and `resolveArtifactDir`.
|
|
632
|
-
*/
|
|
633
|
-
function findNearestPackageRoot(startPath) {
|
|
634
|
-
let current = resolve(startPath);
|
|
635
|
-
if (existsSync(current) && !statSync(current).isDirectory()) current = dirname(current);
|
|
636
|
-
while (true) {
|
|
637
|
-
if (existsSync(join(current, "package.json"))) return current;
|
|
638
|
-
const parent = dirname(current);
|
|
639
|
-
if (parent === current) return null;
|
|
640
|
-
current = parent;
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
//#endregion
|
|
644
|
-
//#region ../utils/src/artifacts.ts
|
|
645
|
-
/**
|
|
646
|
-
* Set by `crust build` while it prepares the Command Snapshot: the absolute
|
|
647
|
-
* entry-isolated directory Extension build hooks write into. Core reads it
|
|
648
|
-
* to run the hooks; `resolveArtifactDir` reads it so sections evaluated during
|
|
649
|
-
* that run see the artifacts being built instead of the wiped `.crust/root`.
|
|
650
|
-
*/
|
|
651
|
-
const BUILD_OUT_DIR_ENV = "CRUST_INTERNAL_BUILD_OUT_DIR";
|
|
652
|
-
/** True inside a `bun build --compile` or `deno compile` executable. */
|
|
653
|
-
function isCompiledExecutable() {
|
|
654
|
-
const { Bun, Deno } = globalThis;
|
|
655
|
-
const bunMain = Bun?.main ?? "";
|
|
656
|
-
return bunMain.startsWith("/$bunfs/") || /^[A-Za-z]:[\\/]~BUN[\\/]/.test(bunMain) || Deno?.build?.standalone === true;
|
|
657
|
-
}
|
|
658
|
-
/**
|
|
659
|
-
* Absolute path of a build artifact or `crust.include` directory shipped with
|
|
660
|
-
* this CLI. `name` is a top-level directory name such as `"skills"`.
|
|
661
|
-
*
|
|
662
|
-
* The artifact path is computed from how the CLI is running — never probed:
|
|
663
|
-
* - Compiled executable (Bun or Deno): `<dir of the executable>/<name>`, which
|
|
664
|
-
* is a platform package's `bin/` or wherever the binary was placed.
|
|
665
|
-
* - Crust-built Node bundle: `<name>` next to the bundle's `bin/` directory,
|
|
666
|
-
* i.e. `.crust/root/<name>` in place and `<installed root>/<name>` after install.
|
|
667
|
-
* - Snapshot preparation inside `crust build`: `<build output dir>/<name>`, the
|
|
668
|
-
* artifacts earlier Extension build hooks wrote in this same build.
|
|
669
|
-
* - Source (`bun run`, `node`, `deno run`): `.crust/root/<name>` under the
|
|
670
|
-
* nearest package root of the real `process.argv[1]` entrypoint (following source
|
|
671
|
-
* links) — the output of the last `crust build`.
|
|
672
|
-
*
|
|
673
|
-
* @throws {Error} when `name` is not a single path segment, or in source mode
|
|
674
|
-
* when the entrypoint cannot be resolved or has no enclosing `package.json`.
|
|
675
|
-
*/
|
|
676
|
-
function resolveArtifactDir(name) {
|
|
677
|
-
if (name === "" || name === "." || name === ".." || /[\\/]/.test(name)) throw new Error(`Artifact name must be a single directory name, got ${JSON.stringify(name)}.`);
|
|
678
|
-
if (isCompiledExecutable()) return join(dirname(process.execPath), name);
|
|
679
|
-
if (process.env.CRUST_INTERNAL_BUILD === "1") return resolve(fileURLToPath(import.meta.url), "..", "..", name);
|
|
680
|
-
const buildOutDir = process.env[BUILD_OUT_DIR_ENV];
|
|
681
|
-
if (buildOutDir) return join(buildOutDir, name);
|
|
682
|
-
const entrypoint = process.argv[1];
|
|
683
|
-
let sourceEntrypoint = entrypoint;
|
|
684
|
-
if (entrypoint) try {
|
|
685
|
-
sourceEntrypoint = realpathSync(entrypoint);
|
|
686
|
-
} catch (cause) {
|
|
687
|
-
throw new Error(`Could not resolve artifact "${name}": could not resolve source entrypoint "${entrypoint}".`, { cause });
|
|
688
|
-
}
|
|
689
|
-
const packageRoot = sourceEntrypoint ? findNearestPackageRoot(sourceEntrypoint) : null;
|
|
690
|
-
if (!packageRoot) throw new Error(`Could not resolve artifact "${name}": no package.json was found above ${entrypoint ? `entrypoint "${entrypoint}"` : "process.argv[1] (unset)"}.`);
|
|
691
|
-
return join(packageRoot, ".crust", "root", name);
|
|
692
|
-
}
|
|
693
|
-
//#endregion
|
|
694
|
-
//#region ../utils/src/terminal.ts
|
|
695
|
-
const STORAGE_KEY = Symbol.for("crustjs.terminal.io");
|
|
696
|
-
const AMBIENT_CALLBACKS_KEY = Symbol.for("crustjs.terminal.ambient-callbacks");
|
|
697
|
-
const globalWithStorage = globalThis;
|
|
698
|
-
const globalWithAmbientCallbacks = globalThis;
|
|
699
|
-
/** Bundled copies in Core, Prompts, and Progress share process-wide terminal state. */
|
|
700
|
-
const storage = globalWithStorage[STORAGE_KEY] ??= new AsyncLocalStorage();
|
|
701
|
-
const ambientCallbacks = globalWithAmbientCallbacks[AMBIENT_CALLBACKS_KEY] ??= /* @__PURE__ */ new WeakMap();
|
|
702
|
-
/** Run a function with terminal streams available in its async scope. */
|
|
703
|
-
function withTerminalIO(io, fn) {
|
|
704
|
-
const current = storage.getStore();
|
|
705
|
-
return storage.run({
|
|
706
|
-
input: io.input ?? current?.input,
|
|
707
|
-
output: io.output ?? current?.output
|
|
708
|
-
}, fn);
|
|
709
|
-
}
|
|
710
|
-
function lineBufferedOutput(io) {
|
|
711
|
-
let pending = "";
|
|
712
|
-
const output = new Writable({
|
|
713
|
-
decodeStrings: false,
|
|
714
|
-
write(chunk, _encoding, callback) {
|
|
715
|
-
pending += chunk.toString();
|
|
716
|
-
let newline = pending.indexOf("\n");
|
|
717
|
-
while (newline !== -1) {
|
|
718
|
-
io.stderr(pending.slice(0, newline));
|
|
719
|
-
pending = pending.slice(newline + 1);
|
|
720
|
-
newline = pending.indexOf("\n");
|
|
721
|
-
}
|
|
722
|
-
callback();
|
|
723
|
-
}
|
|
724
|
-
});
|
|
725
|
-
ambientCallbacks.set(output, io);
|
|
726
|
-
return output;
|
|
727
|
-
}
|
|
728
|
-
/** Run a function with Core's line-oriented output bridged into the terminal stream scope. */
|
|
729
|
-
function withAmbientTerminalIO(io, fn) {
|
|
730
|
-
const current = storage.getStore();
|
|
731
|
-
return withTerminalIO({
|
|
732
|
-
input: current?.input,
|
|
733
|
-
output: current?.output && !ambientCallbacks.has(current.output) ? current.output : lineBufferedOutput(io)
|
|
734
|
-
}, fn);
|
|
735
|
-
}
|
|
736
|
-
//#endregion
|
|
737
|
-
//#region ../utils/src/primitive.ts
|
|
738
|
-
/**
|
|
739
|
-
* Attempts to coerce a string to a number, returning `undefined` only when the
|
|
740
|
-
* result is `NaN`. Callers decide whether `undefined` means throw or fallback.
|
|
741
|
-
*
|
|
742
|
-
* @example
|
|
743
|
-
* ```ts
|
|
744
|
-
* tryCoerceNumber("42"); // 42
|
|
745
|
-
* tryCoerceNumber("abc"); // undefined
|
|
746
|
-
* tryCoerceNumber(""); // 0
|
|
747
|
-
* ```
|
|
748
|
-
*/
|
|
749
|
-
function tryCoerceNumber(raw) {
|
|
750
|
-
const num = Number(raw);
|
|
751
|
-
return Number.isNaN(num) ? void 0 : num;
|
|
752
|
-
}
|
|
753
|
-
/**
|
|
754
|
-
* Coerces Crust boolean strings using the existing strict truthy spellings.
|
|
755
|
-
*
|
|
756
|
-
* @example
|
|
757
|
-
* ```ts
|
|
758
|
-
* coerceBooleanString("true"); // true
|
|
759
|
-
* coerceBooleanString("1"); // true
|
|
760
|
-
* coerceBooleanString("false"); // false
|
|
761
|
-
* ```
|
|
762
|
-
*/
|
|
763
|
-
function coerceBooleanString(raw) {
|
|
764
|
-
return raw === "true" || raw === "1";
|
|
765
|
-
}
|
|
766
|
-
//#endregion
|
|
767
617
|
//#region src/parsing/coercers.ts
|
|
768
618
|
/**
|
|
769
619
|
* Coerce a raw argv string into a {@link URL} instance via the WHATWG
|
|
@@ -1203,67 +1053,6 @@ function validateParsed(command, parsed) {
|
|
|
1203
1053
|
validateRequiredFlags(flagsDef, flags);
|
|
1204
1054
|
}
|
|
1205
1055
|
//#endregion
|
|
1206
|
-
//#region ../utils/src/schema.ts
|
|
1207
|
-
/**
|
|
1208
|
-
* Format an issue path into a dot-path string.
|
|
1209
|
-
*
|
|
1210
|
-
* - Numeric keys (array indexes) are rendered with bracket notation: `items[0]`
|
|
1211
|
-
* - String/symbol keys are joined with dots: `flags.verbose`
|
|
1212
|
-
* - An empty path array produces an empty string (root-level issue)
|
|
1213
|
-
*
|
|
1214
|
-
* @example
|
|
1215
|
-
* ```ts
|
|
1216
|
-
* formatPath(["flags", "verbose"]);
|
|
1217
|
-
* // => "flags.verbose"
|
|
1218
|
-
*
|
|
1219
|
-
* formatPath(["args", 0]);
|
|
1220
|
-
* // => "args[0]"
|
|
1221
|
-
*
|
|
1222
|
-
* formatPath([]);
|
|
1223
|
-
* // => ""
|
|
1224
|
-
* ```
|
|
1225
|
-
*/
|
|
1226
|
-
function isNumericPathSegment(segment) {
|
|
1227
|
-
return typeof segment === "number";
|
|
1228
|
-
}
|
|
1229
|
-
function formatPath(path) {
|
|
1230
|
-
return path.map((segment, index) => isNumericPathSegment(segment) ? `[${segment}]` : index > 0 ? `.${String(segment)}` : String(segment)).join("");
|
|
1231
|
-
}
|
|
1232
|
-
/**
|
|
1233
|
-
* Normalize a Standard Schema issue path to an array of `PropertyKey`.
|
|
1234
|
-
*
|
|
1235
|
-
* Standard Schema paths contain either bare `PropertyKey` values or
|
|
1236
|
-
* `{ key: PropertyKey }` segment objects; both forms are flattened to
|
|
1237
|
-
* plain `PropertyKey`. Returns an empty array for a root-level issue
|
|
1238
|
-
* (`undefined`).
|
|
1239
|
-
*/
|
|
1240
|
-
function isStandardPathSegment(segment) {
|
|
1241
|
-
return typeof segment === "object" && segment !== null && "key" in segment;
|
|
1242
|
-
}
|
|
1243
|
-
function normalizeStandardPath(path) {
|
|
1244
|
-
if (!path) return [];
|
|
1245
|
-
return path.map((segment) => isStandardPathSegment(segment) ? segment.key : segment);
|
|
1246
|
-
}
|
|
1247
|
-
/**
|
|
1248
|
-
* Normalize Standard Schema issues into canonical `ValidationIssue` objects.
|
|
1249
|
-
*
|
|
1250
|
-
* Applies an optional prefix (e.g. `["flags", "verbose"]`) to each issue
|
|
1251
|
-
* path, then formats each path to its canonical dot-path string.
|
|
1252
|
-
*
|
|
1253
|
-
* @param issues — Raw Standard Schema issues from a failed validation
|
|
1254
|
-
* @param prefix — Optional path segments prepended to each issue path
|
|
1255
|
-
*/
|
|
1256
|
-
function normalizeStandardIssues(issues, prefix = []) {
|
|
1257
|
-
return issues.map((issue) => {
|
|
1258
|
-
const resolvedPath = normalizeStandardPath(issue.path);
|
|
1259
|
-
const fullPath = [...prefix, ...resolvedPath];
|
|
1260
|
-
return {
|
|
1261
|
-
message: issue.message,
|
|
1262
|
-
path: formatPath(fullPath)
|
|
1263
|
-
};
|
|
1264
|
-
});
|
|
1265
|
-
}
|
|
1266
|
-
//#endregion
|
|
1267
1056
|
//#region src/parsing/schema.ts
|
|
1268
1057
|
async function runSchema(schema, raw, path, issues) {
|
|
1269
1058
|
const result = await schema["~standard"].validate(raw);
|
|
@@ -1976,4 +1765,4 @@ async function executeInvocation(node, options, materializeCommandDefinition) {
|
|
|
1976
1765
|
return await (hasInjectedIO(options?.io) ? withAmbientTerminalIO(io, invoke) : invoke());
|
|
1977
1766
|
}
|
|
1978
1767
|
//#endregion
|
|
1979
|
-
export {
|
|
1768
|
+
export { CrustError as S, defineContext as _, runInvocation as a, normalizeArg as b, sectionsFor as c, installExtensionContexts as d, validateCommandSections as f, contextSources as g, defineExtension as h, resolveTypedPath as i, visibleSectionsFor as l, registerFlag as m, executeInvocation as n, snapshotCommand as o, createCommandNode as p, prepareInvocation as r, isListed as s, SNAPSHOT_PATH_ENV as t, cloneCommandNode as u, definingOf as v, normalizeFlag as x, validateContextAvailability as y };
|
package/dist/tooling.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Ct as FlagSnapshot, St as CommandSnapshot, g as DeclaredDefault, kt as ExtensionId, m as CommandSection } from "./context-Cud9frNp.js";
|
|
2
|
+
import { BUILD_OUT_DIR_ENV } from "@crustjs/utils/artifacts";
|
|
2
3
|
//#region src/command/invocation.d.ts
|
|
3
4
|
/**
|
|
4
5
|
* Snapshot subprocess protocol used by first-party build tooling.
|
package/dist/tooling.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { c as sectionsFor, l as visibleSectionsFor, s as isListed, t as SNAPSHOT_PATH_ENV
|
|
1
|
+
import { c as sectionsFor, l as visibleSectionsFor, s as isListed, t as SNAPSHOT_PATH_ENV } from "./invocation-B17RvTLb.js";
|
|
2
|
+
import { BUILD_OUT_DIR_ENV } from "@crustjs/utils/artifacts";
|
|
2
3
|
//#region src/command/documentation.ts
|
|
3
4
|
function isNonFiniteNumber(value) {
|
|
4
5
|
return typeof value === "number" && !Number.isFinite(value);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crustjs/core",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Core library for the Crust CLI framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -48,9 +48,11 @@
|
|
|
48
48
|
"prepack": "cp ../../LICENSE LICENSE",
|
|
49
49
|
"postpack": "rm -f LICENSE"
|
|
50
50
|
},
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"@crustjs/utils": "^0.1.0"
|
|
53
|
+
},
|
|
51
54
|
"devDependencies": {
|
|
52
55
|
"@crustjs/config": "0.0.0",
|
|
53
|
-
"@crustjs/utils": "0.0.0",
|
|
54
56
|
"tsdown": "^0.23.0"
|
|
55
57
|
},
|
|
56
58
|
"peerDependencies": {
|