@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.
@@ -705,6 +705,14 @@ export declare function parseSyncCurrent(value: Readonly<Record<string, string>>
705
705
  /** Parse and semantically validate dependency data before Sync performs network I/O. */
706
706
  export declare function parseSyncDependencies(value: unknown, external: boolean): readonly Dependency[];
707
707
 
708
+ /**
709
+ * Parse and semantically validate bare registry package names before Sync performs network I/O.
710
+ *
711
+ * @param value - Untrusted package-name collection.
712
+ * @returns A frozen owned snapshot of valid unique npm package names.
713
+ */
714
+ export declare function parseSyncNames(value: unknown): readonly string[];
715
+
708
716
  /**
709
717
  * Parse the exact bounded `SyncOptions` boundary before allocating resources.
710
718
  *
@@ -779,13 +787,13 @@ export declare function readFileHex(root: string, path: string, code: ScaffoldEr
779
787
  export declare function readFileText(root: string, path: string, code: ScaffoldErrorCode, boundary: string, limit?: number): string;
780
788
 
781
789
  /**
782
- * Read bounded physical local guide mirrors for declared dependencies.
790
+ * Read bounded physical local guide mirrors for package names.
783
791
  *
784
792
  * @param target - The package root.
785
- * @param dependencies - The declared dependencies whose mirrors are eligible.
786
- * @returns Existing guide content keyed by dependency name.
793
+ * @param names - The package names whose mirrors are eligible.
794
+ * @returns Existing guide content keyed by package name.
787
795
  */
788
- export declare function readGuideReferences(target: string, dependencies: readonly Dependency[]): Readonly<Record<string, string>>;
796
+ export declare function readGuideReferences(target: string, names: readonly string[]): Readonly<Record<string, string>>;
789
797
 
790
798
  /**
791
799
  * Read and validate a vendored host root's `manifest.json`, when present.
@@ -1020,8 +1028,10 @@ export declare function storagePath(path: string): string;
1020
1028
  * map, a fetched guide byte-equal to its entry verdicts `current`, anything
1021
1029
  * differing or absent from the map verdicts `behind`; WITHOUT the map, every
1022
1030
  * successful fetch verdicts `behind` (no reference means it needs syncing).
1023
- * `pull` builds that map itself from the TARGET's own `guides/src/<short>.md`
1024
- * mirrors, so its verdicts are target-relative; `write` commits only the
1031
+ * `pull` builds that map itself from the TARGET's declared dependency mirrors;
1032
+ * `mirror` builds it from the registry's exact organization package list,
1033
+ * excluding the target's own guide and issuing no registry version reads. Both
1034
+ * verdicts are target-relative; `write` commits only the
1025
1035
  * `behind` guides (never `current`, `missing`, or `failed`, which carries no
1026
1036
  * trustworthy content) under the same realpath-anchored containment law
1027
1037
  * `Materializer` enforces. After `destroy()` every method throws `DESTROYED`;
@@ -1041,6 +1051,7 @@ export declare class Sync implements SyncInterface {
1041
1051
  #private;
1042
1052
  constructor(options?: SyncOptions);
1043
1053
  get emitter(): EmitterInterface<SyncEventMap>;
1054
+ lookup(names: readonly string[]): Promise<readonly VersionLookup[]>;
1044
1055
  guides(deps: readonly Dependency[], current?: Readonly<Record<string, string>>): Promise<readonly GuideSync[]>;
1045
1056
  versions(deps: readonly Dependency[]): Promise<readonly VersionSync[]>;
1046
1057
  /**
@@ -1071,6 +1082,7 @@ export declare class Sync implements SyncInterface {
1071
1082
  */
1072
1083
  catalog(): Promise<readonly CatalogEntry[]>;
1073
1084
  pull(target: string, dependencies?: readonly Dependency[]): Promise<SyncReport>;
1085
+ mirror(target: string): Promise<SyncReport>;
1074
1086
  write(report: SyncReport, target: string): Promise<readonly string[]>;
1075
1087
  destroy(): void;
1076
1088
  }
