@bridge-ds/core 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audit/index.js +1 -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 +5 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +676 -392
- 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/index.js +3 -3
- package/dist/{report-tNzTriGF.js → report-Bf2HEr9t.js} +2 -2
- package/dist/{report-tNzTriGF.js.map → report-Bf2HEr9t.js.map} +1 -1
- package/dist/token-combine.d.ts +78 -0
- package/dist/token-merge.d.ts +72 -0
- package/package.json +1 -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;
|
|
@@ -562,8 +808,8 @@ class GitHubClient {
|
|
|
562
808
|
try {
|
|
563
809
|
return await this.once(method, path, body);
|
|
564
810
|
} catch (err) {
|
|
565
|
-
const
|
|
566
|
-
if (attempt >= attempts || !(
|
|
811
|
+
const e2 = err;
|
|
812
|
+
if (attempt >= attempts || !(e2 instanceof GitHubError) || !isTransient(e2.status, e2.detail ?? "")) throw err;
|
|
567
813
|
await this.retry.sleep(this.retry.baseDelayMs * 2 ** (attempt - 1));
|
|
568
814
|
}
|
|
569
815
|
}
|
|
@@ -586,7 +832,7 @@ class GitHubClient {
|
|
|
586
832
|
let detail = "";
|
|
587
833
|
try {
|
|
588
834
|
const data = await res.json();
|
|
589
|
-
detail = [data.message, ...(data.errors ?? []).map((
|
|
835
|
+
detail = [data.message, ...(data.errors ?? []).map((e2) => typeof e2 === "string" ? e2 : e2.message)].filter(Boolean).join(" ");
|
|
590
836
|
} catch {
|
|
591
837
|
detail = "";
|
|
592
838
|
}
|
|
@@ -670,7 +916,7 @@ class GitHubClient {
|
|
|
670
916
|
/** Pull requests a commit belongs to (merged ones first): how a commit on main finds its PR. */
|
|
671
917
|
async listPullRequestsForCommit(sha) {
|
|
672
918
|
const r2 = await this.request("GET", `${this.repoPath}/commits/${encodeRef(sha)}/pulls`);
|
|
673
|
-
return r2.map((
|
|
919
|
+
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
920
|
}
|
|
675
921
|
/** Tags with the commit they point to, newest first as GitHub lists them. */
|
|
676
922
|
async listTags(perPage = 30) {
|
|
@@ -751,10 +997,29 @@ class GitHubClient {
|
|
|
751
997
|
variables: { id: pr.node_id }
|
|
752
998
|
});
|
|
753
999
|
if (r2?.errors?.length) {
|
|
754
|
-
const detail = r2.errors.map((
|
|
1000
|
+
const detail = r2.errors.map((e2) => e2.message).filter(Boolean).join(" ");
|
|
755
1001
|
throw new GitHubError(422, `GitHub refused to mark #${n2} ready for review${detail ? `: ${detail}` : "."} Use "Ready for review" on GitHub instead.`, detail);
|
|
756
1002
|
}
|
|
757
1003
|
}
|
|
1004
|
+
/**
|
|
1005
|
+
* Turns an open pull request back into a draft: the GraphQL mutation convertPullRequestToDraft (REST cannot set
|
|
1006
|
+
* `draft` either), same token. No-op when it is already a draft.
|
|
1007
|
+
*/
|
|
1008
|
+
async convertToDraft(n2) {
|
|
1009
|
+
this.assertToken("Turning a pull request back into a draft");
|
|
1010
|
+
const pr = await this.request("GET", `${this.repoPath}/pulls/${n2}`);
|
|
1011
|
+
if (pr.draft === true) return;
|
|
1012
|
+
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.`);
|
|
1013
|
+
if (!pr.node_id) throw new GitHubError(422, `Pull request #${n2} has no node id: convert it to draft on GitHub.`);
|
|
1014
|
+
const r2 = await this.request("POST", "/graphql", {
|
|
1015
|
+
query: "mutation($id: ID!) { convertPullRequestToDraft(input: { pullRequestId: $id }) { pullRequest { number isDraft } } }",
|
|
1016
|
+
variables: { id: pr.node_id }
|
|
1017
|
+
});
|
|
1018
|
+
if (r2?.errors?.length) {
|
|
1019
|
+
const detail = r2.errors.map((e2) => e2.message).filter(Boolean).join(" ");
|
|
1020
|
+
throw new GitHubError(422, `GitHub refused to turn #${n2} back into a draft${detail ? `: ${detail}` : "."} Use "Convert to draft" on GitHub instead.`, detail);
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
758
1023
|
async createPullRequest(input) {
|
|
759
1024
|
this.assertWritableBranch(input.head, input.base);
|
|
760
1025
|
const r2 = await this.request(
|
|
@@ -931,7 +1196,7 @@ class GitHubClient {
|
|
|
931
1196
|
const q2 = ref ? `?ref=${encodeURIComponent(ref)}` : "";
|
|
932
1197
|
const r2 = await this.request("GET", `${this.repoPath}/contents/${encodeRef(path)}${q2}`);
|
|
933
1198
|
if (!Array.isArray(r2)) return [];
|
|
934
|
-
return r2.map((
|
|
1199
|
+
return r2.map((e2) => ({ name: e2.name, path: e2.path, type: e2.type, sha: e2.sha, size: e2.size ?? 0 }));
|
|
935
1200
|
} catch (err) {
|
|
936
1201
|
if (err instanceof GitHubError && err.status === 404) return [];
|
|
937
1202
|
throw err;
|
|
@@ -984,7 +1249,7 @@ class GitHubClient {
|
|
|
984
1249
|
async listFiles(ref) {
|
|
985
1250
|
const r2 = await this.request("GET", `${this.repoPath}/git/trees/${encodeRef(ref)}?recursive=1`);
|
|
986
1251
|
if (r2.truncated) throw new GitHubError(413, "The repository tree is too large to read in one request.");
|
|
987
|
-
return r2.tree.filter((
|
|
1252
|
+
return r2.tree.filter((e2) => e2.type === "blob").map((e2) => e2.path);
|
|
988
1253
|
}
|
|
989
1254
|
// --- Pull requests, reviews, checks ---------------------------------------------------------
|
|
990
1255
|
async listPullRequests(state = "open") {
|
|
@@ -1017,12 +1282,12 @@ class GitHubClient {
|
|
|
1017
1282
|
async listDeployments(sha) {
|
|
1018
1283
|
const list = await this.request("GET", `${this.repoPath}/deployments?sha=${encodeURIComponent(sha)}&per_page=30`);
|
|
1019
1284
|
const latest = /* @__PURE__ */ new Map();
|
|
1020
|
-
for (const
|
|
1285
|
+
for (const d2 of list) if (!latest.has(d2.environment)) latest.set(d2.environment, d2);
|
|
1021
1286
|
return Promise.all(
|
|
1022
|
-
[...latest.values()].map(async (
|
|
1023
|
-
const st = await this.request("GET", `${this.repoPath}/deployments/${
|
|
1287
|
+
[...latest.values()].map(async (d2) => {
|
|
1288
|
+
const st = await this.request("GET", `${this.repoPath}/deployments/${d2.id}/statuses?per_page=1`).catch(() => []);
|
|
1024
1289
|
const s = st[0];
|
|
1025
|
-
return { id:
|
|
1290
|
+
return { id: d2.id, environment: d2.environment, state: s?.state ?? null, environmentUrl: s?.environment_url || null };
|
|
1026
1291
|
})
|
|
1027
1292
|
);
|
|
1028
1293
|
}
|
|
@@ -1109,7 +1374,7 @@ class GitHubClient {
|
|
|
1109
1374
|
function toCommitSummary(c10) {
|
|
1110
1375
|
return {
|
|
1111
1376
|
sha: c10.sha,
|
|
1112
|
-
parents: (c10.parents ?? []).map((
|
|
1377
|
+
parents: (c10.parents ?? []).map((p) => p.sha),
|
|
1113
1378
|
date: c10.commit?.committer?.date ?? c10.commit?.author?.date ?? "",
|
|
1114
1379
|
author: c10.author?.login ?? c10.commit?.author?.name ?? "unknown",
|
|
1115
1380
|
message: c10.commit?.message ?? ""
|
|
@@ -1160,7 +1425,7 @@ async function loadRepoFiles(client, commitSha, fallbackTokensPath) {
|
|
|
1160
1425
|
const warnings = [];
|
|
1161
1426
|
let config;
|
|
1162
1427
|
let configText;
|
|
1163
|
-
const configEntry = entries.find((
|
|
1428
|
+
const configEntry = entries.find((e2) => e2.type === "blob" && e2.path === CONFIG_FILE);
|
|
1164
1429
|
if (configEntry) {
|
|
1165
1430
|
try {
|
|
1166
1431
|
configText = await client.getBlobText(configEntry.sha);
|
|
@@ -1173,16 +1438,16 @@ async function loadRepoFiles(client, commitSha, fallbackTokensPath) {
|
|
|
1173
1438
|
const checksFile = config?.tokens.contrastChecks ?? DEFAULT_TOKEN_PATHS.contrastChecks;
|
|
1174
1439
|
const prefix = tokensPath2 + "/";
|
|
1175
1440
|
const wanted = entries.filter(
|
|
1176
|
-
(
|
|
1441
|
+
(e2) => e2.type === "blob" && e2.path.startsWith(prefix) && (e2.path.endsWith(".tokens.json") || e2.path === prefix + checksFile)
|
|
1177
1442
|
);
|
|
1178
|
-
if (!wanted.some((
|
|
1443
|
+
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
1444
|
const files = {};
|
|
1180
1445
|
let contrastChecks;
|
|
1181
1446
|
const queue = [...wanted];
|
|
1182
1447
|
const worker = async () => {
|
|
1183
|
-
for (let
|
|
1184
|
-
const text = await client.getBlobText(
|
|
1185
|
-
const rel =
|
|
1448
|
+
for (let e2 = queue.shift(); e2; e2 = queue.shift()) {
|
|
1449
|
+
const text = await client.getBlobText(e2.sha);
|
|
1450
|
+
const rel = e2.path.slice(prefix.length);
|
|
1186
1451
|
if (rel === checksFile) contrastChecks = text;
|
|
1187
1452
|
else files[rel] = text;
|
|
1188
1453
|
}
|
|
@@ -1215,7 +1480,7 @@ async function loadProtection(client, defaultBranch) {
|
|
|
1215
1480
|
}
|
|
1216
1481
|
async function loadRequiredChecks(client, defaultBranch) {
|
|
1217
1482
|
const onDefault = await client.listCheckRuns(defaultBranch).catch(() => []);
|
|
1218
|
-
const merged = await client.listPullRequests("closed").then((prs) => prs.find((
|
|
1483
|
+
const merged = await client.listPullRequests("closed").then((prs) => prs.find((p) => p.state === "merged" && p.baseRef === defaultBranch)).catch(() => void 0);
|
|
1219
1484
|
const onPull = merged?.headSha ? await client.listCheckRuns(merged.headSha).catch(() => []) : [];
|
|
1220
1485
|
return requiredCheckNames(onDefault, onPull);
|
|
1221
1486
|
}
|
|
@@ -1248,11 +1513,11 @@ function importsIn(text) {
|
|
|
1248
1513
|
for (const m2 of text.matchAll(IMPORT_RE)) out.add(m2[1] ?? m2[2]);
|
|
1249
1514
|
return [...out];
|
|
1250
1515
|
}
|
|
1251
|
-
const norm = (
|
|
1252
|
-
const isStory = (
|
|
1253
|
-
const isTest = (
|
|
1254
|
-
const isCss = (
|
|
1255
|
-
const isScript = (
|
|
1516
|
+
const norm = (p) => p.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
1517
|
+
const isStory = (p) => /\.stories\.[cm]?[jt]sx?$/.test(p);
|
|
1518
|
+
const isTest = (p) => /\.(test|spec)\.[cm]?[jt]sx?$/.test(p);
|
|
1519
|
+
const isCss = (p) => /\.(css|scss|less)$/.test(p);
|
|
1520
|
+
const isScript = (p) => /\.[cm]?[jt]sx?$/.test(p);
|
|
1256
1521
|
function componentOf(from, spec, componentsDir) {
|
|
1257
1522
|
if (!spec.startsWith(".")) return null;
|
|
1258
1523
|
const parts = from.split("/").slice(0, -1);
|
|
@@ -1267,9 +1532,9 @@ function componentOf(from, spec, componentsDir) {
|
|
|
1267
1532
|
}
|
|
1268
1533
|
function componentForPath(path, componentsDir) {
|
|
1269
1534
|
const dir = norm(componentsDir);
|
|
1270
|
-
const
|
|
1271
|
-
if (!
|
|
1272
|
-
const rest =
|
|
1535
|
+
const p = norm(path);
|
|
1536
|
+
if (!p.startsWith(`${dir}/`)) return null;
|
|
1537
|
+
const rest = p.slice(dir.length + 1).split("/");
|
|
1273
1538
|
return rest.length >= 2 ? rest[0] : null;
|
|
1274
1539
|
}
|
|
1275
1540
|
function buildUsageIndex(files, componentsDir) {
|
|
@@ -1306,7 +1571,7 @@ function buildUsageIndex(files, componentsDir) {
|
|
|
1306
1571
|
mod.files.sort();
|
|
1307
1572
|
mod.vars = [...vars.get(mod.id) ?? []].sort();
|
|
1308
1573
|
mod.imports = [...imports.get(mod.id) ?? []].sort();
|
|
1309
|
-
mod.storyImports = [...storyImports.get(mod.id) ?? []].filter((
|
|
1574
|
+
mod.storyImports = [...storyImports.get(mod.id) ?? []].filter((d2) => !mod.imports.includes(d2)).sort();
|
|
1310
1575
|
for (const v2 of mod.vars) (byVar[v2] ??= []).push(mod.id);
|
|
1311
1576
|
}
|
|
1312
1577
|
for (const k3 of Object.keys(byVar)) byVar[k3].sort();
|
|
@@ -1328,7 +1593,7 @@ function impactedModules(index, input) {
|
|
|
1328
1593
|
if (id && index.modules[id]) add(id, `file changed: ${norm(f)}`, false);
|
|
1329
1594
|
}
|
|
1330
1595
|
const renderedBy = /* @__PURE__ */ new Map();
|
|
1331
|
-
for (const m2 of Object.values(index.modules)) for (const
|
|
1596
|
+
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
1597
|
const queue = [...reasons.keys()];
|
|
1333
1598
|
while (queue.length) {
|
|
1334
1599
|
const id = queue.shift();
|
|
@@ -1351,10 +1616,10 @@ function tokenUsage(tokens, reads) {
|
|
|
1351
1616
|
const used = /* @__PURE__ */ new Set();
|
|
1352
1617
|
const queue = [...direct];
|
|
1353
1618
|
while (queue.length) {
|
|
1354
|
-
const
|
|
1355
|
-
if (used.has(
|
|
1356
|
-
used.add(
|
|
1357
|
-
for (const r2 of byPath.get(
|
|
1619
|
+
const p = queue.pop();
|
|
1620
|
+
if (used.has(p)) continue;
|
|
1621
|
+
used.add(p);
|
|
1622
|
+
for (const r2 of byPath.get(p)?.refs ?? []) if (!used.has(r2) && byPath.has(r2)) queue.push(r2);
|
|
1358
1623
|
}
|
|
1359
1624
|
return { used, direct };
|
|
1360
1625
|
}
|
|
@@ -1395,7 +1660,7 @@ function gitHubHistoryBackend(client, config, opts = {}) {
|
|
|
1395
1660
|
const merges = /* @__PURE__ */ new Map();
|
|
1396
1661
|
const enriched = await pool(list, async (c10) => {
|
|
1397
1662
|
const prs = await client.listPullRequestsForCommit(c10.sha).catch(() => []);
|
|
1398
|
-
const pr = prs.find((
|
|
1663
|
+
const pr = prs.find((p) => p.mergedAt && p.baseRef === ref) ?? prs.find((p) => p.mergedAt);
|
|
1399
1664
|
if (!pr) return c10;
|
|
1400
1665
|
const prInfo = { number: pr.number, title: pr.title, url: pr.url, author: pr.author, ...pr.mergedAt ? { mergedAt: pr.mergedAt } : {}, body: pr.body };
|
|
1401
1666
|
if (pr.mergeCommitSha && pr.mergeCommitSha !== c10.sha) {
|
|
@@ -1419,20 +1684,20 @@ function gitHubHistoryBackend(client, config, opts = {}) {
|
|
|
1419
1684
|
},
|
|
1420
1685
|
async tokenFiles(sha) {
|
|
1421
1686
|
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 (
|
|
1687
|
+
const blobs = entries.filter((e2) => e2.type === "blob" && e2.path.startsWith(prefix));
|
|
1688
|
+
const picked = new Set(selectTokenFiles(config, blobs.map((e2) => e2.path)));
|
|
1689
|
+
const wanted = blobs.filter((e2) => picked.has(prefix ? e2.path.slice(prefix.length) : e2.path));
|
|
1690
|
+
const texts = await pool(wanted, async (e2) => [prefix ? e2.path.slice(prefix.length) : e2.path, await client.getBlobText(e2.sha)]);
|
|
1426
1691
|
return new Map(texts);
|
|
1427
1692
|
},
|
|
1428
1693
|
async changesetPaths(sha) {
|
|
1429
1694
|
const dir = config.changesets?.dir.replace(/\/$/, "");
|
|
1430
1695
|
if (!dir) return [];
|
|
1431
|
-
return (await tree(sha)).filter((
|
|
1696
|
+
return (await tree(sha)).filter((e2) => e2.type === "blob" && e2.path.startsWith(`${dir}/`)).map((e2) => e2.path);
|
|
1432
1697
|
},
|
|
1433
1698
|
async readFile(sha, path) {
|
|
1434
|
-
const
|
|
1435
|
-
return
|
|
1699
|
+
const e2 = (await tree(sha)).find((x) => x.type === "blob" && x.path === path);
|
|
1700
|
+
return e2 ? client.getBlobText(e2.sha) : null;
|
|
1436
1701
|
}
|
|
1437
1702
|
};
|
|
1438
1703
|
}
|
|
@@ -1453,9 +1718,9 @@ async function loadGitHubHistory(client, config, opts = {}) {
|
|
|
1453
1718
|
if (head) {
|
|
1454
1719
|
const files = await backend.tree(head).catch(() => []);
|
|
1455
1720
|
for (const dir of packageDirCandidates(config)) {
|
|
1456
|
-
const
|
|
1457
|
-
const cl = files.find((
|
|
1458
|
-
const pkg = files.find((
|
|
1721
|
+
const p = (f) => dir ? `${dir}/${f}` : f;
|
|
1722
|
+
const cl = files.find((e2) => e2.path === p("CHANGELOG.md"));
|
|
1723
|
+
const pkg = files.find((e2) => e2.path === p("package.json"));
|
|
1459
1724
|
if (!cl && !pkg) continue;
|
|
1460
1725
|
if (cl) changelog = parseChangelog(await client.getBlobText(cl.sha));
|
|
1461
1726
|
if (pkg) {
|
|
@@ -1503,14 +1768,14 @@ function demoHistory(config, current) {
|
|
|
1503
1768
|
}
|
|
1504
1769
|
const entries = [];
|
|
1505
1770
|
kept.forEach((step, i2) => {
|
|
1506
|
-
const
|
|
1771
|
+
const e2 = buildHistoryEntry({
|
|
1507
1772
|
config,
|
|
1508
1773
|
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
1774
|
before: states[i2],
|
|
1510
1775
|
after: states[i2 + 1],
|
|
1511
1776
|
demo: true
|
|
1512
1777
|
});
|
|
1513
|
-
if (
|
|
1778
|
+
if (e2) entries.push(e2);
|
|
1514
1779
|
});
|
|
1515
1780
|
return { ...buildReport({ source: "demo", label: "Demo history (fixture data)", ref: "demo", entries, releases: DEMO_RELEASES, truncated: false }), demo: true };
|
|
1516
1781
|
}
|
|
@@ -1523,14 +1788,14 @@ async function branchPreviewUrls(gh, ref, config, opts = {}) {
|
|
|
1523
1788
|
if (typeof ref === "number") {
|
|
1524
1789
|
pr = ref;
|
|
1525
1790
|
if (!branch || !sha) {
|
|
1526
|
-
const
|
|
1527
|
-
branch ??=
|
|
1528
|
-
sha ??=
|
|
1791
|
+
const p = await gh.getPullRequest(ref);
|
|
1792
|
+
branch ??= p.headRef;
|
|
1793
|
+
sha ??= p.headSha;
|
|
1529
1794
|
}
|
|
1530
1795
|
} else {
|
|
1531
1796
|
sha = ref;
|
|
1532
1797
|
if (!branch || !opts.comments) {
|
|
1533
|
-
const open = (await gh.listPullRequestsForCommit(ref).catch(() => [])).find((
|
|
1798
|
+
const open = (await gh.listPullRequestsForCommit(ref).catch(() => [])).find((p) => p.state === "open");
|
|
1534
1799
|
if (open) {
|
|
1535
1800
|
pr = open.number;
|
|
1536
1801
|
branch ??= open.headRef;
|
|
@@ -1655,12 +1920,12 @@ const brandDisplayName = (slug) => slug.split("-").filter(Boolean).map((w) => w[
|
|
|
1655
1920
|
function brandLayers(config) {
|
|
1656
1921
|
const out = {};
|
|
1657
1922
|
for (const l2 of config.tokens.layers) {
|
|
1658
|
-
const ph = (l2.files.match(/\{([^}]+)\}/g) ?? []).map((
|
|
1923
|
+
const ph = (l2.files.match(/\{([^}]+)\}/g) ?? []).map((p) => p.slice(1, -1));
|
|
1659
1924
|
if (!ph.includes("brand")) continue;
|
|
1660
1925
|
if (ph.length === 1 && !out.brand) out.brand = l2.files;
|
|
1661
1926
|
else if (ph.length === 2 && !out.override) {
|
|
1662
1927
|
out.override = l2.files;
|
|
1663
|
-
out.overrideAxis = ph.find((
|
|
1928
|
+
out.overrideAxis = ph.find((p) => p !== "brand");
|
|
1664
1929
|
}
|
|
1665
1930
|
}
|
|
1666
1931
|
return out;
|
|
@@ -1803,8 +2068,8 @@ function planNewBrand(source, input) {
|
|
|
1803
2068
|
if (r2.issue) issues.push({ ...r2.issue, path: `palette.${role}` });
|
|
1804
2069
|
anchors[role] = r2.anchor;
|
|
1805
2070
|
return r2.ramp;
|
|
1806
|
-
} catch (
|
|
1807
|
-
issues.push({ level: "error", code: "invalid-color", message:
|
|
2071
|
+
} catch (e2) {
|
|
2072
|
+
issues.push({ level: "error", code: "invalid-color", message: e2.message, path: `palette.${role}` });
|
|
1808
2073
|
return void 0;
|
|
1809
2074
|
}
|
|
1810
2075
|
};
|
|
@@ -1889,7 +2154,7 @@ function planNewBrand(source, input) {
|
|
|
1889
2154
|
const primDir = config.tokens.tiers.primitive?.[0]?.replace(/\/$/, "") ?? config.tokens.primitives;
|
|
1890
2155
|
const primFile = `${primDir}/${slug}.tokens.json`;
|
|
1891
2156
|
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((
|
|
2157
|
+
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
2158
|
doc.$description = `${name}: created with Bridge, started from ${blank ? "a blank brand" : brandDisplayName(from)}.`;
|
|
1894
2159
|
doc.$extensions = { ...isObj(doc.$extensions) ? doc.$extensions : {}, "dev.bridge.brand": { name, from, ...Object.keys(anchors).length ? { anchors } : {} } };
|
|
1895
2160
|
const { $description, $extensions, ...rest } = doc;
|
|
@@ -1931,8 +2196,8 @@ function planNewBrand(source, input) {
|
|
|
1931
2196
|
try {
|
|
1932
2197
|
configText = addBrandToConfigText(baseConfigText, slug);
|
|
1933
2198
|
nextConfig = issues.some((i2) => i2.code.startsWith("slug-")) ? config : loadConfig(configText);
|
|
1934
|
-
} catch (
|
|
1935
|
-
issues.push({ level: "error", code: "config", message:
|
|
2199
|
+
} catch (e2) {
|
|
2200
|
+
issues.push({ level: "error", code: "config", message: e2.message });
|
|
1936
2201
|
}
|
|
1937
2202
|
const after = new LayeredTokenSet(nextConfig, nextFiles);
|
|
1938
2203
|
const key = (i2) => `${i2.code}|${i2.path ?? ""}|${i2.message}`;
|
|
@@ -1946,7 +2211,7 @@ function planNewBrand(source, input) {
|
|
|
1946
2211
|
const pairs = parseContrastChecks(source.contrastText);
|
|
1947
2212
|
const inGate = nextConfig.axes.brand?.values.includes(slug);
|
|
1948
2213
|
const combos = inGate ? after.combos(nextConfig.contrast?.modes).filter((c10) => c10.brand === slug) : [];
|
|
1949
|
-
const results = combos.flatMap((c10) => pairs.map((
|
|
2214
|
+
const results = combos.flatMap((c10) => pairs.map((p) => checkLayeredPair(after, p, c10)));
|
|
1950
2215
|
const failing = results.filter((r2) => !r2.pass);
|
|
1951
2216
|
const themes = [...new Set(combos.map((c10) => c10.theme).filter((t3) => !!t3))];
|
|
1952
2217
|
const ramps = {};
|
|
@@ -1960,7 +2225,7 @@ function planNewBrand(source, input) {
|
|
|
1960
2225
|
if (JSON.stringify(r2) !== JSON.stringify(ramps.primary)) ramps[role] = r2;
|
|
1961
2226
|
}
|
|
1962
2227
|
}
|
|
1963
|
-
const addedTokens = after.defs.filter((
|
|
2228
|
+
const addedTokens = after.defs.filter((d2) => created.includes(d2.file)).map((d2) => ({ kind: "added", path: d2.path }));
|
|
1964
2229
|
const risk = classifyRisk({ tokenChanges: addedTokens, files: [{ path: CONFIG_FILE, status: "modified" }], config });
|
|
1965
2230
|
const planned = [
|
|
1966
2231
|
...created.map((f) => ({ path: tokensPath(config, f), action: "create", content: nextFiles.get(f) })),
|
|
@@ -2112,21 +2377,21 @@ function cssModuleClasses(css) {
|
|
|
2112
2377
|
const cell$1 = (s) => s.replace(/\|/g, "\\|").replace(/\n/g, " ");
|
|
2113
2378
|
function propsTable(name, props, inherits) {
|
|
2114
2379
|
if (!props.length) return inherits ? `${name} has no props of its own. It accepts every native <${inherits}> attribute.` : "";
|
|
2115
|
-
const rows = props.map((
|
|
2380
|
+
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
2381
|
const tail = inherits ? `
|
|
2117
2382
|
|
|
2118
2383
|
Also accepts every native <${inherits}> attribute.` : "";
|
|
2119
2384
|
return ["| Prop | Type | Default | Description |", "| --- | --- | --- | --- |", ...rows].join("\n") + tail;
|
|
2120
2385
|
}
|
|
2121
2386
|
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(
|
|
2387
|
+
function missingProse(d2) {
|
|
2388
|
+
return PROSE_KEYS.filter((k3) => isEmpty(d2?.[k3]));
|
|
2124
2389
|
}
|
|
2125
2390
|
function prefillDocs(input) {
|
|
2126
2391
|
const existing = input.existing ?? null;
|
|
2127
2392
|
const anatomy = {};
|
|
2128
2393
|
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((
|
|
2394
|
+
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
2395
|
const missing = missingProse(existing);
|
|
2131
2396
|
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
2397
|
const draft = {
|
|
@@ -2194,7 +2459,7 @@ ${text.trim()}
|
|
|
2194
2459
|
] : [],
|
|
2195
2460
|
...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
2461
|
] : [`Write the missing prose of ${prefill.component}.docs.json for the Bridge design system.`];
|
|
2197
|
-
const
|
|
2462
|
+
const shape2 = keys ? [
|
|
2198
2463
|
`- ${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
2464
|
`- Answer with one JSON object holding only those keys, nothing else: ${docsSectionShapes(keys)}.`
|
|
2200
2465
|
] : [
|
|
@@ -2208,7 +2473,7 @@ ${text.trim()}
|
|
|
2208
2473
|
"- Ground every sentence in the facts and the source below. Never invent props, variants, keyboard behavior or tokens that are not there.",
|
|
2209
2474
|
"- Bridge voice: sentence case, plain words, verbs first, no em dashes, no marketing words.",
|
|
2210
2475
|
'- 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
|
-
...
|
|
2476
|
+
...shape2,
|
|
2212
2477
|
"",
|
|
2213
2478
|
"Facts computed from the code:",
|
|
2214
2479
|
facts.join("\n\n"),
|
|
@@ -2223,29 +2488,29 @@ ${JSON.stringify(prefill.draft, null, 2)}`,
|
|
|
2223
2488
|
block("example-docs-json", conventions.example)
|
|
2224
2489
|
].filter((x, i2, all) => x !== "" || all[i2 - 1] !== "").join("\n");
|
|
2225
2490
|
}
|
|
2226
|
-
function markClaudeDraft(
|
|
2491
|
+
function markClaudeDraft(d2, sections) {
|
|
2227
2492
|
if (!sections) {
|
|
2228
|
-
const { draftedSections: _all, ...rest } =
|
|
2493
|
+
const { draftedSections: _all, ...rest } = d2;
|
|
2229
2494
|
return { ...rest, draftedBy: "claude", needsReview: true };
|
|
2230
2495
|
}
|
|
2231
|
-
const waiting =
|
|
2496
|
+
const waiting = d2.needsReview === true ? d2.draftedSections ?? (d2.draftedBy ? [...PROSE_KEYS] : []) : [];
|
|
2232
2497
|
const union = PROSE_KEYS.filter((k3) => waiting.includes(k3) || sections.includes(k3));
|
|
2233
|
-
return { ...
|
|
2498
|
+
return { ...d2, draftedBy: "claude", needsReview: true, draftedSections: union };
|
|
2234
2499
|
}
|
|
2235
|
-
function sectionsNeedingReview(
|
|
2236
|
-
if (!
|
|
2237
|
-
return
|
|
2500
|
+
function sectionsNeedingReview(d2) {
|
|
2501
|
+
if (!d2 || d2.needsReview !== true) return [];
|
|
2502
|
+
return d2.draftedSections ? PROSE_KEYS.filter((k3) => d2.draftedSections.includes(k3)) : [...PROSE_KEYS];
|
|
2238
2503
|
}
|
|
2239
|
-
const needsDocsReview = (
|
|
2240
|
-
function acceptDocsDraft(
|
|
2241
|
-
const { draftedSections: _done, ...rest } =
|
|
2504
|
+
const needsDocsReview = (d2) => Boolean(d2 && d2.needsReview === true);
|
|
2505
|
+
function acceptDocsDraft(d2) {
|
|
2506
|
+
const { draftedSections: _done, ...rest } = d2;
|
|
2242
2507
|
return { ...rest, needsReview: false };
|
|
2243
2508
|
}
|
|
2244
|
-
function formatDocsJson(
|
|
2509
|
+
function formatDocsJson(d2) {
|
|
2245
2510
|
const order = ["summary", "category", "status", ...DOCS_KEYS.filter((k3) => k3 !== "summary"), "draftedBy", "draftedSections", "needsReview"];
|
|
2246
2511
|
const out = {};
|
|
2247
|
-
for (const k3 of order) if (
|
|
2248
|
-
for (const [k3, v2] of Object.entries(
|
|
2512
|
+
for (const k3 of order) if (d2[k3] !== void 0) out[k3] = d2[k3];
|
|
2513
|
+
for (const [k3, v2] of Object.entries(d2)) if (!(k3 in out) && v2 !== void 0) out[k3] = v2;
|
|
2249
2514
|
return `${JSON.stringify(out, null, 2)}
|
|
2250
2515
|
`;
|
|
2251
2516
|
}
|
|
@@ -2253,7 +2518,7 @@ function patchNode(old, next) {
|
|
|
2253
2518
|
if (plainEqual(toPlain(old), next)) return old;
|
|
2254
2519
|
if (old.kind === "object" && next && typeof next === "object" && !Array.isArray(next)) {
|
|
2255
2520
|
const n2 = next;
|
|
2256
|
-
const kept = old.entries.filter((
|
|
2521
|
+
const kept = old.entries.filter((e2) => n2[e2.key] !== void 0).map((e2) => ({ key: e2.key, value: patchNode(e2.value, n2[e2.key]) }));
|
|
2257
2522
|
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
2523
|
return { kind: "object", inline: old.inline, entries: [...kept, ...added] };
|
|
2259
2524
|
}
|
|
@@ -2293,17 +2558,17 @@ function propShape(s) {
|
|
|
2293
2558
|
}
|
|
2294
2559
|
const VARIANT_FIRST = ["variant", "tone", "intent", "appearance", "kind", "type", "style"];
|
|
2295
2560
|
function shortVariant(key) {
|
|
2296
|
-
const pairs = key.split(", ").map((
|
|
2297
|
-
const at2 =
|
|
2298
|
-
return at2 < 0 ? { k: "", v:
|
|
2561
|
+
const pairs = key.split(", ").map((p) => {
|
|
2562
|
+
const at2 = p.indexOf("=");
|
|
2563
|
+
return at2 < 0 ? { k: "", v: p.trim() } : { k: p.slice(0, at2).trim(), v: p.slice(at2 + 1).trim() };
|
|
2299
2564
|
});
|
|
2300
|
-
const kept = pairs.filter((
|
|
2565
|
+
const kept = pairs.filter((p) => p.v && !/^default$/i.test(p.v));
|
|
2301
2566
|
if (!kept.length) return key;
|
|
2302
2567
|
const rank = (k3) => {
|
|
2303
2568
|
const i2 = VARIANT_FIRST.indexOf(k3.toLowerCase());
|
|
2304
2569
|
return i2 < 0 ? VARIANT_FIRST.length : i2;
|
|
2305
2570
|
};
|
|
2306
|
-
return kept.map((
|
|
2571
|
+
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
2572
|
}
|
|
2308
2573
|
const shortVariants = (list) => list ? andList(list.split("; ").map(shortVariant)) : "every variant";
|
|
2309
2574
|
function parseStructureChange(c10) {
|
|
@@ -2312,8 +2577,8 @@ function parseStructureChange(c10) {
|
|
|
2312
2577
|
const op = m2[1] === "New" ? "added" : m2[1] === "Removed" ? "removed" : "moved";
|
|
2313
2578
|
const where = `in ${shortVariants(m2[3])}`;
|
|
2314
2579
|
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(
|
|
2580
|
+
const shape2 = (op === "added" ? c10.after : c10.before) ?? "";
|
|
2581
|
+
const sm = /^(\S+)(?: (.*?))?, ((?:after|before) ".*"|alone)$/.exec(shape2);
|
|
2317
2582
|
return { op, name: m2[2], where, ...sm ? { type: sm[1], ...sm[2] ? { main: sm[2] } : {}, at: sm[3] } : {} };
|
|
2318
2583
|
}
|
|
2319
2584
|
function structurePhrase(r2) {
|
|
@@ -2397,10 +2662,10 @@ function firstChange(component, kind, c10, all) {
|
|
|
2397
2662
|
function draftRequestText(component, kind, changes) {
|
|
2398
2663
|
const name = component.trim() || "the component";
|
|
2399
2664
|
if (!changes.length && kind !== "new-component") return { title: "", summary: "" };
|
|
2400
|
-
const
|
|
2665
|
+
const d2 = firstChange(name, kind, changes[0], changes);
|
|
2401
2666
|
const rest = kind === "new-component" ? 0 : changes.length - 1;
|
|
2402
|
-
const summary = rest > 0 ? `${
|
|
2403
|
-
return { title: cleanDraftText(
|
|
2667
|
+
const summary = rest > 0 ? `${d2.summary} ${capital(plural(rest, "other change"))} ${rest === 1 ? "is" : "are"} listed below.` : d2.summary;
|
|
2668
|
+
return { title: cleanDraftText(d2.title), summary: cleanDraftText(summary) };
|
|
2404
2669
|
}
|
|
2405
2670
|
const DRAFT_LINE_MAX = 120;
|
|
2406
2671
|
function clipText(text, max) {
|
|
@@ -2461,32 +2726,32 @@ function draftChangeLine(kind, changes) {
|
|
|
2461
2726
|
const tail = rest ? ` (and ${rest} more)` : "";
|
|
2462
2727
|
return capital(clipText(cleanDraftText(first), DRAFT_LINE_MAX - tail.length)) + tail;
|
|
2463
2728
|
}
|
|
2464
|
-
const tokenOf = (
|
|
2465
|
-
const pathOf = (
|
|
2729
|
+
const tokenOf = (e2) => e2.figma ?? e2.head ?? e2.base;
|
|
2730
|
+
const pathOf = (e2) => tokenOf(e2)?.path ?? e2.path;
|
|
2466
2731
|
function pushChangeRows(entries) {
|
|
2467
|
-
return entries.map((
|
|
2468
|
-
const before =
|
|
2469
|
-
const after =
|
|
2732
|
+
return entries.map((e2) => {
|
|
2733
|
+
const before = e2.head ?? e2.base;
|
|
2734
|
+
const after = e2.figma;
|
|
2470
2735
|
const show = (t3) => {
|
|
2471
2736
|
if (!t3) return void 0;
|
|
2472
2737
|
const modes = Object.keys(t3.values);
|
|
2473
2738
|
if (modes.length <= 1) return formatValue(t3.type, t3.values[modes[0] ?? ""]);
|
|
2474
2739
|
return modes.map((m2) => `${modeDisplayName(m2)} ${formatValue(t3.type, t3.values[m2])}`).join("; ");
|
|
2475
2740
|
};
|
|
2476
|
-
const renamed =
|
|
2477
|
-
return { field: renamed ? `${before.path} -> ${after.path}` : pathOf(
|
|
2741
|
+
const renamed = e2.local?.kind === "renamed" && before && after && before.path !== after.path;
|
|
2742
|
+
return { field: renamed ? `${before.path} -> ${after.path}` : pathOf(e2), before: show(before), after: show(after) };
|
|
2478
2743
|
});
|
|
2479
2744
|
}
|
|
2480
2745
|
function names(paths) {
|
|
2481
2746
|
if (paths.length <= 3) return andList(paths);
|
|
2482
2747
|
return `${paths.length} tokens, including ${andList(paths.slice(0, 2))}`;
|
|
2483
2748
|
}
|
|
2484
|
-
function valueModes(
|
|
2485
|
-
return (
|
|
2749
|
+
function valueModes(e2) {
|
|
2750
|
+
return (e2.local?.fields ?? []).filter((f) => f.field === "value" && f.mode && f.mode !== "value").map((f) => modeDisplayName(f.mode));
|
|
2486
2751
|
}
|
|
2487
2752
|
function draftPushDescription(entries, components = []) {
|
|
2488
2753
|
const by2 = { changed: [], added: [], renamed: [], removed: [] };
|
|
2489
|
-
for (const
|
|
2754
|
+
for (const e2 of entries) by2[e2.local?.kind ?? "changed"].push(e2);
|
|
2490
2755
|
const clauses = [];
|
|
2491
2756
|
if (by2.changed.length) {
|
|
2492
2757
|
const only = by2.changed.length === 1 ? by2.changed[0] : void 0;
|
|
@@ -2718,48 +2983,48 @@ function explorationSlugFromBranch(ref) {
|
|
|
2718
2983
|
return isExplorationBranch(ref) ? ref.replace(/^refs\/heads\//, "").slice(EXPLORATION_PREFIX.length) : null;
|
|
2719
2984
|
}
|
|
2720
2985
|
const explorationNotePath = (slug) => `${EXPLORATION_NOTES_DIR}/${slug}.md`;
|
|
2721
|
-
function explorationProblems(
|
|
2986
|
+
function explorationProblems(e2) {
|
|
2722
2987
|
const out = [];
|
|
2723
|
-
const name =
|
|
2724
|
-
const slug =
|
|
2988
|
+
const name = e2.name.trim();
|
|
2989
|
+
const slug = e2.slug?.trim() || explorationSlug(name);
|
|
2725
2990
|
if (!name) out.push('Name the exploration, for example "Denser data tables".');
|
|
2726
2991
|
else if (name.length > 60) out.push("Keep the name under 60 characters: it becomes the branch name.");
|
|
2727
2992
|
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 (!
|
|
2993
|
+
if (!e2.purpose.trim()) out.push("Say what you want to learn, in one sentence.");
|
|
2729
2994
|
return out;
|
|
2730
2995
|
}
|
|
2731
2996
|
const useCaseLabel = (u2) => EXPLORATION_USE_CASES.find((x) => x.value === u2)?.label;
|
|
2732
|
-
function explorationNote(
|
|
2733
|
-
const slug =
|
|
2734
|
-
const uc = useCaseLabel(
|
|
2997
|
+
function explorationNote(e2) {
|
|
2998
|
+
const slug = e2.slug || explorationSlug(e2.name);
|
|
2999
|
+
const uc = useCaseLabel(e2.useCase);
|
|
2735
3000
|
return [
|
|
2736
|
-
`# Exploration: ${
|
|
3001
|
+
`# Exploration: ${e2.name.trim()}`,
|
|
2737
3002
|
"",
|
|
2738
|
-
|
|
3003
|
+
e2.purpose.trim(),
|
|
2739
3004
|
"",
|
|
2740
3005
|
`- Branch: \`${EXPLORATION_PREFIX}${slug}\``,
|
|
2741
3006
|
...uc ? [`- Use case: ${uc}`] : [],
|
|
2742
|
-
...
|
|
3007
|
+
...e2.startedAt ? [`- Started: ${e2.startedAt.slice(0, 10)}${e2.author ? ` by @${e2.author}` : ""}`] : [],
|
|
2743
3008
|
"",
|
|
2744
3009
|
"This file marks the branch as an exploration. It is never merged as is: promoting the pull request to review removes it.",
|
|
2745
3010
|
""
|
|
2746
3011
|
].join("\n");
|
|
2747
3012
|
}
|
|
2748
|
-
function explorationPrTitle(
|
|
2749
|
-
const n2 =
|
|
3013
|
+
function explorationPrTitle(e2) {
|
|
3014
|
+
const n2 = e2.name.trim().replace(/\.$/, "");
|
|
2750
3015
|
return `Exploration: ${n2.charAt(0).toLowerCase()}${n2.slice(1)}`;
|
|
2751
3016
|
}
|
|
2752
|
-
function explorationPrBody(
|
|
2753
|
-
const slug =
|
|
3017
|
+
function explorationPrBody(e2, ctx) {
|
|
3018
|
+
const slug = e2.slug || explorationSlug(e2.name);
|
|
2754
3019
|
const branch = `${EXPLORATION_PREFIX}${slug}`;
|
|
2755
|
-
const uc = useCaseLabel(
|
|
2756
|
-
const experimental = (
|
|
3020
|
+
const uc = useCaseLabel(e2.useCase);
|
|
3021
|
+
const experimental = (e2.experimental ?? []).map((x) => x.trim()).filter(Boolean);
|
|
2757
3022
|
const lines = [
|
|
2758
|
-
`## Exploration: ${
|
|
3023
|
+
`## Exploration: ${e2.name.trim()}`,
|
|
2759
3024
|
"",
|
|
2760
3025
|
`> 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
3026
|
"",
|
|
2762
|
-
`**Purpose.** ${
|
|
3027
|
+
`**Purpose.** ${e2.purpose.trim()}`,
|
|
2763
3028
|
...uc ? ["", `**Use case.** ${uc}`] : [],
|
|
2764
3029
|
"",
|
|
2765
3030
|
"### What is experimental",
|
|
@@ -2784,7 +3049,7 @@ function explorationPrBody(e3, ctx) {
|
|
|
2784
3049
|
`- **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
3050
|
"- **Close:** close this pull request. The branch stays for reference; the preview prerelease is removed.",
|
|
2786
3051
|
"",
|
|
2787
|
-
`<!-- ${MARK$1} ${JSON.stringify({ name:
|
|
3052
|
+
`<!-- ${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
3053
|
);
|
|
2789
3054
|
return lines.join("\n");
|
|
2790
3055
|
}
|
|
@@ -2793,20 +3058,20 @@ function parseExplorationBody(body) {
|
|
|
2793
3058
|
const m2 = new RegExp(`<!-- ${MARK$1} (\\{[\\s\\S]*?\\}) -->`).exec(body);
|
|
2794
3059
|
if (!m2) return void 0;
|
|
2795
3060
|
try {
|
|
2796
|
-
const
|
|
2797
|
-
return
|
|
3061
|
+
const e2 = JSON.parse(m2[1]);
|
|
3062
|
+
return e2 && typeof e2.name === "string" && typeof e2.purpose === "string" ? e2 : void 0;
|
|
2798
3063
|
} catch {
|
|
2799
3064
|
return void 0;
|
|
2800
3065
|
}
|
|
2801
3066
|
}
|
|
2802
3067
|
const labelNames = (labels) => labels.map((l2) => typeof l2 === "string" ? l2 : l2.name ?? "");
|
|
2803
|
-
function isExplorationPull(
|
|
2804
|
-
return isExplorationBranch(
|
|
3068
|
+
function isExplorationPull(p) {
|
|
3069
|
+
return isExplorationBranch(p.headRef) || labelNames(p.labels).includes(EXPLORATION_LABEL) || Boolean(parseExplorationBody(p.body));
|
|
2805
3070
|
}
|
|
2806
|
-
function explorationStatus(
|
|
2807
|
-
const merged = Boolean(
|
|
2808
|
-
if (
|
|
2809
|
-
if (
|
|
3071
|
+
function explorationStatus(p) {
|
|
3072
|
+
const merged = Boolean(p.merged || p.merged_at || p.state === "merged");
|
|
3073
|
+
if (p.state !== "open" && !merged) return "closed";
|
|
3074
|
+
if (p.state === "open" && labelNames(p.labels).includes(EXPLORATION_LABEL)) return "active";
|
|
2810
3075
|
return "promoted";
|
|
2811
3076
|
}
|
|
2812
3077
|
const isActiveExploration = (labels) => labelNames(labels).includes(EXPLORATION_LABEL);
|
|
@@ -2852,7 +3117,7 @@ function explorationTryIt(input) {
|
|
|
2852
3117
|
function explorationShareMessage(input) {
|
|
2853
3118
|
const name = input.name.trim().replace(/"/g, "'");
|
|
2854
3119
|
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((
|
|
3120
|
+
const previews = (input.previews ?? []).map((p) => `${p.label} ${p.url}`);
|
|
2856
3121
|
return [head, previews.length ? `Previews: ${previews.join(", ")}.` : "", `Pull request: ${input.url}.`].filter(Boolean).join(" ");
|
|
2857
3122
|
}
|
|
2858
3123
|
function explorationRules(branch) {
|
|
@@ -2944,8 +3209,8 @@ function declineComment(reason, by2) {
|
|
|
2944
3209
|
}
|
|
2945
3210
|
function requestStatus(issue) {
|
|
2946
3211
|
const prs = issue.linkedPrs ?? [];
|
|
2947
|
-
if (issue.state === "open") return prs.some((
|
|
2948
|
-
if (prs.some((
|
|
3212
|
+
if (issue.state === "open") return prs.some((p) => p.state === "open") ? "in-progress" : "requested";
|
|
3213
|
+
if (prs.some((p) => p.merged) || issue.stateReason === "completed") return "done";
|
|
2949
3214
|
return "declined";
|
|
2950
3215
|
}
|
|
2951
3216
|
const REQUEST_STATUS_LABEL = { requested: "Requested", "in-progress": "In progress", done: "Done", declined: "Declined" };
|
|
@@ -3052,7 +3317,7 @@ function draftImagePath(sessionId, itemId, label) {
|
|
|
3052
3317
|
return `${DRAFTS_DIR}/${sessionId}/${item}-${label}.png`;
|
|
3053
3318
|
}
|
|
3054
3319
|
function composePendingDrafts(input) {
|
|
3055
|
-
const
|
|
3320
|
+
const p = {
|
|
3056
3321
|
version: DRAFT_EXCHANGE_VERSION,
|
|
3057
3322
|
sessionId: input.sessionId ?? newDraftSessionId(input.random),
|
|
3058
3323
|
createdAt: (input.now ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -3060,9 +3325,9 @@ function composePendingDrafts(input) {
|
|
|
3060
3325
|
source: input.source ?? "figma",
|
|
3061
3326
|
items: input.items.map((i2) => ({ ...i2, changes: i2.changes ?? [] }))
|
|
3062
3327
|
};
|
|
3063
|
-
const problems = validatePendingDrafts(
|
|
3328
|
+
const problems = validatePendingDrafts(p);
|
|
3064
3329
|
if (problems.length) throw new Error(problems.join(" "));
|
|
3065
|
-
return
|
|
3330
|
+
return p;
|
|
3066
3331
|
}
|
|
3067
3332
|
const KINDS = new Set(REQUEST_KINDS.map((k3) => k3.value));
|
|
3068
3333
|
const str = (x) => typeof x === "string";
|
|
@@ -3070,15 +3335,15 @@ const optStr = (x) => x === void 0 || typeof x === "string";
|
|
|
3070
3335
|
function validatePendingDrafts(x) {
|
|
3071
3336
|
const out = [];
|
|
3072
3337
|
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(
|
|
3338
|
+
const p = x;
|
|
3339
|
+
if (p.version !== DRAFT_EXCHANGE_VERSION) out.push(`Unsupported draft session version ${String(p.version)}: update Bridge.`);
|
|
3340
|
+
if (!isDraftSessionId(p.sessionId)) out.push("The session id is missing or invalid.");
|
|
3341
|
+
if (!str(p.createdAt) || Number.isNaN(Date.parse(p.createdAt))) out.push("createdAt must be an ISO date.");
|
|
3342
|
+
if (p.status !== "pending" && p.status !== "drafted") out.push('status must be "pending" or "drafted".');
|
|
3343
|
+
if (p.source !== "figma" && p.source !== "studio") out.push('source must be "figma" or "studio".');
|
|
3344
|
+
if (!Array.isArray(p.items) || !p.items.length) out.push("A draft session needs at least one item.");
|
|
3080
3345
|
const ids = /* @__PURE__ */ new Set();
|
|
3081
|
-
for (const [n2, i2] of (Array.isArray(
|
|
3346
|
+
for (const [n2, i2] of (Array.isArray(p.items) ? p.items : []).entries()) {
|
|
3082
3347
|
const at2 = `Item ${n2 + 1}`;
|
|
3083
3348
|
if (!i2 || typeof i2 !== "object") {
|
|
3084
3349
|
out.push(`${at2} is not an object.`);
|
|
@@ -3089,9 +3354,9 @@ function validatePendingDrafts(x) {
|
|
|
3089
3354
|
else ids.add(i2.id);
|
|
3090
3355
|
if (i2.scope !== "request" && i2.scope !== "push" && i2.scope !== "docs") out.push(`${at2}: scope must be "request", "push" or "docs".`);
|
|
3091
3356
|
if (i2.scope === "docs") {
|
|
3092
|
-
const
|
|
3093
|
-
if (!
|
|
3094
|
-
else out.push(...validateDocsTask(
|
|
3357
|
+
const d2 = i2.docs;
|
|
3358
|
+
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}.`);
|
|
3359
|
+
else out.push(...validateDocsTask(d2.task).map((m2) => `${at2}: ${m2}`));
|
|
3095
3360
|
} else if (i2.docs !== void 0) out.push(`${at2}: only a docs item has docs.`);
|
|
3096
3361
|
if (!optStr(i2.component)) out.push(`${at2}: component must be text.`);
|
|
3097
3362
|
if (i2.kind !== void 0 && !KINDS.has(i2.kind)) out.push(`${at2}: unknown kind "${String(i2.kind)}".`);
|
|
@@ -3102,10 +3367,10 @@ function validatePendingDrafts(x) {
|
|
|
3102
3367
|
if (!i2.draft || !str(i2.draft.title) || !str(i2.draft.summary)) out.push(`${at2}: draft must hold a title and a summary.`);
|
|
3103
3368
|
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
3369
|
}
|
|
3105
|
-
if (
|
|
3106
|
-
if (!Array.isArray(
|
|
3370
|
+
if (p.results !== void 0) {
|
|
3371
|
+
if (!Array.isArray(p.results)) out.push("results must be a list.");
|
|
3107
3372
|
else
|
|
3108
|
-
for (const r2 of
|
|
3373
|
+
for (const r2 of p.results) {
|
|
3109
3374
|
if (!r2 || !str(r2.id) || !str(r2.title) || !str(r2.summary)) out.push("Each result needs an id, a title and a summary.");
|
|
3110
3375
|
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
3376
|
else if (!optStr(r2.replacement)) out.push("A result replacement must be text.");
|
|
@@ -3124,8 +3389,8 @@ function parsePendingDrafts(text) {
|
|
|
3124
3389
|
}
|
|
3125
3390
|
return validatePendingDrafts(json).length ? void 0 : json;
|
|
3126
3391
|
}
|
|
3127
|
-
function serializePendingDrafts(
|
|
3128
|
-
return `${JSON.stringify(
|
|
3392
|
+
function serializePendingDrafts(p) {
|
|
3393
|
+
return `${JSON.stringify(p, null, 2)}
|
|
3129
3394
|
`;
|
|
3130
3395
|
}
|
|
3131
3396
|
function clip(text, max) {
|
|
@@ -3181,12 +3446,12 @@ function sanitizeDraftTitle(raw) {
|
|
|
3181
3446
|
const t3 = clip(cleanDraftText(raw.replace(/[\r\n]+/g, " ")), DRAFT_TITLE_MAX);
|
|
3182
3447
|
return t3.charAt(0).toUpperCase() + t3.slice(1);
|
|
3183
3448
|
}
|
|
3184
|
-
function mergeDraftResults(
|
|
3185
|
-
const ids = new Set(
|
|
3186
|
-
const next = new Map((
|
|
3449
|
+
function mergeDraftResults(p, results, at2 = /* @__PURE__ */ new Date()) {
|
|
3450
|
+
const ids = new Set(p.items.map((i2) => i2.id));
|
|
3451
|
+
const next = new Map((p.results ?? []).map((r2) => [r2.id, r2]));
|
|
3187
3452
|
for (const r2 of results) {
|
|
3188
|
-
if (!ids.has(r2.id)) throw new Error(`Session ${
|
|
3189
|
-
const item =
|
|
3453
|
+
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(", ")}.`);
|
|
3454
|
+
const item = p.items.find((i2) => i2.id === r2.id);
|
|
3190
3455
|
if (item.scope === "docs") {
|
|
3191
3456
|
next.set(r2.id, { id: r2.id, title: "", summary: "", ...sanitizeDocsResult(item, r2), by: "claude", at: at2.toISOString() });
|
|
3192
3457
|
continue;
|
|
@@ -3196,8 +3461,8 @@ function mergeDraftResults(p2, results, at2 = /* @__PURE__ */ new Date()) {
|
|
|
3196
3461
|
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
3462
|
next.set(r2.id, { id: r2.id, title: clean.title, summary: clean.summary, ...lines.length ? { lines } : {}, by: "claude", at: at2.toISOString() });
|
|
3198
3463
|
}
|
|
3199
|
-
const merged =
|
|
3200
|
-
return { ...
|
|
3464
|
+
const merged = p.items.map((i2) => next.get(i2.id)).filter((r2) => !!r2);
|
|
3465
|
+
return { ...p, results: merged, status: merged.length === p.items.length ? "drafted" : "pending" };
|
|
3201
3466
|
}
|
|
3202
3467
|
function draftItemBrief(i2) {
|
|
3203
3468
|
if (i2.scope === "docs" && i2.docs) {
|
|
@@ -3232,11 +3497,11 @@ function draftItemBrief(i2) {
|
|
|
3232
3497
|
return lines.join("\n");
|
|
3233
3498
|
}
|
|
3234
3499
|
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(
|
|
3500
|
+
async function createPendingDrafts(client, p, branch = REQUESTS_BRANCH) {
|
|
3501
|
+
const problems = validatePendingDrafts(p);
|
|
3237
3502
|
if (problems.length) throw new Error(problems.join(" "));
|
|
3238
|
-
await client.putFile({ path: draftSessionPath(
|
|
3239
|
-
return
|
|
3503
|
+
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` });
|
|
3504
|
+
return p;
|
|
3240
3505
|
}
|
|
3241
3506
|
async function readPendingDrafts(client, sessionId, branch = REQUESTS_BRANCH) {
|
|
3242
3507
|
const f = await client.getFile(draftSessionPath(sessionId), branch);
|
|
@@ -3245,16 +3510,16 @@ async function readPendingDrafts(client, sessionId, branch = REQUESTS_BRANCH) {
|
|
|
3245
3510
|
}
|
|
3246
3511
|
async function listPendingDrafts(client, opts = {}) {
|
|
3247
3512
|
const branch = opts.branch ?? REQUESTS_BRANCH;
|
|
3248
|
-
const entries = (await client.listDirectory(DRAFTS_DIR, branch)).filter((
|
|
3513
|
+
const entries = (await client.listDirectory(DRAFTS_DIR, branch)).filter((e2) => e2.type === "file" && /\.json$/.test(e2.name));
|
|
3249
3514
|
const out = [];
|
|
3250
|
-
for (const
|
|
3251
|
-
const id =
|
|
3515
|
+
for (const e2 of entries) {
|
|
3516
|
+
const id = e2.name.replace(/\.json$/, "");
|
|
3252
3517
|
if (!isDraftSessionId(id)) continue;
|
|
3253
3518
|
const r2 = await readPendingDrafts(client, id, branch).catch(() => void 0);
|
|
3254
3519
|
if (r2 && r2.drafts.sessionId === id) out.push(r2.drafts);
|
|
3255
3520
|
}
|
|
3256
3521
|
const status = opts.status ?? "all";
|
|
3257
|
-
return out.filter((
|
|
3522
|
+
return out.filter((p) => status === "all" || p.status === status).sort((a, b) => b.createdAt.localeCompare(a.createdAt));
|
|
3258
3523
|
}
|
|
3259
3524
|
async function updatePendingDrafts(client, sessionId, change, opts = {}) {
|
|
3260
3525
|
const branch = opts.branch ?? REQUESTS_BRANCH;
|
|
@@ -3272,7 +3537,7 @@ async function updatePendingDrafts(client, sessionId, change, opts = {}) {
|
|
|
3272
3537
|
}
|
|
3273
3538
|
}
|
|
3274
3539
|
function writeDraftResults(client, sessionId, results, opts = {}) {
|
|
3275
|
-
return updatePendingDrafts(client, sessionId, (
|
|
3540
|
+
return updatePendingDrafts(client, sessionId, (p) => mergeDraftResults(p, results, opts.now), {
|
|
3276
3541
|
...opts,
|
|
3277
3542
|
message: `Bridge drafts: ${results.length === 1 ? "1 text" : `${results.length} texts`} drafted by Claude`
|
|
3278
3543
|
});
|
|
@@ -3284,7 +3549,7 @@ async function deletePendingDrafts(client, sessionId, opts = {}) {
|
|
|
3284
3549
|
if (!f) return false;
|
|
3285
3550
|
await client.deleteFile({ path, branch, message: "Bridge drafts: done", sha: f.sha });
|
|
3286
3551
|
const images = await client.listDirectory(`${DRAFTS_DIR}/${sessionId}`, branch).catch(() => []);
|
|
3287
|
-
for (const img of images.filter((
|
|
3552
|
+
for (const img of images.filter((e2) => e2.type === "file")) {
|
|
3288
3553
|
await client.deleteFile({ path: img.path, branch, message: "Bridge drafts: done", sha: img.sha }).catch(() => void 0);
|
|
3289
3554
|
}
|
|
3290
3555
|
return true;
|
|
@@ -3378,38 +3643,40 @@ class DraftPoller {
|
|
|
3378
3643
|
}
|
|
3379
3644
|
}
|
|
3380
3645
|
}
|
|
3381
|
-
function explorationSummary(
|
|
3382
|
-
const
|
|
3383
|
-
const slug =
|
|
3646
|
+
function explorationSummary(p, _owner, _repo, s) {
|
|
3647
|
+
const e2 = parseExplorationBody(p.body);
|
|
3648
|
+
const slug = p.headRef?.startsWith(EXPLORATION_PREFIX) ? p.headRef.slice(EXPLORATION_PREFIX.length) : e2?.slug ?? explorationSlug(p.title);
|
|
3384
3649
|
return {
|
|
3385
|
-
number:
|
|
3386
|
-
url:
|
|
3387
|
-
branch:
|
|
3650
|
+
number: p.number,
|
|
3651
|
+
url: p.url,
|
|
3652
|
+
branch: p.headRef,
|
|
3388
3653
|
slug,
|
|
3389
|
-
name:
|
|
3390
|
-
purpose:
|
|
3391
|
-
...
|
|
3392
|
-
status: explorationStatus({ state:
|
|
3393
|
-
draft:
|
|
3394
|
-
author:
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3654
|
+
name: e2?.name ?? p.title.replace(/^Exploration:\s*/i, ""),
|
|
3655
|
+
purpose: e2?.purpose ?? "",
|
|
3656
|
+
...e2?.useCase ? { useCase: e2.useCase } : {},
|
|
3657
|
+
status: explorationStatus({ state: p.state, labels: p.labels, headRef: p.headRef, body: p.body }),
|
|
3658
|
+
draft: p.draft,
|
|
3659
|
+
author: e2?.author ?? p.author,
|
|
3660
|
+
...e2?.source ? { source: e2.source } : {},
|
|
3661
|
+
...e2?.agent ? { agent: e2.agent } : {},
|
|
3662
|
+
updatedAt: p.updatedAt,
|
|
3663
|
+
headSha: p.headSha,
|
|
3664
|
+
previews: previewLinks(s.apps, { branch: p.headRef, pr: p.number, sha: p.headSha })
|
|
3398
3665
|
};
|
|
3399
3666
|
}
|
|
3400
3667
|
async function listExplorationsOnGitHub(gh, s, opts = {}) {
|
|
3401
3668
|
const pulls = await gh.listPullRequests(opts.state ?? "open");
|
|
3402
|
-
return pulls.filter((
|
|
3669
|
+
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
3670
|
}
|
|
3404
|
-
async function startExplorationOnGitHub(gh,
|
|
3405
|
-
const problems = explorationProblems(
|
|
3671
|
+
async function startExplorationOnGitHub(gh, e2, s) {
|
|
3672
|
+
const problems = explorationProblems(e2);
|
|
3406
3673
|
if (problems.length) throw new GitHubError(422, problems.join(" "));
|
|
3407
3674
|
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 =
|
|
3675
|
+
const slug = e2.slug || explorationSlug(e2.name);
|
|
3409
3676
|
const branch = `${EXPLORATION_PREFIX}${slug}`;
|
|
3410
3677
|
const x = gh.withBranchPrefix(EXPLORATION_PREFIX);
|
|
3411
3678
|
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 = { ...
|
|
3679
|
+
const full = { ...e2, slug, startedAt: e2.startedAt ?? (/* @__PURE__ */ new Date()).toISOString() };
|
|
3413
3680
|
const commit = await x.commitToNewBranch({
|
|
3414
3681
|
base: s.base,
|
|
3415
3682
|
branch,
|
|
@@ -3434,21 +3701,21 @@ Exploration: never merged as is. Promote it to review or close it.`,
|
|
|
3434
3701
|
previews: previewLinks(s.apps, { branch, pr: pr.number, sha: commit.sha })
|
|
3435
3702
|
};
|
|
3436
3703
|
}
|
|
3437
|
-
async function explorationTryItOnGitHub(gh,
|
|
3438
|
-
const [comments, head] = await Promise.all([gh.listIssueComments(
|
|
3704
|
+
async function explorationTryItOnGitHub(gh, e2) {
|
|
3705
|
+
const [comments, head] = await Promise.all([gh.listIssueComments(e2.number), gh.getBranchSha(e2.branch).catch(() => void 0)]);
|
|
3439
3706
|
return explorationTryIt({ comment: findPreviewComment(comments)?.body, headSha: head });
|
|
3440
3707
|
}
|
|
3441
|
-
async function explorationTestBuildOnGitHub(gh,
|
|
3442
|
-
const [comments, head] = await Promise.all([gh.listIssueComments(
|
|
3708
|
+
async function explorationTestBuildOnGitHub(gh, e2, config) {
|
|
3709
|
+
const [comments, head] = await Promise.all([gh.listIssueComments(e2.number), gh.getBranchSha(e2.branch).catch(() => void 0)]);
|
|
3443
3710
|
const tryIt = explorationTryIt({ comment: findPreviewComment(comments)?.body, headSha: head });
|
|
3444
|
-
const previews = await branchPreviewUrls(gh,
|
|
3711
|
+
const previews = await branchPreviewUrls(gh, e2.number, config, { branch: e2.branch, sha: head, comments }).catch(() => []);
|
|
3445
3712
|
return { tryIt, previews, settled: tryIt.state !== "building" && previewsSettled(previews) };
|
|
3446
3713
|
}
|
|
3447
3714
|
async function findExplorationOnGitHub(gh, ref) {
|
|
3448
3715
|
if (typeof ref === "number" || /^#?\d+$/.test(String(ref))) {
|
|
3449
|
-
const
|
|
3450
|
-
if (!isExplorationPull({ state:
|
|
3451
|
-
return
|
|
3716
|
+
const p = await gh.getPullRequest(Number(String(ref).replace(/^#/, "")));
|
|
3717
|
+
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.`);
|
|
3718
|
+
return p;
|
|
3452
3719
|
}
|
|
3453
3720
|
const branch = `${EXPLORATION_PREFIX}${explorationSlug(String(ref))}`;
|
|
3454
3721
|
const open = await gh.findOpenPullRequest(branch);
|
|
@@ -3456,36 +3723,36 @@ async function findExplorationOnGitHub(gh, ref) {
|
|
|
3456
3723
|
return gh.getPullRequest(open.number);
|
|
3457
3724
|
}
|
|
3458
3725
|
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:
|
|
3726
|
+
const p = await findExplorationOnGitHub(gh, ref);
|
|
3727
|
+
const status = explorationStatus({ state: p.state, labels: p.labels, headRef: p.headRef, body: p.body });
|
|
3728
|
+
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.`);
|
|
3729
|
+
const files = await gh.listPullRequestFiles(p.number);
|
|
3730
|
+
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 });
|
|
3731
|
+
const out = { ...plan, number: p.number, url: p.url, branch: p.headRef, warnings: [] };
|
|
3465
3732
|
if (opts.dryRun) return out;
|
|
3466
3733
|
if (!gh.canWrite) throw new GitHubError(401, "Promoting an exploration needs a GitHub token (contents and pull requests, read and write).");
|
|
3467
3734
|
const x = gh.withBranchPrefix(EXPLORATION_PREFIX);
|
|
3468
3735
|
if (plan.removeFiles.length) {
|
|
3469
|
-
await x.commitToBranch({ branch:
|
|
3470
|
-
if (
|
|
3471
|
-
out.warnings.push(`The exploration note stays on the branch (${
|
|
3736
|
+
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) => {
|
|
3737
|
+
if (e2 instanceof GitHubError && e2.auth === "rejected") throw e2;
|
|
3738
|
+
out.warnings.push(`The exploration note stays on the branch (${e2.message}). Delete ${plan.removeFiles.join(", ")} before merging.`);
|
|
3472
3739
|
});
|
|
3473
3740
|
}
|
|
3474
|
-
await x.addLabels(
|
|
3475
|
-
await x.removeLabel(
|
|
3476
|
-
if (!(
|
|
3741
|
+
await x.addLabels(p.number, plan.addLabels);
|
|
3742
|
+
await x.removeLabel(p.number, EXPLORATION_LABEL).catch((e2) => {
|
|
3743
|
+
if (!(e2 instanceof GitHubError && e2.status === 404)) out.warnings.push(`The exploration label is still on (${e2.message}). Remove it on GitHub.`);
|
|
3477
3744
|
});
|
|
3478
|
-
await x.updatePullRequest(
|
|
3479
|
-
await x.markReadyForReview(
|
|
3480
|
-
out.warnings.push(`Still a draft: ${
|
|
3745
|
+
await x.updatePullRequest(p.number, { title: plan.title, body: plan.body });
|
|
3746
|
+
await x.markReadyForReview(p.number).catch((e2) => {
|
|
3747
|
+
out.warnings.push(`Still a draft: ${e2.message}`);
|
|
3481
3748
|
});
|
|
3482
3749
|
return out;
|
|
3483
3750
|
}
|
|
3484
3751
|
async function closeExplorationOnGitHub(gh, ref) {
|
|
3485
3752
|
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:
|
|
3753
|
+
const p = await findExplorationOnGitHub(gh, ref);
|
|
3754
|
+
await gh.updatePullRequest(p.number, { state: "closed" });
|
|
3755
|
+
return { number: p.number, url: p.url, branch: p.headRef };
|
|
3489
3756
|
}
|
|
3490
3757
|
async function linkRequestToExplorationOnGitHub(gh, n2, branch, opts = {}) {
|
|
3491
3758
|
if (!isExplorationBranch(branch)) throw new GitHubError(422, `"${branch}" is not an exploration branch. Exploration branches are named explore/<name>.`);
|
|
@@ -3519,27 +3786,27 @@ async function declineRequestOnGitHub(gh, n2, reason, by2) {
|
|
|
3519
3786
|
await gh.updateIssue(n2, { state: "closed", state_reason: "not_planned" });
|
|
3520
3787
|
}
|
|
3521
3788
|
export {
|
|
3522
|
-
|
|
3523
|
-
|
|
3789
|
+
ai as AAA_MIN,
|
|
3790
|
+
aj as AA_MIN,
|
|
3524
3791
|
ADMIN_ROLE_ID,
|
|
3525
3792
|
ANTHROPIC_MESSAGES_URL,
|
|
3526
3793
|
BELL,
|
|
3527
3794
|
BRAND_FONTS,
|
|
3528
3795
|
BRAND_SLUG_RE,
|
|
3529
|
-
|
|
3796
|
+
ak as BULK_LIMIT,
|
|
3530
3797
|
k as COLOR_FIELDS,
|
|
3531
3798
|
C as COMPONENT_SPECS_VERSION,
|
|
3532
3799
|
CONFIG_FILE,
|
|
3533
|
-
|
|
3534
|
-
|
|
3800
|
+
al as CONTRAST_CELL_MAX,
|
|
3801
|
+
am as CONTRAST_CHECKS_FILE,
|
|
3535
3802
|
ConfigError,
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3803
|
+
an as DEFAULT_COMPONENT_SPECS_FILE,
|
|
3804
|
+
ao as DEFAULT_GOVERNANCE,
|
|
3805
|
+
ap as DEFAULT_OVERRIDE,
|
|
3539
3806
|
DEFAULT_PROTECTED_BRANCHES,
|
|
3540
|
-
|
|
3807
|
+
aq as DEFAULT_RIGHTS,
|
|
3541
3808
|
DEFAULT_TOKEN_PATHS,
|
|
3542
|
-
|
|
3809
|
+
ar as DEFAULT_VISUAL_IMPACT,
|
|
3543
3810
|
DEMO_RELEASES,
|
|
3544
3811
|
DOCS_CHANGE_BUMP,
|
|
3545
3812
|
DOCS_DRAFT_MODEL,
|
|
@@ -3568,16 +3835,16 @@ export {
|
|
|
3568
3835
|
EXT_SCOPES,
|
|
3569
3836
|
GitHubClient,
|
|
3570
3837
|
GitHubError,
|
|
3571
|
-
|
|
3838
|
+
as as JsonParseError,
|
|
3572
3839
|
LIGHTNESS,
|
|
3573
3840
|
LayeredTokenSet,
|
|
3574
3841
|
NEUTRAL_ANCHORS,
|
|
3575
|
-
|
|
3842
|
+
at as OWNER_GROUPS,
|
|
3576
3843
|
PIN_TOLERANCE,
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3844
|
+
au as PREVIEW_APP_LABELS,
|
|
3845
|
+
av as PREVIEW_COMMENT_MARKER,
|
|
3846
|
+
aw as PREVIEW_PLACEHOLDERS,
|
|
3847
|
+
ax as PRIMITIVE_MODE,
|
|
3581
3848
|
PROSE_KEYS,
|
|
3582
3849
|
PROTECTION_PARTS,
|
|
3583
3850
|
PROTECTION_PART_PHRASE,
|
|
@@ -3587,111 +3854,120 @@ export {
|
|
|
3587
3854
|
REQUEST_KINDS,
|
|
3588
3855
|
REQUEST_LABEL,
|
|
3589
3856
|
REQUEST_STATUS_LABEL,
|
|
3590
|
-
|
|
3591
|
-
|
|
3857
|
+
ay as RIGHTS,
|
|
3858
|
+
az as RIGHT_LABEL,
|
|
3592
3859
|
RISK_LABELS,
|
|
3593
|
-
|
|
3860
|
+
aA as RISK_TIER,
|
|
3594
3861
|
SIMILARITY_THRESHOLD,
|
|
3595
3862
|
SINGLE_MODE,
|
|
3596
|
-
|
|
3863
|
+
aB as SPEC_FIELDS,
|
|
3597
3864
|
STEPS,
|
|
3598
3865
|
TIER_AXIS,
|
|
3599
3866
|
TIER_NAMES,
|
|
3600
|
-
|
|
3867
|
+
aC as TIER_ORDER,
|
|
3601
3868
|
TOKEN_REJECTED_MESSAGE,
|
|
3602
3869
|
TOKEN_SETTINGS_URL,
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3870
|
+
aD as TeamDirectory,
|
|
3871
|
+
aE as UNRELEASED,
|
|
3872
|
+
aF as VISUAL_CHECK_LABEL,
|
|
3606
3873
|
acceptDocsDraft,
|
|
3607
3874
|
acceptDocsText,
|
|
3608
3875
|
addBrandToConfigText,
|
|
3609
|
-
|
|
3610
|
-
|
|
3876
|
+
aG as affectedContrast,
|
|
3877
|
+
d as aliasOf,
|
|
3611
3878
|
aliasTarget,
|
|
3612
3879
|
anatomyLabel,
|
|
3613
3880
|
anchoredRamp,
|
|
3614
3881
|
anthropicReplyText,
|
|
3615
3882
|
anthropicRequest,
|
|
3616
|
-
|
|
3617
|
-
|
|
3883
|
+
aH as appForProject,
|
|
3884
|
+
aI as appUrl,
|
|
3618
3885
|
applyConfirmedRenames,
|
|
3619
3886
|
applyDocsSections,
|
|
3620
3887
|
applyTokenEdits,
|
|
3621
|
-
|
|
3622
|
-
|
|
3888
|
+
applyTokenMerge,
|
|
3889
|
+
aJ as approverEntries,
|
|
3890
|
+
aK as approverLists,
|
|
3623
3891
|
assertSafeBranch,
|
|
3624
3892
|
assignRequestOnGitHub,
|
|
3625
|
-
|
|
3893
|
+
aL as assignVersions,
|
|
3626
3894
|
base64ToUtf8,
|
|
3627
|
-
|
|
3895
|
+
aM as baseBranchOf,
|
|
3628
3896
|
bellChroma,
|
|
3629
|
-
|
|
3897
|
+
aN as branchName,
|
|
3630
3898
|
branchPreviewUrls,
|
|
3631
|
-
|
|
3899
|
+
aO as branchSlug,
|
|
3632
3900
|
brandDisplayName,
|
|
3633
3901
|
brandPreviewVariables,
|
|
3634
3902
|
buildHistoryEntry,
|
|
3635
3903
|
buildReport,
|
|
3636
3904
|
i as buildTokenSet,
|
|
3637
3905
|
buildUsageIndex,
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3906
|
+
aP as canShip,
|
|
3907
|
+
changeEdits,
|
|
3908
|
+
aQ as changeKind,
|
|
3909
|
+
aR as changeTable,
|
|
3910
|
+
aS as changedModes,
|
|
3911
|
+
aT as changesetBump,
|
|
3643
3912
|
checkBrandSlug,
|
|
3644
3913
|
checkLayeredPair,
|
|
3914
|
+
choiceFor,
|
|
3645
3915
|
classifyAuth,
|
|
3646
|
-
|
|
3916
|
+
aU as classifyLayered,
|
|
3647
3917
|
classifyRisk,
|
|
3648
3918
|
cleanDocsText,
|
|
3649
3919
|
cleanDraftText,
|
|
3650
|
-
|
|
3920
|
+
aV as clearRawDeep,
|
|
3651
3921
|
closeExplorationOnGitHub,
|
|
3652
3922
|
closingIssueNumbers,
|
|
3653
3923
|
coerceValue,
|
|
3654
3924
|
collectHistory,
|
|
3655
3925
|
collectRefs,
|
|
3656
|
-
|
|
3657
|
-
|
|
3926
|
+
k2 as colorToCss,
|
|
3927
|
+
l as colorsEqual,
|
|
3928
|
+
combineChanges,
|
|
3929
|
+
combineConflicts,
|
|
3658
3930
|
c as comboKey,
|
|
3659
|
-
|
|
3660
|
-
|
|
3931
|
+
aW as commitMessage,
|
|
3932
|
+
m as compactJson,
|
|
3661
3933
|
componentForPath,
|
|
3662
|
-
|
|
3934
|
+
aX as composeChangeset,
|
|
3663
3935
|
composeFiles,
|
|
3664
3936
|
composeNewBrandBody,
|
|
3665
3937
|
composePendingDrafts,
|
|
3666
|
-
|
|
3667
|
-
|
|
3938
|
+
aY as composePrBody,
|
|
3939
|
+
aZ as composeProposalBody,
|
|
3668
3940
|
composeRequestIssue,
|
|
3669
|
-
|
|
3670
|
-
|
|
3941
|
+
a_ as composite,
|
|
3942
|
+
a$ as contextFor,
|
|
3671
3943
|
contextLabel,
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3944
|
+
b0 as contrastDelta,
|
|
3945
|
+
b1 as contrastDeltaTable,
|
|
3946
|
+
b2 as contrastLevel,
|
|
3947
|
+
b3 as contrastPair,
|
|
3948
|
+
b4 as contrastRatio,
|
|
3677
3949
|
contrastScope,
|
|
3678
|
-
|
|
3950
|
+
b5 as countChanges,
|
|
3679
3951
|
createDocsDraftSession,
|
|
3680
3952
|
createPendingDrafts,
|
|
3681
3953
|
cssModuleClasses,
|
|
3954
|
+
cssOf,
|
|
3955
|
+
cssVarOf,
|
|
3682
3956
|
cssVarsIn,
|
|
3683
3957
|
declineComment,
|
|
3684
3958
|
declineReasonProblems,
|
|
3685
3959
|
declineRequestOnGitHub,
|
|
3686
|
-
|
|
3960
|
+
decodePicks,
|
|
3961
|
+
defaultChoiceFor,
|
|
3962
|
+
b6 as defaultContext,
|
|
3687
3963
|
defaultFile,
|
|
3688
3964
|
defaultScopes,
|
|
3689
|
-
|
|
3690
|
-
|
|
3965
|
+
b7 as defaultTitle,
|
|
3966
|
+
b8 as defineConfig,
|
|
3691
3967
|
deletePendingDrafts,
|
|
3692
3968
|
demoHistory,
|
|
3693
|
-
|
|
3694
|
-
|
|
3969
|
+
b9 as detectLayout,
|
|
3970
|
+
ba as diffLayered,
|
|
3695
3971
|
docsPrompt,
|
|
3696
3972
|
docsSectionShapes,
|
|
3697
3973
|
docsSectionsAnswer,
|
|
@@ -3708,12 +3984,13 @@ export {
|
|
|
3708
3984
|
dtcgColor,
|
|
3709
3985
|
effectiveScopes,
|
|
3710
3986
|
emptyTokenFile,
|
|
3987
|
+
encodePicks,
|
|
3711
3988
|
ensureRequestsBranch,
|
|
3712
|
-
|
|
3713
|
-
|
|
3989
|
+
bb as entriesFor,
|
|
3990
|
+
bc as entryLabel,
|
|
3714
3991
|
evaluatePairs,
|
|
3715
|
-
|
|
3716
|
-
|
|
3992
|
+
bd as expandEntries,
|
|
3993
|
+
be as expandOwners,
|
|
3717
3994
|
explorationBranch,
|
|
3718
3995
|
explorationNote,
|
|
3719
3996
|
explorationNotePath,
|
|
@@ -3729,62 +4006,62 @@ export {
|
|
|
3729
4006
|
explorationTestBuildOnGitHub,
|
|
3730
4007
|
explorationTryIt,
|
|
3731
4008
|
explorationTryItOnGitHub,
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
4009
|
+
bf as fieldChanges,
|
|
4010
|
+
bg as fileModes,
|
|
4011
|
+
bh as filterHistory,
|
|
3735
4012
|
findExplorationOnGitHub,
|
|
3736
4013
|
findPreviewComment,
|
|
3737
4014
|
findReferenceProblems,
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
4015
|
+
bi as findToken,
|
|
4016
|
+
bj as findVercelComment,
|
|
4017
|
+
bk as firstParagraph,
|
|
4018
|
+
n as flattenLayerFile,
|
|
4019
|
+
flattenTokens,
|
|
3743
4020
|
formatChangeset,
|
|
3744
4021
|
formatDocsJson,
|
|
3745
|
-
|
|
3746
|
-
|
|
4022
|
+
bl as formatNumber,
|
|
4023
|
+
bm as formatOwnerEntry,
|
|
3747
4024
|
formatValue,
|
|
3748
4025
|
friendlyMessage,
|
|
3749
4026
|
fromHex,
|
|
3750
4027
|
fromPlain,
|
|
3751
4028
|
generateRamp,
|
|
3752
|
-
|
|
4029
|
+
bn as getChild,
|
|
3753
4030
|
getEntry,
|
|
3754
4031
|
gitHubHistoryBackend,
|
|
3755
4032
|
g2 as globToRegExp,
|
|
3756
|
-
|
|
4033
|
+
bo as groupsPhrase,
|
|
3757
4034
|
h as hashSpec,
|
|
3758
4035
|
h2 as hexToRgba,
|
|
3759
|
-
|
|
4036
|
+
bp as holdersOf,
|
|
3760
4037
|
j as iconComponentName,
|
|
3761
4038
|
idExtensionFor,
|
|
3762
4039
|
impactedModules,
|
|
3763
4040
|
importsIn,
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
4041
|
+
bq as indexByPath,
|
|
4042
|
+
br as inheritedTypeAt,
|
|
4043
|
+
bs as installCommand,
|
|
4044
|
+
bt as installCommandFromPreviewComment,
|
|
3768
4045
|
isActiveExploration,
|
|
3769
4046
|
isAlias,
|
|
3770
4047
|
isDocsSelectionMode,
|
|
3771
4048
|
isDraftSessionId,
|
|
3772
4049
|
isExplorationBranch,
|
|
3773
4050
|
isExplorationPull,
|
|
3774
|
-
|
|
4051
|
+
bu as isGuardrail,
|
|
3775
4052
|
isHex,
|
|
3776
4053
|
isMissingPermission,
|
|
3777
4054
|
isMultiMode,
|
|
3778
4055
|
isPlanRefusal,
|
|
3779
4056
|
isProseKey,
|
|
3780
|
-
|
|
3781
|
-
|
|
4057
|
+
bv as isTeamEntry,
|
|
4058
|
+
bw as isTokenFile,
|
|
3782
4059
|
isTokenRejected,
|
|
3783
|
-
|
|
4060
|
+
bx as isVercelComment,
|
|
3784
4061
|
jiraIssueFields,
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
4062
|
+
by as kindForMin,
|
|
4063
|
+
bz as layeredImpact,
|
|
4064
|
+
o as layeredParityIssues,
|
|
3788
4065
|
g as layoutOptions,
|
|
3789
4066
|
levenshtein,
|
|
3790
4067
|
linkRequestToExplorationOnGitHub,
|
|
@@ -3796,16 +4073,17 @@ export {
|
|
|
3796
4073
|
loadProtection,
|
|
3797
4074
|
loadRepoFiles,
|
|
3798
4075
|
loadRequiredChecks,
|
|
3799
|
-
|
|
4076
|
+
bA as luminance,
|
|
3800
4077
|
markClaudeDraft,
|
|
3801
4078
|
mergeDocsDraft,
|
|
3802
4079
|
mergeDraftResults,
|
|
3803
4080
|
mergeReleases,
|
|
4081
|
+
missingChoices,
|
|
3804
4082
|
missingPermissionMessage,
|
|
3805
4083
|
missingProse,
|
|
3806
4084
|
modeDisplayName,
|
|
3807
4085
|
modeFile,
|
|
3808
|
-
|
|
4086
|
+
bB as modeLabel,
|
|
3809
4087
|
modesOf$1 as modesOf,
|
|
3810
4088
|
moveToken,
|
|
3811
4089
|
nearestStep,
|
|
@@ -3814,54 +4092,57 @@ export {
|
|
|
3814
4092
|
neutralOffset,
|
|
3815
4093
|
newBrandSection,
|
|
3816
4094
|
newDraftSessionId,
|
|
3817
|
-
|
|
3818
|
-
|
|
4095
|
+
bC as nodesEqual,
|
|
4096
|
+
bD as normalizeRights,
|
|
3819
4097
|
oklch,
|
|
3820
4098
|
oklchToLinear,
|
|
3821
|
-
|
|
3822
|
-
|
|
4099
|
+
bE as overrideKey,
|
|
4100
|
+
bF as ownerEntryProblems,
|
|
3823
4101
|
packageDirCandidates,
|
|
3824
|
-
|
|
4102
|
+
bG as pairKey,
|
|
3825
4103
|
parseChangelog,
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
4104
|
+
bH as parseChangesetText,
|
|
4105
|
+
parseColor,
|
|
4106
|
+
bI as parseComponentSpecs,
|
|
3829
4107
|
parseContrastChecks,
|
|
3830
|
-
|
|
4108
|
+
q as parseDimension,
|
|
3831
4109
|
parseDocsReply,
|
|
3832
4110
|
parseDocsTaskAnswer,
|
|
3833
4111
|
parseExplorationBody,
|
|
3834
4112
|
parseJson,
|
|
3835
|
-
|
|
4113
|
+
bJ as parseOwnerEntry,
|
|
3836
4114
|
parsePendingDrafts,
|
|
3837
4115
|
parseRepo,
|
|
3838
4116
|
parseRequestIssue,
|
|
3839
4117
|
parseStructureChange,
|
|
3840
4118
|
parseTokenFile,
|
|
3841
|
-
|
|
4119
|
+
bK as parseVercelComment,
|
|
3842
4120
|
patchDocsJson,
|
|
3843
4121
|
pathId,
|
|
3844
4122
|
pathSimilarity,
|
|
3845
4123
|
permissionLabel,
|
|
4124
|
+
pickedOption,
|
|
3846
4125
|
plainEqual,
|
|
3847
4126
|
planNewBrand,
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
4127
|
+
bL as planRestore,
|
|
4128
|
+
bM as planRevert,
|
|
4129
|
+
bN as planToJson,
|
|
4130
|
+
planTokenMerge,
|
|
3851
4131
|
e as prNumberFromSubject,
|
|
3852
4132
|
prefillDocs,
|
|
3853
|
-
|
|
4133
|
+
bO as previewComment,
|
|
3854
4134
|
previewLinks,
|
|
3855
|
-
|
|
3856
|
-
|
|
4135
|
+
bP as previewLinksOf,
|
|
4136
|
+
previewOverrides,
|
|
4137
|
+
bQ as previewUrl,
|
|
3857
4138
|
previewsSettled,
|
|
3858
|
-
|
|
4139
|
+
bR as projectOfLabel,
|
|
3859
4140
|
promoteExplorationOnGitHub,
|
|
3860
4141
|
promotionPlan,
|
|
3861
4142
|
propsTable,
|
|
3862
4143
|
protectLibrary,
|
|
3863
4144
|
protectRefusal,
|
|
3864
|
-
|
|
4145
|
+
bS as protectedBranchesOf,
|
|
3865
4146
|
protectionRuleset,
|
|
3866
4147
|
protectionRulesetName,
|
|
3867
4148
|
protectionSentence,
|
|
@@ -3873,24 +4154,25 @@ export {
|
|
|
3873
4154
|
reconcileIds,
|
|
3874
4155
|
releasesFromTags,
|
|
3875
4156
|
removeToken,
|
|
3876
|
-
|
|
4157
|
+
removedSide,
|
|
4158
|
+
bT as renderedColor,
|
|
3877
4159
|
requestBrief,
|
|
3878
4160
|
requestImagePath,
|
|
3879
4161
|
requestIssueTitle,
|
|
3880
4162
|
requestStatus,
|
|
3881
4163
|
requiredCheckNames,
|
|
3882
4164
|
requiredPermission,
|
|
3883
|
-
|
|
4165
|
+
resolve,
|
|
3884
4166
|
resolveBranchPreviews,
|
|
3885
4167
|
r as rgbaToDtcg,
|
|
3886
|
-
|
|
3887
|
-
|
|
4168
|
+
rgbaToHex,
|
|
4169
|
+
bU as rightsProblems,
|
|
3888
4170
|
round3,
|
|
3889
4171
|
rulesSettingsUrl,
|
|
3890
4172
|
rulesetTargetsDefault,
|
|
3891
|
-
|
|
4173
|
+
bV as runLayeredContrast,
|
|
3892
4174
|
sameScopes,
|
|
3893
|
-
|
|
4175
|
+
bW as sameToken,
|
|
3894
4176
|
sanitizeDocsReplacement,
|
|
3895
4177
|
sanitizeDocsResult,
|
|
3896
4178
|
sanitizeDocsSections,
|
|
@@ -3904,21 +4186,22 @@ export {
|
|
|
3904
4186
|
serializePendingDrafts,
|
|
3905
4187
|
serializeTokenFile,
|
|
3906
4188
|
shadowedEdits,
|
|
3907
|
-
|
|
4189
|
+
bX as shipPolicy,
|
|
3908
4190
|
shortVariant,
|
|
3909
4191
|
shortVariants,
|
|
3910
4192
|
slugify,
|
|
3911
|
-
|
|
3912
|
-
|
|
4193
|
+
bY as snapshotUrl,
|
|
4194
|
+
bZ as specHash,
|
|
3913
4195
|
startExplorationOnGitHub,
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
b$ as
|
|
3917
|
-
c0 as
|
|
3918
|
-
c1 as
|
|
3919
|
-
c2 as
|
|
3920
|
-
c3 as
|
|
3921
|
-
c4 as
|
|
4196
|
+
b_ as summarizeTokenChange,
|
|
4197
|
+
takeMine,
|
|
4198
|
+
b$ as tarballAssetName,
|
|
4199
|
+
c0 as tarballUrl,
|
|
4200
|
+
c1 as teamKey,
|
|
4201
|
+
c2 as teamKeyOf,
|
|
4202
|
+
c3 as teamUnreadableMessage,
|
|
4203
|
+
c4 as teamsIn,
|
|
4204
|
+
threeWayDiff,
|
|
3922
4205
|
tintedNeutral,
|
|
3923
4206
|
c5 as titleOf,
|
|
3924
4207
|
t2 as toAlias,
|
|
@@ -3934,6 +4217,7 @@ export {
|
|
|
3934
4217
|
ca as tokenNode,
|
|
3935
4218
|
tokenUsage,
|
|
3936
4219
|
tokensPath,
|
|
4220
|
+
unpicked,
|
|
3937
4221
|
cb as unreadableShipTeams,
|
|
3938
4222
|
cc as unreadableTeams,
|
|
3939
4223
|
updatePendingDrafts,
|