@orkestrel/scaffold 0.0.15 → 0.0.17
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/README.md +13 -3
- package/dist/bin/scaffold.js +73 -34
- package/dist/bin/scaffold.js.map +1 -1
- package/dist/host/claude/agents/orkestrel.md +14 -14
- package/dist/host/guides/src/scaffold.md +78 -30
- package/dist/src/core/index.cjs +173 -37
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +3 -3
- package/dist/src/core/index.d.ts +3 -3
- package/dist/src/core/index.js +173 -37
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +119 -48
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +31 -6
- package/dist/src/server/index.d.ts +31 -6
- package/dist/src/server/index.js +120 -50
- package/dist/src/server/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -296,6 +296,34 @@ function isMaterializerEventHooks(value) {
|
|
|
296
296
|
}
|
|
297
297
|
//#endregion
|
|
298
298
|
//#region src/server/parsers.ts
|
|
299
|
+
/**
|
|
300
|
+
* Parse and semantically validate bare registry package names before Sync performs network I/O.
|
|
301
|
+
*
|
|
302
|
+
* @param value - Untrusted package-name collection.
|
|
303
|
+
* @returns A frozen owned snapshot of valid unique npm package names.
|
|
304
|
+
*/
|
|
305
|
+
function parseSyncNames(value) {
|
|
306
|
+
const owned = (0, _orkestrel_contract.attempt)(() => {
|
|
307
|
+
if (!(0, _src_core.isDenseDataArray)(value, 1e3, (candidate) => typeof candidate === "string")) throw new Error("package names are malformed");
|
|
308
|
+
const length = Reflect.getOwnPropertyDescriptor(value, "length")?.value;
|
|
309
|
+
if (typeof length !== "number" || !Number.isSafeInteger(length) || length < 0 || length > 1e3) throw new Error("package name length is malformed");
|
|
310
|
+
const names = [];
|
|
311
|
+
for (let index = 0; index < length; index += 1) {
|
|
312
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(value, String(index));
|
|
313
|
+
if (descriptor === void 0 || !Reflect.has(descriptor, "value") || typeof descriptor.value !== "string") throw new Error("package name is malformed");
|
|
314
|
+
names.push(descriptor.value);
|
|
315
|
+
}
|
|
316
|
+
return Object.freeze(names);
|
|
317
|
+
});
|
|
318
|
+
if (!owned.success) throw new _src_core.ScaffoldError("INVALID", "Sync package names are malformed", { error: owned.error });
|
|
319
|
+
const snapshot = owned.value;
|
|
320
|
+
const seen = /* @__PURE__ */ new Set();
|
|
321
|
+
for (const name of snapshot) {
|
|
322
|
+
if (name.length === 0 || name.length > _src_core.MAX_DEPENDENCY_NAME_LENGTH || !_src_core.EXTRA_NAME_PATTERN.test(name) || seen.has(name)) throw new _src_core.ScaffoldError("INVALID", "Sync package names are invalid", { name });
|
|
323
|
+
seen.add(name);
|
|
324
|
+
}
|
|
325
|
+
return snapshot;
|
|
326
|
+
}
|
|
299
327
|
/** Parse and semantically validate dependency data before Sync performs network I/O. */
|
|
300
328
|
function parseSyncDependencies(value, external) {
|
|
301
329
|
const owned = (0, _orkestrel_contract.attempt)(() => {
|
|
@@ -929,16 +957,16 @@ function packageShortName(name) {
|
|
|
929
957
|
return name.startsWith("@orkestrel/") ? name.slice(11) : name;
|
|
930
958
|
}
|
|
931
959
|
/**
|
|
932
|
-
* Read bounded physical local guide mirrors for
|
|
960
|
+
* Read bounded physical local guide mirrors for package names.
|
|
933
961
|
*
|
|
934
962
|
* @param target - The package root.
|
|
935
|
-
* @param
|
|
936
|
-
* @returns Existing guide content keyed by
|
|
963
|
+
* @param names - The package names whose mirrors are eligible.
|
|
964
|
+
* @returns Existing guide content keyed by package name.
|
|
937
965
|
*/
|
|
938
|
-
function readGuideReferences(target,
|
|
966
|
+
function readGuideReferences(target, names) {
|
|
939
967
|
const current = {};
|
|
940
|
-
for (const
|
|
941
|
-
const path = `guides/src/${packageShortName(
|
|
968
|
+
for (const name of names) {
|
|
969
|
+
const path = `guides/src/${packageShortName(name)}.md`;
|
|
942
970
|
const full = resolvePhysicalPath(target, path, "TARGET", "target");
|
|
943
971
|
const status = (0, _orkestrel_contract.attempt)(() => (0, node_fs.lstatSync)(full));
|
|
944
972
|
if (!status.success) {
|
|
@@ -949,7 +977,7 @@ function readGuideReferences(target, dependencies) {
|
|
|
949
977
|
});
|
|
950
978
|
}
|
|
951
979
|
if (!status.value.isFile() || status.value.isSymbolicLink() || status.value.nlink !== 1) throw new _src_core.ScaffoldError("TARGET", `Local guide is not a physical file at ${path}`, { path });
|
|
952
|
-
current[
|
|
980
|
+
current[name] = readFileText(target, path, "TARGET", "target");
|
|
953
981
|
}
|
|
954
982
|
return Object.freeze(current);
|
|
955
983
|
}
|
|
@@ -3488,8 +3516,10 @@ var Materializer = class {
|
|
|
3488
3516
|
* map, a fetched guide byte-equal to its entry verdicts `current`, anything
|
|
3489
3517
|
* differing or absent from the map verdicts `behind`; WITHOUT the map, every
|
|
3490
3518
|
* successful fetch verdicts `behind` (no reference means it needs syncing).
|
|
3491
|
-
* `pull` builds that map itself from the TARGET's
|
|
3492
|
-
*
|
|
3519
|
+
* `pull` builds that map itself from the TARGET's declared dependency mirrors;
|
|
3520
|
+
* `mirror` builds it from the registry's exact organization package list,
|
|
3521
|
+
* excluding the target's own guide and issuing no registry version reads. Both
|
|
3522
|
+
* verdicts are target-relative; `write` commits only the
|
|
3493
3523
|
* `behind` guides (never `current`, `missing`, or `failed`, which carries no
|
|
3494
3524
|
* trustworthy content) under the same realpath-anchored containment law
|
|
3495
3525
|
* `Materializer` enforces. After `destroy()` every method throws `DESTROYED`;
|
|
@@ -3541,50 +3571,67 @@ var Sync = class Sync {
|
|
|
3541
3571
|
get emitter() {
|
|
3542
3572
|
return this.#emitter;
|
|
3543
3573
|
}
|
|
3574
|
+
async lookup(names) {
|
|
3575
|
+
this.#ensureAlive();
|
|
3576
|
+
return this.#lookup(names, Float64Array.of(this.#budget));
|
|
3577
|
+
}
|
|
3544
3578
|
async guides(deps, current) {
|
|
3545
3579
|
this.#ensureAlive();
|
|
3546
|
-
|
|
3580
|
+
const parsed = parseSyncDependencies(deps, false);
|
|
3581
|
+
return this.#guides(parsed.map((dependency) => dependency.name), current, Float64Array.of(this.#budget));
|
|
3547
3582
|
}
|
|
3548
3583
|
async versions(deps) {
|
|
3549
3584
|
this.#ensureAlive();
|
|
3550
3585
|
return this.#versions(deps, Float64Array.of(this.#budget));
|
|
3551
3586
|
}
|
|
3552
|
-
#guides(
|
|
3553
|
-
const parsed =
|
|
3587
|
+
#guides(names, current, allowance) {
|
|
3588
|
+
const parsed = parseSyncNames(names);
|
|
3554
3589
|
if (parsed.length > this.#items) throw new _src_core.ScaffoldError("INVALID", "Sync dependency count exceeds its item limit", {
|
|
3555
3590
|
items: parsed.length,
|
|
3556
3591
|
limit: this.#items
|
|
3557
3592
|
});
|
|
3558
|
-
const reference = parseSyncCurrent(current, parsed
|
|
3559
|
-
return Sync.#runPool(parsed, this.#concurrency, async (
|
|
3560
|
-
const short = packageShortName(
|
|
3593
|
+
const reference = parseSyncCurrent(current, parsed, this.#budget);
|
|
3594
|
+
return Sync.#runPool(parsed, this.#concurrency, async (name) => {
|
|
3595
|
+
const short = packageShortName(name);
|
|
3561
3596
|
const url = this.#guideUrl(short);
|
|
3562
3597
|
const outcome = await Sync.#fetchText(url, this.#guidesTimeout, this.#retries, this.#limit, allowance, this.#controller.signal);
|
|
3563
|
-
const guide = Sync.#toGuideSync(
|
|
3598
|
+
const guide = Sync.#toGuideSync(name, short, outcome, reference);
|
|
3564
3599
|
if (outcome.kind === "failed") this.#emitter.emit("error", outcome.error);
|
|
3565
|
-
this.#emitter.emit("guide",
|
|
3600
|
+
this.#emitter.emit("guide", name);
|
|
3566
3601
|
if (this.#strict && (guide.freshness === "missing" || guide.freshness === "failed")) throw new _src_core.ScaffoldError("FETCH", `Failed to fetch guide at ${url}`, {
|
|
3567
3602
|
url,
|
|
3568
|
-
name
|
|
3603
|
+
name
|
|
3569
3604
|
});
|
|
3570
3605
|
return guide;
|
|
3571
3606
|
});
|
|
3572
3607
|
}
|
|
3573
|
-
#versions(deps, allowance) {
|
|
3608
|
+
async #versions(deps, allowance) {
|
|
3574
3609
|
const parsed = parseSyncDependencies(deps, true);
|
|
3610
|
+
const lookups = await this.#lookup(parsed.map((dependency) => dependency.name), allowance);
|
|
3611
|
+
const versions = [];
|
|
3612
|
+
for (let index = 0; index < parsed.length; index += 1) {
|
|
3613
|
+
const dep = parsed[index];
|
|
3614
|
+
const lookup = lookups[index];
|
|
3615
|
+
if (dep === void 0 || lookup === void 0) throw new _src_core.ScaffoldError("INVALID", "Sync version lookup result is incomplete");
|
|
3616
|
+
versions.push(Sync.#toVersionSync(dep, lookup));
|
|
3617
|
+
}
|
|
3618
|
+
return Object.freeze(versions);
|
|
3619
|
+
}
|
|
3620
|
+
#lookup(names, allowance) {
|
|
3621
|
+
const parsed = parseSyncNames(names);
|
|
3575
3622
|
if (parsed.length > this.#items) throw new _src_core.ScaffoldError("INVALID", "Sync dependency count exceeds its item limit", {
|
|
3576
3623
|
items: parsed.length,
|
|
3577
3624
|
limit: this.#items
|
|
3578
3625
|
});
|
|
3579
|
-
return Sync.#runPool(parsed, this.#concurrency, async (
|
|
3580
|
-
const url = this.#registryUrl(
|
|
3626
|
+
return Sync.#runPool(parsed, this.#concurrency, async (name) => {
|
|
3627
|
+
const url = this.#registryUrl(name);
|
|
3581
3628
|
const outcome = await Sync.#fetchText(url, this.#registryTimeout, this.#retries, this.#limit, allowance, this.#controller.signal);
|
|
3582
|
-
const version = Sync.#
|
|
3629
|
+
const version = Sync.#toVersionLookup(name, outcome);
|
|
3583
3630
|
if (outcome.kind === "failed") this.#emitter.emit("error", outcome.error);
|
|
3584
|
-
this.#emitter.emit("version",
|
|
3631
|
+
this.#emitter.emit("version", name);
|
|
3585
3632
|
if (this.#strict && (version.freshness === "missing" || version.freshness === "failed")) throw new _src_core.ScaffoldError("FETCH", `Failed to fetch registry version at ${url}`, {
|
|
3586
3633
|
url,
|
|
3587
|
-
name
|
|
3634
|
+
name
|
|
3588
3635
|
});
|
|
3589
3636
|
return version;
|
|
3590
3637
|
});
|
|
@@ -3617,16 +3664,9 @@ var Sync = class Sync {
|
|
|
3617
3664
|
*/
|
|
3618
3665
|
async catalog() {
|
|
3619
3666
|
this.#ensureAlive();
|
|
3620
|
-
const orgUrl = this.#orgUrl();
|
|
3621
3667
|
const allowance = Float64Array.of(this.#budget);
|
|
3622
|
-
const
|
|
3623
|
-
|
|
3624
|
-
if (names.length > this.#items) throw new _src_core.ScaffoldError("FETCH", `Package list at ${orgUrl} exceeds the item limit`, {
|
|
3625
|
-
url: orgUrl,
|
|
3626
|
-
items: names.length,
|
|
3627
|
-
limit: this.#items
|
|
3628
|
-
});
|
|
3629
|
-
return [...await Sync.#runPool(names, this.#concurrency, async (name) => {
|
|
3668
|
+
const names = await this.#packages(allowance);
|
|
3669
|
+
return await Sync.#runPool(names, this.#concurrency, async (name) => {
|
|
3630
3670
|
const packumentOutcome = await Sync.#fetchText(this.#registryUrl(name), this.#registryTimeout, this.#retries, this.#limit, allowance, this.#controller.signal);
|
|
3631
3671
|
const packument = Sync.#toPackument(packumentOutcome);
|
|
3632
3672
|
const short = packageShortName(name);
|
|
@@ -3642,7 +3682,7 @@ var Sync = class Sync {
|
|
|
3642
3682
|
version: packument.version,
|
|
3643
3683
|
description
|
|
3644
3684
|
};
|
|
3645
|
-
})
|
|
3685
|
+
});
|
|
3646
3686
|
}
|
|
3647
3687
|
async pull(target, dependencies) {
|
|
3648
3688
|
this.#ensureAlive();
|
|
@@ -3651,10 +3691,21 @@ var Sync = class Sync {
|
|
|
3651
3691
|
const deps = dependencies === void 0 ? declared : parseSyncDependencies(dependencies, false);
|
|
3652
3692
|
if (dependencies !== void 0 && deps.some((dependency) => !declared.some((candidate) => candidate.name === dependency.name && candidate.range === dependency.range))) throw new _src_core.ScaffoldError("INVALID", "Sync pull selection is not declared by the target");
|
|
3653
3693
|
const name = (0, _src_core.manifestToName)(manifest);
|
|
3654
|
-
const
|
|
3655
|
-
const current = readGuideReferences(target,
|
|
3694
|
+
const guideNames = deps.map((dependency) => dependency.name).filter((dependency) => dependency !== name);
|
|
3695
|
+
const current = readGuideReferences(target, guideNames);
|
|
3656
3696
|
const allowance = Float64Array.of(this.#budget);
|
|
3657
|
-
const report = syncReportOf(target, await this.#guides(
|
|
3697
|
+
const report = syncReportOf(target, await this.#guides(guideNames, current, allowance), await this.#versions(deps, allowance));
|
|
3698
|
+
this.#emitter.emit("done", report);
|
|
3699
|
+
return report;
|
|
3700
|
+
}
|
|
3701
|
+
async mirror(target) {
|
|
3702
|
+
this.#ensureAlive();
|
|
3703
|
+
const manifest = readManifest(target);
|
|
3704
|
+
const name = (0, _src_core.manifestToName)(manifest);
|
|
3705
|
+
const allowance = Float64Array.of(this.#budget);
|
|
3706
|
+
const names = (await this.#packages(allowance)).filter((candidate) => candidate !== name);
|
|
3707
|
+
const current = readGuideReferences(target, names);
|
|
3708
|
+
const report = syncReportOf(target, await this.#guides(names, current, allowance), []);
|
|
3658
3709
|
this.#emitter.emit("done", report);
|
|
3659
3710
|
return report;
|
|
3660
3711
|
}
|
|
@@ -3747,6 +3798,17 @@ var Sync = class Sync {
|
|
|
3747
3798
|
#orgUrl() {
|
|
3748
3799
|
return `${this.#registryBase}/-/org/orkestrel/package`;
|
|
3749
3800
|
}
|
|
3801
|
+
async #packages(allowance) {
|
|
3802
|
+
const url = this.#orgUrl();
|
|
3803
|
+
const outcome = await Sync.#fetchText(url, this.#registryTimeout, this.#retries, this.#limit, allowance, this.#controller.signal);
|
|
3804
|
+
const names = Sync.#toOrgPackages(outcome, url);
|
|
3805
|
+
if (names.length > this.#items) throw new _src_core.ScaffoldError("FETCH", `Package list at ${url} exceeds the item limit`, {
|
|
3806
|
+
url,
|
|
3807
|
+
items: names.length,
|
|
3808
|
+
limit: this.#items
|
|
3809
|
+
});
|
|
3810
|
+
return [...names].sort((left, right) => left < right ? -1 : left > right ? 1 : 0);
|
|
3811
|
+
}
|
|
3750
3812
|
static async #runPool(items, concurrency, worker) {
|
|
3751
3813
|
const results = new Array(items.length);
|
|
3752
3814
|
const state = {
|
|
@@ -3925,34 +3987,42 @@ var Sync = class Sync {
|
|
|
3925
3987
|
...baseline === void 0 ? {} : { baseline }
|
|
3926
3988
|
};
|
|
3927
3989
|
}
|
|
3928
|
-
static #
|
|
3990
|
+
static #toVersionLookup(name, outcome) {
|
|
3929
3991
|
if (outcome.kind === "missing") return {
|
|
3930
|
-
name
|
|
3931
|
-
range: dep.range,
|
|
3932
|
-
latest: "",
|
|
3992
|
+
name,
|
|
3933
3993
|
freshness: "missing",
|
|
3934
3994
|
note: "HTTP 404"
|
|
3935
3995
|
};
|
|
3936
3996
|
if (outcome.kind === "failed") return {
|
|
3937
|
-
name
|
|
3938
|
-
range: dep.range,
|
|
3939
|
-
latest: "",
|
|
3997
|
+
name,
|
|
3940
3998
|
freshness: "failed",
|
|
3941
3999
|
note: outcome.note
|
|
3942
4000
|
};
|
|
3943
4001
|
const latest = Sync.#parseLatest(outcome.text);
|
|
3944
4002
|
if (latest === void 0) return {
|
|
4003
|
+
name,
|
|
4004
|
+
freshness: "failed",
|
|
4005
|
+
note: "malformed registry response (missing dist-tags.latest)"
|
|
4006
|
+
};
|
|
4007
|
+
return {
|
|
4008
|
+
name,
|
|
4009
|
+
latest,
|
|
4010
|
+
freshness: "behind"
|
|
4011
|
+
};
|
|
4012
|
+
}
|
|
4013
|
+
static #toVersionSync(dep, lookup) {
|
|
4014
|
+
if (lookup.freshness !== "behind") return {
|
|
3945
4015
|
name: dep.name,
|
|
3946
4016
|
range: dep.range,
|
|
3947
4017
|
latest: "",
|
|
3948
|
-
freshness:
|
|
3949
|
-
note:
|
|
4018
|
+
freshness: lookup.freshness,
|
|
4019
|
+
note: lookup.note
|
|
3950
4020
|
};
|
|
3951
4021
|
return {
|
|
3952
4022
|
name: dep.name,
|
|
3953
4023
|
range: dep.range,
|
|
3954
|
-
latest,
|
|
3955
|
-
freshness: (0, _src_core.rangeToFreshness)(dep.range, latest)
|
|
4024
|
+
latest: lookup.latest,
|
|
4025
|
+
freshness: (0, _src_core.rangeToFreshness)(dep.range, lookup.latest)
|
|
3956
4026
|
};
|
|
3957
4027
|
}
|
|
3958
4028
|
static #toOrgPackages(outcome, url) {
|
|
@@ -4151,6 +4221,7 @@ exports.parseSyncBase = parseSyncBase;
|
|
|
4151
4221
|
exports.parseSyncBranch = parseSyncBranch;
|
|
4152
4222
|
exports.parseSyncCurrent = parseSyncCurrent;
|
|
4153
4223
|
exports.parseSyncDependencies = parseSyncDependencies;
|
|
4224
|
+
exports.parseSyncNames = parseSyncNames;
|
|
4154
4225
|
exports.parseSyncOptions = parseSyncOptions;
|
|
4155
4226
|
exports.parseWritePreconditions = parseWritePreconditions;
|
|
4156
4227
|
exports.pruneTargets = pruneTargets;
|