@intx/tool-packaging 0.2.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/LICENSE +176 -0
- package/README.md +116 -0
- package/dist/atomic-apply.d.ts +65 -0
- package/dist/atomic-apply.js +223 -0
- package/dist/cache.d.ts +89 -0
- package/dist/cache.js +750 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +30 -0
- package/dist/loader.d.ts +189 -0
- package/dist/loader.js +1387 -0
- package/dist/package-json-extract.d.ts +56 -0
- package/dist/package-json-extract.js +125 -0
- package/dist/resolver.d.ts +241 -0
- package/dist/resolver.js +821 -0
- package/package.json +39 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { PackageJSON } from "@intx/types/package-json";
|
|
2
|
+
/**
|
|
3
|
+
* Outcome of extracting the top-level `package.json` entry from an
|
|
4
|
+
* npm-style tarball.
|
|
5
|
+
*
|
|
6
|
+
* `kind: "ok"` carries a value already validated against `PackageJSON`;
|
|
7
|
+
* `kind: "shape-invalid"` distinguishes "JSON parsed cleanly but did
|
|
8
|
+
* not match the expected schema" from "the JSON itself was malformed"
|
|
9
|
+
* (`kind: "json-error"`). Callers that ship their own domain error type
|
|
10
|
+
* can map the rejected outcomes onto whatever shape they raise.
|
|
11
|
+
*/
|
|
12
|
+
export type ExtractPackageJSONOutcome = {
|
|
13
|
+
kind: "ok";
|
|
14
|
+
parsed: PackageJSON;
|
|
15
|
+
raw: unknown;
|
|
16
|
+
} | {
|
|
17
|
+
kind: "missing-entry";
|
|
18
|
+
} | {
|
|
19
|
+
kind: "multiple-entries";
|
|
20
|
+
paths: string[];
|
|
21
|
+
} | {
|
|
22
|
+
kind: "parse-error";
|
|
23
|
+
message: string;
|
|
24
|
+
} | {
|
|
25
|
+
kind: "json-error";
|
|
26
|
+
message: string;
|
|
27
|
+
} | {
|
|
28
|
+
kind: "shape-invalid";
|
|
29
|
+
message: string;
|
|
30
|
+
raw: unknown;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Stream the tarball bytes through a tar parser, drain only the top-
|
|
34
|
+
* level `package.json` member, JSON.parse the collected bytes, run them
|
|
35
|
+
* through `PackageJSON`, and return a discriminated outcome. The bytes
|
|
36
|
+
* argument may be any Uint8Array (tarball or gzipped tarball —
|
|
37
|
+
* `tar.Parser` auto-detects gzip on the input stream).
|
|
38
|
+
*
|
|
39
|
+
* The tar entry is matched by its tail (`<segment>/package.json` with
|
|
40
|
+
* exactly two segments) rather than the literal `package/package.json`
|
|
41
|
+
* path because the sidecar's tarball extractor uses `strip:1` and
|
|
42
|
+
* therefore accepts any first segment. Matching by tail here keeps the
|
|
43
|
+
* hub's validation aligned with the sidecar's runtime contract — a
|
|
44
|
+
* tarball whose top-level directory is not literally `package/` will
|
|
45
|
+
* load identically in both places.
|
|
46
|
+
*
|
|
47
|
+
* On a tar parser failure the upstream readable is destroyed so a
|
|
48
|
+
* malformed archive does not leave a half-drained source buffered in
|
|
49
|
+
* the parser's internal state.
|
|
50
|
+
*
|
|
51
|
+
* The raw parsed JSON is surfaced alongside the validated descriptor on
|
|
52
|
+
* success so callers that need fields outside `PackageJSON`'s minimum
|
|
53
|
+
* schema (the resolver's `readDependencyFields` consumes `dependencies`,
|
|
54
|
+
* `optionalDependencies`, etc.) can read them without re-extracting.
|
|
55
|
+
*/
|
|
56
|
+
export declare function extractTarballPackageJSON(bytes: Uint8Array): Promise<ExtractPackageJSONOutcome>;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// eslint-disable-next-line @typescript-eslint/triple-slash-reference -- npm-team packages ship no types; declarations.d.ts must be visible to downstream typecheckers that import from this package's source.
|
|
2
|
+
/// <reference path="./declarations.d.ts" />
|
|
3
|
+
// Single source of truth for "open an npm-style tarball, find the
|
|
4
|
+
// package/package.json entry, parse it as JSON, hand the parsed value
|
|
5
|
+
// back". Used by the hub-side resolver (`AssetRegistrySource` builds
|
|
6
|
+
// packuments from asset-stored tarballs) and the hub-sessions package
|
|
7
|
+
// (the `package-registry` kind handler validates uploads before the
|
|
8
|
+
// commit is accepted). Both call sites used to ship near-identical
|
|
9
|
+
// streaming parsers; the duplication drifted independently.
|
|
10
|
+
//
|
|
11
|
+
// The helper returns a discriminated outcome rather than throwing so
|
|
12
|
+
// each caller can construct its own domain-shaped error
|
|
13
|
+
// (ManifestInvalidError on the resolver side, ValidatePushResult
|
|
14
|
+
// reason string on the kind-handler side) without losing the
|
|
15
|
+
// failure-class distinction.
|
|
16
|
+
// This module is Node-bound: it streams through node:stream and the tar
|
|
17
|
+
// library, which are not portable to environments without those APIs.
|
|
18
|
+
import { Readable } from "node:stream";
|
|
19
|
+
import { type } from "arktype";
|
|
20
|
+
import { Parser as TarParser } from "tar/parse";
|
|
21
|
+
import { concatBytes } from "@intx/types";
|
|
22
|
+
import { PackageJSON } from "@intx/types/package-json";
|
|
23
|
+
/**
|
|
24
|
+
* Stream the tarball bytes through a tar parser, drain only the top-
|
|
25
|
+
* level `package.json` member, JSON.parse the collected bytes, run them
|
|
26
|
+
* through `PackageJSON`, and return a discriminated outcome. The bytes
|
|
27
|
+
* argument may be any Uint8Array (tarball or gzipped tarball —
|
|
28
|
+
* `tar.Parser` auto-detects gzip on the input stream).
|
|
29
|
+
*
|
|
30
|
+
* The tar entry is matched by its tail (`<segment>/package.json` with
|
|
31
|
+
* exactly two segments) rather than the literal `package/package.json`
|
|
32
|
+
* path because the sidecar's tarball extractor uses `strip:1` and
|
|
33
|
+
* therefore accepts any first segment. Matching by tail here keeps the
|
|
34
|
+
* hub's validation aligned with the sidecar's runtime contract — a
|
|
35
|
+
* tarball whose top-level directory is not literally `package/` will
|
|
36
|
+
* load identically in both places.
|
|
37
|
+
*
|
|
38
|
+
* On a tar parser failure the upstream readable is destroyed so a
|
|
39
|
+
* malformed archive does not leave a half-drained source buffered in
|
|
40
|
+
* the parser's internal state.
|
|
41
|
+
*
|
|
42
|
+
* The raw parsed JSON is surfaced alongside the validated descriptor on
|
|
43
|
+
* success so callers that need fields outside `PackageJSON`'s minimum
|
|
44
|
+
* schema (the resolver's `readDependencyFields` consumes `dependencies`,
|
|
45
|
+
* `optionalDependencies`, etc.) can read them without re-extracting.
|
|
46
|
+
*/
|
|
47
|
+
export async function extractTarballPackageJSON(bytes) {
|
|
48
|
+
return new Promise((resolve) => {
|
|
49
|
+
let resolved = false;
|
|
50
|
+
let pkgJsonBuf = null;
|
|
51
|
+
const collectChunks = [];
|
|
52
|
+
// Capture every top-level `<seg>/package.json` path we see during
|
|
53
|
+
// the walk so the kind handler can reject ambiguous archives. The
|
|
54
|
+
// hub's resolver and the sidecar's tarball extractor resolve
|
|
55
|
+
// collisions differently — the resolver via this helper captures
|
|
56
|
+
// the first occurrence, while the sidecar's `tar.extract` with
|
|
57
|
+
// `strip:1` overwrites on every subsequent path with the same
|
|
58
|
+
// stripped name. A tarball carrying multiple top-level package
|
|
59
|
+
// directories therefore validates against the first entry on the
|
|
60
|
+
// hub but loads the last entry on the sidecar; the divergence is
|
|
61
|
+
// resolved at the validation boundary by refusing the upload.
|
|
62
|
+
const topLevelPackageJSONPaths = [];
|
|
63
|
+
const source = Readable.from([bytes]);
|
|
64
|
+
const finalize = (outcome) => {
|
|
65
|
+
if (resolved)
|
|
66
|
+
return;
|
|
67
|
+
resolved = true;
|
|
68
|
+
resolve(outcome);
|
|
69
|
+
};
|
|
70
|
+
const parser = new TarParser();
|
|
71
|
+
parser.on("entry", (entry) => {
|
|
72
|
+
const segments = entry.path.split("/");
|
|
73
|
+
const isTopLevelPackageJSON = segments.length === 2 && segments[1] === "package.json";
|
|
74
|
+
if (isTopLevelPackageJSON) {
|
|
75
|
+
topLevelPackageJSONPaths.push(entry.path);
|
|
76
|
+
}
|
|
77
|
+
if (isTopLevelPackageJSON && pkgJsonBuf === null) {
|
|
78
|
+
entry.on("data", (chunk) => {
|
|
79
|
+
collectChunks.push(chunk);
|
|
80
|
+
});
|
|
81
|
+
entry.on("end", () => {
|
|
82
|
+
pkgJsonBuf = concatBytes(collectChunks);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
entry.resume();
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
parser.on("error", (err) => {
|
|
90
|
+
source.destroy();
|
|
91
|
+
finalize({ kind: "parse-error", message: err.message });
|
|
92
|
+
});
|
|
93
|
+
parser.on("end", () => {
|
|
94
|
+
if (pkgJsonBuf === null) {
|
|
95
|
+
finalize({ kind: "missing-entry" });
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (topLevelPackageJSONPaths.length > 1) {
|
|
99
|
+
finalize({
|
|
100
|
+
kind: "multiple-entries",
|
|
101
|
+
paths: topLevelPackageJSONPaths,
|
|
102
|
+
});
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
let raw;
|
|
106
|
+
try {
|
|
107
|
+
raw = JSON.parse(new TextDecoder().decode(pkgJsonBuf));
|
|
108
|
+
}
|
|
109
|
+
catch (cause) {
|
|
110
|
+
finalize({
|
|
111
|
+
kind: "json-error",
|
|
112
|
+
message: cause instanceof Error ? cause.message : String(cause),
|
|
113
|
+
});
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const validated = PackageJSON(raw);
|
|
117
|
+
if (validated instanceof type.errors) {
|
|
118
|
+
finalize({ kind: "shape-invalid", message: validated.summary, raw });
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
finalize({ kind: "ok", parsed: validated, raw });
|
|
122
|
+
});
|
|
123
|
+
source.pipe(parser);
|
|
124
|
+
});
|
|
125
|
+
}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { type ToolPackageManifest, type ToolPackagePin, type ToolPackageSource } from "@intx/types/tool-packages";
|
|
2
|
+
/**
|
|
3
|
+
* One HTTP registry. `url` is the actual endpoint; `auth.token` and
|
|
4
|
+
* `auth.basic` mirror the npm-registry-fetch options for bearer /
|
|
5
|
+
* basic auth.
|
|
6
|
+
*
|
|
7
|
+
* The registry's identifier (the string `scopeRouting` entries and
|
|
8
|
+
* manifest `registry` references point at) is the slot it occupies
|
|
9
|
+
* in the registry map: in the hub-side resolver, the key of the
|
|
10
|
+
* `ReadonlyMap<string, RegistrySource>` passed to
|
|
11
|
+
* `createClosureResolver`; in the sidecar-side loader, the key of
|
|
12
|
+
* the `ReadonlyMap<string, RegistryConfig>` on `LoaderConfig`.
|
|
13
|
+
* Keeping the name on the map slot rather than on the value
|
|
14
|
+
* eliminates the (until-now uninforced) "the map key must equal the
|
|
15
|
+
* config's name" invariant.
|
|
16
|
+
*
|
|
17
|
+
* The default-registry decision is owned by `defaultRegistry` on
|
|
18
|
+
* `ClosureResolverConfig`, not by a per-registry flag; only one
|
|
19
|
+
* entry can be the default and centralizing the choice keeps that
|
|
20
|
+
* invariant in the config object.
|
|
21
|
+
*/
|
|
22
|
+
export interface RegistryConfig {
|
|
23
|
+
readonly url: string;
|
|
24
|
+
readonly auth?: {
|
|
25
|
+
readonly token?: string;
|
|
26
|
+
readonly basic?: {
|
|
27
|
+
readonly user: string;
|
|
28
|
+
readonly pass: string;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/** Route packages in a given scope (e.g. `@intx`) to a named registry. */
|
|
33
|
+
export interface ScopeRoute {
|
|
34
|
+
readonly scope: string;
|
|
35
|
+
readonly registry: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* One version manifest as it appears inside a packument. Fields are
|
|
39
|
+
* non-readonly to match the npm-pick-manifest declaration; consumers
|
|
40
|
+
* should treat values as immutable in practice.
|
|
41
|
+
*/
|
|
42
|
+
export interface PackumentVersion {
|
|
43
|
+
name: string;
|
|
44
|
+
version: string;
|
|
45
|
+
dist: {
|
|
46
|
+
tarball: string;
|
|
47
|
+
integrity?: string;
|
|
48
|
+
};
|
|
49
|
+
dependencies?: Record<string, string>;
|
|
50
|
+
optionalDependencies?: Record<string, string>;
|
|
51
|
+
peerDependencies?: Record<string, string>;
|
|
52
|
+
peerDependenciesMeta?: Record<string, {
|
|
53
|
+
optional?: boolean;
|
|
54
|
+
}>;
|
|
55
|
+
os?: string[];
|
|
56
|
+
cpu?: string[];
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The packument document a registry serves for one package. Shape
|
|
60
|
+
* matches what `npm-pick-manifest` accepts.
|
|
61
|
+
*/
|
|
62
|
+
export interface Packument {
|
|
63
|
+
name: string;
|
|
64
|
+
"dist-tags"?: Record<string, string>;
|
|
65
|
+
versions: Record<string, PackumentVersion>;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Test seam for HTTP packument fetches. The default `HttpRegistrySource`
|
|
69
|
+
* wraps `npm-registry-fetch`; tests inject a function that returns a
|
|
70
|
+
* static packument for a given `(name, registry)` pair.
|
|
71
|
+
*/
|
|
72
|
+
export type PackumentFetcher = (packageName: string, registry: RegistryConfig) => Promise<Packument>;
|
|
73
|
+
/**
|
|
74
|
+
* The shape returned by `RegistrySource.materializeRefForEntry`. The
|
|
75
|
+
* walker copies these fields onto the `ToolPackageManifestEntry` it
|
|
76
|
+
* emits, alongside `name`, `version`, `integrity`, and the platform
|
|
77
|
+
* filter metadata it derives from the picked packument version.
|
|
78
|
+
*/
|
|
79
|
+
export interface MaterializedRef {
|
|
80
|
+
readonly source: ToolPackageSource;
|
|
81
|
+
/** Set for HTTP-sourced entries so the sidecar can fetch without
|
|
82
|
+
* re-resolving against the registry; asset-sourced entries omit it. */
|
|
83
|
+
readonly tarballUrl?: string;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* A pluggable source of npm-style packuments and per-version source
|
|
87
|
+
* tags. The walker treats every named registry as one of these,
|
|
88
|
+
* regardless of whether bytes live behind an HTTP registry or inside a
|
|
89
|
+
* `package-registry` asset.
|
|
90
|
+
*
|
|
91
|
+
* `name` is the identifier the source was registered under in the
|
|
92
|
+
* resolver's registry map. Diagnostics that need to print a
|
|
93
|
+
* registry's name read it from here; the impls are the source of
|
|
94
|
+
* truth and the interface guarantees it is always available.
|
|
95
|
+
*/
|
|
96
|
+
export interface RegistrySource {
|
|
97
|
+
readonly name: string;
|
|
98
|
+
fetchPackument(name: string): Promise<Packument>;
|
|
99
|
+
materializeRefForEntry(name: string, version: string, picked: PackumentVersion): MaterializedRef;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Configuration for `createClosureResolver`.
|
|
103
|
+
*
|
|
104
|
+
* `registries` is keyed by the same name the agent's pin set
|
|
105
|
+
* references via the `registry` field in `ToolPackagePin`-derived
|
|
106
|
+
* entries (today indirectly via scope routing; built-ins reach this
|
|
107
|
+
* map by the registry name the session service resolved them from).
|
|
108
|
+
*
|
|
109
|
+
* `defaultRegistry` names the entry in `registries` consulted for any
|
|
110
|
+
* package whose scope does not match a `scopeRouting` entry. The map
|
|
111
|
+
* must contain this key.
|
|
112
|
+
*/
|
|
113
|
+
export interface ClosureResolverConfig {
|
|
114
|
+
readonly registries: ReadonlyMap<string, RegistrySource>;
|
|
115
|
+
readonly defaultRegistry: string;
|
|
116
|
+
readonly scopeRouting?: readonly ScopeRoute[];
|
|
117
|
+
}
|
|
118
|
+
export interface ClosureResolver {
|
|
119
|
+
resolveClosure(pins: readonly ToolPackagePin[]): Promise<ToolPackageManifest>;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* One unsatisfied peer-dependency declaration discovered while
|
|
123
|
+
* resolving a closure.
|
|
124
|
+
*/
|
|
125
|
+
export interface PeerDependencyViolation {
|
|
126
|
+
readonly dependent: {
|
|
127
|
+
readonly name: string;
|
|
128
|
+
readonly version: string;
|
|
129
|
+
};
|
|
130
|
+
readonly peer: {
|
|
131
|
+
readonly name: string;
|
|
132
|
+
readonly range: string;
|
|
133
|
+
};
|
|
134
|
+
readonly satisfiedBy: {
|
|
135
|
+
readonly version: string;
|
|
136
|
+
} | null;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Thrown by `resolveClosure` when the resolved closure does not satisfy
|
|
140
|
+
* one or more peer-dependency declarations, by the closure walker for
|
|
141
|
+
* duplicate-name pins, and by the hub-side session-launch path when a
|
|
142
|
+
* direct package-registry attachment overlaps a resolver-driven pin
|
|
143
|
+
* for the same asset. The deploy-assembly path maps every shape to
|
|
144
|
+
* the same `manifest.invalid` deploy-apply error category before the
|
|
145
|
+
* deploy ships.
|
|
146
|
+
*
|
|
147
|
+
* The category is intentionally broad: peer-dep violations and
|
|
148
|
+
* duplicate-name pins are structurally distinct defects but both
|
|
149
|
+
* indicate a closure the operator cannot ship without changing the
|
|
150
|
+
* pin set. Adding a per-shape category would expand the wire
|
|
151
|
+
* taxonomy for no operator-facing gain; readers parsing
|
|
152
|
+
* deploy-apply errors should look at the message for the structural
|
|
153
|
+
* distinction.
|
|
154
|
+
*/
|
|
155
|
+
export declare class ManifestInvalidError extends Error {
|
|
156
|
+
/**
|
|
157
|
+
* Populated only when the constructor was invoked with the
|
|
158
|
+
* structured peer-dependency form. String-constructed instances
|
|
159
|
+
* (duplicate-name pins, direct-vs-resolver-asset conflicts) carry
|
|
160
|
+
* an empty array — readers that need the structural distinction
|
|
161
|
+
* should consult `message` rather than treating `violations.length
|
|
162
|
+
* === 0` as a signal.
|
|
163
|
+
*/
|
|
164
|
+
readonly violations: readonly PeerDependencyViolation[];
|
|
165
|
+
constructor(violationsOrMessage: readonly PeerDependencyViolation[] | string);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* HTTP-backed registry source. Wraps `npm-registry-fetch` for packument
|
|
169
|
+
* lookups and emits registry-shaped manifest entries.
|
|
170
|
+
*/
|
|
171
|
+
export declare class HttpRegistrySource implements RegistrySource {
|
|
172
|
+
#private;
|
|
173
|
+
readonly name: string;
|
|
174
|
+
constructor(args: {
|
|
175
|
+
/** Registry identifier. Becomes the map slot key in the
|
|
176
|
+
* resolver's registries map. */
|
|
177
|
+
readonly name: string;
|
|
178
|
+
readonly config: RegistryConfig;
|
|
179
|
+
/** Test seam: when omitted, the default `npm-registry-fetch`
|
|
180
|
+
* wrapper is used. Production callers omit it. */
|
|
181
|
+
readonly fetchPackument?: PackumentFetcher;
|
|
182
|
+
});
|
|
183
|
+
fetchPackument(name: string): Promise<Packument>;
|
|
184
|
+
materializeRefForEntry(_name: string, _version: string, picked: PackumentVersion): MaterializedRef;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Asset-backed registry source. Reads tarballs from a
|
|
188
|
+
* `package-registry` asset via the asset service's in-process read
|
|
189
|
+
* API, extracts each tarball's `package.json`, and synthesizes a
|
|
190
|
+
* packument keyed by package name with one version entry per tarball.
|
|
191
|
+
*
|
|
192
|
+
* Caller passes bound `readBlob`/`listBlobs` methods so the resolver
|
|
193
|
+
* does not import the asset service directly; the session service
|
|
194
|
+
* adapts `AssetService.readAssetBlob` / `AssetService.listAssetBlobs`
|
|
195
|
+
* to these signatures at construction.
|
|
196
|
+
*
|
|
197
|
+
* The packument is cached in-instance for the lifetime of the source,
|
|
198
|
+
* so one resolution pass touches each tarball at most once even when
|
|
199
|
+
* a name is asked for multiple times.
|
|
200
|
+
*/
|
|
201
|
+
export declare class AssetRegistrySource implements RegistrySource {
|
|
202
|
+
#private;
|
|
203
|
+
readonly name: string;
|
|
204
|
+
constructor(args: {
|
|
205
|
+
readonly name: string;
|
|
206
|
+
readonly assetId: string;
|
|
207
|
+
readonly readBlob: (path: string) => Promise<Uint8Array>;
|
|
208
|
+
readonly listBlobs: (dir: string) => Promise<string[]>;
|
|
209
|
+
});
|
|
210
|
+
fetchPackument(name: string): Promise<Packument>;
|
|
211
|
+
/**
|
|
212
|
+
* Resolve a `(name, version)` pair to its asset-relative tarball
|
|
213
|
+
* path. Must be called after `fetchPackument` has populated the
|
|
214
|
+
* internal index for that package — the walker calls them in that
|
|
215
|
+
* order, so the ordering is implicit at the call site. Non-walker
|
|
216
|
+
* callers must call `fetchPackument` first or accept the structured
|
|
217
|
+
* error this method throws when the index lookup misses.
|
|
218
|
+
*/
|
|
219
|
+
materializeRefForEntry(name: string, version: string, _picked: PackumentVersion): MaterializedRef;
|
|
220
|
+
}
|
|
221
|
+
export declare function createClosureResolver(config: ClosureResolverConfig): ClosureResolver;
|
|
222
|
+
/**
|
|
223
|
+
* Parse and canonicalize a `name@range` spec into a pin. Throws on
|
|
224
|
+
* unparseable specs or invalid version ranges. Callers that already
|
|
225
|
+
* have a `ToolPackagePin` do not need this helper.
|
|
226
|
+
*
|
|
227
|
+
* A bare `*` is accepted because npm semantics treat it as the
|
|
228
|
+
* any-version range; the resolver then picks whatever the registry
|
|
229
|
+
* advertises as latest at deploy-assembly time. Operators who care
|
|
230
|
+
* about reproducibility of tool-package closures should pin to a
|
|
231
|
+
* concrete range (`^1.2.3`) — `*` lets the closure shift under the
|
|
232
|
+
* agent without any change to the pin set.
|
|
233
|
+
*
|
|
234
|
+
* NOTE: `*` is special-cased in two places — here and inside the
|
|
235
|
+
* `ToolPackagePinArray` narrow at `@intx/types/tool-packages`. The
|
|
236
|
+
* sites live in separate packages by design (resolver vs. wire-type
|
|
237
|
+
* validation) and cannot import each other; any new magic-range
|
|
238
|
+
* additions need to be made at both call sites to keep the
|
|
239
|
+
* REST-boundary validator and the resolver in agreement.
|
|
240
|
+
*/
|
|
241
|
+
export declare function parsePin(spec: string): ToolPackagePin;
|