@microwiseai/snapshot 0.8.0 → 0.9.0
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/build-info.json +1 -0
- package/dist/commands/access.d.ts +25 -9
- package/dist/commands/access.d.ts.map +1 -1
- package/dist/commands/access.js +101 -47
- package/dist/commands/access.js.map +1 -1
- package/dist/commands/config.d.ts +3 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +37 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/install.d.ts +54 -0
- package/dist/commands/install.d.ts.map +1 -1
- package/dist/commands/install.js +480 -65
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/publish.d.ts +10 -0
- package/dist/commands/publish.d.ts.map +1 -1
- package/dist/commands/publish.js +94 -12
- package/dist/commands/publish.js.map +1 -1
- package/dist/index.js +45 -8
- package/dist/index.js.map +1 -1
- package/dist/lib/access-index.d.ts +81 -0
- package/dist/lib/access-index.d.ts.map +1 -0
- package/dist/lib/access-index.js +175 -0
- package/dist/lib/access-index.js.map +1 -0
- package/dist/lib/access-sync.d.ts +66 -17
- package/dist/lib/access-sync.d.ts.map +1 -1
- package/dist/lib/access-sync.js +391 -217
- package/dist/lib/access-sync.js.map +1 -1
- package/dist/lib/extends-access.d.ts +34 -0
- package/dist/lib/extends-access.d.ts.map +1 -0
- package/dist/lib/extends-access.js +82 -0
- package/dist/lib/extends-access.js.map +1 -0
- package/dist/lib/gitlab-publisher.d.ts +70 -0
- package/dist/lib/gitlab-publisher.d.ts.map +1 -0
- package/dist/lib/gitlab-publisher.js +165 -0
- package/dist/lib/gitlab-publisher.js.map +1 -0
- package/dist/lib/gitlab.d.ts +4 -0
- package/dist/lib/gitlab.d.ts.map +1 -1
- package/dist/lib/gitlab.js +1 -1
- package/dist/lib/gitlab.js.map +1 -1
- package/dist/lib/grant.d.ts +157 -0
- package/dist/lib/grant.d.ts.map +1 -0
- package/dist/lib/grant.js +352 -0
- package/dist/lib/grant.js.map +1 -0
- package/dist/lib/install-verify.d.ts +16 -2
- package/dist/lib/install-verify.d.ts.map +1 -1
- package/dist/lib/install-verify.js +24 -3
- package/dist/lib/install-verify.js.map +1 -1
- package/dist/lib/ledger.d.ts +13 -0
- package/dist/lib/ledger.d.ts.map +1 -1
- package/dist/lib/ledger.js.map +1 -1
- package/dist/lib/prerequisites.d.ts.map +1 -1
- package/dist/lib/prerequisites.js +4 -1
- package/dist/lib/prerequisites.js.map +1 -1
- package/dist/lib/proxy-auth-globalconfig.d.ts +55 -0
- package/dist/lib/proxy-auth-globalconfig.d.ts.map +1 -0
- package/dist/lib/proxy-auth-globalconfig.js +110 -0
- package/dist/lib/proxy-auth-globalconfig.js.map +1 -0
- package/dist/lib/semver-range.d.ts +32 -0
- package/dist/lib/semver-range.d.ts.map +1 -0
- package/dist/lib/semver-range.js +243 -0
- package/dist/lib/semver-range.js.map +1 -0
- package/dist/lib/session.d.ts +5 -0
- package/dist/lib/session.d.ts.map +1 -1
- package/dist/lib/session.js +9 -0
- package/dist/lib/session.js.map +1 -1
- package/dist/lib/skit-adapter.d.ts.map +1 -1
- package/dist/lib/skit-adapter.js +26 -3
- package/dist/lib/skit-adapter.js.map +1 -1
- package/dist/lib/timing.d.ts +7 -0
- package/dist/lib/timing.d.ts.map +1 -0
- package/dist/lib/timing.js +14 -0
- package/dist/lib/timing.js.map +1 -0
- package/dist/lib/types.d.ts +3 -0
- package/dist/lib/types.d.ts.map +1 -1
- package/node_modules/@microwiseai/snapshot-downgrade-guard/dist/command-specs.d.ts +25 -0
- package/node_modules/@microwiseai/snapshot-downgrade-guard/dist/command-specs.d.ts.map +1 -0
- package/node_modules/@microwiseai/snapshot-downgrade-guard/dist/command-specs.js +55 -0
- package/node_modules/@microwiseai/snapshot-downgrade-guard/dist/command-specs.js.map +1 -0
- package/node_modules/@microwiseai/snapshot-downgrade-guard/dist/config.d.ts +52 -0
- package/node_modules/@microwiseai/snapshot-downgrade-guard/dist/config.d.ts.map +1 -0
- package/node_modules/@microwiseai/snapshot-downgrade-guard/dist/config.js +62 -0
- package/node_modules/@microwiseai/snapshot-downgrade-guard/dist/config.js.map +1 -0
- package/node_modules/@microwiseai/snapshot-downgrade-guard/dist/detect.d.ts +30 -0
- package/node_modules/@microwiseai/snapshot-downgrade-guard/dist/detect.d.ts.map +1 -0
- package/node_modules/@microwiseai/snapshot-downgrade-guard/dist/detect.js +39 -0
- package/node_modules/@microwiseai/snapshot-downgrade-guard/dist/detect.js.map +1 -0
- package/node_modules/@microwiseai/snapshot-downgrade-guard/dist/index.d.ts +35 -0
- package/node_modules/@microwiseai/snapshot-downgrade-guard/dist/index.d.ts.map +1 -0
- package/node_modules/@microwiseai/snapshot-downgrade-guard/dist/index.js +31 -0
- package/node_modules/@microwiseai/snapshot-downgrade-guard/dist/index.js.map +1 -0
- package/node_modules/@microwiseai/snapshot-downgrade-guard/dist/policy.d.ts +42 -0
- package/node_modules/@microwiseai/snapshot-downgrade-guard/dist/policy.d.ts.map +1 -0
- package/node_modules/@microwiseai/snapshot-downgrade-guard/dist/policy.js +36 -0
- package/node_modules/@microwiseai/snapshot-downgrade-guard/dist/policy.js.map +1 -0
- package/node_modules/@microwiseai/snapshot-downgrade-guard/dist/report.d.ts +11 -0
- package/node_modules/@microwiseai/snapshot-downgrade-guard/dist/report.d.ts.map +1 -0
- package/node_modules/@microwiseai/snapshot-downgrade-guard/dist/report.js +46 -0
- package/node_modules/@microwiseai/snapshot-downgrade-guard/dist/report.js.map +1 -0
- package/node_modules/@microwiseai/snapshot-downgrade-guard/dist/semver.d.ts +11 -0
- package/node_modules/@microwiseai/snapshot-downgrade-guard/dist/semver.d.ts.map +1 -0
- package/node_modules/@microwiseai/snapshot-downgrade-guard/dist/semver.js +81 -0
- package/node_modules/@microwiseai/snapshot-downgrade-guard/dist/semver.js.map +1 -0
- package/node_modules/@microwiseai/snapshot-downgrade-guard/dist/types.d.ts +29 -0
- package/node_modules/@microwiseai/snapshot-downgrade-guard/dist/types.d.ts.map +1 -0
- package/node_modules/@microwiseai/snapshot-downgrade-guard/dist/types.js +6 -0
- package/node_modules/@microwiseai/snapshot-downgrade-guard/dist/types.js.map +1 -0
- package/node_modules/@microwiseai/snapshot-downgrade-guard/package.json +55 -0
- package/package.json +5 -2
- package/dist/lib/transitive-resolver.d.ts +0 -89
- package/dist/lib/transitive-resolver.d.ts.map +0 -1
- package/dist/lib/transitive-resolver.js +0 -447
- package/dist/lib/transitive-resolver.js.map +0 -1
package/dist/lib/access-sync.js
CHANGED
|
@@ -1,260 +1,434 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Install-set closure resolver (formerly "access sync").
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
|
|
7
|
-
import chalk from 'chalk';
|
|
8
|
-
import { LICENSE_API_URL, GITLAB_GROUP_ID, REGISTRY_PROXY_URL } from './config.js';
|
|
9
|
-
/**
|
|
10
|
-
* Resolve the admin API key from the environment.
|
|
4
|
+
* Walks a snapshot's install-set dependency closure through the registry-proxy.
|
|
5
|
+
* The result feeds access-index.json (see ./access-index.ts), the serving SSOT
|
|
6
|
+
* that registry-proxy reads to grant snapshot members.
|
|
11
7
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
8
|
+
* The former license-api write path (POST /admin/package-access/with-deps,
|
|
9
|
+
* LICENSE_API_ADMIN_KEY) was retired by the 2026-09-28 owner decision
|
|
10
|
+
* (global state `snapshot-tier-access`, `owner-decision-2026-09-28-proxy-snapshot-driven`):
|
|
11
|
+
* "license-api package_access 에 snapshot 의존 패키지를 등록하지 않음(access-sync 의
|
|
12
|
+
* license-api 쓰기 경로 폐기)". Nothing in this module writes anywhere.
|
|
14
13
|
*/
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
import chalk from 'chalk';
|
|
15
|
+
import { createGunzip } from 'zlib';
|
|
16
|
+
import { Readable } from 'stream';
|
|
17
|
+
import { extract } from 'tar-stream';
|
|
18
|
+
import { maxSatisfying, parseVersion } from './semver-range.js';
|
|
19
|
+
import { REGISTRY_PROXY_URL, getLicenseKey, getProxyAuthToken } from './config.js';
|
|
20
|
+
import { cachedGenericVersions, fetchGitlabGenericTarball, fetchGitlabPackument, } from './gitlab-publisher.js';
|
|
21
|
+
function proxyAuthHeaders() {
|
|
22
|
+
const token = getLicenseKey() || getProxyAuthToken();
|
|
23
|
+
return token ? { Authorization: `Bearer ${token}` } : {};
|
|
22
24
|
}
|
|
23
25
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
+
* Normalize a dependency spec. Handles `npm:` aliases (install the real
|
|
27
|
+
* package) and `workspace:`; other protocols (file:, git, url, link:) are not
|
|
28
|
+
* registry packages → null.
|
|
26
29
|
*/
|
|
27
|
-
function
|
|
28
|
-
|
|
29
|
-
if (
|
|
30
|
+
function normalizeSpec(name, spec) {
|
|
31
|
+
let s = (spec ?? '').trim();
|
|
32
|
+
if (s.startsWith('npm:')) {
|
|
33
|
+
const m = /^npm:(@?[^@]+)(?:@(.*))?$/.exec(s);
|
|
34
|
+
if (!m)
|
|
35
|
+
return null;
|
|
36
|
+
return { name: m[1], spec: m[2] ?? 'latest' };
|
|
37
|
+
}
|
|
38
|
+
if (s.startsWith('workspace:'))
|
|
39
|
+
s = s.slice('workspace:'.length);
|
|
40
|
+
if (/^(file:|link:|git\+|git:|https?:|github:)/.test(s) || /^[^@\s]+\/[^\s]+$/.test(s))
|
|
30
41
|
return null;
|
|
31
|
-
return {
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Convert SnapshotAccess to license-api access level
|
|
35
|
-
*/
|
|
36
|
-
function getAccessLevel(access) {
|
|
37
|
-
if (!access)
|
|
38
|
-
return 'licensed'; // Default to licensed
|
|
39
|
-
if (access === 'public')
|
|
40
|
-
return 'public';
|
|
41
|
-
if (access === 'authenticated')
|
|
42
|
-
return 'authenticated';
|
|
43
|
-
if (typeof access === 'object' && access.type === 'licensed')
|
|
44
|
-
return 'licensed';
|
|
45
|
-
return 'licensed';
|
|
42
|
+
return { name, spec: s === '' ? '*' : s };
|
|
46
43
|
}
|
|
47
44
|
/**
|
|
48
|
-
*
|
|
45
|
+
* Resolve a spec against a version list: dist-tag → exact → highest version
|
|
46
|
+
* satisfying the range (prerelease allowed only for prerelease pins).
|
|
47
|
+
* @internal Exported for testing
|
|
49
48
|
*/
|
|
50
|
-
function
|
|
51
|
-
if (
|
|
52
|
-
return
|
|
53
|
-
|
|
54
|
-
|
|
49
|
+
export function resolveSpecVersion(versions, distTags, spec) {
|
|
50
|
+
if (distTags[spec])
|
|
51
|
+
return distTags[spec];
|
|
52
|
+
if (versions.includes(spec))
|
|
53
|
+
return spec;
|
|
54
|
+
return maxSatisfying(versions, spec);
|
|
55
55
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
description: rule.description,
|
|
77
|
-
requiredProductIds: rule.requiredProductIds,
|
|
78
|
-
parentPackage: rule.parentPackage,
|
|
79
|
-
// Disable server-side auto dependency resolution — the client enumerates
|
|
80
|
-
// all packages itself and registers each one individually.
|
|
81
|
-
dependencies: [],
|
|
82
|
-
};
|
|
83
|
-
const url = `${LICENSE_API_URL}/admin/package-access/with-deps`;
|
|
84
|
-
const response = await fetch(url, {
|
|
85
|
-
method: 'POST',
|
|
86
|
-
headers: {
|
|
87
|
-
'Content-Type': 'application/json',
|
|
88
|
-
'X-Admin-Api-Key': getAdminApiKey(),
|
|
89
|
-
},
|
|
90
|
-
body: JSON.stringify(body),
|
|
91
|
-
});
|
|
92
|
-
if (response.ok) {
|
|
93
|
-
return true;
|
|
56
|
+
async function classifyNpm(name, cache, headers, source = 'proxy') {
|
|
57
|
+
const cached = cache.get(name);
|
|
58
|
+
if (cached)
|
|
59
|
+
return cached;
|
|
60
|
+
const p = (async () => {
|
|
61
|
+
if (!name.startsWith('@'))
|
|
62
|
+
return { kind: 'npmjs' };
|
|
63
|
+
if (source === 'gitlab') {
|
|
64
|
+
const r = await fetchGitlabPackument(name);
|
|
65
|
+
if (r.kind === 'npmjs')
|
|
66
|
+
return { kind: 'npmjs' };
|
|
67
|
+
if (r.kind === 'error')
|
|
68
|
+
return { kind: 'unknown', reason: r.reason };
|
|
69
|
+
const packument = r.packument;
|
|
70
|
+
return {
|
|
71
|
+
kind: 'ours',
|
|
72
|
+
versions: Object.keys(packument.versions ?? {}),
|
|
73
|
+
distTags: packument['dist-tags'] ?? {},
|
|
74
|
+
packument,
|
|
75
|
+
};
|
|
94
76
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
77
|
+
try {
|
|
78
|
+
const res = await fetch(`${REGISTRY_PROXY_URL}/${name}`, { headers });
|
|
79
|
+
if (res.ok) {
|
|
80
|
+
const packument = (await res.json());
|
|
81
|
+
return {
|
|
82
|
+
kind: 'ours',
|
|
83
|
+
versions: Object.keys(packument.versions ?? {}),
|
|
84
|
+
distTags: packument['dist-tags'] ?? {},
|
|
85
|
+
packument,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
if (res.status === 404)
|
|
89
|
+
return { kind: 'npmjs' };
|
|
90
|
+
const vres = await fetch(`${REGISTRY_PROXY_URL}/${name}/versions`, { headers });
|
|
91
|
+
if (vres.ok) {
|
|
92
|
+
const v = (await vres.json());
|
|
93
|
+
return {
|
|
94
|
+
kind: 'ours',
|
|
95
|
+
versions: v.versions ?? [],
|
|
96
|
+
distTags: v.latest ? { latest: v.latest } : {},
|
|
97
|
+
packument: null,
|
|
98
|
+
packumentStatus: res.status,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
if (vres.status === 404)
|
|
102
|
+
return { kind: 'npmjs' };
|
|
103
|
+
return { kind: 'unknown', reason: `HTTP ${res.status}/${vres.status}` };
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
return { kind: 'unknown', reason: String(error) };
|
|
107
|
+
}
|
|
108
|
+
})();
|
|
109
|
+
cache.set(name, p);
|
|
110
|
+
return p;
|
|
103
111
|
}
|
|
104
112
|
/**
|
|
105
|
-
*
|
|
113
|
+
* Pick the npm deps (name → spec) a kit's skit.json declares under
|
|
114
|
+
* `dependencies.npm`. Entries marked `source: "npm"` come from public npmjs
|
|
115
|
+
* and are excluded. Object entries use their `version` field.
|
|
116
|
+
* @internal Exported for testing
|
|
106
117
|
*/
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
scopes: ['@ist'],
|
|
120
|
-
includePeer: false,
|
|
121
|
-
}),
|
|
122
|
-
});
|
|
123
|
-
if (!response.ok) {
|
|
124
|
-
return [];
|
|
118
|
+
export function kitNpmDeps(skitJson) {
|
|
119
|
+
const npm = skitJson
|
|
120
|
+
?.dependencies?.npm;
|
|
121
|
+
if (!npm || typeof npm !== 'object')
|
|
122
|
+
return [];
|
|
123
|
+
const out = [];
|
|
124
|
+
for (const [name, spec] of Object.entries(npm)) {
|
|
125
|
+
if (spec && typeof spec === 'object') {
|
|
126
|
+
const o = spec;
|
|
127
|
+
if (o.source === 'npm')
|
|
128
|
+
continue;
|
|
129
|
+
out.push([name, o.version ?? 'latest']);
|
|
125
130
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
// Filter to only @ist scoped packages
|
|
129
|
-
return data.dependencies
|
|
130
|
-
.map(d => d.name)
|
|
131
|
-
.filter(name => name.startsWith('@ist/'));
|
|
131
|
+
else {
|
|
132
|
+
out.push([name, typeof spec === 'string' ? spec : 'latest']);
|
|
132
133
|
}
|
|
133
|
-
return [];
|
|
134
134
|
}
|
|
135
|
-
|
|
135
|
+
return out;
|
|
136
|
+
}
|
|
137
|
+
/** @internal Exported for testing */
|
|
138
|
+
export function kitNpmDepNames(skitJson) {
|
|
139
|
+
return kitNpmDeps(skitJson).map(([name]) => name);
|
|
140
|
+
}
|
|
141
|
+
/** Kit → kit deps from skit.json `dependencies.skit`. */
|
|
142
|
+
function kitSkitDeps(skitJson) {
|
|
143
|
+
const skit = skitJson
|
|
144
|
+
?.dependencies?.skit;
|
|
145
|
+
if (!skit || typeof skit !== 'object')
|
|
136
146
|
return [];
|
|
137
|
-
|
|
147
|
+
return Object.entries(skit).map(([name, spec]) => [
|
|
148
|
+
name,
|
|
149
|
+
typeof spec === 'string' ? spec : (spec?.version ?? 'latest'),
|
|
150
|
+
]);
|
|
151
|
+
}
|
|
152
|
+
/** Read skit.json out of a gzipped kit tarball. */
|
|
153
|
+
function extractSkitJsonFromBuffer(buffer) {
|
|
154
|
+
return new Promise((resolve, reject) => {
|
|
155
|
+
const extractor = extract();
|
|
156
|
+
let skitJson = null;
|
|
157
|
+
extractor.on('entry', (header, stream, next) => {
|
|
158
|
+
const chunks = [];
|
|
159
|
+
stream.on('data', (chunk) => chunks.push(chunk));
|
|
160
|
+
stream.on('end', () => {
|
|
161
|
+
const name = header.name.replace(/^\.\//, '');
|
|
162
|
+
// Top-level skit.json only (either at root or under a single wrapper dir).
|
|
163
|
+
if (skitJson === null && (name === 'skit.json' || /^[^/]+\/skit\.json$/.test(name))) {
|
|
164
|
+
try {
|
|
165
|
+
skitJson = JSON.parse(Buffer.concat(chunks).toString('utf-8'));
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
reject(new Error('Failed to parse skit.json'));
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
next();
|
|
173
|
+
});
|
|
174
|
+
stream.on('error', reject);
|
|
175
|
+
});
|
|
176
|
+
extractor.on('finish', () => {
|
|
177
|
+
if (skitJson !== null)
|
|
178
|
+
resolve(skitJson);
|
|
179
|
+
else
|
|
180
|
+
reject(new Error('skit.json not found in kit tarball'));
|
|
181
|
+
});
|
|
182
|
+
extractor.on('error', reject);
|
|
183
|
+
Readable.from(buffer).pipe(createGunzip()).on('error', reject).pipe(extractor);
|
|
184
|
+
});
|
|
138
185
|
}
|
|
186
|
+
const pendingKey = (p) => `${p.kind}:${p.name}@${p.version}`;
|
|
139
187
|
/**
|
|
140
|
-
*
|
|
188
|
+
* Walker over the install-set dependency closure (see the block comment
|
|
189
|
+
* above). `drain()` processes the queue; items whose deps cannot be read
|
|
190
|
+
* (kit tarball / packument 401, …) are kept in `pending` and reported as gaps.
|
|
191
|
+
*
|
|
192
|
+
* With `transitive: false` only declared entries are collected (install.*
|
|
193
|
+
* members + kit skit.json npm/skit deps); package.json is not descended.
|
|
141
194
|
*/
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
195
|
+
class ClosureWalker {
|
|
196
|
+
opts;
|
|
197
|
+
register = new Set();
|
|
198
|
+
npmjs = new Set();
|
|
199
|
+
unresolved = [];
|
|
200
|
+
/** Non-retryable traversal gaps (e.g. no version satisfies the pin). */
|
|
201
|
+
unmatched = [];
|
|
202
|
+
pending = new Map();
|
|
203
|
+
/** `${kind}:${name}` → exact versions resolved by the walk. */
|
|
204
|
+
resolved = new Map();
|
|
205
|
+
queue = [];
|
|
206
|
+
seenSpec = new Set();
|
|
207
|
+
visited = new Set();
|
|
208
|
+
cache = new Map();
|
|
209
|
+
headers;
|
|
210
|
+
hasToken;
|
|
211
|
+
constructor(snapshot, opts) {
|
|
212
|
+
this.opts = opts;
|
|
213
|
+
// GitLab mode never sends the user's proxy token anywhere.
|
|
214
|
+
this.headers = opts.source === 'gitlab' ? {} : proxyAuthHeaders();
|
|
215
|
+
this.hasToken = opts.source === 'gitlab' || 'Authorization' in this.headers;
|
|
216
|
+
for (const [installer, pkgs] of Object.entries(snapshot.install ?? {})) {
|
|
217
|
+
const kind = installer === 'skit' ? 'kit' : 'npm';
|
|
218
|
+
for (const [name, spec] of Object.entries(pkgs))
|
|
219
|
+
this.queue.push({ kind, name, spec, declared: true });
|
|
220
|
+
}
|
|
221
|
+
for (const [name, spec] of Object.entries(snapshot.packages ?? {})) {
|
|
222
|
+
this.queue.push({ kind: 'npm', name, spec: String(spec), declared: true });
|
|
223
|
+
}
|
|
224
|
+
for (const [name, spec] of Object.entries(snapshot.skits ?? {})) {
|
|
225
|
+
this.queue.push({ kind: 'kit', name, spec, declared: true });
|
|
148
226
|
}
|
|
149
227
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
228
|
+
async drain() {
|
|
229
|
+
while (this.queue.length > 0) {
|
|
230
|
+
const item = this.queue.shift();
|
|
231
|
+
const specKey = `${item.kind}:${item.name}@${item.spec}:${item.declared}`;
|
|
232
|
+
if (this.seenSpec.has(specKey))
|
|
233
|
+
continue;
|
|
234
|
+
this.seenSpec.add(specKey);
|
|
235
|
+
if (item.kind === 'kit')
|
|
236
|
+
await this.processKit(item);
|
|
237
|
+
else
|
|
238
|
+
await this.processNpm(item);
|
|
239
|
+
}
|
|
153
240
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
241
|
+
result() {
|
|
242
|
+
const pending = [...this.pending.values()];
|
|
243
|
+
return {
|
|
244
|
+
packages: [...this.register],
|
|
245
|
+
npmjsExcluded: [...this.npmjs].filter((n) => !this.register.has(n)),
|
|
246
|
+
skippedKits: pending.filter((p) => p.kind === 'kit').map((p) => `${p.name}@${p.version}`),
|
|
247
|
+
untraversed: [
|
|
248
|
+
...pending.filter((p) => p.kind === 'npm').map((p) => `${p.name}@${p.version} (${p.reason})`),
|
|
249
|
+
...this.unmatched,
|
|
250
|
+
],
|
|
251
|
+
unresolved: [...this.unresolved],
|
|
252
|
+
resolved: [...this.resolved.values()].map((r) => ({ ...r, versions: [...r.versions] })),
|
|
253
|
+
};
|
|
157
254
|
}
|
|
158
|
-
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
* @param options - Sync options
|
|
165
|
-
*/
|
|
166
|
-
export async function syncPackageAccess(snapshot, options = {}) {
|
|
167
|
-
const { verbose = false, dryRun = false, includeTransitive = true } = options;
|
|
168
|
-
const result = {
|
|
169
|
-
success: true,
|
|
170
|
-
registered: [],
|
|
171
|
-
failed: [],
|
|
172
|
-
skipped: [],
|
|
173
|
-
};
|
|
174
|
-
const accessLevel = getAccessLevel(snapshot.access);
|
|
175
|
-
const productId = getProductId(snapshot.access);
|
|
176
|
-
console.log(chalk.cyan(`\nSyncing package access for ${snapshot.name}...`));
|
|
177
|
-
console.log(chalk.dim(` Access level: ${accessLevel}`));
|
|
178
|
-
// 1. Extract direct packages from snapshot
|
|
179
|
-
const directPackages = extractPackagesFromSnapshot(snapshot);
|
|
180
|
-
if (verbose) {
|
|
181
|
-
console.log(chalk.dim(` Direct packages: ${directPackages.length}`));
|
|
255
|
+
recordVersion(kind, name, version) {
|
|
256
|
+
const key = `${kind}:${name}`;
|
|
257
|
+
const entry = this.resolved.get(key) ?? { name, kind, versions: [] };
|
|
258
|
+
if (!entry.versions.includes(version))
|
|
259
|
+
entry.versions.push(version);
|
|
260
|
+
this.resolved.set(key, entry);
|
|
182
261
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
262
|
+
addPending(item) {
|
|
263
|
+
this.pending.set(pendingKey(item), item);
|
|
264
|
+
}
|
|
265
|
+
async processNpm(item) {
|
|
266
|
+
if (item.declared)
|
|
267
|
+
this.register.add(item.name);
|
|
268
|
+
if (!this.opts.transitive)
|
|
269
|
+
return;
|
|
270
|
+
const norm = normalizeSpec(item.name, item.spec);
|
|
271
|
+
if (!norm) {
|
|
272
|
+
if (this.opts.verbose)
|
|
273
|
+
console.log(chalk.dim(` Skipping non-registry spec ${item.name}@${item.spec}`));
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
const cls = await classifyNpm(norm.name, this.cache, this.headers, this.opts.source);
|
|
277
|
+
if (cls.kind === 'npmjs') {
|
|
278
|
+
if (!item.declared)
|
|
279
|
+
this.npmjs.add(norm.name);
|
|
280
|
+
return;
|
|
190
281
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
282
|
+
if (cls.kind === 'unknown') {
|
|
283
|
+
console.error(chalk.yellow(` Warning: could not look up ${norm.name} (${cls.reason})`));
|
|
284
|
+
this.unresolved.push(`${norm.name} (${cls.reason})`);
|
|
285
|
+
return;
|
|
194
286
|
}
|
|
287
|
+
this.register.add(norm.name);
|
|
288
|
+
let version = resolveSpecVersion(cls.versions, cls.distTags, norm.spec);
|
|
289
|
+
if (!version) {
|
|
290
|
+
const fallback = cls.distTags.latest ?? maxSatisfying(cls.versions, '*');
|
|
291
|
+
console.error(chalk.yellow(` Warning: no version of ${norm.name} satisfies "${norm.spec}"` +
|
|
292
|
+
(fallback ? `; traversing ${fallback} instead` : '')));
|
|
293
|
+
if (!fallback) {
|
|
294
|
+
this.unmatched.push(`${norm.name}@${norm.spec} (no matching version)`);
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
// Not an exact resolution: the install would not get this version, so it
|
|
298
|
+
// is a gap (not recorded in `resolved`), but deps are still walked.
|
|
299
|
+
this.unmatched.push(`${norm.name}@${norm.spec} (no matching version; traversed ${fallback})`);
|
|
300
|
+
version = fallback;
|
|
301
|
+
}
|
|
302
|
+
else {
|
|
303
|
+
this.recordVersion('npm', norm.name, version);
|
|
304
|
+
}
|
|
305
|
+
const key = `npm:${norm.name}@${version}`;
|
|
306
|
+
if (this.visited.has(key))
|
|
307
|
+
return;
|
|
308
|
+
this.visited.add(key);
|
|
309
|
+
const manifest = cls.packument?.versions?.[version];
|
|
310
|
+
if (!manifest) {
|
|
311
|
+
const reason = cls.packument
|
|
312
|
+
? 'version missing from packument'
|
|
313
|
+
: `packument HTTP ${cls.packumentStatus}${this.hasToken ? '' : ', 토큰 없음'}`;
|
|
314
|
+
console.error(chalk.yellow(` Warning: deps of ${norm.name}@${version} not traversed (${reason})`));
|
|
315
|
+
this.addPending({ kind: 'npm', name: norm.name, version, reason });
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
this.expandManifest(norm.name, version, manifest);
|
|
195
319
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
320
|
+
expandManifest(name, version, manifest) {
|
|
321
|
+
const deps = {
|
|
322
|
+
...manifest.dependencies,
|
|
323
|
+
...manifest.optionalDependencies,
|
|
324
|
+
...manifest.peerDependencies,
|
|
325
|
+
};
|
|
326
|
+
if (this.opts.verbose && Object.keys(deps).length > 0) {
|
|
327
|
+
console.log(chalk.dim(` ${name}@${version} deps: ${Object.keys(deps).join(', ')}`));
|
|
328
|
+
}
|
|
329
|
+
for (const [dep, spec] of Object.entries(deps)) {
|
|
330
|
+
this.queue.push({ kind: 'npm', name: dep, spec, declared: false });
|
|
331
|
+
}
|
|
205
332
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
if (!
|
|
211
|
-
|
|
212
|
-
|
|
333
|
+
async processKit(item) {
|
|
334
|
+
this.register.add(item.name);
|
|
335
|
+
const registryName = kitRegistryName(item.name);
|
|
336
|
+
let version = parseVersion(item.spec) ? item.spec : null;
|
|
337
|
+
if (!version) {
|
|
338
|
+
try {
|
|
339
|
+
if (this.opts.source === 'gitlab') {
|
|
340
|
+
const versions = (await cachedGenericVersions(registryName)).map((p) => p.version);
|
|
341
|
+
const latest = maxSatisfying(versions, '*');
|
|
342
|
+
version = resolveSpecVersion(versions, latest ? { latest } : {}, item.spec);
|
|
343
|
+
}
|
|
344
|
+
else {
|
|
345
|
+
const res = await fetch(`${REGISTRY_PROXY_URL}/generic/${registryName}/versions`, { headers: this.headers });
|
|
346
|
+
if (res.ok) {
|
|
347
|
+
const v = (await res.json());
|
|
348
|
+
version = resolveSpecVersion(v.versions ?? [], v.latest ? { latest: v.latest } : {}, item.spec);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
catch {
|
|
353
|
+
// handled below
|
|
354
|
+
}
|
|
355
|
+
if (!version) {
|
|
356
|
+
this.unmatched.push(`${item.name}@${item.spec} (kit version not resolved)`);
|
|
357
|
+
console.error(chalk.yellow(` Warning: could not resolve kit ${item.name}@${item.spec}; skipping its deps`));
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
213
360
|
}
|
|
214
|
-
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
if (accessLevel === 'licensed' && productId) {
|
|
227
|
-
rule.requiredProductIds = productId;
|
|
361
|
+
this.recordVersion('generic', item.name, version);
|
|
362
|
+
const key = `kit:${item.name}@${version}`;
|
|
363
|
+
if (this.visited.has(key))
|
|
364
|
+
return;
|
|
365
|
+
this.visited.add(key);
|
|
366
|
+
const kit = item.name;
|
|
367
|
+
const out = await this.fetchKit(kit, version);
|
|
368
|
+
if (out.ok)
|
|
369
|
+
return;
|
|
370
|
+
this.addPending({ kind: 'kit', name: kit, version, reason: out.reason });
|
|
371
|
+
if (out.status === undefined) {
|
|
372
|
+
console.error(chalk.yellow(` Warning: could not read kit ${kit}@${version}: ${out.reason}; skipping its npm deps`));
|
|
228
373
|
}
|
|
229
|
-
if (
|
|
230
|
-
console.
|
|
231
|
-
|
|
374
|
+
else if ((out.status === 401 || out.status === 403) && !this.hasToken) {
|
|
375
|
+
console.error(chalk.yellow(` Warning: kit ${kit}@${version} 의 npm 의존을 등록하지 못함 (HTTP ${out.status}, 토큰 없음): ` +
|
|
376
|
+
'IST_SESSION_TOKEN(또는 SNAPSHOT_LICENSE_KEY) 설정 후 다시 실행'));
|
|
232
377
|
}
|
|
233
378
|
else {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
379
|
+
console.error(chalk.yellow(` Warning: could not fetch kit ${kit}@${version} (HTTP ${out.status}); skipping its npm deps`));
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
/** Fetch a kit tarball, read skit.json and enqueue its npm/skit deps. */
|
|
383
|
+
async fetchKit(kit, version) {
|
|
384
|
+
const registryName = kitRegistryName(kit);
|
|
385
|
+
try {
|
|
386
|
+
let buffer;
|
|
387
|
+
if (this.opts.source === 'gitlab') {
|
|
388
|
+
const r = await fetchGitlabGenericTarball(registryName, version);
|
|
389
|
+
if (!r.ok)
|
|
390
|
+
return { ok: false, status: r.status, reason: r.reason };
|
|
391
|
+
buffer = r.body;
|
|
240
392
|
}
|
|
241
393
|
else {
|
|
242
|
-
|
|
243
|
-
|
|
394
|
+
const url = `${REGISTRY_PROXY_URL}/generic/${registryName}/${version}/${registryName}-${version}.tar.gz`;
|
|
395
|
+
const response = await fetch(url, { headers: this.headers });
|
|
396
|
+
if (!response.ok)
|
|
397
|
+
return { ok: false, status: response.status, reason: `HTTP ${response.status}` };
|
|
398
|
+
buffer = Buffer.from(await response.arrayBuffer());
|
|
399
|
+
}
|
|
400
|
+
const skitJson = await extractSkitJsonFromBuffer(buffer);
|
|
401
|
+
const npmDeps = kitNpmDeps(skitJson);
|
|
402
|
+
const skitDeps = kitSkitDeps(skitJson);
|
|
403
|
+
if (this.opts.verbose && npmDeps.length + skitDeps.length > 0) {
|
|
404
|
+
console.log(chalk.dim(` Kit ${kit}@${version} deps: ${[...npmDeps, ...skitDeps].map(([n]) => n).join(', ')}`));
|
|
244
405
|
}
|
|
406
|
+
for (const [name, spec] of npmDeps)
|
|
407
|
+
this.queue.push({ kind: 'npm', name, spec, declared: true });
|
|
408
|
+
for (const [name, spec] of skitDeps)
|
|
409
|
+
this.queue.push({ kind: 'kit', name, spec, declared: true });
|
|
410
|
+
return { ok: true };
|
|
411
|
+
}
|
|
412
|
+
catch (error) {
|
|
413
|
+
return { ok: false, reason: String(error) };
|
|
245
414
|
}
|
|
246
415
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
416
|
+
}
|
|
417
|
+
/** `@ist/qa-kit` → `ist__qa-kit` (registry-proxy generic path form). */
|
|
418
|
+
export function kitRegistryName(kit) {
|
|
419
|
+
return kit.replace(/^@/, '').replace(/\//g, '__');
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Walk the install-set dependency closure once.
|
|
423
|
+
* @internal Exported for testing
|
|
424
|
+
*/
|
|
425
|
+
export async function resolveInstallClosure(snapshot, options = {}) {
|
|
426
|
+
const walker = new ClosureWalker(snapshot, {
|
|
427
|
+
transitive: options.transitive ?? true,
|
|
428
|
+
verbose: options.verbose ?? false,
|
|
429
|
+
source: options.source ?? 'proxy',
|
|
430
|
+
});
|
|
431
|
+
await walker.drain();
|
|
432
|
+
return walker.result();
|
|
259
433
|
}
|
|
260
434
|
//# sourceMappingURL=access-sync.js.map
|