@@ -1121,10 +1133,12 @@ export declare function syncGuideOptionsShape(): ObjectShape<{
1121
1133
  */
1122
1134
  export declare interface SyncInterface {
1123
1135
  readonly emitter: EmitterInterface<SyncEventMap>;
1136
+ lookup(names: readonly string[]): Promise<readonly VersionLookup[]>;
1124
1137
  guides(deps: readonly Dependency[], current?: Readonly<Record<string, string>>): Promise<readonly GuideSync[]>;
1125
1138
  versions(deps: readonly Dependency[]): Promise<readonly VersionSync[]>;
1126
1139
  catalog(): Promise<readonly CatalogEntry[]>;
1127
1140
  pull(target: string, dependencies?: readonly Dependency[]): Promise<SyncReport>;
1141
+ mirror(target: string): Promise<SyncReport>;
1128
1142
  write(report: SyncReport, target: string): Promise<readonly string[]>;
1129
1143
  destroy(): void;
1130
1144
  }
@@ -1282,6 +1296,17 @@ export declare function validateWriteTarget(transaction: WriteTransaction, owned
1282
1296
  */
1283
1297
  export declare function vendoredPruneSet(host: string, directory: string): ReadonlySet<string>;
1284
1298
 
1299
+ /** One bare-name registry lookup, without a caller-invented declaration range. */
1300
+ export declare type VersionLookup = {
1301
+ readonly name: string;
1302
+ readonly latest: string;
1303
+ readonly freshness: 'behind';
1304
+ } | {
1305
+ readonly name: string;
1306
+ readonly freshness: 'missing' | 'failed';
1307
+ readonly note: string;
1308
+ };
1309
+
1285
1310
  /** Exact SHA-256 digest accepted by write preconditions. */
1286
1311
  export declare const WRITE_DIGEST_PATTERN: RegExp;
1287
1312
 
@@ -705,6 +705,14 @@ export declare function parseSyncCurrent(value: Readonly<Record<string, string>>
705
705
  /** Parse and semantically validate dependency data before Sync performs network I/O. */
706
706
  export declare function parseSyncDependencies(value: unknown, external: boolean): readonly Dependency[];
707
707
 
708
+ /**
709
+ * Parse and semantically validate bare registry package names before Sync performs network I/O.
710
+ *
711
+ * @param value - Untrusted package-name collection.
712
+ * @returns A frozen owned snapshot of valid unique npm package names.
713
+ */
714
+ export declare function parseSyncNames(value: unknown): readonly string[];
715
+
708
716
  /**
709
717
  * Parse the exact bounded `SyncOptions` boundary before allocating resources.
710
718
  *
@@ -779,13 +787,13 @@ export declare function readFileHex(root: string, path: string, code: ScaffoldEr
779
787
  export declare function readFileText(root: string, path: string, code: ScaffoldErrorCode, boundary: string, limit?: number): string;
780
788
 
781
789
  /**
782
- * Read bounded physical local guide mirrors for declared dependencies.
790
+ * Read bounded physical local guide mirrors for package names.
783
791
  *
784
792
  * @param target - The package root.
785
- * @param dependencies - The declared dependencies whose mirrors are eligible.
786
- * @returns Existing guide content keyed by dependency name.
793
+ * @param names - The package names whose mirrors are eligible.
794
+ * @returns Existing guide content keyed by package name.
787
795
  */
788
- export declare function readGuideReferences(target: string, dependencies: readonly Dependency[]): Readonly<Record<string, string>>;
796
+ export declare function readGuideReferences(target: string, names: readonly string[]): Readonly<Record<string, string>>;
789
797
 
790
798
  /**
791
799
  * Read and validate a vendored host root's `manifest.json`, when present.
@@ -1020,8 +1028,10 @@ export declare function storagePath(path: string): string;
1020
1028
  * map, a fetched guide byte-equal to its entry verdicts `current`, anything
1021
1029
  * differing or absent from the map verdicts `behind`; WITHOUT the map, every
1022
1030
  * successful fetch verdicts `behind` (no reference means it needs syncing).
1023
- * `pull` builds that map itself from the TARGET's own `guides/src/<short>.md`
1024
- * mirrors, so its verdicts are target-relative; `write` commits only the
1031
+ * `pull` builds that map itself from the TARGET's declared dependency mirrors;
1032
+ * `mirror` builds it from the registry's exact organization package list,
1033
+ * excluding the target's own guide and issuing no registry version reads. Both
1034
+ * verdicts are target-relative; `write` commits only the
1025
1035
  * `behind` guides (never `current`, `missing`, or `failed`, which carries no
1026
1036
  * trustworthy content) under the same realpath-anchored containment law
1027
1037
  * `Materializer` enforces. After `destroy()` every method throws `DESTROYED`;
@@ -1041,6 +1051,7 @@ export declare class Sync implements SyncInterface {
1041
1051
  #private;
1042
1052
  constructor(options?: SyncOptions);
1043
1053
  get emitter(): EmitterInterface<SyncEventMap>;
1054
+ lookup(names: readonly string[]): Promise<readonly VersionLookup[]>;
1044
1055
  guides(deps: readonly Dependency[], current?: Readonly<Record<string, string>>): Promise<readonly GuideSync[]>;
1045
1056
  versions(deps: readonly Dependency[]): Promise<readonly VersionSync[]>;
1046
1057
  /**
@@ -1071,6 +1082,7 @@ export declare class Sync implements SyncInterface {
1071
1082
  */
1072
1083
  catalog(): Promise<readonly CatalogEntry[]>;
1073
1084
  pull(target: string, dependencies?: readonly Dependency[]): Promise<SyncReport>;
1085
+ mirror(target: string): Promise<SyncReport>;
1074
1086
  write(report: SyncReport, target: string): Promise<readonly string[]>;
1075
1087
  destroy(): void;
1076
1088
  }
@@ -1121,10 +1133,12 @@ export declare function syncGuideOptionsShape(): ObjectShape<{
1121
1133
  */
1122
1134
  export declare interface SyncInterface {
1123
1135
  readonly emitter: EmitterInterface<SyncEventMap>;
1136
+ lookup(names: readonly string[]): Promise<readonly VersionLookup[]>;
1124
1137
  guides(deps: readonly Dependency[], current?: Readonly<Record<string, string>>): Promise<readonly GuideSync[]>;
1125
1138
  versions(deps: readonly Dependency[]): Promise<readonly VersionSync[]>;
1126
1139
  catalog(): Promise<readonly CatalogEntry[]>;
1127
1140
  pull(target: string, dependencies?: readonly Dependency[]): Promise<SyncReport>;
1141
+ mirror(target: string): Promise<SyncReport>;
1128
1142
  write(report: SyncReport, target: string): Promise<readonly string[]>;
1129
1143
  destroy(): void;
1130
1144
  }
@@ -1282,6 +1296,17 @@ export declare function validateWriteTarget(transaction: WriteTransaction, owned
1282
1296
  */
1283
1297
  export declare function vendoredPruneSet(host: string, directory: string): ReadonlySet<string>;
1284
1298
 
1299
+ /** One bare-name registry lookup, without a caller-invented declaration range. */
1300
+ export declare type VersionLookup = {
1301
+ readonly name: string;
1302
+ readonly latest: string;
1303
+ readonly freshness: 'behind';
1304
+ } | {
1305
+ readonly name: string;
1306
+ readonly freshness: 'missing' | 'failed';
1307
+ readonly note: string;
1308
+ };
1309
+
1285
1310
  /** Exact SHA-256 digest accepted by write preconditions. */
1286
1311
  export declare const WRITE_DIGEST_PATTERN: RegExp;
1287
1312
 
@@ -1,4 +1,4 @@
1
- import { CONTROL_CHARACTER_PATTERN, DEFAULT_ENGINES, DEFAULT_VERSION, DEPENDENCY_NAME_PATTERN, ENVIRONMENTS, GLOBAL_SETUP_PATH, HEX_PATTERN, HOST_PATHS, INVALID_PATH_CHARACTER_PATTERN, MAX_ARTIFACT_BYTES, MAX_ARTIFACT_HEX_LENGTH, MAX_COLLECTION_ITEMS, MAX_MANIFEST_BYTES, MAX_PATH_LENGTH, MAX_TOTAL_ARTIFACT_BYTES, SERVICE_SCRIPT_PATH, SYNC_BASELINE_PATTERN, ScaffoldError, VERSION_PATTERN, blueprint, bytesToHex, contentByteLength, devDependenciesFor, diffPlan, findFileConflict, findPathConflict, hasOnlyDataProperties, hasValidAuditBytes, hasValidSnapshotBytes, isDenseDataArray, isDependency, isEmitterErrorHandler, isPlan, isSyncReport, isWorkspaceName, manifestToDependencies, manifestToName, ownDataValue, parsePlan, parseSyncReport, rangeToFreshness, snapshotPlan, validateDependencyArray, validatePlan } from "../core/index.js";
1
+ import { CONTROL_CHARACTER_PATTERN, DEFAULT_ENGINES, DEFAULT_VERSION, DEPENDENCY_NAME_PATTERN, ENVIRONMENTS, EXTRA_NAME_PATTERN, GLOBAL_SETUP_PATH, HEX_PATTERN, HOST_PATHS, INVALID_PATH_CHARACTER_PATTERN, MAX_ARTIFACT_BYTES, MAX_ARTIFACT_HEX_LENGTH, MAX_COLLECTION_ITEMS, MAX_DEPENDENCY_NAME_LENGTH, MAX_MANIFEST_BYTES, MAX_PATH_LENGTH, MAX_TOTAL_ARTIFACT_BYTES, SERVICE_SCRIPT_PATH, SYNC_BASELINE_PATTERN, ScaffoldError, VERSION_PATTERN, blueprint, bytesToHex, contentByteLength, devDependenciesFor, diffPlan, findFileConflict, findPathConflict, hasOnlyDataProperties, hasValidAuditBytes, hasValidSnapshotBytes, isDenseDataArray, isDependency, isEmitterErrorHandler, isPlan, isSyncReport, isWorkspaceName, manifestToDependencies, manifestToName, ownDataValue, parsePlan, parseSyncReport, rangeToFreshness, snapshotPlan, validateDependencyArray, validatePlan } from "../core/index.js";
2
2
  import { attempt, booleanShape, createContract, integerShape, isError, isFunction, isRecord, isString, objectShape, optionalShape, parseJSON, parseJSONAs, stringShape } from "@orkestrel/contract";
3
3
  import { createHash, randomUUID } from "node:crypto";
4
4
  import { chmodSync, closeSync, constants, copyFileSync, existsSync, fstatSync, linkSync, lstatSync, mkdirSync, openSync, opendirSync, readSync, readdirSync, realpathSync, renameSync, rmSync, rmdirSync, writeFileSync } from "node:fs";
@@ -295,6 +295,34 @@ function isMaterializerEventHooks(value) {
295
295
  }
296
296
  //#endregion
297
297
  //#region src/server/parsers.ts
298
+ /**
299
+ * Parse and semantically validate bare registry package names before Sync performs network I/O.
300
+ *
301
+ * @param value - Untrusted package-name collection.
302
+ * @returns A frozen owned snapshot of valid unique npm package names.
303
+ */
304
+ function parseSyncNames(value) {
305
+ const owned = attempt(() => {
306
+ if (!isDenseDataArray(value, 1e3, (candidate) => typeof candidate === "string")) throw new Error("package names are malformed");
307
+ const length = Reflect.getOwnPropertyDescriptor(value, "length")?.value;
308
+ if (typeof length !== "number" || !Number.isSafeInteger(length) || length < 0 || length > 1e3) throw new Error("package name length is malformed");
309
+ const names = [];
310
+ for (let index = 0; index < length; index += 1) {
311
+ const descriptor = Reflect.getOwnPropertyDescriptor(value, String(index));
312
+ if (descriptor === void 0 || !Reflect.has(descriptor, "value") || typeof descriptor.value !== "string") throw new Error("package name is malformed");
313
+ names.push(descriptor.value);
314
+ }
315
+ return Object.freeze(names);
316
+ });
317
+ if (!owned.success) throw new ScaffoldError("INVALID", "Sync package names are malformed", { error: owned.error });
318
+ const snapshot = owned.value;
319
+ const seen = /* @__PURE__ */ new Set();
320
+ for (const name of snapshot) {
321
+ if (name.length === 0 || name.length > MAX_DEPENDENCY_NAME_LENGTH || !EXTRA_NAME_PATTERN.test(name) || seen.has(name)) throw new ScaffoldError("INVALID", "Sync package names are invalid", { name });
322
+ seen.add(name);
323
+ }
324
+ return snapshot;
325
+ }
298
326
  /** Parse and semantically validate dependency data before Sync performs network I/O. */
299
327
  function parseSyncDependencies(value, external) {
300
328
  const owned = attempt(() => {
@@ -928,16 +956,16 @@ function packageShortName(name) {
928
956
  return name.startsWith("@orkestrel/") ? name.slice(11) : name;
929
957
  }
930
958
  /**
931
- * Read bounded physical local guide mirrors for declared dependencies.
959
+ * Read bounded physical local guide mirrors for package names.
932
960
  *
933
961
  * @param target - The package root.
934
- * @param dependencies - The declared dependencies whose mirrors are eligible.
935
- * @returns Existing guide content keyed by dependency name.
962
+ * @param names - The package names whose mirrors are eligible.
963
+ * @returns Existing guide content keyed by package name.
936
964
  */
937
- function readGuideReferences(target, dependencies) {
965
+ function readGuideReferences(target, names) {
938
966
  const current = {};
939
- for (const dependency of dependencies) {
940
- const path = `guides/src/${packageShortName(dependency.name)}.md`;
967
+ for (const name of names) {
968
+ const path = `guides/src/${packageShortName(name)}.md`;
941
969
  const full = resolvePhysicalPath(target, path, "TARGET", "target");
942
970
  const status = attempt(() => lstatSync(full));
943
971
  if (!status.success) {
@@ -948,7 +976,7 @@ function readGuideReferences(target, dependencies) {
948
976
  });
949
977
  }
950
978
  if (!status.value.isFile() || status.value.isSymbolicLink() || status.value.nlink !== 1) throw new ScaffoldError("TARGET", `Local guide is not a physical file at ${path}`, { path });
951
- current[dependency.name] = readFileText(target, path, "TARGET", "target");
979
+ current[name] = readFileText(target, path, "TARGET", "target");
952
980
  }
953
981
  return Object.freeze(current);
954
982
  }
@@ -3487,8 +3515,10 @@ var Materializer = class {
3487
3515
  * map, a fetched guide byte-equal to its entry verdicts `current`, anything
3488
3516
  * differing or absent from the map verdicts `behind`; WITHOUT the map, every
3489
3517
  * successful fetch verdicts `behind` (no reference means it needs syncing).
3490
- * `pull` builds that map itself from the TARGET's own `guides/src/<short>.md`
3491
- * mirrors, so its verdicts are target-relative; `write` commits only the
3518
+ * `pull` builds that map itself from the TARGET's declared dependency mirrors;
3519
+ * `mirror` builds it from the registry's exact organization package list,
3520
+ * excluding the target's own guide and issuing no registry version reads. Both
3521
+ * verdicts are target-relative; `write` commits only the
3492
3522
  * `behind` guides (never `current`, `missing`, or `failed`, which carries no
3493
3523
  * trustworthy content) under the same realpath-anchored containment law
3494
3524
  * `Materializer` enforces. After `destroy()` every method throws `DESTROYED`;
@@ -3540,50 +3570,67 @@ var Sync = class Sync {
3540
3570
  get emitter() {
3541
3571
  return this.#emitter;
3542
3572
  }
3573
+ async lookup(names) {
3574
+ this.#ensureAlive();
3575
+ return this.#lookup(names, Float64Array.of(this.#budget));
3576
+ }
3543
3577
  async guides(deps, current) {
3544
3578
  this.#ensureAlive();
3545
- return this.#guides(deps, current, Float64Array.of(this.#budget));
3579
+ const parsed = parseSyncDependencies(deps, false);
3580
+ return this.#guides(parsed.map((dependency) => dependency.name), current, Float64Array.of(this.#budget));
3546
3581
  }
3547
3582
  async versions(deps) {
3548
3583
  this.#ensureAlive();
3549
3584
  return this.#versions(deps, Float64Array.of(this.#budget));
3550
3585
  }
3551
- #guides(deps, current, allowance) {
3552
- const parsed = parseSyncDependencies(deps, false);
3586
+ #guides(names, current, allowance) {
3587
+ const parsed = parseSyncNames(names);
3553
3588
  if (parsed.length > this.#items) throw new ScaffoldError("INVALID", "Sync dependency count exceeds its item limit", {
3554
3589
  items: parsed.length,
3555
3590
  limit: this.#items
3556
3591
  });
3557
- const reference = parseSyncCurrent(current, parsed.map((dependency) => dependency.name), this.#budget);
3558
- return Sync.#runPool(parsed, this.#concurrency, async (dep) => {
3559
- const short = packageShortName(dep.name);
3592
+ const reference = parseSyncCurrent(current, parsed, this.#budget);
3593
+ return Sync.#runPool(parsed, this.#concurrency, async (name) => {
3594
+ const short = packageShortName(name);
3560
3595
  const url = this.#guideUrl(short);
3561
3596
  const outcome = await Sync.#fetchText(url, this.#guidesTimeout, this.#retries, this.#limit, allowance, this.#controller.signal);
3562
- const guide = Sync.#toGuideSync(dep.name, short, outcome, reference);
3597
+ const guide = Sync.#toGuideSync(name, short, outcome, reference);
3563
3598
  if (outcome.kind === "failed") this.#emitter.emit("error", outcome.error);
3564
- this.#emitter.emit("guide", dep.name);
3599
+ this.#emitter.emit("guide", name);
3565
3600
  if (this.#strict && (guide.freshness === "missing" || guide.freshness === "failed")) throw new ScaffoldError("FETCH", `Failed to fetch guide at ${url}`, {
3566
3601
  url,
3567
- name: dep.name
3602
+ name
3568
3603
  });
3569
3604
  return guide;
3570
3605
  });
3571
3606
  }
3572
- #versions(deps, allowance) {
3607
+ async #versions(deps, allowance) {
3573
3608
  const parsed = parseSyncDependencies(deps, true);
3609
+ const lookups = await this.#lookup(parsed.map((dependency) => dependency.name), allowance);
3610
+ const versions = [];
3611
+ for (let index = 0; index < parsed.length; index += 1) {
3612
+ const dep = parsed[index];
3613
+ const lookup = lookups[index];
3614
+ if (dep === void 0 || lookup === void 0) throw new ScaffoldError("INVALID", "Sync version lookup result is incomplete");
3615
+ versions.push(Sync.#toVersionSync(dep, lookup));
3616
+ }
3617
+ return Object.freeze(versions);
3618
+ }
3619
+ #lookup(names, allowance) {
3620
+ const parsed = parseSyncNames(names);
3574
3621
  if (parsed.length > this.#items) throw new ScaffoldError("INVALID", "Sync dependency count exceeds its item limit", {
3575
3622
  items: parsed.length,
3576
3623
  limit: this.#items
3577
3624
  });
3578
- return Sync.#runPool(parsed, this.#concurrency, async (dep) => {
3579
- const url = this.#registryUrl(dep.name);
3625
+ return Sync.#runPool(parsed, this.#concurrency, async (name) => {
3626
+ const url = this.#registryUrl(name);
3580
3627
  const outcome = await Sync.#fetchText(url, this.#registryTimeout, this.#retries, this.#limit, allowance, this.#controller.signal);
3581
- const version = Sync.#toVersionSync(dep, outcome);
3628
+ const version = Sync.#toVersionLookup(name, outcome);
3582
3629
  if (outcome.kind === "failed") this.#emitter.emit("error", outcome.error);
3583
- this.#emitter.emit("version", dep.name);
3630
+ this.#emitter.emit("version", name);
3584
3631
  if (this.#strict && (version.freshness === "missing" || version.freshness === "failed")) throw new ScaffoldError("FETCH", `Failed to fetch registry version at ${url}`, {
3585
3632
  url,
3586
- name: dep.name
3633
+ name
3587
3634
  });
3588
3635
  return version;
3589
3636
  });
@@ -3616,16 +3663,9 @@ var Sync = class Sync {
3616
3663
  */
3617
3664
  async catalog() {
3618
3665
  this.#ensureAlive();
3619
- const orgUrl = this.#orgUrl();
3620
3666
  const allowance = Float64Array.of(this.#budget);
3621
- const orgOutcome = await Sync.#fetchText(orgUrl, this.#registryTimeout, this.#retries, this.#limit, allowance, this.#controller.signal);
3622
- const names = Sync.#toOrgPackages(orgOutcome, orgUrl);
3623
- if (names.length > this.#items) throw new ScaffoldError("FETCH", `Package list at ${orgUrl} exceeds the item limit`, {
3624
- url: orgUrl,
3625
- items: names.length,
3626
- limit: this.#items
3627
- });
3628
- return [...await Sync.#runPool(names, this.#concurrency, async (name) => {
3667
+ const names = await this.#packages(allowance);
3668
+ return await Sync.#runPool(names, this.#concurrency, async (name) => {
3629
3669
  const packumentOutcome = await Sync.#fetchText(this.#registryUrl(name), this.#registryTimeout, this.#retries, this.#limit, allowance, this.#controller.signal);
3630
3670
  const packument = Sync.#toPackument(packumentOutcome);
3631
3671
  const short = packageShortName(name);
@@ -3641,7 +3681,7 @@ var Sync = class Sync {
3641
3681
  version: packument.version,
3642
3682
  description
3643
3683
  };
3644
- })].sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
3684
+ });
3645
3685
  }
