@microwiseai/snapshot 0.5.1 → 0.5.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/commands/install.d.ts.map +1 -1
- package/dist/commands/install.js +195 -34
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/preflight.d.ts +31 -0
- package/dist/commands/preflight.d.ts.map +1 -0
- package/dist/commands/preflight.js +83 -0
- package/dist/commands/preflight.js.map +1 -0
- package/dist/commands/publish.d.ts +4 -0
- package/dist/commands/publish.d.ts.map +1 -1
- package/dist/commands/publish.js +35 -1
- package/dist/commands/publish.js.map +1 -1
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/distkit-closure-bridge.d.ts +102 -0
- package/dist/lib/distkit-closure-bridge.d.ts.map +1 -0
- package/dist/lib/distkit-closure-bridge.js +177 -0
- package/dist/lib/distkit-closure-bridge.js.map +1 -0
- package/dist/lib/distkit-closure-parity.d.ts +110 -0
- package/dist/lib/distkit-closure-parity.d.ts.map +1 -0
- package/dist/lib/distkit-closure-parity.js +134 -0
- package/dist/lib/distkit-closure-parity.js.map +1 -0
- package/dist/lib/distkit-routing-port.d.ts +37 -0
- package/dist/lib/distkit-routing-port.d.ts.map +1 -0
- package/dist/lib/distkit-routing-port.js +72 -0
- package/dist/lib/distkit-routing-port.js.map +1 -0
- package/dist/lib/install-timeline.d.ts +81 -0
- package/dist/lib/install-timeline.d.ts.map +1 -0
- package/dist/lib/install-timeline.js +243 -0
- package/dist/lib/install-timeline.js.map +1 -0
- package/dist/lib/preflight-adapter.d.ts +52 -0
- package/dist/lib/preflight-adapter.d.ts.map +1 -0
- package/dist/lib/preflight-adapter.js +224 -0
- package/dist/lib/preflight-adapter.js.map +1 -0
- package/dist/lib/transitive-resolver.d.ts +121 -0
- package/dist/lib/transitive-resolver.d.ts.map +1 -1
- package/dist/lib/transitive-resolver.js +190 -0
- package/dist/lib/transitive-resolver.js.map +1 -1
- package/node_modules/@microwiseai/install-preflight/dist/index.d.ts +27 -0
- package/node_modules/@microwiseai/install-preflight/dist/index.d.ts.map +1 -0
- package/node_modules/@microwiseai/install-preflight/dist/index.js +26 -0
- package/node_modules/@microwiseai/install-preflight/dist/index.js.map +1 -0
- package/node_modules/@microwiseai/install-preflight/dist/policy.d.ts +42 -0
- package/node_modules/@microwiseai/install-preflight/dist/policy.d.ts.map +1 -0
- package/node_modules/@microwiseai/install-preflight/dist/policy.js +79 -0
- package/node_modules/@microwiseai/install-preflight/dist/policy.js.map +1 -0
- package/node_modules/@microwiseai/install-preflight/dist/preflight.d.ts +18 -0
- package/node_modules/@microwiseai/install-preflight/dist/preflight.d.ts.map +1 -0
- package/node_modules/@microwiseai/install-preflight/dist/preflight.js +0 -0
- package/node_modules/@microwiseai/install-preflight/dist/preflight.js.map +1 -0
- package/node_modules/@microwiseai/install-preflight/dist/resolvers.d.ts +26 -0
- package/node_modules/@microwiseai/install-preflight/dist/resolvers.d.ts.map +1 -0
- package/node_modules/@microwiseai/install-preflight/dist/resolvers.js +63 -0
- package/node_modules/@microwiseai/install-preflight/dist/resolvers.js.map +1 -0
- package/node_modules/@microwiseai/install-preflight/dist/semver.d.ts +45 -0
- package/node_modules/@microwiseai/install-preflight/dist/semver.d.ts.map +1 -0
- package/node_modules/@microwiseai/install-preflight/dist/semver.js +195 -0
- package/node_modules/@microwiseai/install-preflight/dist/semver.js.map +1 -0
- package/node_modules/@microwiseai/install-preflight/dist/types.d.ts +136 -0
- package/node_modules/@microwiseai/install-preflight/dist/types.d.ts.map +1 -0
- package/node_modules/@microwiseai/install-preflight/dist/types.js +11 -0
- package/node_modules/@microwiseai/install-preflight/dist/types.js.map +1 -0
- package/node_modules/@microwiseai/install-preflight/package.json +49 -0
- package/package.json +9 -3
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ============================================================================
|
|
3
|
+
* Phase-2b §1.2/§1.3/§1.4 — flag-on walkClosure → manifest → lock (ACTIVE)
|
|
4
|
+
* ============================================================================
|
|
5
|
+
*
|
|
6
|
+
* distkit is linked into snapshot via local symlinks (workspace file:link):
|
|
7
|
+
* node_modules/@microwiseai/distkit → distkit/packages/core (fresh dist)
|
|
8
|
+
* node_modules/@microwiseai/distkit-schema → distkit/packages/schema (0.3.0)
|
|
9
|
+
* so the §1.2 spec imports resolve to the REAL, distkit-owned algorithm.
|
|
10
|
+
*
|
|
11
|
+
* PIPELINE (SSOT §1.2b + §1.3 recommended path):
|
|
12
|
+
*
|
|
13
|
+
* collectNpmClosureRoots(snapshot) // §1.1 (shipped, unchanged)
|
|
14
|
+
* → ClosureRoot[]
|
|
15
|
+
* → resolveManifestToLock(DistkitManifest, roots, { npm }) // §1.3 (one call)
|
|
16
|
+
* │ walkClosure (BFS, cycle-safe, multi-parent dedup, deterministic)
|
|
17
|
+
* │ npm adapter over the SCOPE-ROUTING PackumentPort (decision A)
|
|
18
|
+
* │ intent join: source/group/url/installTarget/accessTier
|
|
19
|
+
* → LockfileV2 (digest-bound, full B2 closure incl. public deps — decision B)
|
|
20
|
+
*
|
|
21
|
+
* GATED behind SNAPSHOT_DISTKIT_CLOSURE (off by default). On NO live install path
|
|
22
|
+
* yet — install-resolution CUTOVER (§5 step 6) is a separate, post-QA round.
|
|
23
|
+
*
|
|
24
|
+
* ★ DECISION A (routing): the npm adapter runs over
|
|
25
|
+
* {@link createScopeRoutingPackumentPort} — PRIVATE scopes → proxy + token,
|
|
26
|
+
* PUBLIC → npmjs token-less. Token can NOT leak to npmjs (see the port).
|
|
27
|
+
* ★ DECISION B (scope): the lock is the FULL closure including public transitive
|
|
28
|
+
* deps (materially larger than legacy /resolve-deps — owner-signed-off).
|
|
29
|
+
* ★ GUARDRAILS: (1) private=token-in-port, fail-loud on unentitled read;
|
|
30
|
+
* (2) public=token-less; (3) accessTier OBSERVATION-ONLY (no enforcement here);
|
|
31
|
+
* (4) legacy /resolve-deps NOT called on this path.
|
|
32
|
+
* ★ §1.4 FAIL-LOUD: a partial closure ⇒ a wrong lock. This path never degrades —
|
|
33
|
+
* unresolved roots/domain errors from the collector are surfaced as a hard
|
|
34
|
+
* error (see {@link buildNpmAdapterForSnapshot}); adapter/walker throws propagate.
|
|
35
|
+
*/
|
|
36
|
+
import { type ResolvedPackageInput, type LockfileV2 } from '@microwiseai/distkit';
|
|
37
|
+
/** Re-export distkit's real types for downstream (§1.3+) callers. */
|
|
38
|
+
export type { ResolvedPackageInput, LockfileV2 };
|
|
39
|
+
/** Options shared by the flag-on resolution entrypoints. */
|
|
40
|
+
export interface ResolveViaDistkitOptions {
|
|
41
|
+
/** restrict roots to these scopes (passthrough to the §1.1 collector). */
|
|
42
|
+
scopes?: string[];
|
|
43
|
+
/** proxy base url for the PRIVATE lane. Default {@link REGISTRY_PROXY_URL}. */
|
|
44
|
+
proxyUrl?: string;
|
|
45
|
+
/** npmjs base url for the PUBLIC lane. Default distkit's NPMJS_PUBLIC_REGISTRY_URL. */
|
|
46
|
+
npmjsUrl?: string;
|
|
47
|
+
/** session/PAT token for the PRIVATE lane. Default {@link getLicenseKey}. */
|
|
48
|
+
token?: string;
|
|
49
|
+
/** injectable fetch (tests). */
|
|
50
|
+
fetchImpl?: typeof fetch;
|
|
51
|
+
/**
|
|
52
|
+
* `lock.generatedFor` label (e.g. "@ist/minimal@0.6.25"). When omitted for the
|
|
53
|
+
* lock path, it is derived by fetching the snapshot's version.
|
|
54
|
+
*/
|
|
55
|
+
generatedFor?: string;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* FLAG-ON closure candidate (§1.2b). Returns the walked `ResolvedPackageInput[]`
|
|
59
|
+
* (full B2 closure incl. public deps) — used by the parity harness. For the
|
|
60
|
+
* digest-bound lock, use {@link resolveSnapshotToLock}.
|
|
61
|
+
*
|
|
62
|
+
* @throws on flag-off, §1.4 partial closure, or any adapter/walker error (fail-loud).
|
|
63
|
+
*/
|
|
64
|
+
export declare function resolveClosureViaDistkit(snapshotName: string, options?: ResolveViaDistkitOptions): Promise<ResolvedPackageInput[]>;
|
|
65
|
+
/**
|
|
66
|
+
* §5-step6 install-resolution path — resolve the transitive npm closure from an
|
|
67
|
+
* EXPLICIT set of already-resolved pins (the roots the preflight adapter computed
|
|
68
|
+
* from the snapshot's install/packages/skits maps), WITHOUT re-walking the
|
|
69
|
+
* snapshot graph. This is the walkClosure replacement for the legacy
|
|
70
|
+
* `/resolve-deps` call on the install-resolution path (behind the flag).
|
|
71
|
+
*
|
|
72
|
+
* Returns the FULL B2 closure (private + public transitive deps, roots excluded).
|
|
73
|
+
* The install consumer (preflight) filters to the lane(s) it probes.
|
|
74
|
+
*
|
|
75
|
+
* @param pins already-resolved direct npm pins (name + exact version).
|
|
76
|
+
* @param privateScopes the snapshot's registry scopes (drive the routing port +
|
|
77
|
+
* source intent — PRIVATE→proxy+token, PUBLIC→npmjs token-less).
|
|
78
|
+
* @throws on flag-off, §1.4 partial closure (an unfetchable root manifest), or any
|
|
79
|
+
* adapter/walker error (fail-loud — a partial closure ⇒ a wrong result).
|
|
80
|
+
*/
|
|
81
|
+
export declare function resolveTransitiveClosureFromPins(pins: ReadonlyArray<{
|
|
82
|
+
name: string;
|
|
83
|
+
version: string;
|
|
84
|
+
}>, privateScopes: readonly string[], options?: ResolveViaDistkitOptions): Promise<ResolvedPackageInput[]>;
|
|
85
|
+
/**
|
|
86
|
+
* §1.3 + lock emit — resolve a snapshot to a deterministic, digest-bound
|
|
87
|
+
* {@link LockfileV2} via distkit's `resolveManifestToLock`.
|
|
88
|
+
*
|
|
89
|
+
* Intent join (per closure node), decision A/B:
|
|
90
|
+
* - source ← scope-based `ResolveFromFn` (PRIVATE→`proxy`⇒our-proxy;
|
|
91
|
+
* PUBLIC→`native`⇒npmjs-public), via distkit `defaultResolvedFrom`.
|
|
92
|
+
* - group/url← `resolveManifestToLock`'s router over the built `registry`
|
|
93
|
+
* (private scope → group + proxy url; public → forced npmjs url).
|
|
94
|
+
* - installer← ecosystem default (walker) — npm⇒npm.
|
|
95
|
+
* - installTarget ← default `cli-global`.
|
|
96
|
+
* - accessTier ← OBSERVATION-ONLY default `public` (guardrail 3; authority is
|
|
97
|
+
* fetch/install, never this lock).
|
|
98
|
+
*
|
|
99
|
+
* @throws on flag-off, §1.4 partial closure, unresolved routing, or fail-loud walk.
|
|
100
|
+
*/
|
|
101
|
+
export declare function resolveSnapshotToLock(snapshotName: string, options?: ResolveViaDistkitOptions): Promise<LockfileV2>;
|
|
102
|
+
//# sourceMappingURL=distkit-closure-bridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"distkit-closure-bridge.d.ts","sourceRoot":"","sources":["../../src/lib/distkit-closure-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,EAIL,KAAK,oBAAoB,EAEzB,KAAK,UAAU,EAKhB,MAAM,sBAAsB,CAAC;AAa9B,qEAAqE;AACrE,YAAY,EAAE,oBAAoB,EAAE,UAAU,EAAE,CAAC;AAEjD,4DAA4D;AAC5D,MAAM,WAAW,wBAAwB;IACvC,0EAA0E;IAC1E,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uFAAuF;IACvF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gCAAgC;IAChC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAyDD;;;;;;GAMG;AACH,wBAAsB,wBAAwB,CAC5C,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAGjC;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,gCAAgC,CACpD,IAAI,EAAE,aAAa,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,EACtD,aAAa,EAAE,SAAS,MAAM,EAAE,EAChC,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,oBAAoB,EAAE,CAAC,CA2BjC;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,qBAAqB,CACzC,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,UAAU,CAAC,CAuCrB"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ============================================================================
|
|
3
|
+
* Phase-2b §1.2/§1.3/§1.4 — flag-on walkClosure → manifest → lock (ACTIVE)
|
|
4
|
+
* ============================================================================
|
|
5
|
+
*
|
|
6
|
+
* distkit is linked into snapshot via local symlinks (workspace file:link):
|
|
7
|
+
* node_modules/@microwiseai/distkit → distkit/packages/core (fresh dist)
|
|
8
|
+
* node_modules/@microwiseai/distkit-schema → distkit/packages/schema (0.3.0)
|
|
9
|
+
* so the §1.2 spec imports resolve to the REAL, distkit-owned algorithm.
|
|
10
|
+
*
|
|
11
|
+
* PIPELINE (SSOT §1.2b + §1.3 recommended path):
|
|
12
|
+
*
|
|
13
|
+
* collectNpmClosureRoots(snapshot) // §1.1 (shipped, unchanged)
|
|
14
|
+
* → ClosureRoot[]
|
|
15
|
+
* → resolveManifestToLock(DistkitManifest, roots, { npm }) // §1.3 (one call)
|
|
16
|
+
* │ walkClosure (BFS, cycle-safe, multi-parent dedup, deterministic)
|
|
17
|
+
* │ npm adapter over the SCOPE-ROUTING PackumentPort (decision A)
|
|
18
|
+
* │ intent join: source/group/url/installTarget/accessTier
|
|
19
|
+
* → LockfileV2 (digest-bound, full B2 closure incl. public deps — decision B)
|
|
20
|
+
*
|
|
21
|
+
* GATED behind SNAPSHOT_DISTKIT_CLOSURE (off by default). On NO live install path
|
|
22
|
+
* yet — install-resolution CUTOVER (§5 step 6) is a separate, post-QA round.
|
|
23
|
+
*
|
|
24
|
+
* ★ DECISION A (routing): the npm adapter runs over
|
|
25
|
+
* {@link createScopeRoutingPackumentPort} — PRIVATE scopes → proxy + token,
|
|
26
|
+
* PUBLIC → npmjs token-less. Token can NOT leak to npmjs (see the port).
|
|
27
|
+
* ★ DECISION B (scope): the lock is the FULL closure including public transitive
|
|
28
|
+
* deps (materially larger than legacy /resolve-deps — owner-signed-off).
|
|
29
|
+
* ★ GUARDRAILS: (1) private=token-in-port, fail-loud on unentitled read;
|
|
30
|
+
* (2) public=token-less; (3) accessTier OBSERVATION-ONLY (no enforcement here);
|
|
31
|
+
* (4) legacy /resolve-deps NOT called on this path.
|
|
32
|
+
* ★ §1.4 FAIL-LOUD: a partial closure ⇒ a wrong lock. This path never degrades —
|
|
33
|
+
* unresolved roots/domain errors from the collector are surfaced as a hard
|
|
34
|
+
* error (see {@link buildNpmAdapterForSnapshot}); adapter/walker throws propagate.
|
|
35
|
+
*/
|
|
36
|
+
import { createClosureResolver, resolveManifestToLock, defaultResolvedFrom, } from '@microwiseai/distkit';
|
|
37
|
+
import { createNpmAdapter } from '@microwiseai/distkit/adapters';
|
|
38
|
+
import { getLicenseKey, REGISTRY_PROXY_URL } from './config.js';
|
|
39
|
+
import { fetchSnapshotFromRepo } from './gitlab.js';
|
|
40
|
+
import { collectNpmClosureRoots, buildNpmClosureRoots, isDistkitClosureEnabled, } from './transitive-resolver.js';
|
|
41
|
+
import { createScopeRoutingPackumentPort, isPrivateScope } from './distkit-routing-port.js';
|
|
42
|
+
/**
|
|
43
|
+
* Collect roots (§1.1) and build the npm adapter over the scope-routing port
|
|
44
|
+
* (decision A). §1.4: fail LOUD if the collector reported ANY domain/root-manifest
|
|
45
|
+
* error — a partial root set would yield a wrong lock.
|
|
46
|
+
*/
|
|
47
|
+
async function buildNpmAdapterForSnapshot(snapshotName, options) {
|
|
48
|
+
if (!isDistkitClosureEnabled()) {
|
|
49
|
+
throw new Error('distkit closure path called while SNAPSHOT_DISTKIT_CLOSURE is OFF. It is ' +
|
|
50
|
+
'gated behind the flag; the legacy /resolve-deps path remains the default.');
|
|
51
|
+
}
|
|
52
|
+
const proxyUrl = options.proxyUrl ?? REGISTRY_PROXY_URL;
|
|
53
|
+
const { roots, errors, scopeGroups } = await collectNpmClosureRoots(snapshotName, {
|
|
54
|
+
scopes: options.scopes,
|
|
55
|
+
});
|
|
56
|
+
// §1.4 fail-loud: no silent degrade — a missing root manifest / domain fetch
|
|
57
|
+
// failure means an incomplete closure ⇒ a wrong lock. Surface as a hard error.
|
|
58
|
+
if (errors.length > 0) {
|
|
59
|
+
throw new Error(`[distkit closure §1.4] refusing to resolve a PARTIAL closure for "${snapshotName}": ` +
|
|
60
|
+
`${errors.length} unresolved root/domain error(s): ` +
|
|
61
|
+
errors.map((e) => `${e.name}: ${e.error}`).join('; '));
|
|
62
|
+
}
|
|
63
|
+
const privateScopes = Object.keys(scopeGroups);
|
|
64
|
+
const token = options.token ?? getLicenseKey() ?? undefined;
|
|
65
|
+
const port = createScopeRoutingPackumentPort({
|
|
66
|
+
privateScopes,
|
|
67
|
+
proxyUrl,
|
|
68
|
+
npmjsUrl: options.npmjsUrl,
|
|
69
|
+
token, // PRIVATE lane only; the port never sends it to npmjs.
|
|
70
|
+
fetchImpl: options.fetchImpl,
|
|
71
|
+
});
|
|
72
|
+
const npm = createNpmAdapter(port); // includePeer left at distkit default (true).
|
|
73
|
+
return { roots, scopeGroups, privateScopes, npm, proxyUrl };
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* FLAG-ON closure candidate (§1.2b). Returns the walked `ResolvedPackageInput[]`
|
|
77
|
+
* (full B2 closure incl. public deps) — used by the parity harness. For the
|
|
78
|
+
* digest-bound lock, use {@link resolveSnapshotToLock}.
|
|
79
|
+
*
|
|
80
|
+
* @throws on flag-off, §1.4 partial closure, or any adapter/walker error (fail-loud).
|
|
81
|
+
*/
|
|
82
|
+
export async function resolveClosureViaDistkit(snapshotName, options = {}) {
|
|
83
|
+
const { roots, npm } = await buildNpmAdapterForSnapshot(snapshotName, options);
|
|
84
|
+
return createClosureResolver({ npm })(roots, { includeDev: false });
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* §5-step6 install-resolution path — resolve the transitive npm closure from an
|
|
88
|
+
* EXPLICIT set of already-resolved pins (the roots the preflight adapter computed
|
|
89
|
+
* from the snapshot's install/packages/skits maps), WITHOUT re-walking the
|
|
90
|
+
* snapshot graph. This is the walkClosure replacement for the legacy
|
|
91
|
+
* `/resolve-deps` call on the install-resolution path (behind the flag).
|
|
92
|
+
*
|
|
93
|
+
* Returns the FULL B2 closure (private + public transitive deps, roots excluded).
|
|
94
|
+
* The install consumer (preflight) filters to the lane(s) it probes.
|
|
95
|
+
*
|
|
96
|
+
* @param pins already-resolved direct npm pins (name + exact version).
|
|
97
|
+
* @param privateScopes the snapshot's registry scopes (drive the routing port +
|
|
98
|
+
* source intent — PRIVATE→proxy+token, PUBLIC→npmjs token-less).
|
|
99
|
+
* @throws on flag-off, §1.4 partial closure (an unfetchable root manifest), or any
|
|
100
|
+
* adapter/walker error (fail-loud — a partial closure ⇒ a wrong result).
|
|
101
|
+
*/
|
|
102
|
+
export async function resolveTransitiveClosureFromPins(pins, privateScopes, options = {}) {
|
|
103
|
+
if (!isDistkitClosureEnabled()) {
|
|
104
|
+
throw new Error('resolveTransitiveClosureFromPins called while SNAPSHOT_DISTKIT_CLOSURE is OFF. ' +
|
|
105
|
+
'It is gated behind the flag; the legacy /resolve-deps path remains the default.');
|
|
106
|
+
}
|
|
107
|
+
const { roots, errors } = await buildNpmClosureRoots([...pins], {});
|
|
108
|
+
if (errors.length > 0) {
|
|
109
|
+
// §1.4 fail-loud: no silent degrade — a missing root manifest ⇒ partial closure.
|
|
110
|
+
throw new Error(`[distkit closure §1.4] refusing to resolve a PARTIAL closure from ${pins.length} pin(s): ` +
|
|
111
|
+
errors.map((e) => `${e.name}: ${e.error}`).join('; '));
|
|
112
|
+
}
|
|
113
|
+
const token = options.token ?? getLicenseKey() ?? undefined;
|
|
114
|
+
const port = createScopeRoutingPackumentPort({
|
|
115
|
+
privateScopes,
|
|
116
|
+
proxyUrl: options.proxyUrl ?? REGISTRY_PROXY_URL,
|
|
117
|
+
npmjsUrl: options.npmjsUrl,
|
|
118
|
+
token, // PRIVATE lane only; never sent to npmjs.
|
|
119
|
+
fetchImpl: options.fetchImpl,
|
|
120
|
+
});
|
|
121
|
+
const npm = createNpmAdapter(port);
|
|
122
|
+
return createClosureResolver({ npm })(roots, { includeDev: false });
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* §1.3 + lock emit — resolve a snapshot to a deterministic, digest-bound
|
|
126
|
+
* {@link LockfileV2} via distkit's `resolveManifestToLock`.
|
|
127
|
+
*
|
|
128
|
+
* Intent join (per closure node), decision A/B:
|
|
129
|
+
* - source ← scope-based `ResolveFromFn` (PRIVATE→`proxy`⇒our-proxy;
|
|
130
|
+
* PUBLIC→`native`⇒npmjs-public), via distkit `defaultResolvedFrom`.
|
|
131
|
+
* - group/url← `resolveManifestToLock`'s router over the built `registry`
|
|
132
|
+
* (private scope → group + proxy url; public → forced npmjs url).
|
|
133
|
+
* - installer← ecosystem default (walker) — npm⇒npm.
|
|
134
|
+
* - installTarget ← default `cli-global`.
|
|
135
|
+
* - accessTier ← OBSERVATION-ONLY default `public` (guardrail 3; authority is
|
|
136
|
+
* fetch/install, never this lock).
|
|
137
|
+
*
|
|
138
|
+
* @throws on flag-off, §1.4 partial closure, unresolved routing, or fail-loud walk.
|
|
139
|
+
*/
|
|
140
|
+
export async function resolveSnapshotToLock(snapshotName, options = {}) {
|
|
141
|
+
const { roots, scopeGroups, privateScopes, npm, proxyUrl } = await buildNpmAdapterForSnapshot(snapshotName, options);
|
|
142
|
+
// Build the DistkitManifest (ADR 0002) from the snapshot's registry scope-map.
|
|
143
|
+
// Routing only (decision A): every private scope routes to the proxy.
|
|
144
|
+
// ★ Task-5 / ADR 0003: the RegistryModel key (npm scope) is an npm-adapter-LOCAL
|
|
145
|
+
// projection, not a core identity axis — the SAME status skit's
|
|
146
|
+
// `deriveProxyRoutingScopes` (.npmrc projection) keeps. The neutral (group, name)
|
|
147
|
+
// identity is delegated to `resolveManifestToLock` (routes via
|
|
148
|
+
// `resolvePackageOriginByIdentity`); the emitted LockEntry keys on (name, group)
|
|
149
|
+
// and stores NO scope, so this map's scope keys never reach the lock bytes (C2).
|
|
150
|
+
const registry = {};
|
|
151
|
+
for (const scope of privateScopes) {
|
|
152
|
+
registry[scope] = { group: scopeGroups[scope], url: proxyUrl };
|
|
153
|
+
}
|
|
154
|
+
const manifest = {
|
|
155
|
+
manifestVersion: 1,
|
|
156
|
+
target: snapshotName,
|
|
157
|
+
registry,
|
|
158
|
+
// accessTier intentionally omitted — snapshot is not the publishing authority;
|
|
159
|
+
// emitted rows fall back to the observation-only default 'public' (guardrail 3).
|
|
160
|
+
};
|
|
161
|
+
// Scope-based source intent (decision A): PRIVATE ⇒ our-proxy, PUBLIC ⇒ npmjs.
|
|
162
|
+
const resolveFrom = (name, _source, ecosystem) => defaultResolvedFrom(isPrivateScope(name, privateScopes) ? 'proxy' : 'native', ecosystem ?? 'npm');
|
|
163
|
+
const generatedFor = options.generatedFor ?? `${snapshotName}@${await fetchSnapshotVersion(snapshotName)}`;
|
|
164
|
+
return resolveManifestToLock(manifest, roots, { npm }, {
|
|
165
|
+
generatedFor,
|
|
166
|
+
includeDev: false,
|
|
167
|
+
resolveFrom,
|
|
168
|
+
// defaultInstallTarget 'cli-global' and defaultAccessTier 'public' are the
|
|
169
|
+
// distkit defaults (observation-only) — not overridden.
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
/** Fetch the snapshot's exact version for the `generatedFor` label. */
|
|
173
|
+
async function fetchSnapshotVersion(snapshotName) {
|
|
174
|
+
const snap = await fetchSnapshotFromRepo(snapshotName);
|
|
175
|
+
return snap.version;
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=distkit-closure-bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"distkit-closure-bridge.js","sourceRoot":"","sources":["../../src/lib/distkit-closure-bridge.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ============================================================================
|
|
3
|
+
* Phase-2b §1.2c — parity-diff harness (walkClosure vs legacy /resolve-deps)
|
|
4
|
+
* ============================================================================
|
|
5
|
+
*
|
|
6
|
+
* STATUS: harness SKELETON (pure, testable NOW). The distkit side is fed by
|
|
7
|
+
* {@link resolveClosureViaDistkit} (HELD until distkit is linked); this module's
|
|
8
|
+
* diff functions are pure and unit-tested today against synthetic inputs so the
|
|
9
|
+
* classification is ready the moment real outputs exist.
|
|
10
|
+
*
|
|
11
|
+
* PURPOSE (cutover GATE): before any cutover, diff the flag-on distkit closure
|
|
12
|
+
* against the current `/resolve-deps` output on a real snapshot and CLASSIFY the
|
|
13
|
+
* differences so each is either accepted (a known B2 improvement) or flagged (a
|
|
14
|
+
* regression). Four classification axes (SSOT §1.2c):
|
|
15
|
+
*
|
|
16
|
+
* 1. ORDERING — distkit emits deterministic sorted order; legacy order may
|
|
17
|
+
* differ. Same SET, different sequence ⇒ benign.
|
|
18
|
+
* 2. DEDUP — a `name@version` present in one side only. distkit dedups
|
|
19
|
+
* by `name@version` (multi-parent diamond collapses to one).
|
|
20
|
+
* 3. requestedBy — legacy carries a SINGLE parent (`requestedBy?: string`);
|
|
21
|
+
* MERGE distkit carries the MERGED multi-parent set (`string[]`).
|
|
22
|
+
* A superset on the distkit side is the expected B2 gap-close.
|
|
23
|
+
* 4. PEER — distkit includes peerDependencies as OPTIONAL by default;
|
|
24
|
+
* INCLUSION legacy returned peers separately (`includePeer`). Packages
|
|
25
|
+
* present only because of a peer edge are surfaced here.
|
|
26
|
+
*
|
|
27
|
+
* This module is provenance-neutral: it compares by `name@version` identity and
|
|
28
|
+
* `requestedBy` sets, NOT by installer/source (that is §1.3 mapping, out of scope).
|
|
29
|
+
*/
|
|
30
|
+
/** A legacy `/resolve-deps` row (snapshot `ResolvedPackage`, single-parent). */
|
|
31
|
+
export interface LegacyClosureRow {
|
|
32
|
+
name: string;
|
|
33
|
+
version: string;
|
|
34
|
+
isDirect?: boolean;
|
|
35
|
+
/** legacy single-parent provenance (undefined for direct roots). */
|
|
36
|
+
requestedBy?: string;
|
|
37
|
+
}
|
|
38
|
+
/** A distkit `ResolvedPackageInput` row (multi-parent). Structural subset. */
|
|
39
|
+
export interface DistkitClosureRow {
|
|
40
|
+
name: string;
|
|
41
|
+
version: string;
|
|
42
|
+
isDirect: boolean;
|
|
43
|
+
/** merged, deduped, sorted parents. */
|
|
44
|
+
requestedBy: string[];
|
|
45
|
+
}
|
|
46
|
+
/** Optional hints that let the harness classify PEER-only inclusions precisely. */
|
|
47
|
+
export interface PeerClassificationHints {
|
|
48
|
+
/** `name@version` keys known to enter the closure ONLY via a peer edge. */
|
|
49
|
+
peerOnlyKeys?: readonly string[];
|
|
50
|
+
}
|
|
51
|
+
export interface RequestedByDiff {
|
|
52
|
+
key: string;
|
|
53
|
+
legacy: string[];
|
|
54
|
+
distkit: string[];
|
|
55
|
+
/** true iff distkit's parent set ⊇ legacy's (expected multi-parent gap-close). */
|
|
56
|
+
distkitSuperset: boolean;
|
|
57
|
+
}
|
|
58
|
+
export interface ParityReport {
|
|
59
|
+
/** `name@version` present in legacy but missing from distkit (investigate). */
|
|
60
|
+
onlyInLegacy: string[];
|
|
61
|
+
/** `name@version` present in distkit but missing from legacy (dedup/peer/new). */
|
|
62
|
+
onlyInDistkit: string[];
|
|
63
|
+
/** ordering comparison over the shared key set. */
|
|
64
|
+
ordering: {
|
|
65
|
+
sameSet: boolean;
|
|
66
|
+
legacyOrder: string[];
|
|
67
|
+
distkitOrder: string[];
|
|
68
|
+
sameOrder: boolean;
|
|
69
|
+
};
|
|
70
|
+
/** per-key requestedBy differences (only keys present on BOTH sides). */
|
|
71
|
+
requestedBy: RequestedByDiff[];
|
|
72
|
+
/** `onlyInDistkit` keys attributable to peer inclusion (via hints). */
|
|
73
|
+
peerInclusions: string[];
|
|
74
|
+
/** rollup counts for a quick gate read. */
|
|
75
|
+
summary: {
|
|
76
|
+
legacyCount: number;
|
|
77
|
+
distkitCount: number;
|
|
78
|
+
sharedCount: number;
|
|
79
|
+
onlyInLegacyCount: number;
|
|
80
|
+
onlyInDistkitCount: number;
|
|
81
|
+
requestedByDiffCount: number;
|
|
82
|
+
/** requestedBy diffs where distkit is NOT a superset of legacy (⇒ inspect). */
|
|
83
|
+
requestedByNonSupersetCount: number;
|
|
84
|
+
peerInclusionCount: number;
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/** Stable identity key for a closure row. */
|
|
88
|
+
export declare function closureKey(row: {
|
|
89
|
+
name: string;
|
|
90
|
+
version: string;
|
|
91
|
+
}): string;
|
|
92
|
+
/**
|
|
93
|
+
* Classify the differences between the legacy `/resolve-deps` closure and the
|
|
94
|
+
* distkit `walkClosure` closure. PURE — deterministic, no I/O. Ready to run on
|
|
95
|
+
* real outputs the moment {@link resolveClosureViaDistkit} is unblocked.
|
|
96
|
+
*/
|
|
97
|
+
export declare function diffClosures(legacy: readonly LegacyClosureRow[], distkit: readonly DistkitClosureRow[], hints?: PeerClassificationHints): ParityReport;
|
|
98
|
+
/**
|
|
99
|
+
* Gate verdict over a {@link ParityReport}. The cutover GATE passes only when
|
|
100
|
+
* every difference is a KNOWN-BENIGN B2 improvement:
|
|
101
|
+
* - `onlyInLegacy` MUST be empty (distkit dropped nothing).
|
|
102
|
+
* - every `onlyInDistkit` key is attributable to peer inclusion (hints).
|
|
103
|
+
* - every requestedBy diff is a distkit SUPERSET (multi-parent gap-close).
|
|
104
|
+
* Ordering differences are always benign (distkit is canonically sorted).
|
|
105
|
+
*/
|
|
106
|
+
export declare function isParityAcceptable(report: ParityReport): {
|
|
107
|
+
ok: boolean;
|
|
108
|
+
reasons: string[];
|
|
109
|
+
};
|
|
110
|
+
//# sourceMappingURL=distkit-closure-parity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"distkit-closure-parity.d.ts","sourceRoot":"","sources":["../../src/lib/distkit-closure-parity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAIH,gFAAgF;AAChF,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,8EAA8E;AAC9E,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,uCAAuC;IACvC,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,mFAAmF;AACnF,MAAM,WAAW,uBAAuB;IACtC,2EAA2E;IAC3E,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC;AAID,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,kFAAkF;IAClF,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC3B,+EAA+E;IAC/E,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,kFAAkF;IAClF,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,mDAAmD;IACnD,QAAQ,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,MAAM,EAAE,CAAC;QAAC,YAAY,EAAE,MAAM,EAAE,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;IAClG,yEAAyE;IACzE,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B,uEAAuE;IACvE,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,2CAA2C;IAC3C,OAAO,EAAE;QACP,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;QACrB,WAAW,EAAE,MAAM,CAAC;QACpB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,kBAAkB,EAAE,MAAM,CAAC;QAC3B,oBAAoB,EAAE,MAAM,CAAC;QAC7B,+EAA+E;QAC/E,2BAA2B,EAAE,MAAM,CAAC;QACpC,kBAAkB,EAAE,MAAM,CAAC;KAC5B,CAAC;CACH;AAID,6CAA6C;AAC7C,wBAAgB,UAAU,CAAC,GAAG,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAEzE;AAkBD;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,SAAS,gBAAgB,EAAE,EACnC,OAAO,EAAE,SAAS,iBAAiB,EAAE,EACrC,KAAK,GAAE,uBAA4B,GAClC,YAAY,CA8Dd;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG;IACxD,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,CAaA"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ============================================================================
|
|
3
|
+
* Phase-2b §1.2c — parity-diff harness (walkClosure vs legacy /resolve-deps)
|
|
4
|
+
* ============================================================================
|
|
5
|
+
*
|
|
6
|
+
* STATUS: harness SKELETON (pure, testable NOW). The distkit side is fed by
|
|
7
|
+
* {@link resolveClosureViaDistkit} (HELD until distkit is linked); this module's
|
|
8
|
+
* diff functions are pure and unit-tested today against synthetic inputs so the
|
|
9
|
+
* classification is ready the moment real outputs exist.
|
|
10
|
+
*
|
|
11
|
+
* PURPOSE (cutover GATE): before any cutover, diff the flag-on distkit closure
|
|
12
|
+
* against the current `/resolve-deps` output on a real snapshot and CLASSIFY the
|
|
13
|
+
* differences so each is either accepted (a known B2 improvement) or flagged (a
|
|
14
|
+
* regression). Four classification axes (SSOT §1.2c):
|
|
15
|
+
*
|
|
16
|
+
* 1. ORDERING — distkit emits deterministic sorted order; legacy order may
|
|
17
|
+
* differ. Same SET, different sequence ⇒ benign.
|
|
18
|
+
* 2. DEDUP — a `name@version` present in one side only. distkit dedups
|
|
19
|
+
* by `name@version` (multi-parent diamond collapses to one).
|
|
20
|
+
* 3. requestedBy — legacy carries a SINGLE parent (`requestedBy?: string`);
|
|
21
|
+
* MERGE distkit carries the MERGED multi-parent set (`string[]`).
|
|
22
|
+
* A superset on the distkit side is the expected B2 gap-close.
|
|
23
|
+
* 4. PEER — distkit includes peerDependencies as OPTIONAL by default;
|
|
24
|
+
* INCLUSION legacy returned peers separately (`includePeer`). Packages
|
|
25
|
+
* present only because of a peer edge are surfaced here.
|
|
26
|
+
*
|
|
27
|
+
* This module is provenance-neutral: it compares by `name@version` identity and
|
|
28
|
+
* `requestedBy` sets, NOT by installer/source (that is §1.3 mapping, out of scope).
|
|
29
|
+
*/
|
|
30
|
+
// ── Pure helpers ─────────────────────────────────────────────────────────────
|
|
31
|
+
/** Stable identity key for a closure row. */
|
|
32
|
+
export function closureKey(row) {
|
|
33
|
+
return `${row.name}@${row.version}`;
|
|
34
|
+
}
|
|
35
|
+
function sortedUnique(xs) {
|
|
36
|
+
return [...new Set(xs)].sort();
|
|
37
|
+
}
|
|
38
|
+
function setsEqual(a, b) {
|
|
39
|
+
if (a.length !== b.length)
|
|
40
|
+
return false;
|
|
41
|
+
const bs = new Set(b);
|
|
42
|
+
return a.every((x) => bs.has(x));
|
|
43
|
+
}
|
|
44
|
+
/** True iff every element of `sub` is in `sup`. */
|
|
45
|
+
function isSuperset(sup, sub) {
|
|
46
|
+
const s = new Set(sup);
|
|
47
|
+
return sub.every((x) => s.has(x));
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Classify the differences between the legacy `/resolve-deps` closure and the
|
|
51
|
+
* distkit `walkClosure` closure. PURE — deterministic, no I/O. Ready to run on
|
|
52
|
+
* real outputs the moment {@link resolveClosureViaDistkit} is unblocked.
|
|
53
|
+
*/
|
|
54
|
+
export function diffClosures(legacy, distkit, hints = {}) {
|
|
55
|
+
const legacyByKey = new Map();
|
|
56
|
+
for (const r of legacy)
|
|
57
|
+
legacyByKey.set(closureKey(r), r);
|
|
58
|
+
const distkitByKey = new Map();
|
|
59
|
+
for (const r of distkit)
|
|
60
|
+
distkitByKey.set(closureKey(r), r);
|
|
61
|
+
const legacyKeys = [...legacyByKey.keys()];
|
|
62
|
+
const distkitKeys = [...distkitByKey.keys()];
|
|
63
|
+
const onlyInLegacy = legacyKeys.filter((k) => !distkitByKey.has(k)).sort();
|
|
64
|
+
const onlyInDistkit = distkitKeys.filter((k) => !legacyByKey.has(k)).sort();
|
|
65
|
+
// Ordering: compare the sequence of the SHARED keys as each side lists them.
|
|
66
|
+
const sharedLegacyOrder = legacyKeys.filter((k) => distkitByKey.has(k));
|
|
67
|
+
const sharedDistkitOrder = distkitKeys.filter((k) => legacyByKey.has(k));
|
|
68
|
+
const sameSet = setsEqual(legacyKeys, distkitKeys);
|
|
69
|
+
const sameOrder = sharedLegacyOrder.length === sharedDistkitOrder.length &&
|
|
70
|
+
sharedLegacyOrder.every((k, i) => k === sharedDistkitOrder[i]);
|
|
71
|
+
// requestedBy diffs over shared keys (legacy single-parent → set of size ≤1).
|
|
72
|
+
const requestedBy = [];
|
|
73
|
+
for (const key of sharedLegacyOrder) {
|
|
74
|
+
const l = legacyByKey.get(key);
|
|
75
|
+
const d = distkitByKey.get(key);
|
|
76
|
+
const legacyParents = sortedUnique(l.requestedBy ? [l.requestedBy] : []);
|
|
77
|
+
const distkitParents = sortedUnique(d.requestedBy);
|
|
78
|
+
if (!setsEqual(legacyParents, distkitParents)) {
|
|
79
|
+
requestedBy.push({
|
|
80
|
+
key,
|
|
81
|
+
legacy: legacyParents,
|
|
82
|
+
distkit: distkitParents,
|
|
83
|
+
distkitSuperset: isSuperset(distkitParents, legacyParents),
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const peerSet = new Set(hints.peerOnlyKeys ?? []);
|
|
88
|
+
const peerInclusions = onlyInDistkit.filter((k) => peerSet.has(k));
|
|
89
|
+
return {
|
|
90
|
+
onlyInLegacy,
|
|
91
|
+
onlyInDistkit,
|
|
92
|
+
ordering: {
|
|
93
|
+
sameSet,
|
|
94
|
+
legacyOrder: sharedLegacyOrder,
|
|
95
|
+
distkitOrder: sharedDistkitOrder,
|
|
96
|
+
sameOrder,
|
|
97
|
+
},
|
|
98
|
+
requestedBy,
|
|
99
|
+
peerInclusions,
|
|
100
|
+
summary: {
|
|
101
|
+
legacyCount: legacyKeys.length,
|
|
102
|
+
distkitCount: distkitKeys.length,
|
|
103
|
+
sharedCount: sharedLegacyOrder.length,
|
|
104
|
+
onlyInLegacyCount: onlyInLegacy.length,
|
|
105
|
+
onlyInDistkitCount: onlyInDistkit.length,
|
|
106
|
+
requestedByDiffCount: requestedBy.length,
|
|
107
|
+
requestedByNonSupersetCount: requestedBy.filter((r) => !r.distkitSuperset).length,
|
|
108
|
+
peerInclusionCount: peerInclusions.length,
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Gate verdict over a {@link ParityReport}. The cutover GATE passes only when
|
|
114
|
+
* every difference is a KNOWN-BENIGN B2 improvement:
|
|
115
|
+
* - `onlyInLegacy` MUST be empty (distkit dropped nothing).
|
|
116
|
+
* - every `onlyInDistkit` key is attributable to peer inclusion (hints).
|
|
117
|
+
* - every requestedBy diff is a distkit SUPERSET (multi-parent gap-close).
|
|
118
|
+
* Ordering differences are always benign (distkit is canonically sorted).
|
|
119
|
+
*/
|
|
120
|
+
export function isParityAcceptable(report) {
|
|
121
|
+
const reasons = [];
|
|
122
|
+
if (report.summary.onlyInLegacyCount > 0) {
|
|
123
|
+
reasons.push(`${report.summary.onlyInLegacyCount} package(s) present in legacy but MISSING from distkit`);
|
|
124
|
+
}
|
|
125
|
+
const unexplainedNew = report.onlyInDistkit.filter((k) => !report.peerInclusions.includes(k));
|
|
126
|
+
if (unexplainedNew.length > 0) {
|
|
127
|
+
reasons.push(`${unexplainedNew.length} distkit-only package(s) not explained by peer inclusion`);
|
|
128
|
+
}
|
|
129
|
+
if (report.summary.requestedByNonSupersetCount > 0) {
|
|
130
|
+
reasons.push(`${report.summary.requestedByNonSupersetCount} requestedBy diff(s) where distkit is not a superset of legacy`);
|
|
131
|
+
}
|
|
132
|
+
return { ok: reasons.length === 0, reasons };
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=distkit-closure-parity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"distkit-closure-parity.js","sourceRoot":"","sources":["../../src/lib/distkit-closure-parity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type PackumentPort } from '@microwiseai/distkit/adapters';
|
|
2
|
+
/**
|
|
3
|
+
* True iff `name` belongs to one of the PRIVATE scopes (the snapshot's registry
|
|
4
|
+
* scope-map keys, e.g. `@ist`, `@microwiseai`). A private name routes to the
|
|
5
|
+
* proxy (+ token); everything else is PUBLIC (npmjs, token-less).
|
|
6
|
+
*
|
|
7
|
+
* ★ Task-5 / ADR 0003 (ecosystem-neutral identity): `scope` here is an
|
|
8
|
+
* npm-adapter-LOCAL projection — the private/public LANE selector for the npm
|
|
9
|
+
* closure — NOT a core identity axis. It carries no npm privilege: any name not
|
|
10
|
+
* in `privateScopes` (an unscoped-npm public dep AND a pypi-style name alike) is
|
|
11
|
+
* uniformly PUBLIC. The core (group, name) identity is delegated downstream to
|
|
12
|
+
* distkit's `resolveManifestToLock` / `resolvePackageOriginByIdentity`, and the
|
|
13
|
+
* emitted lock keys on (name, group) — never on scope.
|
|
14
|
+
*/
|
|
15
|
+
export declare function isPrivateScope(name: string, privateScopes: readonly string[]): boolean;
|
|
16
|
+
/** Options for {@link createScopeRoutingPackumentPort}. */
|
|
17
|
+
export interface ScopeRoutingPortOptions {
|
|
18
|
+
/** the PRIVATE scopes (snapshot registry scope-map keys). Everything else = public. */
|
|
19
|
+
privateScopes: readonly string[];
|
|
20
|
+
/** our access-controlling proxy base url (private lane). */
|
|
21
|
+
proxyUrl: string;
|
|
22
|
+
/** public npm registry base. Default {@link NPMJS_PUBLIC_REGISTRY_URL}. */
|
|
23
|
+
npmjsUrl?: string;
|
|
24
|
+
/** session/PAT token — injected into the PRIVATE lane ONLY (never public). */
|
|
25
|
+
token?: string;
|
|
26
|
+
/** injectable fetch (tests). Defaults to global `fetch` inside each sub-port. */
|
|
27
|
+
fetchImpl?: typeof fetch;
|
|
28
|
+
/** per-request timeout (ms) forwarded to both sub-ports. */
|
|
29
|
+
timeoutMs?: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Build a {@link PackumentPort} that routes each `fetchPackument(name)` to the
|
|
33
|
+
* proxy (private, tokened) or npmjs (public, token-less) by scope. Plug into
|
|
34
|
+
* `createNpmAdapter(port)`.
|
|
35
|
+
*/
|
|
36
|
+
export declare function createScopeRoutingPackumentPort(opts: ScopeRoutingPortOptions): PackumentPort;
|
|
37
|
+
//# sourceMappingURL=distkit-routing-port.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"distkit-routing-port.d.ts","sourceRoot":"","sources":["../../src/lib/distkit-routing-port.ts"],"names":[],"mappings":"AAuBA,OAAO,EAA4B,KAAK,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAE7F;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAKtF;AAED,2DAA2D;AAC3D,MAAM,WAAW,uBAAuB;IACtC,uFAAuF;IACvF,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,4DAA4D;IAC5D,QAAQ,EAAE,MAAM,CAAC;IACjB,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8EAA8E;IAC9E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,+BAA+B,CAAC,IAAI,EAAE,uBAAuB,GAAG,aAAa,CAuB5F"}
|