@bridge-ds/core 0.3.0 → 0.5.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/README.md +16 -5
- package/audit-report.schema.json +135 -1
- package/dist/audit/code.d.ts +50 -0
- package/dist/audit/ignore.d.ts +34 -0
- package/dist/audit/index.d.ts +3 -0
- package/dist/audit/index.js +45 -29
- package/dist/audit/markup.d.ts +21 -0
- package/dist/audit/report.d.ts +5 -7
- package/dist/audit/style.d.ts +25 -0
- package/dist/audit/types.d.ts +39 -1
- package/dist/{component-spec-IRFGJ7XK.js → component-spec-cvrSvIZj.js} +160 -160
- package/dist/{component-spec-IRFGJ7XK.js.map → component-spec-cvrSvIZj.js.map} +1 -1
- package/dist/css-overrides.d.ts +11 -0
- package/dist/explorations-github.d.ts +4 -1
- package/dist/explorations.d.ts +6 -0
- package/dist/github-client.d.ts +19 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +685 -398
- package/dist/index.js.map +1 -1
- package/dist/{layered-DGate4nv.js → layered-BnbmToGs.js} +10 -10
- package/dist/{layered-DGate4nv.js.map → layered-BnbmToGs.js.map} +1 -1
- package/dist/node/audit.d.ts +5 -1
- package/dist/node/index.js +8 -4
- package/dist/node/index.js.map +1 -1
- package/dist/{report-tNzTriGF.js → report-4Z008HSf.js} +599 -144
- package/dist/report-4Z008HSf.js.map +1 -0
- package/dist/token-combine.d.ts +78 -0
- package/dist/token-merge.d.ts +72 -0
- package/package.json +1 -1
- package/dist/report-tNzTriGF.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { n as modeDisplayName, o as modesOf$1, q as isMultiMode, T as TIER_NAMES, s as TIER_AXIS, u as findReferenceProblems, w as evaluatePairs, x as contrastScope, y as contextLabel, S as SINGLE_MODE, z as pathId, A as
|
|
2
|
-
import {
|
|
3
|
-
import { a as collectRefs, i as isAlias, b as aliasTarget, v as valuesEqual, s as selectTokenFiles, L as LayeredTokenSet,
|
|
4
|
-
import { D,
|
|
1
|
+
import { n as modeDisplayName, o as modesOf$1, q as isMultiMode, T as TIER_NAMES, s as TIER_AXIS, u as findReferenceProblems, w as evaluatePairs, x as contrastScope, y as contextLabel, S as SINGLE_MODE, z as pathId, A as flattenTokens, B as serializeTokenFile, D as removeToken, E as upsertToken, F as threeWayDiff, G as emptyTokenFile, H as parseTokenFile, I as EXT_ID, J as EXT_SCOPES, K as resolve, L as moveToken, M as effectiveScopes, N as sameScopes, O as defaultScopes, P as readTokenAt, Q as modeFile, R as defaultFile, U as DEFAULT_PROTECTED_BRANCHES, a as assertSafeBranch, V as ProtectedBranchError, f as CONFIG_FILE, l as loadConfig, W as ConfigError, X as DEFAULT_TOKEN_PATHS, c as collectHistory, p as packageDirCandidates, b as parseChangelog, r as releasesFromTags, m as mergeReleases, d as buildReport, Y as applyTokenEdits, Z as buildHistoryEntry, _ as resolveBranchPreviews, $ as tokensPath, a0 as coerceValue, a1 as toConfigJson, a2 as parseContrastChecks, a3 as checkLayeredPair, a4 as classifyRisk, a5 as formatChangeset, a6 as RISK_LABELS, a7 as parseJson, a8 as serializeJson, a9 as plainEqual, aa as toPlain, ab as getEntry, ac as fromPlain, ad as previewLinks, ae as slugify, af as readPreviewComment, ag as findPreviewComment, ah as previewsSettled } from "./component-spec-cvrSvIZj.js";
|
|
2
|
+
import { ai, aj, ak, k, C, al, am, an, ao, ap, aq, ar, as, at, au, av, aw, ax, ay, az, aA, aB, aC, aD, aE, aF, aG, aH, aI, aJ, aK, aL, aM, aN, aO, i, aP, aQ, aR, aS, aT, aU, aV, aW, aX, aY, aZ, a_, a$, b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ba, bb, bc, bd, be, bf, bg, bh, bi, bj, bk, bl, bm, bn, bo, h, bp, j, bq, br, bs, bt, bu, bv, bw, bx, by, bz, g, bA, bB, bC, bD, bE, bF, bG, bH, bI, bJ, bK, bL, bM, bN, e, bO, bP, bQ, bR, bS, bT, bU, bV, bW, bX, bY, bZ, b_, b$, c0, c1, c2, c3, c4, c5, c6, c7, c8, t, c9, ca, cb, cc, cd, v, ce, cf, cg, ch } from "./component-spec-cvrSvIZj.js";
|
|
3
|
+
import { a as collectRefs, i as isAlias, b as aliasTarget, v as valuesEqual, p as parseColor, e as rgbaToHex, f as formatValue, s as selectTokenFiles, L as LayeredTokenSet, j as validateLayered } from "./layered-BnbmToGs.js";
|
|
4
|
+
import { D, d, k as k2, l, c, m, n, g as g2, h as h2, o, q, r, t as t2, u } from "./layered-BnbmToGs.js";
|
|
5
5
|
const issueKey = (i2) => `${i2.code}|${i2.path ?? ""}|${i2.mode ?? ""}|${i2.code === "file-problem" ? i2.message : ""}`;
|
|
6
6
|
const modeName = (mode) => mode === SINGLE_MODE ? "all modes" : modeDisplayName(mode);
|
|
7
7
|
const isPalette = (t3) => t3.tier === "brand" && t3.path.startsWith("palette.");
|
|
@@ -54,7 +54,7 @@ function checkValue(set, owner, value, mode, where, issues) {
|
|
|
54
54
|
}
|
|
55
55
|
function validateSet(set, pairs) {
|
|
56
56
|
const issues = [];
|
|
57
|
-
for (const
|
|
57
|
+
for (const p of set.problems) issues.push({ code: "file-problem", severity: "error", message: p });
|
|
58
58
|
for (const t3 of set.tokens.values()) {
|
|
59
59
|
if (t3.scopes?.includes("ALL_SCOPES")) {
|
|
60
60
|
issues.push({
|
|
@@ -102,20 +102,20 @@ function validateSet(set, pairs) {
|
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
const axes = { brand: modesOf$1(set.layout, "brand"), theme: modesOf$1(set.layout, "semantic"), density: modesOf$1(set.layout, "density") };
|
|
105
|
-
for (const
|
|
105
|
+
for (const p of findReferenceProblems(set.byPath, axes)) {
|
|
106
106
|
issues.push(
|
|
107
|
-
|
|
107
|
+
p.kind === "broken" ? {
|
|
108
108
|
code: "broken-reference",
|
|
109
109
|
severity: "error",
|
|
110
|
-
path:
|
|
111
|
-
mode:
|
|
112
|
-
message: `${
|
|
110
|
+
path: p.path,
|
|
111
|
+
mode: p.mode,
|
|
112
|
+
message: `${p.path} points to ${p.target}, which does not exist (${modeName(p.mode)}).`
|
|
113
113
|
} : {
|
|
114
114
|
code: "alias-cycle",
|
|
115
115
|
severity: "error",
|
|
116
|
-
path:
|
|
117
|
-
mode:
|
|
118
|
-
message: `${
|
|
116
|
+
path: p.path,
|
|
117
|
+
mode: p.mode,
|
|
118
|
+
message: `${p.path} is part of a loop of references: ${p.chain.join(" -> ")} (${modeName(p.mode)}).`
|
|
119
119
|
}
|
|
120
120
|
);
|
|
121
121
|
}
|
|
@@ -139,16 +139,16 @@ function validateSet(set, pairs) {
|
|
|
139
139
|
}
|
|
140
140
|
function shadowedEdits(set, edits) {
|
|
141
141
|
const out = [];
|
|
142
|
-
for (const
|
|
143
|
-
if (
|
|
142
|
+
for (const e2 of edits) {
|
|
143
|
+
if (e2.tier !== "semantic") continue;
|
|
144
144
|
for (const o2 of set.overrides) {
|
|
145
|
-
if (o2.path !==
|
|
145
|
+
if (o2.path !== e2.path || !e2.modes.includes(o2.theme)) continue;
|
|
146
146
|
out.push({
|
|
147
147
|
code: "override-shadowed",
|
|
148
148
|
severity: "warning",
|
|
149
|
-
path:
|
|
149
|
+
path: e2.path,
|
|
150
150
|
mode: `${o2.brand}.${o2.theme}`,
|
|
151
|
-
message: `${
|
|
151
|
+
message: `${e2.path} is overridden for ${modeDisplayName(o2.brand)} in ${o2.theme} (${o2.file}), so this edit does not change ${modeDisplayName(o2.brand)} ${o2.theme}. Brand overrides stay code-only.`
|
|
152
152
|
});
|
|
153
153
|
}
|
|
154
154
|
}
|
|
@@ -180,9 +180,9 @@ function levenshtein(a, b) {
|
|
|
180
180
|
for (let i2 = 1; i2 <= a.length; i2++) {
|
|
181
181
|
let diag = prev[0];
|
|
182
182
|
prev[0] = i2;
|
|
183
|
-
for (let
|
|
184
|
-
const tmp = prev[
|
|
185
|
-
prev[
|
|
183
|
+
for (let j2 = 1; j2 <= b.length; j2++) {
|
|
184
|
+
const tmp = prev[j2];
|
|
185
|
+
prev[j2] = Math.min(prev[j2] + 1, prev[j2 - 1] + 1, diag + (a[i2 - 1] === b[j2 - 1] ? 0 : 1));
|
|
186
186
|
diag = tmp;
|
|
187
187
|
}
|
|
188
188
|
}
|
|
@@ -249,17 +249,263 @@ function applyConfirmedRenames(tokens, confirmed) {
|
|
|
249
249
|
function idExtensionFor(t3) {
|
|
250
250
|
return t3.id === pathId(t3.path) ? void 0 : t3.id;
|
|
251
251
|
}
|
|
252
|
+
const idOf$1 = (f) => {
|
|
253
|
+
const id = f.extensions?.[EXT_ID];
|
|
254
|
+
return typeof id === "string" && id ? id : pathId(f.path);
|
|
255
|
+
};
|
|
256
|
+
function logical(f) {
|
|
257
|
+
const scopes = f.extensions?.[EXT_SCOPES];
|
|
258
|
+
return {
|
|
259
|
+
id: idOf$1(f),
|
|
260
|
+
path: f.path,
|
|
261
|
+
type: f.type ?? "unknown",
|
|
262
|
+
// One file holds one tier: the tier only feeds default scopes, the same on every side.
|
|
263
|
+
tier: "primitive",
|
|
264
|
+
values: { [SINGLE_MODE]: f.value },
|
|
265
|
+
description: f.description,
|
|
266
|
+
scopes: Array.isArray(scopes) ? scopes.map(String) : void 0,
|
|
267
|
+
files: { [SINGLE_MODE]: f.file }
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
function readSide(file, text, problems, label) {
|
|
271
|
+
const side = { doc: void 0, flat: /* @__PURE__ */ new Map(), logical: /* @__PURE__ */ new Map() };
|
|
272
|
+
if (text === void 0) return side;
|
|
273
|
+
try {
|
|
274
|
+
side.doc = parseTokenFile(file, text);
|
|
275
|
+
} catch (err) {
|
|
276
|
+
problems.push(`${file} (${label}) is not valid JSON: ${err.message}`);
|
|
277
|
+
return side;
|
|
278
|
+
}
|
|
279
|
+
for (const f of flattenTokens(side.doc)) {
|
|
280
|
+
const id = idOf$1(f);
|
|
281
|
+
side.flat.set(id, f);
|
|
282
|
+
side.logical.set(id, logical(f));
|
|
283
|
+
}
|
|
284
|
+
return side;
|
|
285
|
+
}
|
|
286
|
+
function diffFile(input, file, problems) {
|
|
287
|
+
const base = readSide(file, input.base[file], problems, "shared start");
|
|
288
|
+
const mine = readSide(file, input.mine[file], problems, "this change");
|
|
289
|
+
const theirs = readSide(file, input.theirs[file], problems, "latest library");
|
|
290
|
+
const r2 = threeWayDiff(base.logical, mine.logical, theirs.logical);
|
|
291
|
+
return { file, base, mine, theirs, local: r2.local, remote: r2.remote, conflicts: r2.conflicts };
|
|
292
|
+
}
|
|
293
|
+
function removedSide(c10) {
|
|
294
|
+
if (!c10.base) return null;
|
|
295
|
+
if (!c10.mine && c10.theirs) return "mine";
|
|
296
|
+
if (!c10.theirs && c10.mine) return "theirs";
|
|
297
|
+
return null;
|
|
298
|
+
}
|
|
299
|
+
const defaultChoiceFor = (c10) => removedSide(c10) ? null : "mine";
|
|
300
|
+
function planTokenMerge(input) {
|
|
301
|
+
const problems = [];
|
|
302
|
+
const files = [...new Set(input.files)].sort();
|
|
303
|
+
const conflicts = [];
|
|
304
|
+
let mineOnly = 0;
|
|
305
|
+
let theirsOnly = 0;
|
|
306
|
+
for (const file of files) {
|
|
307
|
+
const d2 = diffFile(input, file, problems);
|
|
308
|
+
mineOnly += d2.local.length;
|
|
309
|
+
theirsOnly += d2.remote.length;
|
|
310
|
+
for (const e2 of d2.conflicts) {
|
|
311
|
+
const c10 = { base: d2.base.flat.get(e2.id), mine: d2.mine.flat.get(e2.id), theirs: d2.theirs.flat.get(e2.id) };
|
|
312
|
+
conflicts.push({ key: `${file}#${e2.id}`, file, path: (c10.mine ?? c10.theirs ?? c10.base).path, ...c10, removedOn: removedSide(c10), defaultChoice: defaultChoiceFor(c10) });
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return { files, conflicts, mineOnly, theirsOnly, problems };
|
|
316
|
+
}
|
|
317
|
+
const choiceFor = (c10, choices) => choices[c10.key] ?? c10.defaultChoice;
|
|
318
|
+
function missingChoices(plan, choices) {
|
|
319
|
+
return plan.conflicts.filter((c10) => choiceFor(c10, choices) === null);
|
|
320
|
+
}
|
|
321
|
+
function takeMine(doc, mine, existing) {
|
|
322
|
+
if (!mine) return existing ? removeToken(doc, existing.path) : false;
|
|
323
|
+
let changed = false;
|
|
324
|
+
if (existing && existing.path !== mine.path) changed = removeToken(doc, existing.path) || changed;
|
|
325
|
+
const extensions = {};
|
|
326
|
+
for (const k3 of Object.keys(existing?.path === mine.path ? existing.extensions ?? {} : {})) extensions[k3] = void 0;
|
|
327
|
+
Object.assign(extensions, mine.extensions ?? {});
|
|
328
|
+
return upsertToken(doc, mine.path, { type: mine.type, value: mine.value, description: mine.description, extensions }) || changed;
|
|
329
|
+
}
|
|
330
|
+
function applyTokenMerge(input, choices) {
|
|
331
|
+
const plan = planTokenMerge(input);
|
|
332
|
+
if (plan.problems.length) throw new Error(plan.problems[0]);
|
|
333
|
+
const missing = missingChoices(plan, choices);
|
|
334
|
+
if (missing.length) throw new Error(`Pick a value for ${missing.map((c10) => c10.path).join(", ")} first.`);
|
|
335
|
+
const byKey = new Map(plan.conflicts.map((c10) => [c10.key, c10]));
|
|
336
|
+
const kept = { mine: 0, theirs: 0 };
|
|
337
|
+
const files = {};
|
|
338
|
+
for (const file of plan.files) {
|
|
339
|
+
const d2 = diffFile(input, file, []);
|
|
340
|
+
const doc = d2.theirs.doc ?? emptyTokenFile(file);
|
|
341
|
+
for (const e2 of d2.local) takeMine(doc, d2.mine.flat.get(e2.id), d2.theirs.flat.get(e2.id) ?? d2.base.flat.get(e2.id));
|
|
342
|
+
for (const e2 of d2.conflicts) {
|
|
343
|
+
const side = choiceFor(byKey.get(`${file}#${e2.id}`), choices);
|
|
344
|
+
kept[side]++;
|
|
345
|
+
if (side === "mine") takeMine(doc, d2.mine.flat.get(e2.id), d2.theirs.flat.get(e2.id));
|
|
346
|
+
}
|
|
347
|
+
const empty = flattenTokens(doc).length === 0;
|
|
348
|
+
files[file] = empty && (input.theirs[file] === void 0 || input.mine[file] === void 0) ? null : serializeTokenFile(doc);
|
|
349
|
+
}
|
|
350
|
+
return { files, kept };
|
|
351
|
+
}
|
|
352
|
+
const idOf = (f) => {
|
|
353
|
+
const id = f.extensions?.[EXT_ID];
|
|
354
|
+
return typeof id === "string" && id ? id : pathId(f.path);
|
|
355
|
+
};
|
|
356
|
+
function tokensOf(file, text, problems, label) {
|
|
357
|
+
const out = /* @__PURE__ */ new Map();
|
|
358
|
+
if (text === void 0) return out;
|
|
359
|
+
try {
|
|
360
|
+
for (const t3 of flattenTokens(parseTokenFile(file, text))) out.set(idOf(t3), t3);
|
|
361
|
+
} catch (err) {
|
|
362
|
+
problems.push(`${file} (${label}) is not valid JSON: ${err.message}`);
|
|
363
|
+
}
|
|
364
|
+
return out;
|
|
365
|
+
}
|
|
366
|
+
const shape = (t3) => t3 ? JSON.stringify([t3.path, t3.type ?? null, t3.value, t3.description ?? null]) : "removed";
|
|
367
|
+
const look = (t3) => t3 ? JSON.stringify([t3.path, t3.type ?? null, t3.value]) : "removed";
|
|
368
|
+
function changeEdits(src) {
|
|
369
|
+
const problems = [];
|
|
370
|
+
const edits = [];
|
|
371
|
+
for (const file of [...new Set(src.files)].sort()) {
|
|
372
|
+
const before = tokensOf(file, src.base[file], problems, "start");
|
|
373
|
+
const after = tokensOf(file, src.head[file], problems, `change ${src.number}`);
|
|
374
|
+
for (const id of /* @__PURE__ */ new Set([...before.keys(), ...after.keys()])) {
|
|
375
|
+
const a = before.get(id);
|
|
376
|
+
const b = after.get(id);
|
|
377
|
+
if (shape(a) === shape(b)) continue;
|
|
378
|
+
edits.push({ key: `${file}#${id}`, file, path: (b ?? a).path, change: src.number, token: b });
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return { number: src.number, edits, problems };
|
|
382
|
+
}
|
|
383
|
+
function byToken(changes) {
|
|
384
|
+
const out = /* @__PURE__ */ new Map();
|
|
385
|
+
for (const c10 of changes) for (const e2 of c10.edits) out.set(e2.key, [...out.get(e2.key) ?? [], e2]);
|
|
386
|
+
return out;
|
|
387
|
+
}
|
|
388
|
+
function optionsOf(edits) {
|
|
389
|
+
const out = [];
|
|
390
|
+
for (const e2 of edits) {
|
|
391
|
+
const l2 = look(e2.token);
|
|
392
|
+
const hit = out.find((o2) => o2.look === l2);
|
|
393
|
+
if (hit) hit.option.changes.push(e2.change);
|
|
394
|
+
else out.push({ look: l2, option: { changes: [e2.change], token: e2.token } });
|
|
395
|
+
}
|
|
396
|
+
return out.map((o2) => o2.option);
|
|
397
|
+
}
|
|
398
|
+
function combineConflicts(changes) {
|
|
399
|
+
const out = [];
|
|
400
|
+
for (const [key, edits] of byToken(changes)) {
|
|
401
|
+
const options = optionsOf(edits);
|
|
402
|
+
if (options.length > 1) out.push({ key, file: edits[0].file, path: edits[0].path, options });
|
|
403
|
+
}
|
|
404
|
+
return out;
|
|
405
|
+
}
|
|
406
|
+
function pickedOption(c10, picks) {
|
|
407
|
+
const n2 = picks[c10.key];
|
|
408
|
+
return n2 === void 0 ? null : c10.options.find((o2) => o2.changes.includes(n2)) ?? null;
|
|
409
|
+
}
|
|
410
|
+
const unpicked = (conflicts, picks) => conflicts.filter((c10) => !pickedOption(c10, picks));
|
|
411
|
+
function combineChanges(latest, changes, picks = {}, opts = {}) {
|
|
412
|
+
const on = opts.include ? changes.filter((c10) => opts.include.includes(c10.number)) : changes;
|
|
413
|
+
const problems = on.flatMap((c10) => c10.problems);
|
|
414
|
+
const groups = byToken(on);
|
|
415
|
+
const conflicts = [];
|
|
416
|
+
const missing = [];
|
|
417
|
+
const from = {};
|
|
418
|
+
const docs = /* @__PURE__ */ new Map();
|
|
419
|
+
const docOf = (file) => {
|
|
420
|
+
if (docs.has(file)) return docs.get(file);
|
|
421
|
+
const text = latest[file];
|
|
422
|
+
let doc;
|
|
423
|
+
try {
|
|
424
|
+
doc = text === void 0 ? emptyTokenFile(file) : parseTokenFile(file, text);
|
|
425
|
+
} catch (err) {
|
|
426
|
+
problems.push(`${file} (latest library) is not valid JSON: ${err.message}`);
|
|
427
|
+
return null;
|
|
428
|
+
}
|
|
429
|
+
docs.set(file, doc);
|
|
430
|
+
return doc;
|
|
431
|
+
};
|
|
432
|
+
for (const [key, edits] of groups) {
|
|
433
|
+
const options = optionsOf(edits);
|
|
434
|
+
let use = options[0];
|
|
435
|
+
if (options.length > 1) {
|
|
436
|
+
const conflict = { key, file: edits[0].file, path: edits[0].path, options };
|
|
437
|
+
conflicts.push(conflict);
|
|
438
|
+
use = pickedOption(conflict, picks);
|
|
439
|
+
if (!use) {
|
|
440
|
+
missing.push(conflict);
|
|
441
|
+
use = opts.unpicked === "first" ? options[0] : null;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
if (!use) continue;
|
|
445
|
+
const doc = docOf(edits[0].file);
|
|
446
|
+
if (!doc) continue;
|
|
447
|
+
const existing = flattenTokens(doc).find((t3) => idOf(t3) === key.slice(key.indexOf("#") + 1));
|
|
448
|
+
takeMine(doc, use.token, existing);
|
|
449
|
+
const path = use.token?.path ?? edits[0].path;
|
|
450
|
+
from[path] = [.../* @__PURE__ */ new Set([...from[path] ?? [], ...use.changes])];
|
|
451
|
+
}
|
|
452
|
+
const files = { ...latest };
|
|
453
|
+
for (const [file, doc] of docs) {
|
|
454
|
+
if (latest[file] === void 0 && flattenTokens(doc).length === 0) continue;
|
|
455
|
+
files[file] = serializeTokenFile(doc);
|
|
456
|
+
}
|
|
457
|
+
const tokens = new Set([...groups.values()].map((e2) => e2[0].path)).size;
|
|
458
|
+
return { files, included: on.map((c10) => c10.number), edits: groups.size, tokens, conflicts, missing, from, problems };
|
|
459
|
+
}
|
|
460
|
+
function encodePicks(picks) {
|
|
461
|
+
return Object.entries(picks).filter(([, n2]) => Number.isSafeInteger(n2) && n2 > 0).sort(([a], [b]) => a.localeCompare(b)).map(([key, n2]) => `${n2}:${key}`).join("~");
|
|
462
|
+
}
|
|
463
|
+
function decodePicks(raw) {
|
|
464
|
+
const out = {};
|
|
465
|
+
for (const part of (raw ?? "").split("~")) {
|
|
466
|
+
const i2 = part.indexOf(":");
|
|
467
|
+
if (i2 < 1) continue;
|
|
468
|
+
const n2 = Number(part.slice(0, i2));
|
|
469
|
+
const key = part.slice(i2 + 1);
|
|
470
|
+
if (/^\d{1,7}$/.test(part.slice(0, i2)) && n2 > 0 && key.includes("#")) out[key] = n2;
|
|
471
|
+
}
|
|
472
|
+
return out;
|
|
473
|
+
}
|
|
474
|
+
function cssOf(type, value) {
|
|
475
|
+
if (value === void 0) return void 0;
|
|
476
|
+
if (type === "color") {
|
|
477
|
+
const hex = value && typeof value === "object" ? value.hex : void 0;
|
|
478
|
+
const c10 = parseColor(value);
|
|
479
|
+
if (!c10) return void 0;
|
|
480
|
+
if (c10.a < 1) return `rgb(${Math.round(c10.r * 255)} ${Math.round(c10.g * 255)} ${Math.round(c10.b * 255)} / ${c10.a})`;
|
|
481
|
+
return typeof hex === "string" ? hex.toLowerCase() : rgbaToHex(c10);
|
|
482
|
+
}
|
|
483
|
+
return formatValue(type, value);
|
|
484
|
+
}
|
|
485
|
+
const cssVarOf = (path) => `--${path.replace(/\./g, "-")}`;
|
|
486
|
+
function previewOverrides(head, proposed, ctx) {
|
|
487
|
+
const out = {};
|
|
488
|
+
const hctx = { ...ctx, overrides: head.overrideIndex };
|
|
489
|
+
const pctx = { ...ctx, overrides: proposed.overrideIndex };
|
|
490
|
+
for (const t3 of proposed.byPath.values()) {
|
|
491
|
+
const a = resolve(head.byPath, t3.path, hctx);
|
|
492
|
+
const b = resolve(proposed.byPath, t3.path, pctx);
|
|
493
|
+
const css = cssOf(t3.type, b.value);
|
|
494
|
+
if (css !== void 0 && JSON.stringify(a.value) !== JSON.stringify(b.value)) out[cssVarOf(t3.path)] = css;
|
|
495
|
+
}
|
|
496
|
+
return out;
|
|
497
|
+
}
|
|
252
498
|
const modesOf = (t3) => Object.keys(t3.values);
|
|
253
499
|
function composeFiles(head, originals, changes) {
|
|
254
500
|
const docs = /* @__PURE__ */ new Map();
|
|
255
501
|
const doc = (file) => {
|
|
256
|
-
let
|
|
257
|
-
if (!
|
|
502
|
+
let d2 = docs.get(file);
|
|
503
|
+
if (!d2) {
|
|
258
504
|
const text = originals[file];
|
|
259
|
-
|
|
260
|
-
docs.set(file,
|
|
505
|
+
d2 = text === void 0 ? emptyTokenFile(file) : parseTokenFile(file, text);
|
|
506
|
+
docs.set(file, d2);
|
|
261
507
|
}
|
|
262
|
-
return
|
|
508
|
+
return d2;
|
|
263
509
|
};
|
|
264
510
|
const fileFor = (t3, mode, current) => current?.files[mode] ?? t3.files[mode] ?? (isMultiMode(t3.tier) ? modeFile(head.layout, t3.tier, mode) : void 0) ?? defaultFile(t3.tier, t3.path, head.options.dirs);
|
|
265
511
|
for (const { target, current } of changes) {
|
|
@@ -270,9 +516,9 @@ function composeFiles(head, originals, changes) {
|
|
|
270
516
|
const renamed = !!current && current.path !== target.path;
|
|
271
517
|
if (current) {
|
|
272
518
|
for (const mode of modesOf(current)) {
|
|
273
|
-
const
|
|
274
|
-
if (target.values[mode] === void 0) removeToken(
|
|
275
|
-
else if (renamed) moveToken(
|
|
519
|
+
const d2 = doc(fileFor(current, mode));
|
|
520
|
+
if (target.values[mode] === void 0) removeToken(d2, current.path);
|
|
521
|
+
else if (renamed) moveToken(d2, current.path, target.path);
|
|
276
522
|
}
|
|
277
523
|
}
|
|
278
524
|
const targetScopes = effectiveScopes(target);
|
|
@@ -280,14 +526,14 @@ function composeFiles(head, originals, changes) {
|
|
|
280
526
|
const scopesChanged = !current || !sameScopes(targetScopes, effectiveScopes(current));
|
|
281
527
|
const descChanged = (target.description ?? "").trim() !== (current?.description ?? "").trim();
|
|
282
528
|
for (const mode of modesOf(target)) {
|
|
283
|
-
const
|
|
284
|
-
const existing = readTokenAt(
|
|
529
|
+
const d2 = doc(fileFor(target, mode, current));
|
|
530
|
+
const existing = readTokenAt(d2, target.path);
|
|
285
531
|
const currentValue = current?.values[mode];
|
|
286
532
|
const value = currentValue !== void 0 && valuesEqual(target.type, target.values[mode], currentValue) ? currentValue : target.values[mode];
|
|
287
533
|
const extensions = {};
|
|
288
534
|
if (scopesChanged) extensions[EXT_SCOPES] = scopesExt;
|
|
289
535
|
if (renamed || !existing) extensions[EXT_ID] = idExtensionFor(target);
|
|
290
|
-
upsertToken(
|
|
536
|
+
upsertToken(d2, target.path, {
|
|
291
537
|
type: target.type,
|
|
292
538
|
value,
|
|
293
539
|
description: descChanged || !existing ? target.description : existing.description,
|
|
@@ -297,8 +543,8 @@ function composeFiles(head, originals, changes) {
|
|
|
297
543
|
}
|
|
298
544
|
const files = {};
|
|
299
545
|
const allFiles = { ...originals };
|
|
300
|
-
for (const [file,
|
|
301
|
-
const text = serializeTokenFile(
|
|
546
|
+
for (const [file, d2] of docs) {
|
|
547
|
+
const text = serializeTokenFile(d2);
|
|
302
548
|
allFiles[file] = text;
|
|
303
549
|
if (text !== originals[file]) files[file] = text;
|
|
304
550
|
}
|
|
@@ -358,7 +604,7 @@ function refPattern(pattern) {
|
|
|
358
604
|
function rulesetTargetsDefault(ruleset, defaultBranch) {
|
|
359
605
|
if (ruleset.target && ruleset.target !== "branch") return false;
|
|
360
606
|
const ref = `refs/heads/${defaultBranch}`;
|
|
361
|
-
const hit = (
|
|
607
|
+
const hit = (p) => p === "~ALL" || p === "~DEFAULT_BRANCH" || p === ref || p === defaultBranch || refPattern(p.startsWith("refs/") ? p : `refs/heads/${p}`).test(ref);
|
|
362
608
|
const cond = ruleset.conditions?.ref_name;
|
|
363
609
|
if (!cond) return false;
|
|
364
610
|
if ((cond.exclude ?? []).some(hit)) return false;
|
|
@@ -376,12 +622,12 @@ function rulesetParts(ruleset) {
|
|
|
376
622
|
}
|
|
377
623
|
return parts;
|
|
378
624
|
}
|
|
379
|
-
function classicParts(
|
|
625
|
+
function classicParts(p) {
|
|
380
626
|
const parts = /* @__PURE__ */ new Set();
|
|
381
|
-
if (count(
|
|
382
|
-
if (((
|
|
383
|
-
if (!
|
|
384
|
-
if (!
|
|
627
|
+
if (count(p.required_pull_request_reviews?.required_approving_review_count) >= 1) parts.add("review");
|
|
628
|
+
if (((p.required_status_checks?.contexts?.length ?? 0) || (p.required_status_checks?.checks?.length ?? 0)) > 0) parts.add("checks");
|
|
629
|
+
if (!p.allow_force_pushes?.enabled) parts.add("history");
|
|
630
|
+
if (!p.allow_deletions?.enabled) parts.add("deletion");
|
|
385
631
|
return parts;
|
|
386
632
|
}
|
|
387
633
|
const rulesetUrl = (r2) => r2.html_url ?? r2._links?.html?.href;
|
|
@@ -394,10 +640,10 @@ function protectionState(rulesets, branchProtection, defaultBranch) {
|
|
|
394
640
|
if (!rulesetTargetsDefault(r2, defaultBranch)) continue;
|
|
395
641
|
const parts = rulesetParts(r2);
|
|
396
642
|
if (parts.size && !url) url = rulesetUrl(r2);
|
|
397
|
-
parts.forEach((
|
|
643
|
+
parts.forEach((p) => covered.add(p));
|
|
398
644
|
}
|
|
399
|
-
if (branchProtection) classicParts(branchProtection).forEach((
|
|
400
|
-
const missing = PROTECTION_PARTS.filter((
|
|
645
|
+
if (branchProtection) classicParts(branchProtection).forEach((p) => covered.add(p));
|
|
646
|
+
const missing = PROTECTION_PARTS.filter((p) => !covered.has(p));
|
|
401
647
|
const withUrl = url ? { url } : {};
|
|
402
648
|
if (!missing.length) return { kind: "protected", missing, ...extra, ...withUrl };
|
|
403
649
|
const unreadable = rulesets == null || branchProtection === void 0;
|
|
@@ -418,7 +664,7 @@ function protectionSentence(state) {
|
|
|
418
664
|
case "unprotected":
|
|
419
665
|
return "Your library can be changed without review.";
|
|
420
666
|
case "partial": {
|
|
421
|
-
const s = joinAnd(state.missing.map((
|
|
667
|
+
const s = joinAnd(state.missing.map((p) => PROTECTION_PART_PHRASE[p]));
|
|
422
668
|
return `Partly protected: ${s}.`;
|
|
423
669
|
}
|
|
424
670
|
default:
|
|
@@ -467,18 +713,18 @@ function missingPermissionMessage(permission, action) {
|
|
|
467
713
|
}
|
|
468
714
|
function requiredPermission(path, acceptedHeader) {
|
|
469
715
|
if (acceptedHeader) {
|
|
470
|
-
const first = acceptedHeader.split(";")[0].split(",").map((
|
|
716
|
+
const first = acceptedHeader.split(";")[0].split(",").map((p2) => p2.trim().split("=")[0].trim()).filter((p2) => p2 && p2 !== "metadata");
|
|
471
717
|
if (first.length) return first[0];
|
|
472
718
|
}
|
|
473
|
-
const
|
|
474
|
-
if (/\/repos\/[^/]+\/[^/]+\/(issues|labels|milestones)(\/|$)/.test(
|
|
475
|
-
if (/\/repos\/[^/]+\/[^/]+\/pulls(\/|$)/.test(
|
|
476
|
-
if (/\/repos\/[^/]+\/[^/]+\/(rulesets(\/|$)|branches\/.+\/protection$)/.test(
|
|
477
|
-
if (/\/repos\/[^/]+\/[^/]+\/(contents|git|commits|branches|merges)(\/|$)/.test(
|
|
478
|
-
if (/\/repos\/[^/]+\/[^/]+\/(check-runs|check-suites)(\/|$)/.test(
|
|
479
|
-
if (/\/repos\/[^/]+\/[^/]+\/statuses(\/|$)/.test(
|
|
480
|
-
if (/\/repos\/[^/]+\/[^/]+\/deployments(\/|$)/.test(
|
|
481
|
-
if (/\/repos\/[^/]+\/[^/]+\/actions(\/|$)/.test(
|
|
719
|
+
const p = path.replace(/[?#].*$/, "");
|
|
720
|
+
if (/\/repos\/[^/]+\/[^/]+\/(issues|labels|milestones)(\/|$)/.test(p)) return "issues";
|
|
721
|
+
if (/\/repos\/[^/]+\/[^/]+\/pulls(\/|$)/.test(p)) return "pull_requests";
|
|
722
|
+
if (/\/repos\/[^/]+\/[^/]+\/(rulesets(\/|$)|branches\/.+\/protection$)/.test(p)) return "administration";
|
|
723
|
+
if (/\/repos\/[^/]+\/[^/]+\/(contents|git|commits|branches|merges)(\/|$)/.test(p)) return "contents";
|
|
724
|
+
if (/\/repos\/[^/]+\/[^/]+\/(check-runs|check-suites)(\/|$)/.test(p)) return "checks";
|
|
725
|
+
if (/\/repos\/[^/]+\/[^/]+\/statuses(\/|$)/.test(p)) return "statuses";
|
|
726
|
+
if (/\/repos\/[^/]+\/[^/]+\/deployments(\/|$)/.test(p)) return "deployments";
|
|
727
|
+
if (/\/repos\/[^/]+\/[^/]+\/actions(\/|$)/.test(p)) return "actions";
|
|
482
728
|
return void 0;
|
|
483
729
|
}
|
|
484
730
|
const PERMISSION_DENIED = /not accessible by (personal access token|integration)|must have (admin|push|write) (rights|access)|write access to repository not granted|permission to .* denied/i;
|
|
@@ -490,6 +736,12 @@ function classifyAuth(status, detail, opts = {}) {
|
|
|
490
736
|
}
|
|
491
737
|
return {};
|
|
492
738
|
}
|
|
739
|
+
function rateLimitMessage(x) {
|
|
740
|
+
const when = x.resetsAt ? ` It resets at ${x.resetsAt.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })}.` : "";
|
|
741
|
+
if (x.withToken) return `GitHub's hourly request limit for this token is used up.${when} Try again after that.`;
|
|
742
|
+
const hint = x.tokenHint ?? "add a GitHub token";
|
|
743
|
+
return `GitHub's hourly limit for requests without a token (60 an hour) is used up.${when} To keep going now, ${hint}: a token gets 5,000 requests an hour.`;
|
|
744
|
+
}
|
|
493
745
|
const DEFAULT_RETRY = { attempts: 3, baseDelayMs: 600, sleep: (ms) => new Promise((r2) => setTimeout(r2, ms)) };
|
|
494
746
|
const isTransient = (status, detail) => status === 0 || status >= 500 || status === 429 || status === 403 && /secondary rate limit/i.test(detail);
|
|
495
747
|
const API = "https://api.github.com";
|
|
@@ -562,8 +814,8 @@ class GitHubClient {
|
|
|
562
814
|
try {
|
|
563
815
|
return await this.once(method, path, body);
|
|
564
816
|
} catch (err) {
|
|
565
|
-
const
|
|
566
|
-
if (attempt >= attempts || !(
|
|
817
|
+
const e2 = err;
|
|
818
|
+
if (attempt >= attempts || !(e2 instanceof GitHubError) || !isTransient(e2.status, e2.detail ?? "")) throw err;
|
|
567
819
|
await this.retry.sleep(this.retry.baseDelayMs * 2 ** (attempt - 1));
|
|
568
820
|
}
|
|
569
821
|
}
|
|
@@ -586,19 +838,15 @@ class GitHubClient {
|
|
|
586
838
|
let detail = "";
|
|
587
839
|
try {
|
|
588
840
|
const data = await res.json();
|
|
589
|
-
detail = [data.message, ...(data.errors ?? []).map((
|
|
841
|
+
detail = [data.message, ...(data.errors ?? []).map((e2) => typeof e2 === "string" ? e2 : e2.message)].filter(Boolean).join(" ");
|
|
590
842
|
} catch {
|
|
591
843
|
detail = "";
|
|
592
844
|
}
|
|
593
845
|
const remaining = res.headers?.get("x-ratelimit-remaining");
|
|
594
846
|
const reset = res.headers?.get("x-ratelimit-reset");
|
|
595
847
|
if ((res.status === 403 || res.status === 429) && remaining === "0") {
|
|
596
|
-
const
|
|
597
|
-
throw new GitHubError(
|
|
598
|
-
429,
|
|
599
|
-
`GitHub's hourly request limit for this token is used up.${when ? ` It resets at ${when.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })}.` : ""} Try again after that.`,
|
|
600
|
-
detail
|
|
601
|
-
);
|
|
848
|
+
const resetsAt = reset ? new Date(Number(reset) * 1e3) : void 0;
|
|
849
|
+
throw new GitHubError(429, rateLimitMessage({ withToken: Boolean(this.token), resetsAt, tokenHint: this.options.tokenHint }), detail);
|
|
602
850
|
}
|
|
603
851
|
const info = classifyAuth(res.status, detail, {
|
|
604
852
|
path,
|
|
@@ -670,7 +918,7 @@ class GitHubClient {
|
|
|
670
918
|
/** Pull requests a commit belongs to (merged ones first): how a commit on main finds its PR. */
|
|
671
919
|
async listPullRequestsForCommit(sha) {
|
|
672
920
|
const r2 = await this.request("GET", `${this.repoPath}/commits/${encodeRef(sha)}/pulls`);
|
|
673
|
-
return r2.map((
|
|
921
|
+
return r2.map((p) => ({ ...toSummary(p), body: p.body ?? "", ...p.merged_at ? { mergedAt: p.merged_at } : {}, ...p.merge_commit_sha ? { mergeCommitSha: p.merge_commit_sha } : {} })).sort((a, b) => Number(Boolean(b.mergedAt)) - Number(Boolean(a.mergedAt)));
|
|
674
922
|
}
|
|
675
923
|
/** Tags with the commit they point to, newest first as GitHub lists them. */
|
|
676
924
|
async listTags(perPage = 30) {
|
|
@@ -751,10 +999,29 @@ class GitHubClient {
|
|
|
751
999
|
variables: { id: pr.node_id }
|
|
752
1000
|
});
|
|
753
1001
|
if (r2?.errors?.length) {
|
|
754
|
-
const detail = r2.errors.map((
|
|
1002
|
+
const detail = r2.errors.map((e2) => e2.message).filter(Boolean).join(" ");
|
|
755
1003
|
throw new GitHubError(422, `GitHub refused to mark #${n2} ready for review${detail ? `: ${detail}` : "."} Use "Ready for review" on GitHub instead.`, detail);
|
|
756
1004
|
}
|
|
757
1005
|
}
|
|
1006
|
+
/**
|
|
1007
|
+
* Turns an open pull request back into a draft: the GraphQL mutation convertPullRequestToDraft (REST cannot set
|
|
1008
|
+
* `draft` either), same token. No-op when it is already a draft.
|
|
1009
|
+
*/
|
|
1010
|
+
async convertToDraft(n2) {
|
|
1011
|
+
this.assertToken("Turning a pull request back into a draft");
|
|
1012
|
+
const pr = await this.request("GET", `${this.repoPath}/pulls/${n2}`);
|
|
1013
|
+
if (pr.draft === true) return;
|
|
1014
|
+
if (pr.state && pr.state !== "open") throw new GitHubError(422, `Pull request #${n2} is ${pr.state}: only an open one can go back to draft.`);
|
|
1015
|
+
if (!pr.node_id) throw new GitHubError(422, `Pull request #${n2} has no node id: convert it to draft on GitHub.`);
|
|
1016
|
+
const r2 = await this.request("POST", "/graphql", {
|
|
1017
|
+
query: "mutation($id: ID!) { convertPullRequestToDraft(input: { pullRequestId: $id }) { pullRequest { number isDraft } } }",
|
|
1018
|
+
variables: { id: pr.node_id }
|
|
1019
|
+
});
|
|
1020
|
+
if (r2?.errors?.length) {
|
|
1021
|
+
const detail = r2.errors.map((e2) => e2.message).filter(Boolean).join(" ");
|
|
1022
|
+
throw new GitHubError(422, `GitHub refused to turn #${n2} back into a draft${detail ? `: ${detail}` : "."} Use "Convert to draft" on GitHub instead.`, detail);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
758
1025
|
async createPullRequest(input) {
|
|
759
1026
|
this.assertWritableBranch(input.head, input.base);
|
|
760
1027
|
const r2 = await this.request(
|
|
@@ -931,7 +1198,7 @@ class GitHubClient {
|
|
|
931
1198
|
const q2 = ref ? `?ref=${encodeURIComponent(ref)}` : "";
|
|
932
1199
|
const r2 = await this.request("GET", `${this.repoPath}/contents/${encodeRef(path)}${q2}`);
|
|
933
1200
|
if (!Array.isArray(r2)) return [];
|
|
934
|
-
return r2.map((
|
|
1201
|
+
return r2.map((e2) => ({ name: e2.name, path: e2.path, type: e2.type, sha: e2.sha, size: e2.size ?? 0 }));
|
|
935
1202
|
} catch (err) {
|
|
936
1203
|
if (err instanceof GitHubError && err.status === 404) return [];
|
|
937
1204
|
throw err;
|
|
@@ -984,7 +1251,7 @@ class GitHubClient {
|
|
|
984
1251
|
async listFiles(ref) {
|
|
985
1252
|
const r2 = await this.request("GET", `${this.repoPath}/git/trees/${encodeRef(ref)}?recursive=1`);
|
|
986
1253
|
if (r2.truncated) throw new GitHubError(413, "The repository tree is too large to read in one request.");
|
|
987
|
-
return r2.tree.filter((
|
|
1254
|
+
return r2.tree.filter((e2) => e2.type === "blob").map((e2) => e2.path);
|
|
988
1255
|
}
|
|
989
1256
|
// --- Pull requests, reviews, checks ---------------------------------------------------------
|
|
990
1257
|
async listPullRequests(state = "open") {
|
|
@@ -1017,12 +1284,12 @@ class GitHubClient {
|
|
|
1017
1284
|
async listDeployments(sha) {
|
|
1018
1285
|
const list = await this.request("GET", `${this.repoPath}/deployments?sha=${encodeURIComponent(sha)}&per_page=30`);
|
|
1019
1286
|
const latest = /* @__PURE__ */ new Map();
|
|
1020
|
-
for (const
|
|
1287
|
+
for (const d2 of list) if (!latest.has(d2.environment)) latest.set(d2.environment, d2);
|
|
1021
1288
|
return Promise.all(
|
|
1022
|
-
[...latest.values()].map(async (
|
|
1023
|
-
const st = await this.request("GET", `${this.repoPath}/deployments/${
|
|
1289
|
+
[...latest.values()].map(async (d2) => {
|
|
1290
|
+
const st = await this.request("GET", `${this.repoPath}/deployments/${d2.id}/statuses?per_page=1`).catch(() => []);
|
|
1024
1291
|
const s = st[0];
|
|
1025
|
-
return { id:
|
|
1292
|
+
return { id: d2.id, environment: d2.environment, state: s?.state ?? null, environmentUrl: s?.environment_url || null };
|
|
1026
1293
|
})
|
|
1027
1294
|
);
|
|
1028
1295
|
}
|
|
@@ -1109,7 +1376,7 @@ class GitHubClient {
|
|
|
1109
1376
|
function toCommitSummary(c10) {
|
|
1110
1377
|
return {
|
|
1111
1378
|
sha: c10.sha,
|
|
1112
|
-
parents: (c10.parents ?? []).map((
|
|
1379
|
+
parents: (c10.parents ?? []).map((p) => p.sha),
|
|
1113
1380
|
date: c10.commit?.committer?.date ?? c10.commit?.author?.date ?? "",
|
|
1114
1381
|
author: c10.author?.login ?? c10.commit?.author?.name ?? "unknown",
|
|
1115
1382
|
message: c10.commit?.message ?? ""
|
|
@@ -1160,7 +1427,7 @@ async function loadRepoFiles(client, commitSha, fallbackTokensPath) {
|
|
|
1160
1427
|
const warnings = [];
|
|
1161
1428
|
let config;
|
|
1162
1429
|
let configText;
|
|
1163
|
-
const configEntry = entries.find((
|
|
1430
|
+
const configEntry = entries.find((e2) => e2.type === "blob" && e2.path === CONFIG_FILE);
|
|
1164
1431
|
if (configEntry) {
|
|
1165
1432
|
try {
|
|
1166
1433
|
configText = await client.getBlobText(configEntry.sha);
|
|
@@ -1173,16 +1440,16 @@ async function loadRepoFiles(client, commitSha, fallbackTokensPath) {
|
|
|
1173
1440
|
const checksFile = config?.tokens.contrastChecks ?? DEFAULT_TOKEN_PATHS.contrastChecks;
|
|
1174
1441
|
const prefix = tokensPath2 + "/";
|
|
1175
1442
|
const wanted = entries.filter(
|
|
1176
|
-
(
|
|
1443
|
+
(e2) => e2.type === "blob" && e2.path.startsWith(prefix) && (e2.path.endsWith(".tokens.json") || e2.path === prefix + checksFile)
|
|
1177
1444
|
);
|
|
1178
|
-
if (!wanted.some((
|
|
1445
|
+
if (!wanted.some((e2) => e2.path.endsWith(".tokens.json"))) throw new Error(`No *.tokens.json files found under ${tokensPath2} at ${commitSha.slice(0, 7)}.`);
|
|
1179
1446
|
const files = {};
|
|
1180
1447
|
let contrastChecks;
|
|
1181
1448
|
const queue = [...wanted];
|
|
1182
1449
|
const worker = async () => {
|
|
1183
|
-
for (let
|
|
1184
|
-
const text = await client.getBlobText(
|
|
1185
|
-
const rel =
|
|
1450
|
+
for (let e2 = queue.shift(); e2; e2 = queue.shift()) {
|
|
1451
|
+
const text = await client.getBlobText(e2.sha);
|
|
1452
|
+
const rel = e2.path.slice(prefix.length);
|
|
1186
1453
|
if (rel === checksFile) contrastChecks = text;
|
|
1187
1454
|
else files[rel] = text;
|
|
1188
1455
|
}
|
|
@@ -1215,7 +1482,7 @@ async function loadProtection(client, defaultBranch) {
|
|
|
1215
1482
|
}
|
|
1216
1483
|
async function loadRequiredChecks(client, defaultBranch) {
|
|
1217
1484
|
const onDefault = await client.listCheckRuns(defaultBranch).catch(() => []);
|
|
1218
|
-
const merged = await client.listPullRequests("closed").then((prs) => prs.find((
|
|
1485
|
+
const merged = await client.listPullRequests("closed").then((prs) => prs.find((p) => p.state === "merged" && p.baseRef === defaultBranch)).catch(() => void 0);
|
|
1219
1486
|
const onPull = merged?.headSha ? await client.listCheckRuns(merged.headSha).catch(() => []) : [];
|
|
1220
1487
|
return requiredCheckNames(onDefault, onPull);
|
|
1221
1488
|
}
|
|
@@ -1248,11 +1515,11 @@ function importsIn(text) {
|
|
|
1248
1515
|
for (const m2 of text.matchAll(IMPORT_RE)) out.add(m2[1] ?? m2[2]);
|
|
1249
1516
|
return [...out];
|
|
1250
1517
|
}
|
|
1251
|
-
const norm = (
|
|
1252
|
-
const isStory = (
|
|
1253
|
-
const isTest = (
|
|
1254
|
-
const isCss = (
|
|
1255
|
-
const isScript = (
|
|
1518
|
+
const norm = (p) => p.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
1519
|
+
const isStory = (p) => /\.stories\.[cm]?[jt]sx?$/.test(p);
|
|
1520
|
+
const isTest = (p) => /\.(test|spec)\.[cm]?[jt]sx?$/.test(p);
|
|
1521
|
+
const isCss = (p) => /\.(css|scss|less)$/.test(p);
|
|
1522
|
+
const isScript = (p) => /\.[cm]?[jt]sx?$/.test(p);
|
|
1256
1523
|
function componentOf(from, spec, componentsDir) {
|
|
1257
1524
|
if (!spec.startsWith(".")) return null;
|
|
1258
1525
|
const parts = from.split("/").slice(0, -1);
|
|
@@ -1267,9 +1534,9 @@ function componentOf(from, spec, componentsDir) {
|
|
|
1267
1534
|
}
|
|
1268
1535
|
function componentForPath(path, componentsDir) {
|
|
1269
1536
|
const dir = norm(componentsDir);
|
|
1270
|
-
const
|
|
1271
|
-
if (!
|
|
1272
|
-
const rest =
|
|
1537
|
+
const p = norm(path);
|
|
1538
|
+
if (!p.startsWith(`${dir}/`)) return null;
|
|
1539
|
+
const rest = p.slice(dir.length + 1).split("/");
|
|
1273
1540
|
return rest.length >= 2 ? rest[0] : null;
|
|
1274
1541
|
}
|
|
1275
1542
|
function buildUsageIndex(files, componentsDir) {
|
|
@@ -1306,7 +1573,7 @@ function buildUsageIndex(files, componentsDir) {
|
|
|
1306
1573
|
mod.files.sort();
|
|
1307
1574
|
mod.vars = [...vars.get(mod.id) ?? []].sort();
|
|
1308
1575
|
mod.imports = [...imports.get(mod.id) ?? []].sort();
|
|
1309
|
-
mod.storyImports = [...storyImports.get(mod.id) ?? []].filter((
|
|
1576
|
+
mod.storyImports = [...storyImports.get(mod.id) ?? []].filter((d2) => !mod.imports.includes(d2)).sort();
|
|
1310
1577
|
for (const v2 of mod.vars) (byVar[v2] ??= []).push(mod.id);
|
|
1311
1578
|
}
|
|
1312
1579
|
for (const k3 of Object.keys(byVar)) byVar[k3].sort();
|
|
@@ -1328,7 +1595,7 @@ function impactedModules(index, input) {
|
|
|
1328
1595
|
if (id && index.modules[id]) add(id, `file changed: ${norm(f)}`, false);
|
|
1329
1596
|
}
|
|
1330
1597
|
const renderedBy = /* @__PURE__ */ new Map();
|
|
1331
|
-
for (const m2 of Object.values(index.modules)) for (const
|
|
1598
|
+
for (const m2 of Object.values(index.modules)) for (const d2 of m2.imports) (renderedBy.get(d2) ?? renderedBy.set(d2, []).get(d2)).push(m2.id);
|
|
1332
1599
|
const queue = [...reasons.keys()];
|
|
1333
1600
|
while (queue.length) {
|
|
1334
1601
|
const id = queue.shift();
|
|
@@ -1351,10 +1618,10 @@ function tokenUsage(tokens, reads) {
|
|
|
1351
1618
|
const used = /* @__PURE__ */ new Set();
|
|
1352
1619
|
const queue = [...direct];
|
|
1353
1620
|
while (queue.length) {
|
|
1354
|
-
const
|
|
1355
|
-
if (used.has(
|
|
1356
|
-
used.add(
|
|
1357
|
-
for (const r2 of byPath.get(
|
|
1621
|
+
const p = queue.pop();
|
|
1622
|
+
if (used.has(p)) continue;
|
|
1623
|
+
used.add(p);
|
|
1624
|
+
for (const r2 of byPath.get(p)?.refs ?? []) if (!used.has(r2) && byPath.has(r2)) queue.push(r2);
|
|
1358
1625
|
}
|
|
1359
1626
|
return { used, direct };
|
|
1360
1627
|
}
|
|
@@ -1395,7 +1662,7 @@ function gitHubHistoryBackend(client, config, opts = {}) {
|
|
|
1395
1662
|
const merges = /* @__PURE__ */ new Map();
|
|
1396
1663
|
const enriched = await pool(list, async (c10) => {
|
|
1397
1664
|
const prs = await client.listPullRequestsForCommit(c10.sha).catch(() => []);
|
|
1398
|
-
const pr = prs.find((
|
|
1665
|
+
const pr = prs.find((p) => p.mergedAt && p.baseRef === ref) ?? prs.find((p) => p.mergedAt);
|
|
1399
1666
|
if (!pr) return c10;
|
|
1400
1667
|
const prInfo = { number: pr.number, title: pr.title, url: pr.url, author: pr.author, ...pr.mergedAt ? { mergedAt: pr.mergedAt } : {}, body: pr.body };
|
|
1401
1668
|
if (pr.mergeCommitSha && pr.mergeCommitSha !== c10.sha) {
|
|
@@ -1419,20 +1686,20 @@ function gitHubHistoryBackend(client, config, opts = {}) {
|
|
|
1419
1686
|
},
|
|
1420
1687
|
async tokenFiles(sha) {
|
|
1421
1688
|
const entries = await tree(sha);
|
|
1422
|
-
const blobs = entries.filter((
|
|
1423
|
-
const picked = new Set(selectTokenFiles(config, blobs.map((
|
|
1424
|
-
const wanted = blobs.filter((
|
|
1425
|
-
const texts = await pool(wanted, async (
|
|
1689
|
+
const blobs = entries.filter((e2) => e2.type === "blob" && e2.path.startsWith(prefix));
|
|
1690
|
+
const picked = new Set(selectTokenFiles(config, blobs.map((e2) => e2.path)));
|
|
1691
|
+
const wanted = blobs.filter((e2) => picked.has(prefix ? e2.path.slice(prefix.length) : e2.path));
|
|
1692
|
+
const texts = await pool(wanted, async (e2) => [prefix ? e2.path.slice(prefix.length) : e2.path, await client.getBlobText(e2.sha)]);
|
|
1426
1693
|
return new Map(texts);
|
|
1427
1694
|
},
|
|
1428
1695
|
async changesetPaths(sha) {
|
|
1429
1696
|
const dir = config.changesets?.dir.replace(/\/$/, "");
|
|
1430
1697
|
if (!dir) return [];
|
|
1431
|
-
return (await tree(sha)).filter((
|
|
1698
|
+
return (await tree(sha)).filter((e2) => e2.type === "blob" && e2.path.startsWith(`${dir}/`)).map((e2) => e2.path);
|
|
1432
1699
|
},
|
|
1433
1700
|
async readFile(sha, path) {
|
|
1434
|
-
const
|
|
1435
|
-
return
|
|
1701
|
+
const e2 = (await tree(sha)).find((x) => x.type === "blob" && x.path === path);
|
|
1702
|
+
return e2 ? client.getBlobText(e2.sha) : null;
|
|
1436
1703
|
}
|
|
1437
1704
|
};
|
|
1438
1705
|
}
|
|
@@ -1453,9 +1720,9 @@ async function loadGitHubHistory(client, config, opts = {}) {
|
|
|
1453
1720
|
if (head) {
|
|
1454
1721
|
const files = await backend.tree(head).catch(() => []);
|
|
1455
1722
|
for (const dir of packageDirCandidates(config)) {
|
|
1456
|
-
const
|
|
1457
|
-
const cl = files.find((
|
|
1458
|
-
const pkg = files.find((
|
|
1723
|
+
const p = (f) => dir ? `${dir}/${f}` : f;
|
|
1724
|
+
const cl = files.find((e2) => e2.path === p("CHANGELOG.md"));
|
|
1725
|
+
const pkg = files.find((e2) => e2.path === p("package.json"));
|
|
1459
1726
|
if (!cl && !pkg) continue;
|
|
1460
1727
|
if (cl) changelog = parseChangelog(await client.getBlobText(cl.sha));
|
|
1461
1728
|
if (pkg) {
|
|
@@ -1503,14 +1770,14 @@ function demoHistory(config, current) {
|
|
|
1503
1770
|
}
|
|
1504
1771
|
const entries = [];
|
|
1505
1772
|
kept.forEach((step, i2) => {
|
|
1506
|
-
const
|
|
1773
|
+
const e2 = buildHistoryEntry({
|
|
1507
1774
|
config,
|
|
1508
1775
|
commit: { sha: `demo${String(step.pr).padStart(36, "0")}`, parents: [`demo${String(step.pr - 1).padStart(36, "0")}`], date: step.date, author: step.author, subject: step.title, body: step.why, pr: { number: step.pr, title: step.title, author: step.author, mergedAt: step.date, body: step.why } },
|
|
1509
1776
|
before: states[i2],
|
|
1510
1777
|
after: states[i2 + 1],
|
|
1511
1778
|
demo: true
|
|
1512
1779
|
});
|
|
1513
|
-
if (
|
|
1780
|
+
if (e2) entries.push(e2);
|
|
1514
1781
|
});
|
|
1515
1782
|
return { ...buildReport({ source: "demo", label: "Demo history (fixture data)", ref: "demo", entries, releases: DEMO_RELEASES, truncated: false }), demo: true };
|
|
1516
1783
|
}
|
|
@@ -1523,14 +1790,14 @@ async function branchPreviewUrls(gh, ref, config, opts = {}) {
|
|
|
1523
1790
|
if (typeof ref === "number") {
|
|
1524
1791
|
pr = ref;
|
|
1525
1792
|
if (!branch || !sha) {
|
|
1526
|
-
const
|
|
1527
|
-
branch ??=
|
|
1528
|
-
sha ??=
|
|
1793
|
+
const p = await gh.getPullRequest(ref);
|
|
1794
|
+
branch ??= p.headRef;
|
|
1795
|
+
sha ??= p.headSha;
|
|
1529
1796
|
}
|
|
1530
1797
|
} else {
|
|
1531
1798
|
sha = ref;
|
|
1532
1799
|
if (!branch || !opts.comments) {
|
|
1533
|
-
const open = (await gh.listPullRequestsForCommit(ref).catch(() => [])).find((
|
|
1800
|
+
const open = (await gh.listPullRequestsForCommit(ref).catch(() => [])).find((p) => p.state === "open");
|
|
1534
1801
|
if (open) {
|
|
1535
1802
|
pr = open.number;
|
|
1536
1803
|
branch ??= open.headRef;
|
|
@@ -1655,12 +1922,12 @@ const brandDisplayName = (slug) => slug.split("-").filter(Boolean).map((w) => w[
|
|
|
1655
1922
|
function brandLayers(config) {
|
|
1656
1923
|
const out = {};
|
|
1657
1924
|
for (const l2 of config.tokens.layers) {
|
|
1658
|
-
const ph = (l2.files.match(/\{([^}]+)\}/g) ?? []).map((
|
|
1925
|
+
const ph = (l2.files.match(/\{([^}]+)\}/g) ?? []).map((p) => p.slice(1, -1));
|
|
1659
1926
|
if (!ph.includes("brand")) continue;
|
|
1660
1927
|
if (ph.length === 1 && !out.brand) out.brand = l2.files;
|
|
1661
1928
|
else if (ph.length === 2 && !out.override) {
|
|
1662
1929
|
out.override = l2.files;
|
|
1663
|
-
out.overrideAxis = ph.find((
|
|
1930
|
+
out.overrideAxis = ph.find((p) => p !== "brand");
|
|
1664
1931
|
}
|
|
1665
1932
|
}
|
|
1666
1933
|
return out;
|
|
@@ -1803,8 +2070,8 @@ function planNewBrand(source, input) {
|
|
|
1803
2070
|
if (r2.issue) issues.push({ ...r2.issue, path: `palette.${role}` });
|
|
1804
2071
|
anchors[role] = r2.anchor;
|
|
1805
2072
|
return r2.ramp;
|
|
1806
|
-
} catch (
|
|
1807
|
-
issues.push({ level: "error", code: "invalid-color", message:
|
|
2073
|
+
} catch (e2) {
|
|
2074
|
+
issues.push({ level: "error", code: "invalid-color", message: e2.message, path: `palette.${role}` });
|
|
1808
2075
|
return void 0;
|
|
1809
2076
|
}
|
|
1810
2077
|
};
|
|
@@ -1889,7 +2156,7 @@ function planNewBrand(source, input) {
|
|
|
1889
2156
|
const primDir = config.tokens.tiers.primitive?.[0]?.replace(/\/$/, "") ?? config.tokens.primitives;
|
|
1890
2157
|
const primFile = `${primDir}/${slug}.tokens.json`;
|
|
1891
2158
|
if (prims.size && files.has(primFile)) issues.push({ level: "error", code: "slug-file-exists", message: `${tokensPath(config, primFile)} already exists. Pick another name.` });
|
|
1892
|
-
for (const r2 of prims.keys()) if (before.allPaths().some((
|
|
2159
|
+
for (const r2 of prims.keys()) if (before.allPaths().some((p) => p.startsWith(`color.${r2}.`))) issues.push({ level: "error", code: "ramp-exists", message: `A primitive ramp color.${r2} already exists. Pick another name.` });
|
|
1893
2160
|
doc.$description = `${name}: created with Bridge, started from ${blank ? "a blank brand" : brandDisplayName(from)}.`;
|
|
1894
2161
|
doc.$extensions = { ...isObj(doc.$extensions) ? doc.$extensions : {}, "dev.bridge.brand": { name, from, ...Object.keys(anchors).length ? { anchors } : {} } };
|
|
1895
2162
|
const { $description, $extensions, ...rest } = doc;
|
|
@@ -1931,8 +2198,8 @@ function planNewBrand(source, input) {
|
|
|
1931
2198
|
try {
|
|
1932
2199
|
configText = addBrandToConfigText(baseConfigText, slug);
|
|
1933
2200
|
nextConfig = issues.some((i2) => i2.code.startsWith("slug-")) ? config : loadConfig(configText);
|
|
1934
|
-
} catch (
|
|
1935
|
-
issues.push({ level: "error", code: "config", message:
|
|
2201
|
+
} catch (e2) {
|
|
2202
|
+
issues.push({ level: "error", code: "config", message: e2.message });
|
|
1936
2203
|
}
|
|
1937
2204
|
const after = new LayeredTokenSet(nextConfig, nextFiles);
|
|
1938
2205
|
const key = (i2) => `${i2.code}|${i2.path ?? ""}|${i2.message}`;
|
|
@@ -1946,7 +2213,7 @@ function planNewBrand(source, input) {
|
|
|
1946
2213
|
const pairs = parseContrastChecks(source.contrastText);
|
|
1947
2214
|
const inGate = nextConfig.axes.brand?.values.includes(slug);
|
|
1948
2215
|
const combos = inGate ? after.combos(nextConfig.contrast?.modes).filter((c10) => c10.brand === slug) : [];
|
|
1949
|
-
const results = combos.flatMap((c10) => pairs.map((
|
|
2216
|
+
const results = combos.flatMap((c10) => pairs.map((p) => checkLayeredPair(after, p, c10)));
|
|
1950
2217
|
const failing = results.filter((r2) => !r2.pass);
|
|
1951
2218
|
const themes = [...new Set(combos.map((c10) => c10.theme).filter((t3) => !!t3))];
|
|
1952
2219
|
const ramps = {};
|
|
@@ -1960,7 +2227,7 @@ function planNewBrand(source, input) {
|
|
|
1960
2227
|
if (JSON.stringify(r2) !== JSON.stringify(ramps.primary)) ramps[role] = r2;
|
|
1961
2228
|
}
|
|
1962
2229
|
}
|
|
1963
|
-
const addedTokens = after.defs.filter((
|
|
2230
|
+
const addedTokens = after.defs.filter((d2) => created.includes(d2.file)).map((d2) => ({ kind: "added", path: d2.path }));
|
|
1964
2231
|
const risk = classifyRisk({ tokenChanges: addedTokens, files: [{ path: CONFIG_FILE, status: "modified" }], config });
|
|
1965
2232
|
const planned = [
|
|
1966
2233
|
...created.map((f) => ({ path: tokensPath(config, f), action: "create", content: nextFiles.get(f) })),
|
|
@@ -2112,21 +2379,21 @@ function cssModuleClasses(css) {
|
|
|
2112
2379
|
const cell$1 = (s) => s.replace(/\|/g, "\\|").replace(/\n/g, " ");
|
|
2113
2380
|
function propsTable(name, props, inherits) {
|
|
2114
2381
|
if (!props.length) return inherits ? `${name} has no props of its own. It accepts every native <${inherits}> attribute.` : "";
|
|
2115
|
-
const rows = props.map((
|
|
2382
|
+
const rows = props.map((p) => `| \`${p.name}\`${p.required ? " (required)" : ""} | \`${cell$1(p.type)}\` | ${p.defaultValue ? `\`${cell$1(p.defaultValue)}\`` : ""} | ${cell$1(p.description ?? "")} |`);
|
|
2116
2383
|
const tail = inherits ? `
|
|
2117
2384
|
|
|
2118
2385
|
Also accepts every native <${inherits}> attribute.` : "";
|
|
2119
2386
|
return ["| Prop | Type | Default | Description |", "| --- | --- | --- | --- |", ...rows].join("\n") + tail;
|
|
2120
2387
|
}
|
|
2121
2388
|
const isEmpty = (v2) => v2 === void 0 || v2 === null || typeof v2 === "string" && !v2.trim() || Array.isArray(v2) && v2.length === 0;
|
|
2122
|
-
function missingProse(
|
|
2123
|
-
return PROSE_KEYS.filter((k3) => isEmpty(
|
|
2389
|
+
function missingProse(d2) {
|
|
2390
|
+
return PROSE_KEYS.filter((k3) => isEmpty(d2?.[k3]));
|
|
2124
2391
|
}
|
|
2125
2392
|
function prefillDocs(input) {
|
|
2126
2393
|
const existing = input.existing ?? null;
|
|
2127
2394
|
const anatomy = {};
|
|
2128
2395
|
for (const c10 of input.cssClasses) anatomy[c10] = existing?.anatomy?.[c10] ?? anatomyLabel(c10);
|
|
2129
|
-
const contrastNotes = (input.contrast ?? []).slice().sort((a, b) => a.ratio - b.ratio).map((
|
|
2396
|
+
const contrastNotes = (input.contrast ?? []).slice().sort((a, b) => a.ratio - b.ratio).map((p) => `${p.foreground} on ${p.background}: ${p.ratio.toFixed(2)}:1 at worst over every brand and theme, needs ${p.min}:1 (${p.ratio >= p.min ? "passes" : "fails"}).`);
|
|
2130
2397
|
const missing = missingProse(existing);
|
|
2131
2398
|
const changelogLine = input.change ? `${input.name}: ${input.change.replace(/\.$/, "")}.` : missing.length === PROSE_KEYS.length ? `${input.name}: usage guidance added.` : `${input.name}: usage guidance updated.`;
|
|
2132
2399
|
const draft = {
|
|
@@ -2194,7 +2461,7 @@ ${text.trim()}
|
|
|
2194
2461
|
] : [],
|
|
2195
2462
|
...options.rewrite ? ["Rewrite each one from the facts and the source: keep what is true in the current text, drop what the code does not support."] : []
|
|
2196
2463
|
] : [`Write the missing prose of ${prefill.component}.docs.json for the Bridge design system.`];
|
|
2197
|
-
const
|
|
2464
|
+
const shape2 = keys ? [
|
|
2198
2465
|
`- ${check.length ? "Return" : "Write"} only these keys: ${keys.join(", ")}${check.length ? ` (the checked ones, ${check.join(", ")}, only when they need a fix)` : ""}. Never return anatomy, preview or any other key: code decides them.`,
|
|
2199
2466
|
`- Answer with one JSON object holding only those keys, nothing else: ${docsSectionShapes(keys)}.`
|
|
2200
2467
|
] : [
|
|
@@ -2208,7 +2475,7 @@ ${text.trim()}
|
|
|
2208
2475
|
"- Ground every sentence in the facts and the source below. Never invent props, variants, keyboard behavior or tokens that are not there.",
|
|
2209
2476
|
"- Bridge voice: sentence case, plain words, verbs first, no em dashes, no marketing words.",
|
|
2210
2477
|
'- Keep the keys and shapes of the example docs.json. dos and donts are { "title", "text" } objects; add an "example" only with components and props that exist.',
|
|
2211
|
-
...
|
|
2478
|
+
...shape2,
|
|
2212
2479
|
"",
|
|
2213
2480
|
"Facts computed from the code:",
|
|
2214
2481
|
facts.join("\n\n"),
|
|
@@ -2223,29 +2490,29 @@ ${JSON.stringify(prefill.draft, null, 2)}`,
|
|
|
2223
2490
|
block("example-docs-json", conventions.example)
|
|
2224
2491
|
].filter((x, i2, all) => x !== "" || all[i2 - 1] !== "").join("\n");
|
|
2225
2492
|
}
|
|
2226
|
-
function markClaudeDraft(
|
|
2493
|
+
function markClaudeDraft(d2, sections) {
|
|
2227
2494
|
if (!sections) {
|
|
2228
|
-
const { draftedSections: _all, ...rest } =
|
|
2495
|
+
const { draftedSections: _all, ...rest } = d2;
|
|
2229
2496
|
return { ...rest, draftedBy: "claude", needsReview: true };
|
|
2230
2497
|
}
|
|
2231
|
-
const waiting =
|
|
2498
|
+
const waiting = d2.needsReview === true ? d2.draftedSections ?? (d2.draftedBy ? [...PROSE_KEYS] : []) : [];
|
|
2232
2499
|
const union = PROSE_KEYS.filter((k3) => waiting.includes(k3) || sections.includes(k3));
|
|
2233
|
-
return { ...
|
|
2500
|
+
return { ...d2, draftedBy: "claude", needsReview: true, draftedSections: union };
|
|
2234
2501
|
}
|
|
2235
|
-
function sectionsNeedingReview(
|
|
2236
|
-
if (!
|
|
2237
|
-
return
|
|
2502
|
+
function sectionsNeedingReview(d2) {
|
|
2503
|
+
if (!d2 || d2.needsReview !== true) return [];
|
|
2504
|
+
return d2.draftedSections ? PROSE_KEYS.filter((k3) => d2.draftedSections.includes(k3)) : [...PROSE_KEYS];
|
|
2238
2505
|
}
|
|
2239
|
-
const needsDocsReview = (
|
|
2240
|
-
function acceptDocsDraft(
|
|
2241
|
-
const { draftedSections: _done, ...rest } =
|
|
2506
|
+
const needsDocsReview = (d2) => Boolean(d2 && d2.needsReview === true);
|
|
2507
|
+
function acceptDocsDraft(d2) {
|
|
2508
|
+
const { draftedSections: _done, ...rest } = d2;
|
|
2242
2509
|
return { ...rest, needsReview: false };
|
|
2243
2510
|
}
|
|
2244
|
-
function formatDocsJson(
|
|
2511
|
+
function formatDocsJson(d2) {
|
|
2245
2512
|
const order = ["summary", "category", "status", ...DOCS_KEYS.filter((k3) => k3 !== "summary"), "draftedBy", "draftedSections", "needsReview"];
|
|
2246
2513
|
const out = {};
|
|
2247
|
-
for (const k3 of order) if (
|
|
2248
|
-
for (const [k3, v2] of Object.entries(
|
|
2514
|
+
for (const k3 of order) if (d2[k3] !== void 0) out[k3] = d2[k3];
|
|
2515
|
+
for (const [k3, v2] of Object.entries(d2)) if (!(k3 in out) && v2 !== void 0) out[k3] = v2;
|
|
2249
2516
|
return `${JSON.stringify(out, null, 2)}
|
|
2250
2517
|
`;
|
|
2251
2518
|
}
|
|
@@ -2253,7 +2520,7 @@ function patchNode(old, next) {
|
|
|
2253
2520
|
if (plainEqual(toPlain(old), next)) return old;
|
|
2254
2521
|
if (old.kind === "object" && next && typeof next === "object" && !Array.isArray(next)) {
|
|
2255
2522
|
const n2 = next;
|
|
2256
|
-
const kept = old.entries.filter((
|
|
2523
|
+
const kept = old.entries.filter((e2) => n2[e2.key] !== void 0).map((e2) => ({ key: e2.key, value: patchNode(e2.value, n2[e2.key]) }));
|
|
2257
2524
|
const added = Object.keys(n2).filter((k3) => n2[k3] !== void 0 && !getEntry(old, k3)).map((k3) => ({ key: k3, value: fromPlain(n2[k3], void 0, old.inline) }));
|
|
2258
2525
|
return { kind: "object", inline: old.inline, entries: [...kept, ...added] };
|
|
2259
2526
|
}
|
|
@@ -2293,17 +2560,17 @@ function propShape(s) {
|
|
|
2293
2560
|
}
|
|
2294
2561
|
const VARIANT_FIRST = ["variant", "tone", "intent", "appearance", "kind", "type", "style"];
|
|
2295
2562
|
function shortVariant(key) {
|
|
2296
|
-
const pairs = key.split(", ").map((
|
|
2297
|
-
const at2 =
|
|
2298
|
-
return at2 < 0 ? { k: "", v:
|
|
2563
|
+
const pairs = key.split(", ").map((p) => {
|
|
2564
|
+
const at2 = p.indexOf("=");
|
|
2565
|
+
return at2 < 0 ? { k: "", v: p.trim() } : { k: p.slice(0, at2).trim(), v: p.slice(at2 + 1).trim() };
|
|
2299
2566
|
});
|
|
2300
|
-
const kept = pairs.filter((
|
|
2567
|
+
const kept = pairs.filter((p) => p.v && !/^default$/i.test(p.v));
|
|
2301
2568
|
if (!kept.length) return key;
|
|
2302
2569
|
const rank = (k3) => {
|
|
2303
2570
|
const i2 = VARIANT_FIRST.indexOf(k3.toLowerCase());
|
|
2304
2571
|
return i2 < 0 ? VARIANT_FIRST.length : i2;
|
|
2305
2572
|
};
|
|
2306
|
-
return kept.map((
|
|
2573
|
+
return kept.map((p, i2) => ({ ...p, i: i2 })).sort((a, b) => rank(a.k) - rank(b.k) || a.i - b.i).map((p) => p.v).join(" ");
|
|
2307
2574
|
}
|
|
2308
2575
|
const shortVariants = (list) => list ? andList(list.split("; ").map(shortVariant)) : "every variant";
|
|
2309
2576
|
function parseStructureChange(c10) {
|
|
@@ -2312,8 +2579,8 @@ function parseStructureChange(c10) {
|
|
|
2312
2579
|
const op = m2[1] === "New" ? "added" : m2[1] === "Removed" ? "removed" : "moved";
|
|
2313
2580
|
const where = `in ${shortVariants(m2[3])}`;
|
|
2314
2581
|
if (op === "moved") return { op, name: m2[2], where, at: c10.after, from: c10.before };
|
|
2315
|
-
const
|
|
2316
|
-
const sm = /^(\S+)(?: (.*?))?, ((?:after|before) ".*"|alone)$/.exec(
|
|
2582
|
+
const shape2 = (op === "added" ? c10.after : c10.before) ?? "";
|
|
2583
|
+
const sm = /^(\S+)(?: (.*?))?, ((?:after|before) ".*"|alone)$/.exec(shape2);
|
|
2317
2584
|
return { op, name: m2[2], where, ...sm ? { type: sm[1], ...sm[2] ? { main: sm[2] } : {}, at: sm[3] } : {} };
|
|
2318
2585
|
}
|
|
2319
2586
|
function structurePhrase(r2) {
|
|
@@ -2397,10 +2664,10 @@ function firstChange(component, kind, c10, all) {
|
|
|
2397
2664
|
function draftRequestText(component, kind, changes) {
|
|
2398
2665
|
const name = component.trim() || "the component";
|
|
2399
2666
|
if (!changes.length && kind !== "new-component") return { title: "", summary: "" };
|
|
2400
|
-
const
|
|
2667
|
+
const d2 = firstChange(name, kind, changes[0], changes);
|
|
2401
2668
|
const rest = kind === "new-component" ? 0 : changes.length - 1;
|
|
2402
|
-
const summary = rest > 0 ? `${
|
|
2403
|
-
return { title: cleanDraftText(
|
|
2669
|
+
const summary = rest > 0 ? `${d2.summary} ${capital(plural(rest, "other change"))} ${rest === 1 ? "is" : "are"} listed below.` : d2.summary;
|
|
2670
|
+
return { title: cleanDraftText(d2.title), summary: cleanDraftText(summary) };
|
|
2404
2671
|
}
|
|
2405
2672
|
const DRAFT_LINE_MAX = 120;
|
|
2406
2673
|
function clipText(text, max) {
|
|
@@ -2461,32 +2728,32 @@ function draftChangeLine(kind, changes) {
|
|
|
2461
2728
|
const tail = rest ? ` (and ${rest} more)` : "";
|
|
2462
2729
|
return capital(clipText(cleanDraftText(first), DRAFT_LINE_MAX - tail.length)) + tail;
|
|
2463
2730
|
}
|
|
2464
|
-
const tokenOf = (
|
|
2465
|
-
const pathOf = (
|
|
2731
|
+
const tokenOf = (e2) => e2.figma ?? e2.head ?? e2.base;
|
|
2732
|
+
const pathOf = (e2) => tokenOf(e2)?.path ?? e2.path;
|
|
2466
2733
|
function pushChangeRows(entries) {
|
|
2467
|
-
return entries.map((
|
|
2468
|
-
const before =
|
|
2469
|
-
const after =
|
|
2734
|
+
return entries.map((e2) => {
|
|
2735
|
+
const before = e2.head ?? e2.base;
|
|
2736
|
+
const after = e2.figma;
|
|
2470
2737
|
const show = (t3) => {
|
|
2471
2738
|
if (!t3) return void 0;
|
|
2472
2739
|
const modes = Object.keys(t3.values);
|
|
2473
2740
|
if (modes.length <= 1) return formatValue(t3.type, t3.values[modes[0] ?? ""]);
|
|
2474
2741
|
return modes.map((m2) => `${modeDisplayName(m2)} ${formatValue(t3.type, t3.values[m2])}`).join("; ");
|
|
2475
2742
|
};
|
|
2476
|
-
const renamed =
|
|
2477
|
-
return { field: renamed ? `${before.path} -> ${after.path}` : pathOf(
|
|
2743
|
+
const renamed = e2.local?.kind === "renamed" && before && after && before.path !== after.path;
|
|
2744
|
+
return { field: renamed ? `${before.path} -> ${after.path}` : pathOf(e2), before: show(before), after: show(after) };
|
|
2478
2745
|
});
|
|
2479
2746
|
}
|
|
2480
2747
|
function names(paths) {
|
|
2481
2748
|
if (paths.length <= 3) return andList(paths);
|
|
2482
2749
|
return `${paths.length} tokens, including ${andList(paths.slice(0, 2))}`;
|
|
2483
2750
|
}
|
|
2484
|
-
function valueModes(
|
|
2485
|
-
return (
|
|
2751
|
+
function valueModes(e2) {
|
|
2752
|
+
return (e2.local?.fields ?? []).filter((f) => f.field === "value" && f.mode && f.mode !== "value").map((f) => modeDisplayName(f.mode));
|
|
2486
2753
|
}
|
|
2487
2754
|
function draftPushDescription(entries, components = []) {
|
|
2488
2755
|
const by2 = { changed: [], added: [], renamed: [], removed: [] };
|
|
2489
|
-
for (const
|
|
2756
|
+
for (const e2 of entries) by2[e2.local?.kind ?? "changed"].push(e2);
|
|
2490
2757
|
const clauses = [];
|
|
2491
2758
|
if (by2.changed.length) {
|
|
2492
2759
|
const only = by2.changed.length === 1 ? by2.changed[0] : void 0;
|
|
@@ -2718,48 +2985,48 @@ function explorationSlugFromBranch(ref) {
|
|
|
2718
2985
|
return isExplorationBranch(ref) ? ref.replace(/^refs\/heads\//, "").slice(EXPLORATION_PREFIX.length) : null;
|
|
2719
2986
|
}
|
|
2720
2987
|
const explorationNotePath = (slug) => `${EXPLORATION_NOTES_DIR}/${slug}.md`;
|
|
2721
|
-
function explorationProblems(
|
|
2988
|
+
function explorationProblems(e2) {
|
|
2722
2989
|
const out = [];
|
|
2723
|
-
const name =
|
|
2724
|
-
const slug =
|
|
2990
|
+
const name = e2.name.trim();
|
|
2991
|
+
const slug = e2.slug?.trim() || explorationSlug(name);
|
|
2725
2992
|
if (!name) out.push('Name the exploration, for example "Denser data tables".');
|
|
2726
2993
|
else if (name.length > 60) out.push("Keep the name under 60 characters: it becomes the branch name.");
|
|
2727
2994
|
else if (slug.length < 2 || !SLUG_RE.test(slug)) out.push("Use letters or digits in the name: it becomes the branch explore/<name>.");
|
|
2728
|
-
if (!
|
|
2995
|
+
if (!e2.purpose.trim()) out.push("Say what you want to learn, in one sentence.");
|
|
2729
2996
|
return out;
|
|
2730
2997
|
}
|
|
2731
2998
|
const useCaseLabel = (u2) => EXPLORATION_USE_CASES.find((x) => x.value === u2)?.label;
|
|
2732
|
-
function explorationNote(
|
|
2733
|
-
const slug =
|
|
2734
|
-
const uc = useCaseLabel(
|
|
2999
|
+
function explorationNote(e2) {
|
|
3000
|
+
const slug = e2.slug || explorationSlug(e2.name);
|
|
3001
|
+
const uc = useCaseLabel(e2.useCase);
|
|
2735
3002
|
return [
|
|
2736
|
-
`# Exploration: ${
|
|
3003
|
+
`# Exploration: ${e2.name.trim()}`,
|
|
2737
3004
|
"",
|
|
2738
|
-
|
|
3005
|
+
e2.purpose.trim(),
|
|
2739
3006
|
"",
|
|
2740
3007
|
`- Branch: \`${EXPLORATION_PREFIX}${slug}\``,
|
|
2741
3008
|
...uc ? [`- Use case: ${uc}`] : [],
|
|
2742
|
-
...
|
|
3009
|
+
...e2.startedAt ? [`- Started: ${e2.startedAt.slice(0, 10)}${e2.author ? ` by @${e2.author}` : ""}`] : [],
|
|
2743
3010
|
"",
|
|
2744
3011
|
"This file marks the branch as an exploration. It is never merged as is: promoting the pull request to review removes it.",
|
|
2745
3012
|
""
|
|
2746
3013
|
].join("\n");
|
|
2747
3014
|
}
|
|
2748
|
-
function explorationPrTitle(
|
|
2749
|
-
const n2 =
|
|
3015
|
+
function explorationPrTitle(e2) {
|
|
3016
|
+
const n2 = e2.name.trim().replace(/\.$/, "");
|
|
2750
3017
|
return `Exploration: ${n2.charAt(0).toLowerCase()}${n2.slice(1)}`;
|
|
2751
3018
|
}
|
|
2752
|
-
function explorationPrBody(
|
|
2753
|
-
const slug =
|
|
3019
|
+
function explorationPrBody(e2, ctx) {
|
|
3020
|
+
const slug = e2.slug || explorationSlug(e2.name);
|
|
2754
3021
|
const branch = `${EXPLORATION_PREFIX}${slug}`;
|
|
2755
|
-
const uc = useCaseLabel(
|
|
2756
|
-
const experimental = (
|
|
3022
|
+
const uc = useCaseLabel(e2.useCase);
|
|
3023
|
+
const experimental = (e2.experimental ?? []).map((x) => x.trim()).filter(Boolean);
|
|
2757
3024
|
const lines = [
|
|
2758
|
-
`## Exploration: ${
|
|
3025
|
+
`## Exploration: ${e2.name.trim()}`,
|
|
2759
3026
|
"",
|
|
2760
3027
|
`> Not mergeable as is. This draft pull request is a sandbox on \`${branch}\`: promote it to a normal review when the idea is validated, or close it.`,
|
|
2761
3028
|
"",
|
|
2762
|
-
`**Purpose.** ${
|
|
3029
|
+
`**Purpose.** ${e2.purpose.trim()}`,
|
|
2763
3030
|
...uc ? ["", `**Use case.** ${uc}`] : [],
|
|
2764
3031
|
"",
|
|
2765
3032
|
"### What is experimental",
|
|
@@ -2784,7 +3051,7 @@ function explorationPrBody(e3, ctx) {
|
|
|
2784
3051
|
`- **Promote:** in Bridge Studio (Changes, Explorations, Promote to review) or \`bridge explore promote ${slug}\`. Bridge marks the pull request ready for review, removes the \`${EXPLORATION_LABEL}\` label and the exploration note, and adds the tier labels. Then it goes through the usual review.`,
|
|
2785
3052
|
"- **Close:** close this pull request. The branch stays for reference; the preview prerelease is removed.",
|
|
2786
3053
|
"",
|
|
2787
|
-
`<!-- ${MARK$1} ${JSON.stringify({ name:
|
|
3054
|
+
`<!-- ${MARK$1} ${JSON.stringify({ name: e2.name.trim(), purpose: e2.purpose.trim(), slug, ...e2.useCase ? { useCase: e2.useCase } : {}, ...experimental.length ? { experimental } : {}, ...e2.author ? { author: e2.author } : {}, ...e2.startedAt ? { startedAt: e2.startedAt } : {}, ...e2.source ? { source: e2.source } : {}, ...e2.agent ? { agent: e2.agent } : {} }).replace(/-->/g, "--\\u003e")} -->`
|
|
2788
3055
|
);
|
|
2789
3056
|
return lines.join("\n");
|
|
2790
3057
|
}
|
|
@@ -2793,20 +3060,20 @@ function parseExplorationBody(body) {
|
|
|
2793
3060
|
const m2 = new RegExp(`<!-- ${MARK$1} (\\{[\\s\\S]*?\\}) -->`).exec(body);
|
|
2794
3061
|
if (!m2) return void 0;
|
|
2795
3062
|
try {
|
|
2796
|
-
const
|
|
2797
|
-
return
|
|
3063
|
+
const e2 = JSON.parse(m2[1]);
|
|
3064
|
+
return e2 && typeof e2.name === "string" && typeof e2.purpose === "string" ? e2 : void 0;
|
|
2798
3065
|
} catch {
|
|
2799
3066
|
return void 0;
|
|
2800
3067
|
}
|
|
2801
3068
|
}
|
|
2802
3069
|
const labelNames = (labels) => labels.map((l2) => typeof l2 === "string" ? l2 : l2.name ?? "");
|
|
2803
|
-
function isExplorationPull(
|
|
2804
|
-
return isExplorationBranch(
|
|
3070
|
+
function isExplorationPull(p) {
|
|
3071
|
+
return isExplorationBranch(p.headRef) || labelNames(p.labels).includes(EXPLORATION_LABEL) || Boolean(parseExplorationBody(p.body));
|
|
2805
3072
|
}
|
|
2806
|
-
function explorationStatus(
|
|
2807
|
-
const merged = Boolean(
|
|
2808
|
-
if (
|
|
2809
|
-
if (
|
|
3073
|
+
function explorationStatus(p) {
|
|
3074
|
+
const merged = Boolean(p.merged || p.merged_at || p.state === "merged");
|
|
3075
|
+
if (p.state !== "open" && !merged) return "closed";
|
|
3076
|
+
if (p.state === "open" && labelNames(p.labels).includes(EXPLORATION_LABEL)) return "active";
|
|
2810
3077
|
return "promoted";
|
|
2811
3078
|
}
|
|
2812
3079
|
const isActiveExploration = (labels) => labelNames(labels).includes(EXPLORATION_LABEL);
|
|
@@ -2852,7 +3119,7 @@ function explorationTryIt(input) {
|
|
|
2852
3119
|
function explorationShareMessage(input) {
|
|
2853
3120
|
const name = input.name.trim().replace(/"/g, "'");
|
|
2854
3121
|
const head = input.command ? `Exploration "${name}" (${input.branch}): try it in a staging branch of your product with: ${input.command}.` : `Exploration "${name}" (${input.branch}): its test build is being prepared, the install command for a staging branch of your product will be on the pull request in a few minutes.`;
|
|
2855
|
-
const previews = (input.previews ?? []).map((
|
|
3122
|
+
const previews = (input.previews ?? []).map((p) => `${p.label} ${p.url}`);
|
|
2856
3123
|
return [head, previews.length ? `Previews: ${previews.join(", ")}.` : "", `Pull request: ${input.url}.`].filter(Boolean).join(" ");
|
|
2857
3124
|
}
|
|
2858
3125
|
function explorationRules(branch) {
|
|
@@ -2944,8 +3211,8 @@ function declineComment(reason, by2) {
|
|
|
2944
3211
|
}
|
|
2945
3212
|
function requestStatus(issue) {
|
|
2946
3213
|
const prs = issue.linkedPrs ?? [];
|
|
2947
|
-
if (issue.state === "open") return prs.some((
|
|
2948
|
-
if (prs.some((
|
|
3214
|
+
if (issue.state === "open") return prs.some((p) => p.state === "open") ? "in-progress" : "requested";
|
|
3215
|
+
if (prs.some((p) => p.merged) || issue.stateReason === "completed") return "done";
|
|
2949
3216
|
return "declined";
|
|
2950
3217
|
}
|
|
2951
3218
|
const REQUEST_STATUS_LABEL = { requested: "Requested", "in-progress": "In progress", done: "Done", declined: "Declined" };
|
|
@@ -3052,7 +3319,7 @@ function draftImagePath(sessionId, itemId, label) {
|
|
|
3052
3319
|
return `${DRAFTS_DIR}/${sessionId}/${item}-${label}.png`;
|
|
3053
3320
|
}
|
|
3054
3321
|
function composePendingDrafts(input) {
|
|
3055
|
-
const
|
|
3322
|
+
const p = {
|
|
3056
3323
|
version: DRAFT_EXCHANGE_VERSION,
|
|
3057
3324
|
sessionId: input.sessionId ?? newDraftSessionId(input.random),
|
|
3058
3325
|
createdAt: (input.now ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -3060,9 +3327,9 @@ function composePendingDrafts(input) {
|
|
|
3060
3327
|
source: input.source ?? "figma",
|
|
3061
3328
|
items: input.items.map((i2) => ({ ...i2, changes: i2.changes ?? [] }))
|
|
3062
3329
|
};
|
|
3063
|
-
const problems = validatePendingDrafts(
|
|
3330
|
+
const problems = validatePendingDrafts(p);
|
|
3064
3331
|
if (problems.length) throw new Error(problems.join(" "));
|
|
3065
|
-
return
|
|
3332
|
+
return p;
|
|
3066
3333
|
}
|
|
3067
3334
|
const KINDS = new Set(REQUEST_KINDS.map((k3) => k3.value));
|
|
3068
3335
|
const str = (x) => typeof x === "string";
|
|
@@ -3070,15 +3337,15 @@ const optStr = (x) => x === void 0 || typeof x === "string";
|
|
|
3070
3337
|
function validatePendingDrafts(x) {
|
|
3071
3338
|
const out = [];
|
|
3072
3339
|
if (!x || typeof x !== "object" || Array.isArray(x)) return ["A draft session is a JSON object."];
|
|
3073
|
-
const
|
|
3074
|
-
if (
|
|
3075
|
-
if (!isDraftSessionId(
|
|
3076
|
-
if (!str(
|
|
3077
|
-
if (
|
|
3078
|
-
if (
|
|
3079
|
-
if (!Array.isArray(
|
|
3340
|
+
const p = x;
|
|
3341
|
+
if (p.version !== DRAFT_EXCHANGE_VERSION) out.push(`Unsupported draft session version ${String(p.version)}: update Bridge.`);
|
|
3342
|
+
if (!isDraftSessionId(p.sessionId)) out.push("The session id is missing or invalid.");
|
|
3343
|
+
if (!str(p.createdAt) || Number.isNaN(Date.parse(p.createdAt))) out.push("createdAt must be an ISO date.");
|
|
3344
|
+
if (p.status !== "pending" && p.status !== "drafted") out.push('status must be "pending" or "drafted".');
|
|
3345
|
+
if (p.source !== "figma" && p.source !== "studio") out.push('source must be "figma" or "studio".');
|
|
3346
|
+
if (!Array.isArray(p.items) || !p.items.length) out.push("A draft session needs at least one item.");
|
|
3080
3347
|
const ids = /* @__PURE__ */ new Set();
|
|
3081
|
-
for (const [n2, i2] of (Array.isArray(
|
|
3348
|
+
for (const [n2, i2] of (Array.isArray(p.items) ? p.items : []).entries()) {
|
|
3082
3349
|
const at2 = `Item ${n2 + 1}`;
|
|
3083
3350
|
if (!i2 || typeof i2 !== "object") {
|
|
3084
3351
|
out.push(`${at2} is not an object.`);
|
|
@@ -3089,9 +3356,9 @@ function validatePendingDrafts(x) {
|
|
|
3089
3356
|
else ids.add(i2.id);
|
|
3090
3357
|
if (i2.scope !== "request" && i2.scope !== "push" && i2.scope !== "docs") out.push(`${at2}: scope must be "request", "push" or "docs".`);
|
|
3091
3358
|
if (i2.scope === "docs") {
|
|
3092
|
-
const
|
|
3093
|
-
if (!
|
|
3094
|
-
else out.push(...validateDocsTask(
|
|
3359
|
+
const d2 = i2.docs;
|
|
3360
|
+
if (!d2 || typeof d2 !== "object" || !str(d2.slug) || !d2.slug || !str(d2.prompt) || !d2.prompt || !optStr(d2.path)) out.push(`${at2}: a docs item needs docs {slug, task, prompt}.`);
|
|
3361
|
+
else out.push(...validateDocsTask(d2.task).map((m2) => `${at2}: ${m2}`));
|
|
3095
3362
|
} else if (i2.docs !== void 0) out.push(`${at2}: only a docs item has docs.`);
|
|
3096
3363
|
if (!optStr(i2.component)) out.push(`${at2}: component must be text.`);
|
|
3097
3364
|
if (i2.kind !== void 0 && !KINDS.has(i2.kind)) out.push(`${at2}: unknown kind "${String(i2.kind)}".`);
|
|
@@ -3102,10 +3369,10 @@ function validatePendingDrafts(x) {
|
|
|
3102
3369
|
if (!i2.draft || !str(i2.draft.title) || !str(i2.draft.summary)) out.push(`${at2}: draft must hold a title and a summary.`);
|
|
3103
3370
|
if (i2.lines !== void 0 && (!Array.isArray(i2.lines) || i2.lines.some((l2) => !l2 || !str(l2.id) || !l2.id || !str(l2.line) || !optStr(l2.title) || !Array.isArray(l2.changes)))) out.push(`${at2}: lines must be a list of {id, changes, line, title}.`);
|
|
3104
3371
|
}
|
|
3105
|
-
if (
|
|
3106
|
-
if (!Array.isArray(
|
|
3372
|
+
if (p.results !== void 0) {
|
|
3373
|
+
if (!Array.isArray(p.results)) out.push("results must be a list.");
|
|
3107
3374
|
else
|
|
3108
|
-
for (const r2 of
|
|
3375
|
+
for (const r2 of p.results) {
|
|
3109
3376
|
if (!r2 || !str(r2.id) || !str(r2.title) || !str(r2.summary)) out.push("Each result needs an id, a title and a summary.");
|
|
3110
3377
|
else if (r2.docs !== void 0 && (!r2.docs || typeof r2.docs !== "object" || Array.isArray(r2.docs))) out.push("Result docs must be an object of sections.");
|
|
3111
3378
|
else if (!optStr(r2.replacement)) out.push("A result replacement must be text.");
|
|
@@ -3124,8 +3391,8 @@ function parsePendingDrafts(text) {
|
|
|
3124
3391
|
}
|
|
3125
3392
|
return validatePendingDrafts(json).length ? void 0 : json;
|
|
3126
3393
|
}
|
|
3127
|
-
function serializePendingDrafts(
|
|
3128
|
-
return `${JSON.stringify(
|
|
3394
|
+
function serializePendingDrafts(p) {
|
|
3395
|
+
return `${JSON.stringify(p, null, 2)}
|
|
3129
3396
|
`;
|
|
3130
3397
|
}
|
|
3131
3398
|
function clip(text, max) {
|
|
@@ -3181,12 +3448,12 @@ function sanitizeDraftTitle(raw) {
|
|
|
3181
3448
|
const t3 = clip(cleanDraftText(raw.replace(/[\r\n]+/g, " ")), DRAFT_TITLE_MAX);
|
|
3182
3449
|
return t3.charAt(0).toUpperCase() + t3.slice(1);
|
|
3183
3450
|
}
|
|
3184
|
-
function mergeDraftResults(
|
|
3185
|
-
const ids = new Set(
|
|
3186
|
-
const next = new Map((
|
|
3451
|
+
function mergeDraftResults(p, results, at2 = /* @__PURE__ */ new Date()) {
|
|
3452
|
+
const ids = new Set(p.items.map((i2) => i2.id));
|
|
3453
|
+
const next = new Map((p.results ?? []).map((r2) => [r2.id, r2]));
|
|
3187
3454
|
for (const r2 of results) {
|
|
3188
|
-
if (!ids.has(r2.id)) throw new Error(`Session ${
|
|
3189
|
-
const item =
|
|
3455
|
+
if (!ids.has(r2.id)) throw new Error(`Session ${p.sessionId} has no item "${r2.id}". Use the ids from bridge_pending_drafts: ${[...ids].join(", ")}.`);
|
|
3456
|
+
const item = p.items.find((i2) => i2.id === r2.id);
|
|
3190
3457
|
if (item.scope === "docs") {
|
|
3191
3458
|
next.set(r2.id, { id: r2.id, title: "", summary: "", ...sanitizeDocsResult(item, r2), by: "claude", at: at2.toISOString() });
|
|
3192
3459
|
continue;
|
|
@@ -3196,8 +3463,8 @@ function mergeDraftResults(p2, results, at2 = /* @__PURE__ */ new Date()) {
|
|
|
3196
3463
|
if (!clean.title && !clean.summary && !lines.length) throw new Error(`The result for "${r2.id}" has no title and no summary, and no line.`);
|
|
3197
3464
|
next.set(r2.id, { id: r2.id, title: clean.title, summary: clean.summary, ...lines.length ? { lines } : {}, by: "claude", at: at2.toISOString() });
|
|
3198
3465
|
}
|
|
3199
|
-
const merged =
|
|
3200
|
-
return { ...
|
|
3466
|
+
const merged = p.items.map((i2) => next.get(i2.id)).filter((r2) => !!r2);
|
|
3467
|
+
return { ...p, results: merged, status: merged.length === p.items.length ? "drafted" : "pending" };
|
|
3201
3468
|
}
|
|
3202
3469
|
function draftItemBrief(i2) {
|
|
3203
3470
|
if (i2.scope === "docs" && i2.docs) {
|
|
@@ -3232,11 +3499,11 @@ function draftItemBrief(i2) {
|
|
|
3232
3499
|
return lines.join("\n");
|
|
3233
3500
|
}
|
|
3234
3501
|
const isConflict = (err) => err instanceof GitHubError ? err.status === 409 || err.status === 422 && /sha/i.test(`${err.detail ?? ""} ${err.message}`) : err?.status === 409;
|
|
3235
|
-
async function createPendingDrafts(client,
|
|
3236
|
-
const problems = validatePendingDrafts(
|
|
3502
|
+
async function createPendingDrafts(client, p, branch = REQUESTS_BRANCH) {
|
|
3503
|
+
const problems = validatePendingDrafts(p);
|
|
3237
3504
|
if (problems.length) throw new Error(problems.join(" "));
|
|
3238
|
-
await client.putFile({ path: draftSessionPath(
|
|
3239
|
-
return
|
|
3505
|
+
await client.putFile({ path: draftSessionPath(p.sessionId), content: serializePendingDrafts(p), branch, message: `Bridge drafts: ${p.items.length === 1 ? "1 item" : `${p.items.length} items`} waiting for Claude` });
|
|
3506
|
+
return p;
|
|
3240
3507
|
}
|
|
3241
3508
|
async function readPendingDrafts(client, sessionId, branch = REQUESTS_BRANCH) {
|
|
3242
3509
|
const f = await client.getFile(draftSessionPath(sessionId), branch);
|
|
@@ -3245,16 +3512,16 @@ async function readPendingDrafts(client, sessionId, branch = REQUESTS_BRANCH) {
|
|
|
3245
3512
|
}
|
|
3246
3513
|
async function listPendingDrafts(client, opts = {}) {
|
|
3247
3514
|
const branch = opts.branch ?? REQUESTS_BRANCH;
|
|
3248
|
-
const entries = (await client.listDirectory(DRAFTS_DIR, branch)).filter((
|
|
3515
|
+
const entries = (await client.listDirectory(DRAFTS_DIR, branch)).filter((e2) => e2.type === "file" && /\.json$/.test(e2.name));
|
|
3249
3516
|
const out = [];
|
|
3250
|
-
for (const
|
|
3251
|
-
const id =
|
|
3517
|
+
for (const e2 of entries) {
|
|
3518
|
+
const id = e2.name.replace(/\.json$/, "");
|
|
3252
3519
|
if (!isDraftSessionId(id)) continue;
|
|
3253
3520
|
const r2 = await readPendingDrafts(client, id, branch).catch(() => void 0);
|
|
3254
3521
|
if (r2 && r2.drafts.sessionId === id) out.push(r2.drafts);
|
|
3255
3522
|
}
|
|
3256
3523
|
const status = opts.status ?? "all";
|
|
3257
|
-
return out.filter((
|
|
3524
|
+
return out.filter((p) => status === "all" || p.status === status).sort((a, b) => b.createdAt.localeCompare(a.createdAt));
|
|
3258
3525
|
}
|
|
3259
3526
|
async function updatePendingDrafts(client, sessionId, change, opts = {}) {
|
|
3260
3527
|
const branch = opts.branch ?? REQUESTS_BRANCH;
|
|
@@ -3272,7 +3539,7 @@ async function updatePendingDrafts(client, sessionId, change, opts = {}) {
|
|
|
3272
3539
|
}
|
|
3273
3540
|
}
|
|
3274
3541
|
function writeDraftResults(client, sessionId, results, opts = {}) {
|
|
3275
|
-
return updatePendingDrafts(client, sessionId, (
|
|
3542
|
+
return updatePendingDrafts(client, sessionId, (p) => mergeDraftResults(p, results, opts.now), {
|
|
3276
3543
|
...opts,
|
|
3277
3544
|
message: `Bridge drafts: ${results.length === 1 ? "1 text" : `${results.length} texts`} drafted by Claude`
|
|
3278
3545
|
});
|
|
@@ -3284,7 +3551,7 @@ async function deletePendingDrafts(client, sessionId, opts = {}) {
|
|
|
3284
3551
|
if (!f) return false;
|
|
3285
3552
|
await client.deleteFile({ path, branch, message: "Bridge drafts: done", sha: f.sha });
|
|
3286
3553
|
const images = await client.listDirectory(`${DRAFTS_DIR}/${sessionId}`, branch).catch(() => []);
|
|
3287
|
-
for (const img of images.filter((
|
|
3554
|
+
for (const img of images.filter((e2) => e2.type === "file")) {
|
|
3288
3555
|
await client.deleteFile({ path: img.path, branch, message: "Bridge drafts: done", sha: img.sha }).catch(() => void 0);
|
|
3289
3556
|
}
|
|
3290
3557
|
return true;
|
|
@@ -3378,38 +3645,40 @@ class DraftPoller {
|
|
|
3378
3645
|
}
|
|
3379
3646
|
}
|
|
3380
3647
|
}
|
|
3381
|
-
function explorationSummary(
|
|
3382
|
-
const
|
|
3383
|
-
const slug =
|
|
3648
|
+
function explorationSummary(p, _owner, _repo, s) {
|
|
3649
|
+
const e2 = parseExplorationBody(p.body);
|
|
3650
|
+
const slug = p.headRef?.startsWith(EXPLORATION_PREFIX) ? p.headRef.slice(EXPLORATION_PREFIX.length) : e2?.slug ?? explorationSlug(p.title);
|
|
3384
3651
|
return {
|
|
3385
|
-
number:
|
|
3386
|
-
url:
|
|
3387
|
-
branch:
|
|
3652
|
+
number: p.number,
|
|
3653
|
+
url: p.url,
|
|
3654
|
+
branch: p.headRef,
|
|
3388
3655
|
slug,
|
|
3389
|
-
name:
|
|
3390
|
-
purpose:
|
|
3391
|
-
...
|
|
3392
|
-
status: explorationStatus({ state:
|
|
3393
|
-
draft:
|
|
3394
|
-
author:
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3656
|
+
name: e2?.name ?? p.title.replace(/^Exploration:\s*/i, ""),
|
|
3657
|
+
purpose: e2?.purpose ?? "",
|
|
3658
|
+
...e2?.useCase ? { useCase: e2.useCase } : {},
|
|
3659
|
+
status: explorationStatus({ state: p.state, labels: p.labels, headRef: p.headRef, body: p.body }),
|
|
3660
|
+
draft: p.draft,
|
|
3661
|
+
author: e2?.author ?? p.author,
|
|
3662
|
+
...e2?.source ? { source: e2.source } : {},
|
|
3663
|
+
...e2?.agent ? { agent: e2.agent } : {},
|
|
3664
|
+
updatedAt: p.updatedAt,
|
|
3665
|
+
headSha: p.headSha,
|
|
3666
|
+
previews: previewLinks(s.apps, { branch: p.headRef, pr: p.number, sha: p.headSha })
|
|
3398
3667
|
};
|
|
3399
3668
|
}
|
|
3400
3669
|
async function listExplorationsOnGitHub(gh, s, opts = {}) {
|
|
3401
3670
|
const pulls = await gh.listPullRequests(opts.state ?? "open");
|
|
3402
|
-
return pulls.filter((
|
|
3671
|
+
return pulls.filter((p) => isExplorationPull({ state: p.state, labels: p.labels, headRef: p.headRef, body: p.body })).map((p) => explorationSummary(p, gh.owner, gh.name, s));
|
|
3403
3672
|
}
|
|
3404
|
-
async function startExplorationOnGitHub(gh,
|
|
3405
|
-
const problems = explorationProblems(
|
|
3673
|
+
async function startExplorationOnGitHub(gh, e2, s) {
|
|
3674
|
+
const problems = explorationProblems(e2);
|
|
3406
3675
|
if (problems.length) throw new GitHubError(422, problems.join(" "));
|
|
3407
3676
|
if (!gh.canWrite) throw new GitHubError(401, "Starting an exploration needs a GitHub token (contents and pull requests, read and write, on this repository only).");
|
|
3408
|
-
const slug =
|
|
3677
|
+
const slug = e2.slug || explorationSlug(e2.name);
|
|
3409
3678
|
const branch = `${EXPLORATION_PREFIX}${slug}`;
|
|
3410
3679
|
const x = gh.withBranchPrefix(EXPLORATION_PREFIX);
|
|
3411
3680
|
if (await x.getBranchSha(branch)) throw new GitHubError(422, `An exploration named "${slug}" already exists (${branch}). Pick another name, or continue that one.`);
|
|
3412
|
-
const full = { ...
|
|
3681
|
+
const full = { ...e2, slug, startedAt: e2.startedAt ?? (/* @__PURE__ */ new Date()).toISOString() };
|
|
3413
3682
|
const commit = await x.commitToNewBranch({
|
|
3414
3683
|
base: s.base,
|
|
3415
3684
|
branch,
|
|
@@ -3434,21 +3703,21 @@ Exploration: never merged as is. Promote it to review or close it.`,
|
|
|
3434
3703
|
previews: previewLinks(s.apps, { branch, pr: pr.number, sha: commit.sha })
|
|
3435
3704
|
};
|
|
3436
3705
|
}
|
|
3437
|
-
async function explorationTryItOnGitHub(gh,
|
|
3438
|
-
const [comments, head] = await Promise.all([gh.listIssueComments(
|
|
3706
|
+
async function explorationTryItOnGitHub(gh, e2) {
|
|
3707
|
+
const [comments, head] = await Promise.all([gh.listIssueComments(e2.number), gh.getBranchSha(e2.branch).catch(() => void 0)]);
|
|
3439
3708
|
return explorationTryIt({ comment: findPreviewComment(comments)?.body, headSha: head });
|
|
3440
3709
|
}
|
|
3441
|
-
async function explorationTestBuildOnGitHub(gh,
|
|
3442
|
-
const [comments, head] = await Promise.all([gh.listIssueComments(
|
|
3710
|
+
async function explorationTestBuildOnGitHub(gh, e2, config) {
|
|
3711
|
+
const [comments, head] = await Promise.all([gh.listIssueComments(e2.number), gh.getBranchSha(e2.branch).catch(() => void 0)]);
|
|
3443
3712
|
const tryIt = explorationTryIt({ comment: findPreviewComment(comments)?.body, headSha: head });
|
|
3444
|
-
const previews = await branchPreviewUrls(gh,
|
|
3713
|
+
const previews = await branchPreviewUrls(gh, e2.number, config, { branch: e2.branch, sha: head, comments }).catch(() => []);
|
|
3445
3714
|
return { tryIt, previews, settled: tryIt.state !== "building" && previewsSettled(previews) };
|
|
3446
3715
|
}
|
|
3447
3716
|
async function findExplorationOnGitHub(gh, ref) {
|
|
3448
3717
|
if (typeof ref === "number" || /^#?\d+$/.test(String(ref))) {
|
|
3449
|
-
const
|
|
3450
|
-
if (!isExplorationPull({ state:
|
|
3451
|
-
return
|
|
3718
|
+
const p = await gh.getPullRequest(Number(String(ref).replace(/^#/, "")));
|
|
3719
|
+
if (!isExplorationPull({ state: p.state, labels: p.labels, headRef: p.headRef, body: p.body })) throw new GitHubError(422, `Pull request #${p.number} is not an exploration.`);
|
|
3720
|
+
return p;
|
|
3452
3721
|
}
|
|
3453
3722
|
const branch = `${EXPLORATION_PREFIX}${explorationSlug(String(ref))}`;
|
|
3454
3723
|
const open = await gh.findOpenPullRequest(branch);
|
|
@@ -3456,36 +3725,36 @@ async function findExplorationOnGitHub(gh, ref) {
|
|
|
3456
3725
|
return gh.getPullRequest(open.number);
|
|
3457
3726
|
}
|
|
3458
3727
|
async function promoteExplorationOnGitHub(gh, ref, opts = {}) {
|
|
3459
|
-
const
|
|
3460
|
-
const status = explorationStatus({ state:
|
|
3461
|
-
if (status !== "active") throw new GitHubError(422, status === "closed" ? `Exploration #${
|
|
3462
|
-
const files = await gh.listPullRequestFiles(
|
|
3463
|
-
const plan = promotionPlan({ title:
|
|
3464
|
-
const out = { ...plan, number:
|
|
3728
|
+
const p = await findExplorationOnGitHub(gh, ref);
|
|
3729
|
+
const status = explorationStatus({ state: p.state, labels: p.labels, headRef: p.headRef, body: p.body });
|
|
3730
|
+
if (status !== "active") throw new GitHubError(422, status === "closed" ? `Exploration #${p.number} is closed. Reopen it on GitHub first.` : `#${p.number} is already promoted to review.`);
|
|
3731
|
+
const files = await gh.listPullRequestFiles(p.number);
|
|
3732
|
+
const plan = promotionPlan({ title: p.title, body: p.body, labels: p.labels, files: files.map((f) => ({ path: f.filename, status: f.status })), config: opts.config, requests: opts.requests, date: opts.date });
|
|
3733
|
+
const out = { ...plan, number: p.number, url: p.url, branch: p.headRef, warnings: [] };
|
|
3465
3734
|
if (opts.dryRun) return out;
|
|
3466
3735
|
if (!gh.canWrite) throw new GitHubError(401, "Promoting an exploration needs a GitHub token (contents and pull requests, read and write).");
|
|
3467
3736
|
const x = gh.withBranchPrefix(EXPLORATION_PREFIX);
|
|
3468
3737
|
if (plan.removeFiles.length) {
|
|
3469
|
-
await x.commitToBranch({ branch:
|
|
3470
|
-
if (
|
|
3471
|
-
out.warnings.push(`The exploration note stays on the branch (${
|
|
3738
|
+
await x.commitToBranch({ branch: p.headRef, message: "chore(explore): promote to review, remove the exploration note", files: plan.removeFiles.map((path) => ({ path, content: null })) }).catch((e2) => {
|
|
3739
|
+
if (e2 instanceof GitHubError && e2.auth === "rejected") throw e2;
|
|
3740
|
+
out.warnings.push(`The exploration note stays on the branch (${e2.message}). Delete ${plan.removeFiles.join(", ")} before merging.`);
|
|
3472
3741
|
});
|
|
3473
3742
|
}
|
|
3474
|
-
await x.addLabels(
|
|
3475
|
-
await x.removeLabel(
|
|
3476
|
-
if (!(
|
|
3743
|
+
await x.addLabels(p.number, plan.addLabels);
|
|
3744
|
+
await x.removeLabel(p.number, EXPLORATION_LABEL).catch((e2) => {
|
|
3745
|
+
if (!(e2 instanceof GitHubError && e2.status === 404)) out.warnings.push(`The exploration label is still on (${e2.message}). Remove it on GitHub.`);
|
|
3477
3746
|
});
|
|
3478
|
-
await x.updatePullRequest(
|
|
3479
|
-
await x.markReadyForReview(
|
|
3480
|
-
out.warnings.push(`Still a draft: ${
|
|
3747
|
+
await x.updatePullRequest(p.number, { title: plan.title, body: plan.body });
|
|
3748
|
+
await x.markReadyForReview(p.number).catch((e2) => {
|
|
3749
|
+
out.warnings.push(`Still a draft: ${e2.message}`);
|
|
3481
3750
|
});
|
|
3482
3751
|
return out;
|
|
3483
3752
|
}
|
|
3484
3753
|
async function closeExplorationOnGitHub(gh, ref) {
|
|
3485
3754
|
if (!gh.canWrite) throw new GitHubError(401, "Closing an exploration needs a GitHub token (pull requests: write).");
|
|
3486
|
-
const
|
|
3487
|
-
await gh.updatePullRequest(
|
|
3488
|
-
return { number:
|
|
3755
|
+
const p = await findExplorationOnGitHub(gh, ref);
|
|
3756
|
+
await gh.updatePullRequest(p.number, { state: "closed" });
|
|
3757
|
+
return { number: p.number, url: p.url, branch: p.headRef };
|
|
3489
3758
|
}
|
|
3490
3759
|
async function linkRequestToExplorationOnGitHub(gh, n2, branch, opts = {}) {
|
|
3491
3760
|
if (!isExplorationBranch(branch)) throw new GitHubError(422, `"${branch}" is not an exploration branch. Exploration branches are named explore/<name>.`);
|
|
@@ -3519,27 +3788,27 @@ async function declineRequestOnGitHub(gh, n2, reason, by2) {
|
|
|
3519
3788
|
await gh.updateIssue(n2, { state: "closed", state_reason: "not_planned" });
|
|
3520
3789
|
}
|
|
3521
3790
|
export {
|
|
3522
|
-
|
|
3523
|
-
|
|
3791
|
+
ai as AAA_MIN,
|
|
3792
|
+
aj as AA_MIN,
|
|
3524
3793
|
ADMIN_ROLE_ID,
|
|
3525
3794
|
ANTHROPIC_MESSAGES_URL,
|
|
3526
3795
|
BELL,
|
|
3527
3796
|
BRAND_FONTS,
|
|
3528
3797
|
BRAND_SLUG_RE,
|
|
3529
|
-
|
|
3798
|
+
ak as BULK_LIMIT,
|
|
3530
3799
|
k as COLOR_FIELDS,
|
|
3531
3800
|
C as COMPONENT_SPECS_VERSION,
|
|
3532
3801
|
CONFIG_FILE,
|
|
3533
|
-
|
|
3534
|
-
|
|
3802
|
+
al as CONTRAST_CELL_MAX,
|
|
3803
|
+
am as CONTRAST_CHECKS_FILE,
|
|
3535
3804
|
ConfigError,
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3805
|
+
an as DEFAULT_COMPONENT_SPECS_FILE,
|
|
3806
|
+
ao as DEFAULT_GOVERNANCE,
|
|
3807
|
+
ap as DEFAULT_OVERRIDE,
|
|
3539
3808
|
DEFAULT_PROTECTED_BRANCHES,
|
|
3540
|
-
|
|
3809
|
+
aq as DEFAULT_RIGHTS,
|
|
3541
3810
|
DEFAULT_TOKEN_PATHS,
|
|
3542
|
-
|
|
3811
|
+
ar as DEFAULT_VISUAL_IMPACT,
|
|
3543
3812
|
DEMO_RELEASES,
|
|
3544
3813
|
DOCS_CHANGE_BUMP,
|
|
3545
3814
|
DOCS_DRAFT_MODEL,
|
|
@@ -3568,16 +3837,16 @@ export {
|
|
|
3568
3837
|
EXT_SCOPES,
|
|
3569
3838
|
GitHubClient,
|
|
3570
3839
|
GitHubError,
|
|
3571
|
-
|
|
3840
|
+
as as JsonParseError,
|
|
3572
3841
|
LIGHTNESS,
|
|
3573
3842
|
LayeredTokenSet,
|
|
3574
3843
|
NEUTRAL_ANCHORS,
|
|
3575
|
-
|
|
3844
|
+
at as OWNER_GROUPS,
|
|
3576
3845
|
PIN_TOLERANCE,
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3846
|
+
au as PREVIEW_APP_LABELS,
|
|
3847
|
+
av as PREVIEW_COMMENT_MARKER,
|
|
3848
|
+
aw as PREVIEW_PLACEHOLDERS,
|
|
3849
|
+
ax as PRIMITIVE_MODE,
|
|
3581
3850
|
PROSE_KEYS,
|
|
3582
3851
|
PROTECTION_PARTS,
|
|
3583
3852
|
PROTECTION_PART_PHRASE,
|
|
@@ -3587,111 +3856,120 @@ export {
|
|
|
3587
3856
|
REQUEST_KINDS,
|
|
3588
3857
|
REQUEST_LABEL,
|
|
3589
3858
|
REQUEST_STATUS_LABEL,
|
|
3590
|
-
|
|
3591
|
-
|
|
3859
|
+
ay as RIGHTS,
|
|
3860
|
+
az as RIGHT_LABEL,
|
|
3592
3861
|
RISK_LABELS,
|
|
3593
|
-
|
|
3862
|
+
aA as RISK_TIER,
|
|
3594
3863
|
SIMILARITY_THRESHOLD,
|
|
3595
3864
|
SINGLE_MODE,
|
|
3596
|
-
|
|
3865
|
+
aB as SPEC_FIELDS,
|
|
3597
3866
|
STEPS,
|
|
3598
3867
|
TIER_AXIS,
|
|
3599
3868
|
TIER_NAMES,
|
|
3600
|
-
|
|
3869
|
+
aC as TIER_ORDER,
|
|
3601
3870
|
TOKEN_REJECTED_MESSAGE,
|
|
3602
3871
|
TOKEN_SETTINGS_URL,
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3872
|
+
aD as TeamDirectory,
|
|
3873
|
+
aE as UNRELEASED,
|
|
3874
|
+
aF as VISUAL_CHECK_LABEL,
|
|
3606
3875
|
acceptDocsDraft,
|
|
3607
3876
|
acceptDocsText,
|
|
3608
3877
|
addBrandToConfigText,
|
|
3609
|
-
|
|
3610
|
-
|
|
3878
|
+
aG as affectedContrast,
|
|
3879
|
+
d as aliasOf,
|
|
3611
3880
|
aliasTarget,
|
|
3612
3881
|
anatomyLabel,
|
|
3613
3882
|
anchoredRamp,
|
|
3614
3883
|
anthropicReplyText,
|
|
3615
3884
|
anthropicRequest,
|
|
3616
|
-
|
|
3617
|
-
|
|
3885
|
+
aH as appForProject,
|
|
3886
|
+
aI as appUrl,
|
|
3618
3887
|
applyConfirmedRenames,
|
|
3619
3888
|
applyDocsSections,
|
|
3620
3889
|
applyTokenEdits,
|
|
3621
|
-
|
|
3622
|
-
|
|
3890
|
+
applyTokenMerge,
|
|
3891
|
+
aJ as approverEntries,
|
|
3892
|
+
aK as approverLists,
|
|
3623
3893
|
assertSafeBranch,
|
|
3624
3894
|
assignRequestOnGitHub,
|
|
3625
|
-
|
|
3895
|
+
aL as assignVersions,
|
|
3626
3896
|
base64ToUtf8,
|
|
3627
|
-
|
|
3897
|
+
aM as baseBranchOf,
|
|
3628
3898
|
bellChroma,
|
|
3629
|
-
|
|
3899
|
+
aN as branchName,
|
|
3630
3900
|
branchPreviewUrls,
|
|
3631
|
-
|
|
3901
|
+
aO as branchSlug,
|
|
3632
3902
|
brandDisplayName,
|
|
3633
3903
|
brandPreviewVariables,
|
|
3634
3904
|
buildHistoryEntry,
|
|
3635
3905
|
buildReport,
|
|
3636
3906
|
i as buildTokenSet,
|
|
3637
3907
|
buildUsageIndex,
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3908
|
+
aP as canShip,
|
|
3909
|
+
changeEdits,
|
|
3910
|
+
aQ as changeKind,
|
|
3911
|
+
aR as changeTable,
|
|
3912
|
+
aS as changedModes,
|
|
3913
|
+
aT as changesetBump,
|
|
3643
3914
|
checkBrandSlug,
|
|
3644
3915
|
checkLayeredPair,
|
|
3916
|
+
choiceFor,
|
|
3645
3917
|
classifyAuth,
|
|
3646
|
-
|
|
3918
|
+
aU as classifyLayered,
|
|
3647
3919
|
classifyRisk,
|
|
3648
3920
|
cleanDocsText,
|
|
3649
3921
|
cleanDraftText,
|
|
3650
|
-
|
|
3922
|
+
aV as clearRawDeep,
|
|
3651
3923
|
closeExplorationOnGitHub,
|
|
3652
3924
|
closingIssueNumbers,
|
|
3653
3925
|
coerceValue,
|
|
3654
3926
|
collectHistory,
|
|
3655
3927
|
collectRefs,
|
|
3656
|
-
|
|
3657
|
-
|
|
3928
|
+
k2 as colorToCss,
|
|
3929
|
+
l as colorsEqual,
|
|
3930
|
+
combineChanges,
|
|
3931
|
+
combineConflicts,
|
|
3658
3932
|
c as comboKey,
|
|
3659
|
-
|
|
3660
|
-
|
|
3933
|
+
aW as commitMessage,
|
|
3934
|
+
m as compactJson,
|
|
3661
3935
|
componentForPath,
|
|
3662
|
-
|
|
3936
|
+
aX as composeChangeset,
|
|
3663
3937
|
composeFiles,
|
|
3664
3938
|
composeNewBrandBody,
|
|
3665
3939
|
composePendingDrafts,
|
|
3666
|
-
|
|
3667
|
-
|
|
3940
|
+
aY as composePrBody,
|
|
3941
|
+
aZ as composeProposalBody,
|
|
3668
3942
|
composeRequestIssue,
|
|
3669
|
-
|
|
3670
|
-
|
|
3943
|
+
a_ as composite,
|
|
3944
|
+
a$ as contextFor,
|
|
3671
3945
|
contextLabel,
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3946
|
+
b0 as contrastDelta,
|
|
3947
|
+
b1 as contrastDeltaTable,
|
|
3948
|
+
b2 as contrastLevel,
|
|
3949
|
+
b3 as contrastPair,
|
|
3950
|
+
b4 as contrastRatio,
|
|
3677
3951
|
contrastScope,
|
|
3678
|
-
|
|
3952
|
+
b5 as countChanges,
|
|
3679
3953
|
createDocsDraftSession,
|
|
3680
3954
|
createPendingDrafts,
|
|
3681
3955
|
cssModuleClasses,
|
|
3956
|
+
cssOf,
|
|
3957
|
+
cssVarOf,
|
|
3682
3958
|
cssVarsIn,
|
|
3683
3959
|
declineComment,
|
|
3684
3960
|
declineReasonProblems,
|
|
3685
3961
|
declineRequestOnGitHub,
|
|
3686
|
-
|
|
3962
|
+
decodePicks,
|
|
3963
|
+
defaultChoiceFor,
|
|
3964
|
+
b6 as defaultContext,
|
|
3687
3965
|
defaultFile,
|
|
3688
3966
|
defaultScopes,
|
|
3689
|
-
|
|
3690
|
-
|
|
3967
|
+
b7 as defaultTitle,
|
|
3968
|
+
b8 as defineConfig,
|
|
3691
3969
|
deletePendingDrafts,
|
|
3692
3970
|
demoHistory,
|
|
3693
|
-
|
|
3694
|
-
|
|
3971
|
+
b9 as detectLayout,
|
|
3972
|
+
ba as diffLayered,
|
|
3695
3973
|
docsPrompt,
|
|
3696
3974
|
docsSectionShapes,
|
|
3697
3975
|
docsSectionsAnswer,
|
|
@@ -3708,12 +3986,13 @@ export {
|
|
|
3708
3986
|
dtcgColor,
|
|
3709
3987
|
effectiveScopes,
|
|
3710
3988
|
emptyTokenFile,
|
|
3989
|
+
encodePicks,
|
|
3711
3990
|
ensureRequestsBranch,
|
|
3712
|
-
|
|
3713
|
-
|
|
3991
|
+
bb as entriesFor,
|
|
3992
|
+
bc as entryLabel,
|
|
3714
3993
|
evaluatePairs,
|
|
3715
|
-
|
|
3716
|
-
|
|
3994
|
+
bd as expandEntries,
|
|
3995
|
+
be as expandOwners,
|
|
3717
3996
|
explorationBranch,
|
|
3718
3997
|
explorationNote,
|
|
3719
3998
|
explorationNotePath,
|
|
@@ -3729,62 +4008,62 @@ export {
|
|
|
3729
4008
|
explorationTestBuildOnGitHub,
|
|
3730
4009
|
explorationTryIt,
|
|
3731
4010
|
explorationTryItOnGitHub,
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
4011
|
+
bf as fieldChanges,
|
|
4012
|
+
bg as fileModes,
|
|
4013
|
+
bh as filterHistory,
|
|
3735
4014
|
findExplorationOnGitHub,
|
|
3736
4015
|
findPreviewComment,
|
|
3737
4016
|
findReferenceProblems,
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
4017
|
+
bi as findToken,
|
|
4018
|
+
bj as findVercelComment,
|
|
4019
|
+
bk as firstParagraph,
|
|
4020
|
+
n as flattenLayerFile,
|
|
4021
|
+
flattenTokens,
|
|
3743
4022
|
formatChangeset,
|
|
3744
4023
|
formatDocsJson,
|
|
3745
|
-
|
|
3746
|
-
|
|
4024
|
+
bl as formatNumber,
|
|
4025
|
+
bm as formatOwnerEntry,
|
|
3747
4026
|
formatValue,
|
|
3748
4027
|
friendlyMessage,
|
|
3749
4028
|
fromHex,
|
|
3750
4029
|
fromPlain,
|
|
3751
4030
|
generateRamp,
|
|
3752
|
-
|
|
4031
|
+
bn as getChild,
|
|
3753
4032
|
getEntry,
|
|
3754
4033
|
gitHubHistoryBackend,
|
|
3755
4034
|
g2 as globToRegExp,
|
|
3756
|
-
|
|
4035
|
+
bo as groupsPhrase,
|
|
3757
4036
|
h as hashSpec,
|
|
3758
4037
|
h2 as hexToRgba,
|
|
3759
|
-
|
|
4038
|
+
bp as holdersOf,
|
|
3760
4039
|
j as iconComponentName,
|
|
3761
4040
|
idExtensionFor,
|
|
3762
4041
|
impactedModules,
|
|
3763
4042
|
importsIn,
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
4043
|
+
bq as indexByPath,
|
|
4044
|
+
br as inheritedTypeAt,
|
|
4045
|
+
bs as installCommand,
|
|
4046
|
+
bt as installCommandFromPreviewComment,
|
|
3768
4047
|
isActiveExploration,
|
|
3769
4048
|
isAlias,
|
|
3770
4049
|
isDocsSelectionMode,
|
|
3771
4050
|
isDraftSessionId,
|
|
3772
4051
|
isExplorationBranch,
|
|
3773
4052
|
isExplorationPull,
|
|
3774
|
-
|
|
4053
|
+
bu as isGuardrail,
|
|
3775
4054
|
isHex,
|
|
3776
4055
|
isMissingPermission,
|
|
3777
4056
|
isMultiMode,
|
|
3778
4057
|
isPlanRefusal,
|
|
3779
4058
|
isProseKey,
|
|
3780
|
-
|
|
3781
|
-
|
|
4059
|
+
bv as isTeamEntry,
|
|
4060
|
+
bw as isTokenFile,
|
|
3782
4061
|
isTokenRejected,
|
|
3783
|
-
|
|
4062
|
+
bx as isVercelComment,
|
|
3784
4063
|
jiraIssueFields,
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
4064
|
+
by as kindForMin,
|
|
4065
|
+
bz as layeredImpact,
|
|
4066
|
+
o as layeredParityIssues,
|
|
3788
4067
|
g as layoutOptions,
|
|
3789
4068
|
levenshtein,
|
|
3790
4069
|
linkRequestToExplorationOnGitHub,
|
|
@@ -3796,16 +4075,17 @@ export {
|
|
|
3796
4075
|
loadProtection,
|
|
3797
4076
|
loadRepoFiles,
|
|
3798
4077
|
loadRequiredChecks,
|
|
3799
|
-
|
|
4078
|
+
bA as luminance,
|
|
3800
4079
|
markClaudeDraft,
|
|
3801
4080
|
mergeDocsDraft,
|
|
3802
4081
|
mergeDraftResults,
|
|
3803
4082
|
mergeReleases,
|
|
4083
|
+
missingChoices,
|
|
3804
4084
|
missingPermissionMessage,
|
|
3805
4085
|
missingProse,
|
|
3806
4086
|
modeDisplayName,
|
|
3807
4087
|
modeFile,
|
|
3808
|
-
|
|
4088
|
+
bB as modeLabel,
|
|
3809
4089
|
modesOf$1 as modesOf,
|
|
3810
4090
|
moveToken,
|
|
3811
4091
|
nearestStep,
|
|
@@ -3814,83 +4094,88 @@ export {
|
|
|
3814
4094
|
neutralOffset,
|
|
3815
4095
|
newBrandSection,
|
|
3816
4096
|
newDraftSessionId,
|
|
3817
|
-
|
|
3818
|
-
|
|
4097
|
+
bC as nodesEqual,
|
|
4098
|
+
bD as normalizeRights,
|
|
3819
4099
|
oklch,
|
|
3820
4100
|
oklchToLinear,
|
|
3821
|
-
|
|
3822
|
-
|
|
4101
|
+
bE as overrideKey,
|
|
4102
|
+
bF as ownerEntryProblems,
|
|
3823
4103
|
packageDirCandidates,
|
|
3824
|
-
|
|
4104
|
+
bG as pairKey,
|
|
3825
4105
|
parseChangelog,
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
4106
|
+
bH as parseChangesetText,
|
|
4107
|
+
parseColor,
|
|
4108
|
+
bI as parseComponentSpecs,
|
|
3829
4109
|
parseContrastChecks,
|
|
3830
|
-
|
|
4110
|
+
q as parseDimension,
|
|
3831
4111
|
parseDocsReply,
|
|
3832
4112
|
parseDocsTaskAnswer,
|
|
3833
4113
|
parseExplorationBody,
|
|
3834
4114
|
parseJson,
|
|
3835
|
-
|
|
4115
|
+
bJ as parseOwnerEntry,
|
|
3836
4116
|
parsePendingDrafts,
|
|
3837
4117
|
parseRepo,
|
|
3838
4118
|
parseRequestIssue,
|
|
3839
4119
|
parseStructureChange,
|
|
3840
4120
|
parseTokenFile,
|
|
3841
|
-
|
|
4121
|
+
bK as parseVercelComment,
|
|
3842
4122
|
patchDocsJson,
|
|
3843
4123
|
pathId,
|
|
3844
4124
|
pathSimilarity,
|
|
3845
4125
|
permissionLabel,
|
|
4126
|
+
pickedOption,
|
|
3846
4127
|
plainEqual,
|
|
3847
4128
|
planNewBrand,
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
4129
|
+
bL as planRestore,
|
|
4130
|
+
bM as planRevert,
|
|
4131
|
+
bN as planToJson,
|
|
4132
|
+
planTokenMerge,
|
|
3851
4133
|
e as prNumberFromSubject,
|
|
3852
4134
|
prefillDocs,
|
|
3853
|
-
|
|
4135
|
+
bO as previewComment,
|
|
3854
4136
|
previewLinks,
|
|
3855
|
-
|
|
3856
|
-
|
|
4137
|
+
bP as previewLinksOf,
|
|
4138
|
+
previewOverrides,
|
|
4139
|
+
bQ as previewUrl,
|
|
3857
4140
|
previewsSettled,
|
|
3858
|
-
|
|
4141
|
+
bR as projectOfLabel,
|
|
3859
4142
|
promoteExplorationOnGitHub,
|
|
3860
4143
|
promotionPlan,
|
|
3861
4144
|
propsTable,
|
|
3862
4145
|
protectLibrary,
|
|
3863
4146
|
protectRefusal,
|
|
3864
|
-
|
|
4147
|
+
bS as protectedBranchesOf,
|
|
3865
4148
|
protectionRuleset,
|
|
3866
4149
|
protectionRulesetName,
|
|
3867
4150
|
protectionSentence,
|
|
3868
4151
|
protectionState,
|
|
3869
4152
|
pushChangeRows,
|
|
4153
|
+
rateLimitMessage,
|
|
3870
4154
|
readPendingDrafts,
|
|
3871
4155
|
readPreviewComment,
|
|
3872
4156
|
readTokenAt,
|
|
3873
4157
|
reconcileIds,
|
|
3874
4158
|
releasesFromTags,
|
|
3875
4159
|
removeToken,
|
|
3876
|
-
|
|
4160
|
+
removedSide,
|
|
4161
|
+
bT as renderedColor,
|
|
3877
4162
|
requestBrief,
|
|
3878
4163
|
requestImagePath,
|
|
3879
4164
|
requestIssueTitle,
|
|
3880
4165
|
requestStatus,
|
|
3881
4166
|
requiredCheckNames,
|
|
3882
4167
|
requiredPermission,
|
|
3883
|
-
|
|
4168
|
+
resolve,
|
|
3884
4169
|
resolveBranchPreviews,
|
|
3885
4170
|
r as rgbaToDtcg,
|
|
3886
|
-
|
|
3887
|
-
|
|
4171
|
+
rgbaToHex,
|
|
4172
|
+
bU as rightsProblems,
|
|
3888
4173
|
round3,
|
|
3889
4174
|
rulesSettingsUrl,
|
|
3890
4175
|
rulesetTargetsDefault,
|
|
3891
|
-
|
|
4176
|
+
bV as runLayeredContrast,
|
|
3892
4177
|
sameScopes,
|
|
3893
|
-
|
|
4178
|
+
bW as sameToken,
|
|
3894
4179
|
sanitizeDocsReplacement,
|
|
3895
4180
|
sanitizeDocsResult,
|
|
3896
4181
|
sanitizeDocsSections,
|
|
@@ -3904,21 +4189,22 @@ export {
|
|
|
3904
4189
|
serializePendingDrafts,
|
|
3905
4190
|
serializeTokenFile,
|
|
3906
4191
|
shadowedEdits,
|
|
3907
|
-
|
|
4192
|
+
bX as shipPolicy,
|
|
3908
4193
|
shortVariant,
|
|
3909
4194
|
shortVariants,
|
|
3910
4195
|
slugify,
|
|
3911
|
-
|
|
3912
|
-
|
|
4196
|
+
bY as snapshotUrl,
|
|
4197
|
+
bZ as specHash,
|
|
3913
4198
|
startExplorationOnGitHub,
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
b$ as
|
|
3917
|
-
c0 as
|
|
3918
|
-
c1 as
|
|
3919
|
-
c2 as
|
|
3920
|
-
c3 as
|
|
3921
|
-
c4 as
|
|
4199
|
+
b_ as summarizeTokenChange,
|
|
4200
|
+
takeMine,
|
|
4201
|
+
b$ as tarballAssetName,
|
|
4202
|
+
c0 as tarballUrl,
|
|
4203
|
+
c1 as teamKey,
|
|
4204
|
+
c2 as teamKeyOf,
|
|
4205
|
+
c3 as teamUnreadableMessage,
|
|
4206
|
+
c4 as teamsIn,
|
|
4207
|
+
threeWayDiff,
|
|
3922
4208
|
tintedNeutral,
|
|
3923
4209
|
c5 as titleOf,
|
|
3924
4210
|
t2 as toAlias,
|
|
@@ -3934,6 +4220,7 @@ export {
|
|
|
3934
4220
|
ca as tokenNode,
|
|
3935
4221
|
tokenUsage,
|
|
3936
4222
|
tokensPath,
|
|
4223
|
+
unpicked,
|
|
3937
4224
|
cb as unreadableShipTeams,
|
|
3938
4225
|
cc as unreadableTeams,
|
|
3939
4226
|
updatePendingDrafts,
|