3646
3686
  async pull(target, dependencies) {
3647
3687
  this.#ensureAlive();
@@ -3650,10 +3690,21 @@ var Sync = class Sync {
3650
3690
  const deps = dependencies === void 0 ? declared : parseSyncDependencies(dependencies, false);
3651
3691
  if (dependencies !== void 0 && deps.some((dependency) => !declared.some((candidate) => candidate.name === dependency.name && candidate.range === dependency.range))) throw new ScaffoldError("INVALID", "Sync pull selection is not declared by the target");
3652
3692
  const name = manifestToName(manifest);
3653
- const guideDependencies = name === void 0 ? deps : deps.filter((dependency) => dependency.name !== name);
3654
- const current = readGuideReferences(target, guideDependencies);
3693
+ const guideNames = deps.map((dependency) => dependency.name).filter((dependency) => dependency !== name);
3694
+ const current = readGuideReferences(target, guideNames);
3655
3695
  const allowance = Float64Array.of(this.#budget);
3656
- const report = syncReportOf(target, await this.#guides(guideDependencies, current, allowance), await this.#versions(deps, allowance));
3696
+ const report = syncReportOf(target, await this.#guides(guideNames, current, allowance), await this.#versions(deps, allowance));
3697
+ this.#emitter.emit("done", report);
3698
+ return report;
3699
+ }
3700
+ async mirror(target) {
3701
+ this.#ensureAlive();
3702
+ const manifest = readManifest(target);
3703
+ const name = manifestToName(manifest);
3704
+ const allowance = Float64Array.of(this.#budget);
3705
+ const names = (await this.#packages(allowance)).filter((candidate) => candidate !== name);
3706
+ const current = readGuideReferences(target, names);
3707
+ const report = syncReportOf(target, await this.#guides(names, current, allowance), []);
3657
3708
  this.#emitter.emit("done", report);
3658
3709
  return report;
3659
3710
  }
@@ -3746,6 +3797,17 @@ var Sync = class Sync {
3746
3797
  #orgUrl() {
3747
3798
  return `${this.#registryBase}/-/org/orkestrel/package`;
3748
3799
  }
3800
+ async #packages(allowance) {
3801
+ const url = this.#orgUrl();
3802
+ const outcome = await Sync.#fetchText(url, this.#registryTimeout, this.#retries, this.#limit, allowance, this.#controller.signal);
3803
+ const names = Sync.#toOrgPackages(outcome, url);
3804
+ if (names.length > this.#items) throw new ScaffoldError("FETCH", `Package list at ${url} exceeds the item limit`, {
3805
+ url,
3806
+ items: names.length,
3807
+ limit: this.#items
3808
+ });
3809
+ return [...names].sort((left, right) => left < right ? -1 : left > right ? 1 : 0);
3810
+ }
3749
3811
  static async #runPool(items, concurrency, worker) {
3750
3812
  const results = new Array(items.length);
3751
3813
  const state = {
@@ -3924,34 +3986,42 @@ var Sync = class Sync {
3924
3986
  ...baseline === void 0 ? {} : { baseline }
3925
3987
  };
3926
3988
  }
3927
- static #toVersionSync(dep, outcome) {
3989
+ static #toVersionLookup(name, outcome) {
3928
3990
  if (outcome.kind === "missing") return {
3929
- name: dep.name,
3930
- range: dep.range,
3931
- latest: "",
3991
+ name,
3932
3992
  freshness: "missing",
3933
3993
  note: "HTTP 404"
3934
3994
  };
3935
3995
  if (outcome.kind === "failed") return {
3936
- name: dep.name,
3937
- range: dep.range,
3938
- latest: "",
3996
+ name,
3939
3997
  freshness: "failed",
3940
3998
  note: outcome.note
3941
3999
  };
3942
4000
  const latest = Sync.#parseLatest(outcome.text);
3943
4001
  if (latest === void 0) return {
4002
+ name,
4003
+ freshness: "failed",
4004
+ note: "malformed registry response (missing dist-tags.latest)"
4005
+ };
4006
+ return {
4007
+ name,
4008
+ latest,
4009
+ freshness: "behind"
4010
+ };
4011
+ }
4012
+ static #toVersionSync(dep, lookup) {
4013
+ if (lookup.freshness !== "behind") return {
3944
4014
  name: dep.name,
3945
4015
  range: dep.range,
3946
4016
  latest: "",
3947
- freshness: "failed",
3948
- note: "malformed registry response (missing dist-tags.latest)"
4017
+ freshness: lookup.freshness,
4018
+ note: lookup.note
3949
4019
  };
3950
4020
  return {
3951
4021
  name: dep.name,
3952
4022
  range: dep.range,
3953
- latest,
3954
- freshness: rangeToFreshness(dep.range, latest)
4023
+ latest: lookup.latest,
4024
+ freshness: rangeToFreshness(dep.range, lookup.latest)
3955
4025
  };
3956
4026
  }
3957
4027
  static #toOrgPackages(outcome, url) {
@@ -4075,6 +4145,6 @@ function createSync(options) {
4075
4145
  return new Sync(options);
4076
4146
  }
4077
4147
  //#endregion
4078
- export { DEFAULT_SYNC_BUDGET, DEFAULT_SYNC_CONCURRENCY, DEFAULT_SYNC_ITEMS, DEFAULT_SYNC_LIMIT, DEFAULT_SYNC_TIMEOUT, HOST_MANIFEST_PATH, MAX_CATALOG_DESCRIPTION_LENGTH, MAX_FILESYSTEM_DEPTH, MAX_GUIDE_BYTES, MAX_HOST_DEPTH, MAX_HOST_ENTRIES, MAX_PATH_SEGMENT_BYTES, MAX_SYNC_BASE_LENGTH, MAX_SYNC_BRANCH_LENGTH, MAX_SYNC_BUDGET, MAX_SYNC_CONCURRENCY, MAX_SYNC_ITEMS, MAX_SYNC_LIMIT, MAX_SYNC_RETRIES, MAX_SYNC_TIMEOUT, Materializer, PRUNE_DIRECTORIES, RESERVED_PATH_SEGMENT_PATTERN, RESERVED_TARGET_PATH_PATTERN, SENSITIVE_HOST_PATH_PATTERN, SYNC_BRANCH_PATTERN, Sync, WRITE_DIGEST_PATTERN, WriteTransaction, catalogPackages, commitWriteTransaction, consumeCatalogAllowance, createMaterializer, createSync, createWriteDirectory, deriveBlueprint, digestFile, digestHex, digestText, discardWriteTransaction, discoverPackages, guideStub, guideToDescription, hostRoot, hydratePlan, isCatalogAllowance, isCatalogDescription, isDependencyData, isFilesystemPath, isHostManifest, isManifestEntry, isMaterializerEventHooks, isMissingPathError, isPortablePath, isRealDirectory, isRealFile, isReservedTargetPath, isSensitiveHostPath, isSyncEventHooks, isTerminalText, isVacant, isWritePrecondition, listDirectories, listFiles, locateHostSource, materializerOptionsContract, materializerOptionsShape, packageShortName, parseFilesystemPaths, parseMaterializerOptions, parsePortablePaths, parseSyncBase, parseSyncBranch, parseSyncCurrent, parseSyncDependencies, parseSyncOptions, parseWritePreconditions, pruneTargets, readFileHex, readFileText, readGuideReferences, readHostManifest, readManifest, readTarget, remapArtifactPath, replaceDirectory, resolveContainedPath, resolveGuideWrites, resolvePhysicalPath, resolveRealPath, restoreFiles, selectOrkestrelEntries, stageHost, storagePath, syncGuideOptionsShape, syncOptionsContract, syncOptionsShape, syncRegistryOptionsShape, syncReportOf, validateWriteAnchor, validateWriteDirectories, validateWriteTarget, vendoredPruneSet };
4148
+ export { DEFAULT_SYNC_BUDGET, DEFAULT_SYNC_CONCURRENCY, DEFAULT_SYNC_ITEMS, DEFAULT_SYNC_LIMIT, DEFAULT_SYNC_TIMEOUT, HOST_MANIFEST_PATH, MAX_CATALOG_DESCRIPTION_LENGTH, MAX_FILESYSTEM_DEPTH, MAX_GUIDE_BYTES, MAX_HOST_DEPTH, MAX_HOST_ENTRIES, MAX_PATH_SEGMENT_BYTES, MAX_SYNC_BASE_LENGTH, MAX_SYNC_BRANCH_LENGTH, MAX_SYNC_BUDGET, MAX_SYNC_CONCURRENCY, MAX_SYNC_ITEMS, MAX_SYNC_LIMIT, MAX_SYNC_RETRIES, MAX_SYNC_TIMEOUT, Materializer, PRUNE_DIRECTORIES, RESERVED_PATH_SEGMENT_PATTERN, RESERVED_TARGET_PATH_PATTERN, SENSITIVE_HOST_PATH_PATTERN, SYNC_BRANCH_PATTERN, Sync, WRITE_DIGEST_PATTERN, WriteTransaction, catalogPackages, commitWriteTransaction, consumeCatalogAllowance, createMaterializer, createSync, createWriteDirectory, deriveBlueprint, digestFile, digestHex, digestText, discardWriteTransaction, discoverPackages, guideStub, guideToDescription, hostRoot, hydratePlan, isCatalogAllowance, isCatalogDescription, isDependencyData, isFilesystemPath, isHostManifest, isManifestEntry, isMaterializerEventHooks, isMissingPathError, isPortablePath, isRealDirectory, isRealFile, isReservedTargetPath, isSensitiveHostPath, isSyncEventHooks, isTerminalText, isVacant, isWritePrecondition, listDirectories, listFiles, locateHostSource, materializerOptionsContract, materializerOptionsShape, packageShortName, parseFilesystemPaths, parseMaterializerOptions, parsePortablePaths, parseSyncBase, parseSyncBranch, parseSyncCurrent, parseSyncDependencies, parseSyncNames, parseSyncOptions, parseWritePreconditions, pruneTargets, readFileHex, readFileText, readGuideReferences, readHostManifest, readManifest, readTarget, remapArtifactPath, replaceDirectory, resolveContainedPath, resolveGuideWrites, resolvePhysicalPath, resolveRealPath, restoreFiles, selectOrkestrelEntries, stageHost, storagePath, syncGuideOptionsShape, syncOptionsContract, syncOptionsShape, syncRegistryOptionsShape, syncReportOf, validateWriteAnchor, validateWriteDirectories, validateWriteTarget, vendoredPruneSet };
4079
4149
 
4080
4150
  //# sourceMappingURL=index.js.map