@stll/oxlint-config 1.4.0 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +90 -14
- package/dist/cloud-setup-cli.cjs +3 -3
- package/dist/cloud-setup-cli.mjs +3 -3
- package/dist/consumer-compat-cli.cjs +2 -2
- package/dist/consumer-compat-cli.mjs +2 -2
- package/dist/dependabot-cli.cjs +34 -10
- package/dist/dependabot-cli.cjs.map +1 -1
- package/dist/dependabot-cli.mjs +35 -11
- package/dist/dependabot-cli.mjs.map +1 -1
- package/dist/dependabot.cjs +2 -0
- package/dist/dependabot.d.cts +12 -2
- package/dist/dependabot.d.mts +12 -2
- package/dist/dependabot.mjs +2 -2
- package/dist/generated-file.cjs +2 -2
- package/dist/generated-file.mjs +2 -2
- package/dist/git-tracked-output.cjs +2 -2
- package/dist/git-tracked-output.mjs +1 -1
- package/dist/jsonc.cjs +66 -0
- package/dist/jsonc.cjs.map +1 -0
- package/dist/jsonc.mjs +61 -0
- package/dist/jsonc.mjs.map +1 -0
- package/dist/publish-artifact-syntax.cjs +2 -2
- package/dist/publish-artifact-syntax.mjs +2 -2
- package/dist/publish-build-target-vite-dts-helper2.cjs +1 -1
- package/dist/publish-build-target-vite-dts-helper2.mjs +1 -1
- package/dist/publish-contract-cli.cjs +2 -2
- package/dist/publish-contract-cli.mjs +1 -1
- package/dist/quarantine-cli.cjs +66 -0
- package/dist/quarantine-cli.cjs.map +1 -0
- package/dist/quarantine-cli.d.cts +1 -0
- package/dist/quarantine-cli.d.mts +1 -0
- package/dist/quarantine-cli.mjs +66 -0
- package/dist/quarantine-cli.mjs.map +1 -0
- package/dist/quarantine-policy.cjs +10 -0
- package/dist/quarantine-policy.d.cts +27 -0
- package/dist/quarantine-policy.d.mts +27 -0
- package/dist/quarantine-policy.mjs +1 -0
- package/dist/quarantine-policy2.cjs +399 -0
- package/dist/quarantine-policy2.cjs.map +1 -0
- package/dist/quarantine-policy2.mjs +352 -0
- package/dist/quarantine-policy2.mjs.map +1 -0
- package/dist/toolchain-changed.cjs +11 -66
- package/dist/toolchain-changed.cjs.map +1 -1
- package/dist/toolchain-changed.mjs +13 -68
- package/dist/toolchain-changed.mjs.map +1 -1
- package/dist/toolchain-check-cli.cjs +1 -0
- package/dist/toolchain-check-cli.cjs.map +1 -1
- package/dist/toolchain-check-cli.mjs +1 -0
- package/dist/toolchain-check-cli.mjs.map +1 -1
- package/dist/toolchain-dependabot.cjs +77 -3
- package/dist/toolchain-dependabot.cjs.map +1 -1
- package/dist/toolchain-dependabot.mjs +66 -4
- package/dist/toolchain-dependabot.mjs.map +1 -1
- package/dist/toolchain-guard.cjs +49 -10
- package/dist/toolchain-guard.cjs.map +1 -1
- package/dist/toolchain-guard.mjs +51 -12
- package/dist/toolchain-guard.mjs.map +1 -1
- package/dist/toolchain-inputs.cjs +104 -18
- package/dist/toolchain-inputs.cjs.map +1 -1
- package/dist/toolchain-inputs.mjs +106 -20
- package/dist/toolchain-inputs.mjs.map +1 -1
- package/dist/typecheck-parity.cjs +4 -3
- package/dist/typecheck-parity.cjs.map +1 -1
- package/dist/typecheck-parity.mjs +4 -3
- package/dist/typecheck-parity.mjs.map +1 -1
- package/dist/typecheck-probe-cli.cjs +2 -2
- package/dist/typecheck-probe-cli.mjs +2 -2
- package/package.json +13 -1
- package/toolchain.json +11 -4
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
import { t as parseJsonc } from "./jsonc.mjs";
|
|
2
|
+
import { parse } from "smol-toml";
|
|
3
|
+
//#region src/quarantine-policy-core.ts
|
|
4
|
+
const REQUIRED_RELEASE_AGE_SECONDS = 432e3;
|
|
5
|
+
const NPM_REGISTRY = "https://registry.npmjs.org";
|
|
6
|
+
const EXACT_UTC_TIMESTAMP = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/u;
|
|
7
|
+
const HOUR_MS = 36e5;
|
|
8
|
+
const EXACT_PACKAGE = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/u;
|
|
9
|
+
const REGISTRY_VERSION = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/u;
|
|
10
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11
|
+
const fail = (message) => {
|
|
12
|
+
throw new Error(message);
|
|
13
|
+
};
|
|
14
|
+
const ownPackage = (name) => name.startsWith("@stll/");
|
|
15
|
+
const exactPackage = (name) => name.length <= 214 && EXACT_PACKAGE.test(name);
|
|
16
|
+
const isFirstPartyPackageName = (name) => exactPackage(name) && ownPackage(name);
|
|
17
|
+
const exactTimestamp = (value) => {
|
|
18
|
+
const timestamp = Date.parse(value);
|
|
19
|
+
return EXACT_UTC_TIMESTAMP.test(value) && !Number.isNaN(timestamp) && new Date(timestamp).toISOString() === value;
|
|
20
|
+
};
|
|
21
|
+
const readParsedExcludes = (bunfig, parseToml) => {
|
|
22
|
+
const parsed = parseToml(bunfig);
|
|
23
|
+
if (!isRecord(parsed) || !isRecord(parsed["install"])) return fail("bunfig.toml must contain an [install] table");
|
|
24
|
+
const install = parsed["install"];
|
|
25
|
+
if ("registry" in install && install["registry"] !== "https://registry.npmjs.org" || "scopes" in install && (!isRecord(install["scopes"]) || Object.keys(install["scopes"]).length > 0)) fail("bunfig.toml quarantine policy requires the canonical npm registry");
|
|
26
|
+
if (install["minimumReleaseAge"] !== 432e3) fail(`bunfig.toml must set minimumReleaseAge = ${REQUIRED_RELEASE_AGE_SECONDS}`);
|
|
27
|
+
if (!Array.isArray(install["minimumReleaseAgeExcludes"])) return fail("bunfig.toml must declare minimumReleaseAgeExcludes as an array");
|
|
28
|
+
const names = [];
|
|
29
|
+
for (const value of install["minimumReleaseAgeExcludes"]) {
|
|
30
|
+
if (typeof value !== "string" || !exactPackage(value)) return fail("bunfig.toml minimumReleaseAgeExcludes must contain only exact npm package names");
|
|
31
|
+
names.push(value);
|
|
32
|
+
}
|
|
33
|
+
if (new Set(names).size !== names.length) fail("bunfig.toml minimumReleaseAgeExcludes must not contain duplicates");
|
|
34
|
+
return names;
|
|
35
|
+
};
|
|
36
|
+
const tokensOf = (text) => {
|
|
37
|
+
const tokens = [];
|
|
38
|
+
let index = 0;
|
|
39
|
+
while (index < text.length) {
|
|
40
|
+
const start = index;
|
|
41
|
+
const character = text.charAt(index++);
|
|
42
|
+
if (character === "\n") {
|
|
43
|
+
tokens.push({
|
|
44
|
+
type: "newline",
|
|
45
|
+
start,
|
|
46
|
+
end: index,
|
|
47
|
+
text: character
|
|
48
|
+
});
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (/\s/u.test(character)) continue;
|
|
52
|
+
let type;
|
|
53
|
+
if (character === "#") {
|
|
54
|
+
type = "comment";
|
|
55
|
+
while (index < text.length && text.charAt(index) !== "\n") index++;
|
|
56
|
+
} else if (character === "\"" || character === "'") {
|
|
57
|
+
type = "string";
|
|
58
|
+
const triple = text.slice(start, start + 3) === character.repeat(3);
|
|
59
|
+
if (triple) index = start + 3;
|
|
60
|
+
while (index < text.length) {
|
|
61
|
+
const current = text.charAt(index++);
|
|
62
|
+
if (character === "\"" && current === "\\") {
|
|
63
|
+
index++;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (current !== character) continue;
|
|
67
|
+
if (!triple) break;
|
|
68
|
+
if (text.slice(index - 1, index + 2) !== character.repeat(3)) continue;
|
|
69
|
+
index += 2;
|
|
70
|
+
while (text.charAt(index) === character) index++;
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
} else if ("[]=.,{}".includes(character)) type = "punctuation";
|
|
74
|
+
else {
|
|
75
|
+
type = "atom";
|
|
76
|
+
while (index < text.length && !/[\s#[\]=.,{}"']/u.test(text.charAt(index))) index++;
|
|
77
|
+
}
|
|
78
|
+
tokens.push({
|
|
79
|
+
type,
|
|
80
|
+
start,
|
|
81
|
+
end: index,
|
|
82
|
+
text: type === "comment" ? text.slice(start, index).replace(/\r$/u, "") : text.slice(start, index)
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
return tokens;
|
|
86
|
+
};
|
|
87
|
+
const stringValue = (token, parseToml) => {
|
|
88
|
+
const parsed = parseToml(`value = ${token.text}`);
|
|
89
|
+
if (!isRecord(parsed) || typeof parsed["value"] !== "string") return fail("bunfig.toml has an unsupported quarantine string");
|
|
90
|
+
return parsed["value"];
|
|
91
|
+
};
|
|
92
|
+
const keyParts = (tokens, parseToml) => tokens.filter(({ text }) => text !== ".").map((token) => token.type === "string" ? stringValue(token, parseToml) : token.text);
|
|
93
|
+
const readSourceArray = (bunfig, parseToml) => {
|
|
94
|
+
const names = readParsedExcludes(bunfig, parseToml);
|
|
95
|
+
let table = [];
|
|
96
|
+
let statement = [];
|
|
97
|
+
let depth = 0;
|
|
98
|
+
let found;
|
|
99
|
+
const visitStatement = () => {
|
|
100
|
+
const meaningful = statement.filter(({ type }) => type !== "comment" && type !== "newline");
|
|
101
|
+
if (meaningful.at(0)?.text === "[") {
|
|
102
|
+
if (meaningful.at(-1)?.text === "]") table = keyParts(meaningful.slice(1, -1), parseToml);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const equals = meaningful.findIndex(({ text }) => text === "=");
|
|
106
|
+
if (equals < 0) return;
|
|
107
|
+
const key = [...table, ...keyParts(meaningful.slice(0, equals), parseToml)];
|
|
108
|
+
if (key.join(".") !== "install.minimumReleaseAgeExcludes" || key.length !== 2) return;
|
|
109
|
+
const open = meaningful.at(equals + 1);
|
|
110
|
+
const close = meaningful.at(-1);
|
|
111
|
+
if (open === void 0 || close === void 0 || open.text !== "[" || close.text !== "]" || found !== void 0) fail("bunfig.toml must declare a single install minimumReleaseAgeExcludes array");
|
|
112
|
+
const values = meaningful.slice(equals + 2, -1);
|
|
113
|
+
if (values.some(({ type, text }) => type !== "string" && text !== ",")) fail("bunfig.toml quarantine array must contain package strings");
|
|
114
|
+
const strings = values.filter(({ type }) => type === "string");
|
|
115
|
+
const comments = statement.filter(({ type }) => type === "comment");
|
|
116
|
+
const entries = strings.map((token, index) => {
|
|
117
|
+
const next = strings.at(index + 1)?.start ?? Number.POSITIVE_INFINITY;
|
|
118
|
+
const comment = comments.find((candidate) => candidate.start >= token.end && candidate.start < next && !bunfig.slice(token.end, candidate.start).includes("\n"));
|
|
119
|
+
const following = values.find((candidate) => candidate.start >= token.end);
|
|
120
|
+
return {
|
|
121
|
+
name: stringValue(token, parseToml),
|
|
122
|
+
token,
|
|
123
|
+
comment,
|
|
124
|
+
comma: following?.text === "," ? following : void 0
|
|
125
|
+
};
|
|
126
|
+
});
|
|
127
|
+
if (entries.length !== names.length || entries.some(({ name }, index) => name !== names.at(index))) fail("bunfig.toml quarantine source does not match its parsed install array");
|
|
128
|
+
found = {
|
|
129
|
+
open,
|
|
130
|
+
close,
|
|
131
|
+
entries,
|
|
132
|
+
comments
|
|
133
|
+
};
|
|
134
|
+
};
|
|
135
|
+
for (const token of tokensOf(bunfig)) {
|
|
136
|
+
if (token.type === "newline" && depth === 0) {
|
|
137
|
+
visitStatement();
|
|
138
|
+
statement = [];
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
statement.push(token);
|
|
142
|
+
if (token.type !== "punctuation") continue;
|
|
143
|
+
if (token.text === "[" || token.text === "{") depth++;
|
|
144
|
+
if (token.text === "]" || token.text === "}") depth--;
|
|
145
|
+
}
|
|
146
|
+
visitStatement();
|
|
147
|
+
if (found === void 0) return fail("bunfig.toml must declare an explicit [install] minimumReleaseAgeExcludes array");
|
|
148
|
+
return found;
|
|
149
|
+
};
|
|
150
|
+
const annotationOf = ({ comment }) => {
|
|
151
|
+
if (comment === void 0) return { type: "none" };
|
|
152
|
+
const match = /^#\s*quarantine-(expires|excluded-since):\s*(\S+)\s*$/u.exec(comment.text);
|
|
153
|
+
const marker = match?.at(1);
|
|
154
|
+
const timestamp = match?.at(2);
|
|
155
|
+
if ((marker === "expires" || marker === "excluded-since") && timestamp !== void 0) return {
|
|
156
|
+
type: marker,
|
|
157
|
+
timestamp
|
|
158
|
+
};
|
|
159
|
+
return { type: "invalid" };
|
|
160
|
+
};
|
|
161
|
+
const readRegistryFirstPartyPackages = (lockfile) => {
|
|
162
|
+
if (lockfile === void 0) return /* @__PURE__ */ new Set();
|
|
163
|
+
const parsed = parseJsonc(lockfile);
|
|
164
|
+
if (!isRecord(parsed) || !isRecord(parsed["packages"])) return fail("bun.lock must contain a packages mapping");
|
|
165
|
+
const names = /* @__PURE__ */ new Set();
|
|
166
|
+
for (const value of Object.values(parsed["packages"])) {
|
|
167
|
+
if (!Array.isArray(value)) continue;
|
|
168
|
+
const identity = value.at(0);
|
|
169
|
+
if (typeof identity !== "string") continue;
|
|
170
|
+
const separator = identity.lastIndexOf("@");
|
|
171
|
+
const name = identity.slice(0, separator);
|
|
172
|
+
if (exactPackage(name) && ownPackage(name) && REGISTRY_VERSION.test(identity.slice(separator + 1))) names.add(name);
|
|
173
|
+
}
|
|
174
|
+
return names;
|
|
175
|
+
};
|
|
176
|
+
const ownNames = (names) => {
|
|
177
|
+
const result = new Set(names);
|
|
178
|
+
for (const name of result) if (!exactPackage(name) || !ownPackage(name)) fail(`Expected an exact first-party npm package name: ${name}`);
|
|
179
|
+
return result;
|
|
180
|
+
};
|
|
181
|
+
const validNow = (now) => {
|
|
182
|
+
if (!Number.isFinite(now.getTime())) return fail("Quarantine policy requires a valid current date");
|
|
183
|
+
return now.getTime();
|
|
184
|
+
};
|
|
185
|
+
const checkQuarantinePolicy = ({ bunfig, lockfile, parseToml, npmrcPresent = false, now = /* @__PURE__ */ new Date(), firstPartyPackages }) => {
|
|
186
|
+
const errors = [];
|
|
187
|
+
const warnings = [];
|
|
188
|
+
try {
|
|
189
|
+
const nowMs = validNow(now);
|
|
190
|
+
const { entries } = readSourceArray(bunfig, parseToml);
|
|
191
|
+
if (npmrcPresent) errors.push("bunfig.toml quarantine policy does not allow a repository .npmrc");
|
|
192
|
+
for (const entry of entries) {
|
|
193
|
+
const annotation = annotationOf(entry);
|
|
194
|
+
if (ownPackage(entry.name) && annotation.type === "none") continue;
|
|
195
|
+
if (annotation.type === "none" || annotation.type === "invalid") {
|
|
196
|
+
errors.push(`bunfig.toml has an unannotated or malformed quarantine exclude: ${entry.name}`);
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (!exactTimestamp(annotation.timestamp)) {
|
|
200
|
+
errors.push(`bunfig.toml quarantine exclude "${entry.name}" has an invalid UTC timestamp: ${annotation.timestamp}`);
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
const timestampMs = Date.parse(annotation.timestamp);
|
|
204
|
+
if (ownPackage(entry.name)) {
|
|
205
|
+
if (annotation.type !== "excluded-since") errors.push(`bunfig.toml first-party quarantine exclude "${entry.name}" must be permanent (quarantine-excluded-since: or no timestamp)`);
|
|
206
|
+
else if (timestampMs > nowMs) errors.push(`bunfig.toml first-party quarantine exclude "${entry.name}" has a future exclusion date`);
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (annotation.type !== "expires") {
|
|
210
|
+
errors.push(`bunfig.toml third-party quarantine exclude "${entry.name}" must use quarantine-expires:`);
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
if (nowMs >= timestampMs + 24 * HOUR_MS) errors.push(`bunfig.toml temporary quarantine exclude "${entry.name}" expired at ${annotation.timestamp} and is still present`);
|
|
214
|
+
else if (nowMs >= timestampMs) warnings.push(`bunfig.toml temporary quarantine exclude "${entry.name}" expired at ${annotation.timestamp}; its removal PR should be merged`);
|
|
215
|
+
else if (nowMs >= timestampMs - 48 * HOUR_MS) warnings.push(`bunfig.toml temporary quarantine exclude "${entry.name}" expires at ${annotation.timestamp}; prepare its removal PR`);
|
|
216
|
+
}
|
|
217
|
+
const required = readRegistryFirstPartyPackages(lockfile);
|
|
218
|
+
if (firstPartyPackages !== void 0) for (const name of ownNames(firstPartyPackages)) required.add(name);
|
|
219
|
+
const declared = new Set(entries.map(({ name }) => name));
|
|
220
|
+
const missing = [...required].filter((name) => !declared.has(name)).sort();
|
|
221
|
+
if (missing.length > 0) errors.push(`bunfig.toml is missing registry-backed first-party quarantine excludes: ${missing.join(", ")}`);
|
|
222
|
+
} catch (error) {
|
|
223
|
+
errors.push(error instanceof Error ? error.message : String(error));
|
|
224
|
+
}
|
|
225
|
+
return {
|
|
226
|
+
errors,
|
|
227
|
+
warnings
|
|
228
|
+
};
|
|
229
|
+
};
|
|
230
|
+
const pruneExpiredExcludes = ({ bunfig, parseToml, now = /* @__PURE__ */ new Date() }) => {
|
|
231
|
+
const nowMs = validNow(now);
|
|
232
|
+
const { entries, close } = readSourceArray(bunfig, parseToml);
|
|
233
|
+
const removals = [];
|
|
234
|
+
const pruned = [];
|
|
235
|
+
for (const entry of entries) {
|
|
236
|
+
if (ownPackage(entry.name)) continue;
|
|
237
|
+
const annotation = annotationOf(entry);
|
|
238
|
+
if (annotation.type !== "expires" || !exactTimestamp(annotation.timestamp)) return fail(`bunfig.toml third-party quarantine exclude "${entry.name}" must have an exact UTC quarantine-expires: annotation`);
|
|
239
|
+
if (nowMs < Date.parse(annotation.timestamp)) continue;
|
|
240
|
+
const lineStart = bunfig.lastIndexOf("\n", entry.token.start - 1) + 1;
|
|
241
|
+
const nextNewline = bunfig.indexOf("\n", entry.token.end);
|
|
242
|
+
const lineEnd = nextNewline < 0 ? bunfig.length : nextNewline;
|
|
243
|
+
const commentInside = entry.comment !== void 0 && entry.comment.start < close.start;
|
|
244
|
+
const end = commentInside ? entry.comment?.end ?? entry.token.end : entry.comma?.end ?? entry.token.end;
|
|
245
|
+
if (bunfig.slice(lineStart, entry.token.start).trim() === "" && bunfig.slice(end, lineEnd).trim() === "") removals.push({
|
|
246
|
+
start: lineStart,
|
|
247
|
+
end: nextNewline < 0 ? lineEnd : lineEnd + 1
|
|
248
|
+
});
|
|
249
|
+
else removals.push({
|
|
250
|
+
start: entry.token.start,
|
|
251
|
+
end
|
|
252
|
+
});
|
|
253
|
+
if (entry.comment !== void 0 && !commentInside) removals.push({
|
|
254
|
+
start: entry.comment.start,
|
|
255
|
+
end: entry.comment.end
|
|
256
|
+
});
|
|
257
|
+
pruned.push(entry.name);
|
|
258
|
+
}
|
|
259
|
+
let result = bunfig;
|
|
260
|
+
for (const removal of removals.toReversed()) result = result.slice(0, removal.start) + result.slice(removal.end);
|
|
261
|
+
readSourceArray(result, parseToml);
|
|
262
|
+
return {
|
|
263
|
+
bunfig: result,
|
|
264
|
+
pruned
|
|
265
|
+
};
|
|
266
|
+
};
|
|
267
|
+
const synchronizeFirstPartyExcludes = ({ bunfig, firstPartyPackages, parseToml, now = /* @__PURE__ */ new Date() }) => {
|
|
268
|
+
validNow(now);
|
|
269
|
+
const required = ownNames(firstPartyPackages);
|
|
270
|
+
const { open, close, entries, comments } = readSourceArray(bunfig, parseToml);
|
|
271
|
+
const attachedComments = new Set(entries.flatMap(({ comment }) => comment === void 0 ? [] : [comment]));
|
|
272
|
+
const lines = comments.filter((comment) => !attachedComments.has(comment)).map(({ text }) => ` ${text}`);
|
|
273
|
+
const seen = /* @__PURE__ */ new Set();
|
|
274
|
+
for (const entry of entries) {
|
|
275
|
+
if (!ownPackage(entry.name)) {
|
|
276
|
+
const annotation = annotationOf(entry);
|
|
277
|
+
if (annotation.type !== "expires" || !exactTimestamp(annotation.timestamp)) return fail(`bunfig.toml third-party quarantine exclude "${entry.name}" must have an exact UTC quarantine-expires: annotation`);
|
|
278
|
+
lines.push(` ${entry.token.text},${entry.comment === void 0 ? "" : ` ${entry.comment.text}`}`);
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
if (!required.has(entry.name)) continue;
|
|
282
|
+
seen.add(entry.name);
|
|
283
|
+
const annotation = annotationOf(entry);
|
|
284
|
+
const timestamp = annotation.type === "excluded-since" && exactTimestamp(annotation.timestamp) ? annotation.timestamp : now.toISOString();
|
|
285
|
+
lines.push(` ${JSON.stringify(entry.name)}, # quarantine-excluded-since: ${timestamp}`);
|
|
286
|
+
}
|
|
287
|
+
for (const name of [...required].filter((requiredPackage) => !seen.has(requiredPackage)).sort()) lines.push(` ${JSON.stringify(name)}, # quarantine-excluded-since: ${now.toISOString()}`);
|
|
288
|
+
const newline = bunfig.includes("\r\n") ? "\r\n" : "\n";
|
|
289
|
+
const trailingComment = entries.at(-1)?.comment;
|
|
290
|
+
const end = trailingComment !== void 0 && trailingComment.start > close.end ? trailingComment.end : close.end;
|
|
291
|
+
const result = bunfig.slice(0, open.start) + `[${newline}${lines.join(newline)}${lines.length === 0 ? "" : newline}]` + bunfig.slice(end);
|
|
292
|
+
readSourceArray(result, parseToml);
|
|
293
|
+
return result;
|
|
294
|
+
};
|
|
295
|
+
//#endregion
|
|
296
|
+
//#region src/quarantine-policy.ts
|
|
297
|
+
const record = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
298
|
+
/** Derive exact registry identities before install, including catalog references. */
|
|
299
|
+
const declaredFirstPartyPackages = (files) => {
|
|
300
|
+
const names = /* @__PURE__ */ new Set();
|
|
301
|
+
for (const [file, text] of Object.entries(files)) {
|
|
302
|
+
if (!/(?:^|\/)package\.json$/.test(file)) continue;
|
|
303
|
+
const manifest = JSON.parse(text);
|
|
304
|
+
if (!record(manifest)) throw new Error(`${file} must contain an object`);
|
|
305
|
+
for (const field of [
|
|
306
|
+
"dependencies",
|
|
307
|
+
"devDependencies",
|
|
308
|
+
"optionalDependencies",
|
|
309
|
+
"peerDependencies"
|
|
310
|
+
]) {
|
|
311
|
+
const dependencies = manifest[field];
|
|
312
|
+
if (!record(dependencies)) continue;
|
|
313
|
+
for (const [name, specifier] of Object.entries(dependencies)) {
|
|
314
|
+
if (typeof specifier !== "string") continue;
|
|
315
|
+
if (/^(?:workspace:|file:|link:|git(?:\+|:)|https?:|github:|\.\.?\/|\/)/.test(specifier)) continue;
|
|
316
|
+
if (specifier.startsWith("npm:")) {
|
|
317
|
+
const alias = /^npm:(@[^/]+\/[^@]+|[^@]+)@/.exec(specifier)?.at(1);
|
|
318
|
+
if (alias !== void 0 && isFirstPartyPackageName(alias)) names.add(alias);
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
if (isFirstPartyPackageName(name)) names.add(name);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
return names;
|
|
326
|
+
};
|
|
327
|
+
const checkRepositoryQuarantine = ({ files, now, npmrcPresent }) => checkQuarantinePolicy({
|
|
328
|
+
bunfig: files["bunfig.toml"] ?? "",
|
|
329
|
+
lockfile: files["bun.lock"],
|
|
330
|
+
firstPartyPackages: declaredFirstPartyPackages(files),
|
|
331
|
+
parseToml: parse,
|
|
332
|
+
...now === void 0 ? {} : { now },
|
|
333
|
+
...npmrcPresent === void 0 ? {} : { npmrcPresent }
|
|
334
|
+
});
|
|
335
|
+
const synchronizeRepositoryQuarantine = ({ files, now }) => {
|
|
336
|
+
const firstPartyPackages = /* @__PURE__ */ new Set([...declaredFirstPartyPackages(files), ...readRegistryFirstPartyPackages(files["bun.lock"])]);
|
|
337
|
+
return synchronizeFirstPartyExcludes({
|
|
338
|
+
bunfig: files["bunfig.toml"] ?? `[install]\nminimumReleaseAge = 432000\nminimumReleaseAgeExcludes = []\n`,
|
|
339
|
+
firstPartyPackages,
|
|
340
|
+
parseToml: parse,
|
|
341
|
+
...now === void 0 ? {} : { now }
|
|
342
|
+
});
|
|
343
|
+
};
|
|
344
|
+
const pruneRepositoryQuarantine = ({ files, now }) => pruneExpiredExcludes({
|
|
345
|
+
bunfig: files["bunfig.toml"] ?? "",
|
|
346
|
+
parseToml: parse,
|
|
347
|
+
...now === void 0 ? {} : { now }
|
|
348
|
+
});
|
|
349
|
+
//#endregion
|
|
350
|
+
export { EXACT_UTC_TIMESTAMP as a, exactTimestamp as c, synchronizeRepositoryQuarantine as i, declaredFirstPartyPackages as n, NPM_REGISTRY as o, pruneRepositoryQuarantine as r, REQUIRED_RELEASE_AGE_SECONDS as s, checkRepositoryQuarantine as t };
|
|
351
|
+
|
|
352
|
+
//# sourceMappingURL=quarantine-policy2.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quarantine-policy2.mjs","names":[],"sources":["../src/quarantine-policy-core.ts","../src/quarantine-policy.ts"],"sourcesContent":["import { parseJsonc } from \"./jsonc\";\n\nexport const REQUIRED_RELEASE_AGE_SECONDS = 5 * 24 * 60 * 60;\nexport const NPM_REGISTRY = \"https://registry.npmjs.org\";\nexport const EXACT_UTC_TIMESTAMP =\n /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z$/u;\nconst HOUR_MS = 60 * 60 * 1000;\nconst EXACT_PACKAGE = /^(?:@[a-z0-9][a-z0-9._-]*\\/)?[a-z0-9][a-z0-9._-]*$/u;\nconst REGISTRY_VERSION =\n /^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?$/u;\n\ntype ParseToml = (text: string) => unknown;\ntype FirstPartyPackages = ReadonlySet<string> | readonly string[];\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\nconst fail: (message: string) => never = (message) => {\n throw new Error(message);\n};\nconst ownPackage = (name: string) => name.startsWith(\"@stll/\");\nconst exactPackage = (name: string) =>\n name.length <= 214 && EXACT_PACKAGE.test(name);\nexport const isFirstPartyPackageName = (name: string): boolean =>\n exactPackage(name) && ownPackage(name);\n\nexport const exactTimestamp = (value: string): boolean => {\n const timestamp = Date.parse(value);\n return (\n EXACT_UTC_TIMESTAMP.test(value) &&\n !Number.isNaN(timestamp) &&\n new Date(timestamp).toISOString() === value\n );\n};\n\nexport const readParsedExcludes = (\n bunfig: string,\n parseToml: ParseToml,\n): string[] => {\n const parsed = parseToml(bunfig);\n if (!isRecord(parsed) || !isRecord(parsed[\"install\"]))\n return fail(\"bunfig.toml must contain an [install] table\");\n const install = parsed[\"install\"];\n if (\n (\"registry\" in install && install[\"registry\"] !== NPM_REGISTRY) ||\n (\"scopes\" in install &&\n (!isRecord(install[\"scopes\"]) ||\n Object.keys(install[\"scopes\"]).length > 0))\n )\n fail(\"bunfig.toml quarantine policy requires the canonical npm registry\");\n if (install[\"minimumReleaseAge\"] !== REQUIRED_RELEASE_AGE_SECONDS)\n fail(\n `bunfig.toml must set minimumReleaseAge = ${REQUIRED_RELEASE_AGE_SECONDS}`,\n );\n if (!Array.isArray(install[\"minimumReleaseAgeExcludes\"]))\n return fail(\n \"bunfig.toml must declare minimumReleaseAgeExcludes as an array\",\n );\n const names: string[] = [];\n for (const value of install[\"minimumReleaseAgeExcludes\"]) {\n if (typeof value !== \"string\" || !exactPackage(value))\n return fail(\n \"bunfig.toml minimumReleaseAgeExcludes must contain only exact npm package names\",\n );\n names.push(value);\n }\n if (new Set(names).size !== names.length)\n fail(\"bunfig.toml minimumReleaseAgeExcludes must not contain duplicates\");\n return names;\n};\n\ntype Token = {\n type: \"string\" | \"atom\" | \"punctuation\" | \"comment\" | \"newline\";\n start: number;\n end: number;\n text: string;\n};\n// Tokenize before locating declarations: text in comments, multiline strings and\n// unrelated tables cannot masquerade as the install policy's source array.\nconst tokensOf = (text: string): Token[] => {\n const tokens: Token[] = [];\n let index = 0;\n while (index < text.length) {\n const start = index;\n const character = text.charAt(index++);\n if (character === \"\\n\") {\n tokens.push({ type: \"newline\", start, end: index, text: character });\n continue;\n }\n if (/\\s/u.test(character)) continue;\n let type: Token[\"type\"];\n if (character === \"#\") {\n type = \"comment\";\n while (index < text.length && text.charAt(index) !== \"\\n\") index++;\n } else if (character === '\"' || character === \"'\") {\n type = \"string\";\n const triple = text.slice(start, start + 3) === character.repeat(3);\n if (triple) index = start + 3;\n while (index < text.length) {\n const current = text.charAt(index++);\n if (character === '\"' && current === \"\\\\\") {\n index++;\n continue;\n }\n if (current !== character) continue;\n if (!triple) break;\n if (text.slice(index - 1, index + 2) !== character.repeat(3)) continue;\n index += 2;\n while (text.charAt(index) === character) index++;\n break;\n }\n } else if (\"[]=.,{}\".includes(character)) type = \"punctuation\";\n else {\n type = \"atom\";\n while (\n index < text.length &&\n !/[\\s#[\\]=.,{}\"']/u.test(text.charAt(index))\n )\n index++;\n }\n tokens.push({\n type,\n start,\n end: index,\n text:\n type === \"comment\"\n ? text.slice(start, index).replace(/\\r$/u, \"\")\n : text.slice(start, index),\n });\n }\n return tokens;\n};\n\nconst stringValue = (token: Token, parseToml: ParseToml): string => {\n const parsed = parseToml(`value = ${token.text}`);\n if (!isRecord(parsed) || typeof parsed[\"value\"] !== \"string\")\n return fail(\"bunfig.toml has an unsupported quarantine string\");\n return parsed[\"value\"];\n};\nconst keyParts = (tokens: readonly Token[], parseToml: ParseToml): string[] =>\n tokens\n .filter(({ text }) => text !== \".\")\n .map((token) =>\n token.type === \"string\" ? stringValue(token, parseToml) : token.text,\n );\n\ntype Entry = {\n name: string;\n token: Token;\n comment: Token | undefined;\n comma: Token | undefined;\n};\ntype SourceArray = {\n open: Token;\n close: Token;\n entries: Entry[];\n comments: Token[];\n};\nconst readSourceArray = (bunfig: string, parseToml: ParseToml): SourceArray => {\n const names = readParsedExcludes(bunfig, parseToml);\n let table: string[] = [];\n let statement: Token[] = [];\n let depth = 0;\n let found: SourceArray | undefined;\n const visitStatement = () => {\n const meaningful = statement.filter(\n ({ type }) => type !== \"comment\" && type !== \"newline\",\n );\n const first = meaningful.at(0);\n if (first?.text === \"[\") {\n const end = meaningful.at(-1);\n if (end?.text === \"]\")\n table = keyParts(meaningful.slice(1, -1), parseToml);\n return;\n }\n const equals = meaningful.findIndex(({ text }) => text === \"=\");\n if (equals < 0) return;\n const key = [...table, ...keyParts(meaningful.slice(0, equals), parseToml)];\n if (\n key.join(\".\") !== \"install.minimumReleaseAgeExcludes\" ||\n key.length !== 2\n )\n return;\n const open = meaningful.at(equals + 1);\n const close = meaningful.at(-1);\n if (\n open === undefined ||\n close === undefined ||\n open.text !== \"[\" ||\n close.text !== \"]\" ||\n found !== undefined\n )\n fail(\n \"bunfig.toml must declare a single install minimumReleaseAgeExcludes array\",\n );\n const values = meaningful.slice(equals + 2, -1);\n if (values.some(({ type, text }) => type !== \"string\" && text !== \",\"))\n fail(\"bunfig.toml quarantine array must contain package strings\");\n const strings = values.filter(({ type }) => type === \"string\");\n const comments = statement.filter(({ type }) => type === \"comment\");\n const entries = strings.map((token, index) => {\n const next = strings.at(index + 1)?.start ?? Number.POSITIVE_INFINITY;\n const comment = comments.find(\n (candidate) =>\n candidate.start >= token.end &&\n candidate.start < next &&\n !bunfig.slice(token.end, candidate.start).includes(\"\\n\"),\n );\n const following = values.find(\n (candidate) => candidate.start >= token.end,\n );\n return {\n name: stringValue(token, parseToml),\n token,\n comment,\n comma: following?.text === \",\" ? following : undefined,\n };\n });\n if (\n entries.length !== names.length ||\n entries.some(({ name }, index) => name !== names.at(index))\n )\n fail(\n \"bunfig.toml quarantine source does not match its parsed install array\",\n );\n found = { open, close, entries, comments };\n };\n for (const token of tokensOf(bunfig)) {\n if (token.type === \"newline\" && depth === 0) {\n visitStatement();\n statement = [];\n continue;\n }\n statement.push(token);\n if (token.type !== \"punctuation\") continue;\n if (token.text === \"[\" || token.text === \"{\") depth++;\n if (token.text === \"]\" || token.text === \"}\") depth--;\n }\n visitStatement();\n if (found === undefined)\n return fail(\n \"bunfig.toml must declare an explicit [install] minimumReleaseAgeExcludes array\",\n );\n return found;\n};\n\ntype Annotation =\n | { type: \"none\" }\n | { type: \"invalid\" }\n | { type: \"expires\" | \"excluded-since\"; timestamp: string };\nconst annotationOf = ({ comment }: Entry): Annotation => {\n if (comment === undefined) return { type: \"none\" };\n const match = /^#\\s*quarantine-(expires|excluded-since):\\s*(\\S+)\\s*$/u.exec(\n comment.text,\n );\n const marker = match?.at(1);\n const timestamp = match?.at(2);\n if (\n (marker === \"expires\" || marker === \"excluded-since\") &&\n timestamp !== undefined\n )\n return { type: marker, timestamp };\n return { type: \"invalid\" };\n};\n\nexport const readRegistryFirstPartyPackages = (\n lockfile: string | undefined,\n): Set<string> => {\n if (lockfile === undefined) return new Set();\n const parsed = parseJsonc(lockfile);\n if (!isRecord(parsed) || !isRecord(parsed[\"packages\"]))\n return fail(\"bun.lock must contain a packages mapping\");\n const names = new Set<string>();\n for (const value of Object.values(parsed[\"packages\"])) {\n if (!Array.isArray(value)) continue;\n const identity: unknown = value.at(0);\n if (typeof identity !== \"string\") continue;\n const separator = identity.lastIndexOf(\"@\");\n const name = identity.slice(0, separator);\n if (\n exactPackage(name) &&\n ownPackage(name) &&\n REGISTRY_VERSION.test(identity.slice(separator + 1))\n )\n names.add(name);\n }\n return names;\n};\n\nconst ownNames = (names: FirstPartyPackages): Set<string> => {\n const result = new Set(names);\n for (const name of result)\n if (!exactPackage(name) || !ownPackage(name))\n fail(`Expected an exact first-party npm package name: ${name}`);\n return result;\n};\nconst validNow = (now: Date): number => {\n if (!Number.isFinite(now.getTime()))\n return fail(\"Quarantine policy requires a valid current date\");\n return now.getTime();\n};\nexport type CheckResult = { errors: string[]; warnings: string[] };\ntype CheckOptions = {\n bunfig: string;\n lockfile: string | undefined;\n parseToml: ParseToml;\n npmrcPresent?: boolean;\n now?: Date;\n firstPartyPackages?: FirstPartyPackages;\n};\nexport const checkQuarantinePolicy = ({\n bunfig,\n lockfile,\n parseToml,\n npmrcPresent = false,\n now = new Date(),\n firstPartyPackages,\n}: CheckOptions): CheckResult => {\n const errors: string[] = [];\n const warnings: string[] = [];\n try {\n const nowMs = validNow(now);\n const { entries } = readSourceArray(bunfig, parseToml);\n if (npmrcPresent)\n errors.push(\n \"bunfig.toml quarantine policy does not allow a repository .npmrc\",\n );\n for (const entry of entries) {\n const annotation = annotationOf(entry);\n if (ownPackage(entry.name) && annotation.type === \"none\") continue;\n if (annotation.type === \"none\" || annotation.type === \"invalid\") {\n errors.push(\n `bunfig.toml has an unannotated or malformed quarantine exclude: ${entry.name}`,\n );\n continue;\n }\n if (!exactTimestamp(annotation.timestamp)) {\n errors.push(\n `bunfig.toml quarantine exclude \"${entry.name}\" has an invalid UTC timestamp: ${annotation.timestamp}`,\n );\n continue;\n }\n const timestampMs = Date.parse(annotation.timestamp);\n if (ownPackage(entry.name)) {\n if (annotation.type !== \"excluded-since\")\n errors.push(\n `bunfig.toml first-party quarantine exclude \"${entry.name}\" must be permanent (quarantine-excluded-since: or no timestamp)`,\n );\n else if (timestampMs > nowMs)\n errors.push(\n `bunfig.toml first-party quarantine exclude \"${entry.name}\" has a future exclusion date`,\n );\n continue;\n }\n if (annotation.type !== \"expires\") {\n errors.push(\n `bunfig.toml third-party quarantine exclude \"${entry.name}\" must use quarantine-expires:`,\n );\n continue;\n }\n if (nowMs >= timestampMs + 24 * HOUR_MS)\n errors.push(\n `bunfig.toml temporary quarantine exclude \"${entry.name}\" expired at ${annotation.timestamp} and is still present`,\n );\n else if (nowMs >= timestampMs)\n warnings.push(\n `bunfig.toml temporary quarantine exclude \"${entry.name}\" expired at ${annotation.timestamp}; its removal PR should be merged`,\n );\n else if (nowMs >= timestampMs - 48 * HOUR_MS)\n warnings.push(\n `bunfig.toml temporary quarantine exclude \"${entry.name}\" expires at ${annotation.timestamp}; prepare its removal PR`,\n );\n }\n const required = readRegistryFirstPartyPackages(lockfile);\n if (firstPartyPackages !== undefined) {\n for (const name of ownNames(firstPartyPackages)) required.add(name);\n }\n const declared = new Set(entries.map(({ name }) => name));\n const missing = [...required].filter((name) => !declared.has(name)).sort();\n if (missing.length > 0)\n errors.push(\n `bunfig.toml is missing registry-backed first-party quarantine excludes: ${missing.join(\", \")}`,\n );\n } catch (error) {\n errors.push(error instanceof Error ? error.message : String(error));\n }\n return { errors, warnings };\n};\n\ntype PruneOptions = { bunfig: string; parseToml: ParseToml; now?: Date };\nexport const pruneExpiredExcludes = ({\n bunfig,\n parseToml,\n now = new Date(),\n}: PruneOptions): { bunfig: string; pruned: string[] } => {\n const nowMs = validNow(now);\n const { entries, close } = readSourceArray(bunfig, parseToml);\n const removals: { start: number; end: number }[] = [];\n const pruned: string[] = [];\n for (const entry of entries) {\n if (ownPackage(entry.name)) continue;\n const annotation = annotationOf(entry);\n if (annotation.type !== \"expires\" || !exactTimestamp(annotation.timestamp))\n return fail(\n `bunfig.toml third-party quarantine exclude \"${entry.name}\" must have an exact UTC quarantine-expires: annotation`,\n );\n if (nowMs < Date.parse(annotation.timestamp)) continue;\n const lineStart = bunfig.lastIndexOf(\"\\n\", entry.token.start - 1) + 1;\n const nextNewline = bunfig.indexOf(\"\\n\", entry.token.end);\n const lineEnd = nextNewline < 0 ? bunfig.length : nextNewline;\n const commentInside =\n entry.comment !== undefined && entry.comment.start < close.start;\n const end = commentInside\n ? (entry.comment?.end ?? entry.token.end)\n : (entry.comma?.end ?? entry.token.end);\n if (\n bunfig.slice(lineStart, entry.token.start).trim() === \"\" &&\n bunfig.slice(end, lineEnd).trim() === \"\"\n )\n removals.push({\n start: lineStart,\n end: nextNewline < 0 ? lineEnd : lineEnd + 1,\n });\n else removals.push({ start: entry.token.start, end });\n if (entry.comment !== undefined && !commentInside)\n removals.push({ start: entry.comment.start, end: entry.comment.end });\n pruned.push(entry.name);\n }\n let result = bunfig;\n for (const removal of removals.toReversed())\n result = result.slice(0, removal.start) + result.slice(removal.end);\n // Never return edited TOML without confirming the resulting source is valid.\n readSourceArray(result, parseToml);\n return { bunfig: result, pruned };\n};\n\ntype SynchronizeOptions = {\n bunfig: string;\n firstPartyPackages: FirstPartyPackages;\n parseToml: ParseToml;\n now?: Date;\n};\nexport const synchronizeFirstPartyExcludes = ({\n bunfig,\n firstPartyPackages,\n parseToml,\n now = new Date(),\n}: SynchronizeOptions): string => {\n validNow(now);\n const required = ownNames(firstPartyPackages);\n const { open, close, entries, comments } = readSourceArray(bunfig, parseToml);\n const attachedComments = new Set(\n entries.flatMap(({ comment }) => (comment === undefined ? [] : [comment])),\n );\n const lines = comments\n .filter((comment) => !attachedComments.has(comment))\n .map(({ text }) => ` ${text}`);\n const seen = new Set<string>();\n for (const entry of entries) {\n if (!ownPackage(entry.name)) {\n const annotation = annotationOf(entry);\n if (\n annotation.type !== \"expires\" ||\n !exactTimestamp(annotation.timestamp)\n )\n return fail(\n `bunfig.toml third-party quarantine exclude \"${entry.name}\" must have an exact UTC quarantine-expires: annotation`,\n );\n lines.push(\n ` ${entry.token.text},${entry.comment === undefined ? \"\" : ` ${entry.comment.text}`}`,\n );\n continue;\n }\n if (!required.has(entry.name)) continue;\n seen.add(entry.name);\n const annotation = annotationOf(entry);\n const timestamp =\n annotation.type === \"excluded-since\" &&\n exactTimestamp(annotation.timestamp)\n ? annotation.timestamp\n : now.toISOString();\n lines.push(\n ` ${JSON.stringify(entry.name)}, # quarantine-excluded-since: ${timestamp}`,\n );\n }\n for (const name of [...required]\n .filter((requiredPackage) => !seen.has(requiredPackage))\n .sort())\n lines.push(\n ` ${JSON.stringify(name)}, # quarantine-excluded-since: ${now.toISOString()}`,\n );\n const newline = bunfig.includes(\"\\r\\n\") ? \"\\r\\n\" : \"\\n\";\n const trailingComment = entries.at(-1)?.comment;\n const end =\n trailingComment !== undefined && trailingComment.start > close.end\n ? trailingComment.end\n : close.end;\n const result =\n bunfig.slice(0, open.start) +\n `[${newline}${lines.join(newline)}${lines.length === 0 ? \"\" : newline}]` +\n bunfig.slice(end);\n readSourceArray(result, parseToml);\n return result;\n};\n","import { parse } from \"smol-toml\";\n\nimport {\n REQUIRED_RELEASE_AGE_SECONDS,\n checkQuarantinePolicy,\n isFirstPartyPackageName,\n pruneExpiredExcludes,\n readRegistryFirstPartyPackages,\n synchronizeFirstPartyExcludes,\n} from \"./quarantine-policy-core\";\n\nexport {\n EXACT_UTC_TIMESTAMP,\n NPM_REGISTRY,\n REQUIRED_RELEASE_AGE_SECONDS,\n exactTimestamp,\n} from \"./quarantine-policy-core\";\n\nconst record = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\n/** Derive exact registry identities before install, including catalog references. */\nexport const declaredFirstPartyPackages = (files: Record<string, string>) => {\n const names = new Set<string>();\n for (const [file, text] of Object.entries(files)) {\n if (!/(?:^|\\/)package\\.json$/.test(file)) continue;\n const manifest: unknown = JSON.parse(text);\n if (!record(manifest)) throw new Error(`${file} must contain an object`);\n for (const field of [\n \"dependencies\",\n \"devDependencies\",\n \"optionalDependencies\",\n \"peerDependencies\",\n ]) {\n const dependencies = manifest[field];\n if (!record(dependencies)) continue;\n for (const [name, specifier] of Object.entries(dependencies)) {\n if (typeof specifier !== \"string\") continue;\n if (\n /^(?:workspace:|file:|link:|git(?:\\+|:)|https?:|github:|\\.\\.?\\/|\\/)/.test(\n specifier,\n )\n )\n continue;\n if (specifier.startsWith(\"npm:\")) {\n const alias = /^npm:(@[^/]+\\/[^@]+|[^@]+)@/.exec(specifier)?.at(1);\n if (alias !== undefined && isFirstPartyPackageName(alias))\n names.add(alias);\n continue;\n }\n if (isFirstPartyPackageName(name)) names.add(name);\n }\n }\n }\n return names;\n};\n\ntype RepositoryQuarantineOptions = {\n files: Record<string, string>;\n now?: Date | undefined;\n npmrcPresent?: boolean | undefined;\n};\n\nexport const checkRepositoryQuarantine = ({\n files,\n now,\n npmrcPresent,\n}: RepositoryQuarantineOptions) =>\n checkQuarantinePolicy({\n bunfig: files[\"bunfig.toml\"] ?? \"\",\n lockfile: files[\"bun.lock\"],\n firstPartyPackages: declaredFirstPartyPackages(files),\n parseToml: parse,\n ...(now === undefined ? {} : { now }),\n ...(npmrcPresent === undefined ? {} : { npmrcPresent }),\n });\n\nexport const synchronizeRepositoryQuarantine = ({\n files,\n now,\n}: RepositoryQuarantineOptions) => {\n const firstPartyPackages = new Set([\n ...declaredFirstPartyPackages(files),\n ...readRegistryFirstPartyPackages(files[\"bun.lock\"]),\n ]);\n return synchronizeFirstPartyExcludes({\n bunfig:\n files[\"bunfig.toml\"] ??\n `[install]\\nminimumReleaseAge = ${REQUIRED_RELEASE_AGE_SECONDS}\\nminimumReleaseAgeExcludes = []\\n`,\n firstPartyPackages,\n parseToml: parse,\n ...(now === undefined ? {} : { now }),\n });\n};\n\nexport const pruneRepositoryQuarantine = ({\n files,\n now,\n}: RepositoryQuarantineOptions) =>\n pruneExpiredExcludes({\n bunfig: files[\"bunfig.toml\"] ?? \"\",\n parseToml: parse,\n ...(now === undefined ? {} : { now }),\n });\n"],"mappings":"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"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
const require_rolldown_runtime = require("./rolldown-runtime.cjs");
|
|
2
|
+
const require_jsonc = require("./jsonc.cjs");
|
|
2
3
|
const require_toolchain_inputs = require("./toolchain-inputs.cjs");
|
|
3
4
|
const require_compiler_packages = require("./compiler-packages.cjs");
|
|
4
|
-
let node_child_process = require("node:child_process");
|
|
5
5
|
let node_path = require("node:path");
|
|
6
6
|
node_path = require_rolldown_runtime.__toESM(node_path, 1);
|
|
7
|
-
let semver = require("semver");
|
|
8
7
|
let smol_toml = require("smol-toml");
|
|
8
|
+
let node_child_process = require("node:child_process");
|
|
9
|
+
let semver = require("semver");
|
|
9
10
|
let node_util = require("node:util");
|
|
10
11
|
let yaml = require("yaml");
|
|
11
12
|
//#region src/toolchain-changed-locks.ts
|
|
@@ -58,62 +59,6 @@ const changedCompilerPatches = ({ value, stringsArePaths, compilerNames }) => {
|
|
|
58
59
|
}
|
|
59
60
|
return patches;
|
|
60
61
|
};
|
|
61
|
-
/** JSONC permits comments and trailing commas, never alterations inside strings. */
|
|
62
|
-
const parseChangedJson = (text) => {
|
|
63
|
-
let clean = "";
|
|
64
|
-
let quote = false;
|
|
65
|
-
for (let index = 0; index < text.length; index++) {
|
|
66
|
-
const char = text[index];
|
|
67
|
-
if (quote) {
|
|
68
|
-
clean += char;
|
|
69
|
-
if (char === "\\") {
|
|
70
|
-
index++;
|
|
71
|
-
clean += text[index] ?? "";
|
|
72
|
-
} else if (char === "\"") quote = false;
|
|
73
|
-
continue;
|
|
74
|
-
}
|
|
75
|
-
if (char === "\"") {
|
|
76
|
-
quote = true;
|
|
77
|
-
clean += char;
|
|
78
|
-
continue;
|
|
79
|
-
}
|
|
80
|
-
if (char === "/" && text[index + 1] === "/") {
|
|
81
|
-
while (index < text.length && text[index] !== "\n") index++;
|
|
82
|
-
clean += "\n";
|
|
83
|
-
continue;
|
|
84
|
-
}
|
|
85
|
-
if (char === "/" && text[index + 1] === "*") {
|
|
86
|
-
index += 2;
|
|
87
|
-
while (index < text.length && !(text[index] === "*" && text[index + 1] === "/")) index++;
|
|
88
|
-
if (index >= text.length) throw new Error("Unterminated JSONC comment");
|
|
89
|
-
index++;
|
|
90
|
-
clean += " ";
|
|
91
|
-
continue;
|
|
92
|
-
}
|
|
93
|
-
clean += char;
|
|
94
|
-
}
|
|
95
|
-
let result = "";
|
|
96
|
-
quote = false;
|
|
97
|
-
for (let index = 0; index < clean.length; index++) {
|
|
98
|
-
const char = clean[index];
|
|
99
|
-
if (quote) {
|
|
100
|
-
result += char;
|
|
101
|
-
if (char === "\\") {
|
|
102
|
-
index++;
|
|
103
|
-
result += clean[index] ?? "";
|
|
104
|
-
} else if (char === "\"") quote = false;
|
|
105
|
-
continue;
|
|
106
|
-
}
|
|
107
|
-
if (char === "\"") quote = true;
|
|
108
|
-
if (char === ",") {
|
|
109
|
-
let next = index + 1;
|
|
110
|
-
while (/\s/.test(clean[next] ?? "") && next < clean.length) next++;
|
|
111
|
-
if (clean[next] === "}" || clean[next] === "]") continue;
|
|
112
|
-
}
|
|
113
|
-
result += char;
|
|
114
|
-
}
|
|
115
|
-
return JSON.parse(result);
|
|
116
|
-
};
|
|
117
62
|
const changedYarnSelectors = (location) => location.replace(/"/g, "").split(/,\s*/).map((selector) => selector.trim());
|
|
118
63
|
const packageDescriptor = (descriptor) => {
|
|
119
64
|
const match = /^(@[^/]+\/[^@]+|[^@]+)@(.+)$/.exec(descriptor.replace(/^\//, ""));
|
|
@@ -287,7 +232,7 @@ const parseChangedLock = ({ file, text }) => {
|
|
|
287
232
|
const resolutions = [];
|
|
288
233
|
const importers = {};
|
|
289
234
|
if (file.endsWith("bun.lock")) {
|
|
290
|
-
const parsed =
|
|
235
|
+
const parsed = require_jsonc.parseJsonc(text);
|
|
291
236
|
if (!changedRecord(parsed) || !BUN_TEXT_LOCKFILE_VERSIONS.has(Number(parsed["lockfileVersion"])) || !changedRecord(parsed["packages"]) || !changedRecord(parsed["workspaces"])) throw new Error("Unsupported Bun lockfile shape");
|
|
292
237
|
for (const [directory, workspace] of Object.entries(parsed["workspaces"])) importers[directory || "."] = importerDependencies(workspace);
|
|
293
238
|
for (const [location, value] of Object.entries(parsed["packages"])) {
|
|
@@ -385,7 +330,7 @@ const parseChangedLock = ({ file, text }) => {
|
|
|
385
330
|
}
|
|
386
331
|
}
|
|
387
332
|
} else {
|
|
388
|
-
const parsed =
|
|
333
|
+
const parsed = require_jsonc.parseJsonc(text);
|
|
389
334
|
if (!changedRecord(parsed) || ![
|
|
390
335
|
1,
|
|
391
336
|
2,
|
|
@@ -711,7 +656,7 @@ const compilerInstallationNames = (snapshot) => {
|
|
|
711
656
|
for (const file of snapshot.entries.keys()) {
|
|
712
657
|
const basename = node_path.default.posix.basename(file);
|
|
713
658
|
if (basename === "package.json") {
|
|
714
|
-
const manifest =
|
|
659
|
+
const manifest = require_jsonc.parseJsonc(snapshotText({
|
|
715
660
|
snapshot,
|
|
716
661
|
file
|
|
717
662
|
}));
|
|
@@ -771,7 +716,7 @@ const effectiveCatalogSpecifier = ({ snapshot, file, dependency, specifier }) =>
|
|
|
771
716
|
const document = (0, yaml.parseDocument)(text);
|
|
772
717
|
if (document.errors.length > 0) throw new Error(`Invalid catalog YAML: ${owner}`);
|
|
773
718
|
value = document.toJS({ maxAliasCount: 100 });
|
|
774
|
-
} else value =
|
|
719
|
+
} else value = require_jsonc.parseJsonc(text);
|
|
775
720
|
if (!changedRecord(value)) continue;
|
|
776
721
|
const sources = [value];
|
|
777
722
|
if (changedRecord(value["workspaces"])) sources.push(value["workspaces"]);
|
|
@@ -808,7 +753,7 @@ const compilerPatches = (snapshot) => {
|
|
|
808
753
|
const document = (0, yaml.parseDocument)(text);
|
|
809
754
|
if (document.errors.length > 0) throw new Error(`Invalid compiler patch YAML: ${file}`);
|
|
810
755
|
value = document.toJS({ maxAliasCount: 100 });
|
|
811
|
-
} else value =
|
|
756
|
+
} else value = require_jsonc.parseJsonc(text);
|
|
812
757
|
const sources = [value];
|
|
813
758
|
if (name === "package.json" && changedRecord(value)) sources.push(value["pnpm"]);
|
|
814
759
|
for (const source of sources) for (const patch of changedCompilerPatches({
|
|
@@ -1000,7 +945,7 @@ const setupRuntimeFileVersion = ({ snapshot, file, runtime }) => {
|
|
|
1000
945
|
file
|
|
1001
946
|
}).trim();
|
|
1002
947
|
if (runtime === "bun" && file.endsWith(".json")) {
|
|
1003
|
-
const manifest =
|
|
948
|
+
const manifest = require_jsonc.parseJsonc(text);
|
|
1004
949
|
const manager = changedRecord(manifest) ? manifest["packageManager"] : void 0;
|
|
1005
950
|
if (typeof manager !== "string" || !manager.startsWith("bun@")) throw new Error("Bun selector manifest lacks an exact Bun packageManager");
|
|
1006
951
|
return stableVersion(manager.slice(4));
|
|
@@ -1196,7 +1141,7 @@ const parseSnapshot = (snapshot) => {
|
|
|
1196
1141
|
if (compilerPatchPackageFile(file, compilerNames)) tools.typescript.add(`patch-package:${file}:${text}`);
|
|
1197
1142
|
for (const tool of selectedFiles.get(file) ?? []) tools[tool].add(`selector-file:${file}:${text}`);
|
|
1198
1143
|
if (name === "package.json") {
|
|
1199
|
-
const manifest =
|
|
1144
|
+
const manifest = require_jsonc.parseJsonc(text);
|
|
1200
1145
|
if (!changedRecord(manifest)) throw new Error(`Invalid tracked manifest: ${file}`);
|
|
1201
1146
|
if (typeof manifest["name"] === "string" && changedPackageTool(manifest["name"]) === "typescript") throw new Error(`Tracked compiler source has no immutable artifact identity: ${file}`);
|
|
1202
1147
|
manifests.set(file, manifest);
|
|
@@ -1232,7 +1177,7 @@ const parseSnapshot = (snapshot) => {
|
|
|
1232
1177
|
else if (otherRuntimeFiles.has(name)) tools.shared.add(`${file}:${text}`);
|
|
1233
1178
|
else if (name === ".node-version" || name === ".nvmrc") tools.node.add(`${file}:${stableVersion(text)}`);
|
|
1234
1179
|
else if (name === "toolchain.json") {
|
|
1235
|
-
const policy =
|
|
1180
|
+
const policy = require_jsonc.parseJsonc(text);
|
|
1236
1181
|
if (!changedRecord(policy)) throw new Error("Invalid shared toolchain policy");
|
|
1237
1182
|
tools.shared.add(`${file}:${stableJson(policy)}`);
|
|
1238
1183
|
if (typeof policy["bun"] === "string") addBun(policy["bun"]);
|