@bridge-ds/core 0.16.0 → 0.18.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/activity.d.ts +207 -0
- package/dist/audit/index.js +3 -3
- package/dist/index.d.ts +2 -0
- package/dist/index.js +780 -346
- package/dist/index.js.map +1 -1
- package/dist/node/index.js +3 -3
- package/dist/{prod-branch-CSM0c2OF.js → prod-branch-DenQkr_e.js} +128 -115
- package/dist/prod-branch-DenQkr_e.js.map +1 -0
- package/dist/{repo-source-73kARDJv.js → repo-source-CRfhLO87.js} +677 -535
- package/dist/repo-source-CRfhLO87.js.map +1 -0
- package/dist/{report-DbE6yJCw.js → report-DVEmUr0p.js} +2 -2
- package/dist/{report-DbE6yJCw.js.map → report-DVEmUr0p.js.map} +1 -1
- package/dist/review-gate.d.ts +17 -6
- package/dist/rights.d.ts +19 -0
- package/dist/suggestions-github.d.ts +52 -4
- package/dist/suggestions.d.ts +25 -1
- package/dist/titles.d.ts +9 -0
- package/package.json +1 -1
- package/dist/prod-branch-CSM0c2OF.js.map +0 -1
- package/dist/repo-source-73kARDJv.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { cn as tokenBump, aU as changeKind, L as LayeredTokenSet, aL as aliasOf, c1 as rgbaToDtcg, bq as hexToRgba, w as parseTokenFile, q as emptyTokenFile, s as serializeTokenFile, C as moveToken, r as removeToken, bt as inheritedTypeAt, u as upsertToken, H as readTokenAt, a_ as comboKey, c0 as renderedColor, b6 as contrastRatio, z as rgbaToHex, U as classifyRisk, W as RISK_LABELS, V as CONFIG_FILE, N as loadConfig, ah as ConfigError, _ as DEFAULT_TOKEN_PATHS, ag as globToRegExp, ae as slugify, Q as validateLayered, bM as parseJson, c5 as serializeJson, bR as plainEqual, cm as toPlain, bn as getEntry, bl as fromPlain, m as modeDisplayName, A as formatValue, X as previewLinks, a1 as readPreviewComment, aT as canShip, cv as whoCanShipSentence, cp as unreadableShipTeams, c8 as soloApprover, aP as approverLists, O as tokensPath, a6 as NEEDED_BY_LABEL, K as selectTokenFiles, R as parseContrastChecks } from "./prod-branch-DenQkr_e.js";
|
|
2
2
|
function changesetBump(changes) {
|
|
3
3
|
return tokenBump(changes) ?? "patch";
|
|
4
4
|
}
|
|
@@ -2574,6 +2574,31 @@ function explorationRules(branch) {
|
|
|
2574
2574
|
"- Do not mark the pull request ready for review and do not remove the exploration label: promoting is a person's decision."
|
|
2575
2575
|
];
|
|
2576
2576
|
}
|
|
2577
|
+
const escape = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2578
|
+
function properWord(word, names2) {
|
|
2579
|
+
const bare2 = word.replace(/[^\p{L}\p{N}]+$/u, "");
|
|
2580
|
+
if (!bare2) return false;
|
|
2581
|
+
if (names2.some((n) => n === bare2)) return true;
|
|
2582
|
+
return /\p{Lu}/u.test(bare2.slice(1));
|
|
2583
|
+
}
|
|
2584
|
+
function lowerFirstWord(text, names2 = []) {
|
|
2585
|
+
const first = text.split(/\s+/)[0] ?? "";
|
|
2586
|
+
if (!first || properWord(first, names2)) return text;
|
|
2587
|
+
return `${text.charAt(0).toLowerCase()}${text.slice(1)}`;
|
|
2588
|
+
}
|
|
2589
|
+
function subjectTitle(subject, title, names2 = []) {
|
|
2590
|
+
const t = title.trim().replace(/\.$/, "");
|
|
2591
|
+
const keep = [subject, ...names2];
|
|
2592
|
+
const s = subject.trim();
|
|
2593
|
+
if (!s) return t;
|
|
2594
|
+
const m = new RegExp(`^${escape(s)}(?=$|[^\\p{L}\\p{N}])\\s*(:)?\\s*`, "iu").exec(t);
|
|
2595
|
+
if (m) {
|
|
2596
|
+
const rest = t.slice(m[0].length);
|
|
2597
|
+
if (!rest) return s;
|
|
2598
|
+
return m[1] ? `${s}: ${lowerFirstWord(rest, keep)}` : `${s} ${rest}`;
|
|
2599
|
+
}
|
|
2600
|
+
return `${s}: ${lowerFirstWord(t, keep)}`;
|
|
2601
|
+
}
|
|
2577
2602
|
const REQUEST_KINDS = [
|
|
2578
2603
|
{ value: "new-component", label: "New component", hint: "A component the design system does not have yet." },
|
|
2579
2604
|
{ value: "new-variant", label: "New variant", hint: "A size, tone or state the component does not have yet." },
|
|
@@ -2589,8 +2614,7 @@ const MARK$1 = "bridge-request";
|
|
|
2589
2614
|
const kindLabel$1 = (k) => REQUEST_KINDS.find((x) => x.value === k)?.label ?? "Other";
|
|
2590
2615
|
const kindSlug = (k) => `request: ${k}`;
|
|
2591
2616
|
function requestIssueTitle(r) {
|
|
2592
|
-
|
|
2593
|
-
return `${r.component}: ${t.charAt(0).toLowerCase()}${t.slice(1)}`;
|
|
2617
|
+
return subjectTitle(r.component, r.title);
|
|
2594
2618
|
}
|
|
2595
2619
|
function composeRequestIssue(r, opts = {}) {
|
|
2596
2620
|
const rows = r.changes.length ? ["| What | Now | Requested |", "| --- | --- | --- |", ...r.changes.map((c) => `| ${cell$1(c.field)} | ${cell$1(c.before ?? "")} | ${cell$1(c.after ?? "")} |`)] : [];
|
|
@@ -3149,15 +3173,36 @@ function reviewStatusKind(input) {
|
|
|
3149
3173
|
if (input.mergeable === false) return out("conflict");
|
|
3150
3174
|
if (input.ci?.state === "pending") return out("ci-running");
|
|
3151
3175
|
if (!input.ci || !input.approvals) return out("unknown");
|
|
3152
|
-
if (input.mergeable === null) return out("checking");
|
|
3153
3176
|
const tier = input.tier ?? 3;
|
|
3154
3177
|
if (tier >= 2) {
|
|
3155
3178
|
if (!input.approvals.design) return out("needs-design");
|
|
3156
3179
|
if (!input.approvals.tech) return out("needs-tech");
|
|
3157
3180
|
} else if (!input.approvals.approvers.length) return out("needs-approval");
|
|
3181
|
+
if (input.mergeable === null) return out("checking");
|
|
3158
3182
|
const gate = mergeGate({ tier: input.tier, ci: input.ci, approvals: input.approvals, draft: Boolean(input.draft), mergeable: input.mergeable, state: input.state ?? "open", merged_at: input.merged_at });
|
|
3159
3183
|
return gate.allowed ? out("ready") : out("unknown");
|
|
3160
3184
|
}
|
|
3185
|
+
const REVIEW_STATUS_LABEL = {
|
|
3186
|
+
merged: "Merged",
|
|
3187
|
+
closed: "Closed",
|
|
3188
|
+
draft: "Draft",
|
|
3189
|
+
"ci-failing": "Blocked by tests",
|
|
3190
|
+
"changes-requested": "Changes requested",
|
|
3191
|
+
"ci-running": "Tests running",
|
|
3192
|
+
"needs-design": "Needs design approval",
|
|
3193
|
+
"needs-tech": "Needs tech approval",
|
|
3194
|
+
"needs-approval": "Needs approval",
|
|
3195
|
+
conflict: "Conflict with main",
|
|
3196
|
+
checking: "Checking for conflicts",
|
|
3197
|
+
ready: "Ready to merge",
|
|
3198
|
+
unknown: "Status unknown"
|
|
3199
|
+
};
|
|
3200
|
+
const CI_STATE_LABEL = {
|
|
3201
|
+
pass: "Tests pass",
|
|
3202
|
+
fail: "Tests fail",
|
|
3203
|
+
pending: "Tests running",
|
|
3204
|
+
none: "No automated checks"
|
|
3205
|
+
};
|
|
3161
3206
|
const BOTS = /* @__PURE__ */ new Set(["github-actions[bot]", "app/github-actions", "changeset-bot[bot]"]);
|
|
3162
3207
|
const isReleasePull = (p) => p.head.ref.startsWith("changeset-release/");
|
|
3163
3208
|
function isAutomationPull(p) {
|
|
@@ -3539,6 +3584,10 @@ const SUGGESTION_STATE_LABELS = {
|
|
|
3539
3584
|
approved: "suggestion: approved",
|
|
3540
3585
|
applying: "suggestion: applying"
|
|
3541
3586
|
};
|
|
3587
|
+
const SUGGESTION_FINAL_LABELS = {
|
|
3588
|
+
applied: "suggestion: applied",
|
|
3589
|
+
declined: "suggestion: declined"
|
|
3590
|
+
};
|
|
3542
3591
|
const SUGGESTION_STATUS_LABEL = {
|
|
3543
3592
|
"needs-review": "Needs review",
|
|
3544
3593
|
"changes-requested": "Changes requested",
|
|
@@ -3759,8 +3808,8 @@ function suggestionSubject(p) {
|
|
|
3759
3808
|
return KIND_SUBJECT[p.kind];
|
|
3760
3809
|
}
|
|
3761
3810
|
function suggestionIssueTitle(p) {
|
|
3762
|
-
const
|
|
3763
|
-
return
|
|
3811
|
+
const names2 = [...(p.docs ?? []).map((d) => d.component), ...p.componentSpec?.component ? [p.componentSpec.component] : []];
|
|
3812
|
+
return subjectTitle(suggestionSubject(p), p.title, names2);
|
|
3764
3813
|
}
|
|
3765
3814
|
function suggestionLabels(p, opts = {}) {
|
|
3766
3815
|
return [
|
|
@@ -3794,9 +3843,22 @@ function suggestionStatus(i) {
|
|
|
3794
3843
|
if (i.outdated) return "outdated";
|
|
3795
3844
|
return i.review ?? "needs-review";
|
|
3796
3845
|
}
|
|
3846
|
+
function withOutdated(status, outdated) {
|
|
3847
|
+
return outdated && (status === "needs-review" || status === "changes-requested" || status === "approved") ? "outdated" : status;
|
|
3848
|
+
}
|
|
3797
3849
|
function suggestionStateLabel(status) {
|
|
3850
|
+
if (status === "applied" || status === "declined") return SUGGESTION_FINAL_LABELS[status];
|
|
3798
3851
|
return status in SUGGESTION_STATE_LABELS ? SUGGESTION_STATE_LABELS[status] : null;
|
|
3799
3852
|
}
|
|
3853
|
+
const SUGGESTION_STATUS_LABELS = [...Object.values(SUGGESTION_STATE_LABELS), ...Object.values(SUGGESTION_FINAL_LABELS)];
|
|
3854
|
+
function suggestionLabelChanges(labels, status) {
|
|
3855
|
+
if (status === "outdated") return { add: [], remove: [] };
|
|
3856
|
+
const want = suggestionStateLabel(status);
|
|
3857
|
+
return {
|
|
3858
|
+
add: want && !labels.includes(want) ? [want] : [],
|
|
3859
|
+
remove: SUGGESTION_STATUS_LABELS.filter((l) => l !== want && labels.includes(l))
|
|
3860
|
+
};
|
|
3861
|
+
}
|
|
3800
3862
|
const bridgeMcpInstallCommand = (repo) => `claude mcp add bridge -s user -- npx -y @bridge-ds/mcp --repo ${repo?.trim() || "<owner/name>"}`;
|
|
3801
3863
|
const MARK = "bridge-suggestion";
|
|
3802
3864
|
const PART_SCHEMA = "bridge.suggestion-part/1";
|
|
@@ -4185,82 +4247,403 @@ function suggestionPathProblems(p, config) {
|
|
|
4185
4247
|
}
|
|
4186
4248
|
return [...new Set(out)];
|
|
4187
4249
|
}
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
const
|
|
4200
|
-
|
|
4201
|
-
if (created.labels.length && missing.length) warnings.push(`GitHub did not keep these labels on #${created.number}: ${missing.join(", ")}.`);
|
|
4202
|
-
return { number: created.number, url: created.url, sha: issue.sha, parts: issue.parts.length, warnings };
|
|
4250
|
+
const SUGGESTION_APPLY_EXIT = {
|
|
4251
|
+
ready: 0,
|
|
4252
|
+
"already-applied": 0,
|
|
4253
|
+
conflict: 3,
|
|
4254
|
+
"not-approved": 1,
|
|
4255
|
+
blocked: 1,
|
|
4256
|
+
refused: 1
|
|
4257
|
+
};
|
|
4258
|
+
const same = (a, b) => canonicalJson(a) === canonicalJson(b);
|
|
4259
|
+
const show = (v) => v === void 0 ? "nothing" : typeof v === "string" ? v : JSON.stringify(v);
|
|
4260
|
+
function tokenRoot(config) {
|
|
4261
|
+
const r = config.tokens.root.replace(/\/+$/, "");
|
|
4262
|
+
return r === "." ? "" : r;
|
|
4203
4263
|
}
|
|
4204
|
-
function
|
|
4205
|
-
|
|
4264
|
+
function tokenFileOf(config, path) {
|
|
4265
|
+
const root = tokenRoot(config);
|
|
4266
|
+
if (root && !path.startsWith(`${root}/`)) return null;
|
|
4267
|
+
if (!selectTokenFiles(config, [path]).length) return null;
|
|
4268
|
+
return root ? path.slice(root.length + 1) : path;
|
|
4206
4269
|
}
|
|
4207
|
-
|
|
4208
|
-
const
|
|
4209
|
-
const
|
|
4210
|
-
|
|
4270
|
+
function planTokenEdit(set, e, changes) {
|
|
4271
|
+
const base = { type: "token", path: e.path, edit: e, ...e.file ? { file: e.file } : {} };
|
|
4272
|
+
const defsAt = (path) => set.defs.filter((d) => d.path === path && (!e.file || d.file === e.file));
|
|
4273
|
+
const recorded = (file, path = e.path) => changes.find((c) => c.path === path && c.file === file && (e.op === "rename" ? c.kind === "renamed" : true));
|
|
4274
|
+
const conflict = (detail, extra = {}) => ({ op: { ...base, state: "conflict", detail, ...extra }, notes: [] });
|
|
4275
|
+
const done = (detail) => ({ op: { ...base, state: "already-applied", detail }, notes: [] });
|
|
4276
|
+
let result;
|
|
4277
|
+
try {
|
|
4278
|
+
result = applyTokenEdits(set, [e]);
|
|
4279
|
+
} catch (err) {
|
|
4280
|
+
const why = err instanceof Error ? err.message : String(err);
|
|
4281
|
+
switch (e.op) {
|
|
4282
|
+
case "rename": {
|
|
4283
|
+
const hasOld = defsAt(e.path).length > 0;
|
|
4284
|
+
const hasNew = set.defs.some((d) => d.path === e.newPath);
|
|
4285
|
+
if (!hasOld && hasNew) return done(`Already renamed to ${e.newPath}.`);
|
|
4286
|
+
return conflict(hasOld && hasNew ? `Both ${e.path} and ${e.newPath} exist now: rename it by hand.` : `${e.path} no longer exists: ${why}`);
|
|
4287
|
+
}
|
|
4288
|
+
case "remove":
|
|
4289
|
+
if (!defsAt(e.path).length) return done("Already removed.");
|
|
4290
|
+
return conflict(why);
|
|
4291
|
+
case "add": {
|
|
4292
|
+
const existing = defsAt(e.path);
|
|
4293
|
+
if (!existing.length) return conflict(why);
|
|
4294
|
+
const want = (file) => {
|
|
4295
|
+
const c = recorded(file);
|
|
4296
|
+
if (c && c.after !== void 0) return c.after;
|
|
4297
|
+
return coerceValue(e.value, e.type ?? existing.find((d2) => d2.file === file)?.type);
|
|
4298
|
+
};
|
|
4299
|
+
const all = existing.every((d2) => same(d2.value, want(d2.file)));
|
|
4300
|
+
if (all) return done("Already added with the suggested value.");
|
|
4301
|
+
const d = existing.find((x) => !same(x.value, want(x.file)));
|
|
4302
|
+
return conflict(`Someone added ${e.path} with another value since.`, { file: d.file, after: want(d.file), now: d.value });
|
|
4303
|
+
}
|
|
4304
|
+
default:
|
|
4305
|
+
return conflict(defsAt(e.path).length ? why : `${e.path} no longer exists, so its value cannot be set.`);
|
|
4306
|
+
}
|
|
4307
|
+
}
|
|
4308
|
+
if (!result.touched.length) return done("Already has the suggested value.");
|
|
4309
|
+
if (e.op === "set" || e.op === "remove" || e.op === "rename") {
|
|
4310
|
+
for (const d of defsAt(e.path)) {
|
|
4311
|
+
const c = recorded(d.file, e.op === "rename" ? e.newPath ?? e.path : e.path);
|
|
4312
|
+
if (!c) continue;
|
|
4313
|
+
const okNow = same(d.value, c.before) || e.op === "set" && same(d.value, c.after);
|
|
4314
|
+
if (!okNow) return conflict(`${e.path} changed since it was suggested (now ${show(d.value)}, suggested from ${show(c.before)}).`, { file: d.file, before: c.before, after: c.after, now: d.value });
|
|
4315
|
+
}
|
|
4316
|
+
}
|
|
4317
|
+
const verb = e.op === "set" ? "Set" : e.op === "add" ? "Add" : e.op === "remove" ? "Remove" : `Rename to ${e.newPath ?? ""}`;
|
|
4318
|
+
return { op: { ...base, state: "apply", detail: `${verb} in ${result.touched.join(", ")}.` }, files: result.files, notes: result.notes };
|
|
4211
4319
|
}
|
|
4212
|
-
function
|
|
4213
|
-
const
|
|
4214
|
-
const
|
|
4215
|
-
const
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
};
|
|
4320
|
+
async function planFile(ws, baseSha, f) {
|
|
4321
|
+
const base = { type: "file", path: f.path };
|
|
4322
|
+
const cur = await ws.read(f.path);
|
|
4323
|
+
const curSha = cur?.sha ?? null;
|
|
4324
|
+
const theirs = f.afterSha === null ? null : f.content ?? null;
|
|
4325
|
+
if (curSha === f.afterSha) return { op: { ...base, state: "already-applied", detail: f.afterSha === null ? "Already deleted." : "Already has the suggested content." } };
|
|
4326
|
+
if (curSha === f.beforeSha) return { op: { ...base, state: "apply", detail: f.afterSha === null ? "Delete it." : f.beforeSha === null ? "Create it." : "Replace it with the suggested content." }, content: theirs };
|
|
4327
|
+
if (f.beforeSha === null) return { op: { ...base, state: "conflict", detail: "The suggestion creates this file, but a different one exists now." } };
|
|
4328
|
+
if (!cur) return { op: { ...base, state: "conflict", detail: "The suggestion changes this file, but it was deleted since." } };
|
|
4329
|
+
if (theirs === null) return { op: { ...base, state: "conflict", detail: "The suggestion deletes this file, but it changed since." } };
|
|
4330
|
+
const original = await ws.readAt(baseSha, f.path);
|
|
4331
|
+
if (original === null || gitBlobSha(original) !== f.beforeSha || !ws.merge) {
|
|
4332
|
+
return { op: { ...base, state: "conflict", detail: original === null ? "The file changed since and its version at the suggestion base cannot be read, so it cannot be merged." : "The file changed since and cannot be merged here." } };
|
|
4333
|
+
}
|
|
4334
|
+
const merged = await ws.merge({ path: f.path, ours: cur.text, base: original, theirs });
|
|
4335
|
+
if (!merged.clean) return { op: { ...base, state: "conflict", detail: `The file changed since and the merge has ${merged.conflicts} conflict(s).`, hunks: merged.conflicts } };
|
|
4336
|
+
if (merged.text === cur.text) return { op: { ...base, state: "already-applied", detail: "Already has the suggested lines." } };
|
|
4337
|
+
return { op: { ...base, state: "apply-merged", detail: "The file changed since: merged with the suggested lines, no conflict." }, content: merged.text };
|
|
4230
4338
|
}
|
|
4231
|
-
async function
|
|
4232
|
-
const
|
|
4233
|
-
const
|
|
4234
|
-
const
|
|
4235
|
-
const
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4339
|
+
async function planSuggestionApply(input) {
|
|
4340
|
+
const { parsed, config, workspace: ws } = input;
|
|
4341
|
+
const payload = parsed.payload;
|
|
4342
|
+
const notes = [];
|
|
4343
|
+
const out = (outcome, message, rest2 = {}) => ({
|
|
4344
|
+
outcome,
|
|
4345
|
+
exitCode: SUGGESTION_APPLY_EXIT[outcome],
|
|
4346
|
+
message,
|
|
4347
|
+
payloadSha: parsed.sha,
|
|
4348
|
+
payload,
|
|
4349
|
+
gate: null,
|
|
4350
|
+
ops: [],
|
|
4351
|
+
writes: [],
|
|
4352
|
+
checks: null,
|
|
4353
|
+
problems: [],
|
|
4354
|
+
notes,
|
|
4355
|
+
...payload.componentSpec ? { brief: payload.componentSpec.brief } : {},
|
|
4356
|
+
...rest2
|
|
4244
4357
|
});
|
|
4245
|
-
|
|
4246
|
-
}
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4358
|
+
const problems = suggestionPathProblems(payload, config);
|
|
4359
|
+
if (payload.checks.blocked) problems.push(`It was suggested with a failing check (${payload.checks.blocked}): ask the author to fix it and send it again.`);
|
|
4360
|
+
for (const f of payload.files ?? []) {
|
|
4361
|
+
if (f.afterSha !== null && typeof f.content !== "string") problems.push(`"${f.path}" has no content in the suggestion: it is incomplete, ask the author to send it again.`);
|
|
4362
|
+
else if (f.afterSha !== null && gitBlobSha(f.content) !== f.afterSha) problems.push(`"${f.path}" does not match the content it was suggested with: someone edited it by hand.`);
|
|
4363
|
+
}
|
|
4364
|
+
if (problems.length) return out("refused", "Not applied: the suggestion asks for something Bridge never writes.", { problems: [...new Set(problems)] });
|
|
4365
|
+
if (parsed.edited && !input.acceptEdited) {
|
|
4366
|
+
return out("refused", 'Not applied: the suggestion was edited by hand since Bridge wrote it. Read the change, then apply it with "accept edited" if it is right.', {
|
|
4367
|
+
problems: ["The payload no longer matches the sha Bridge recorded."]
|
|
4368
|
+
});
|
|
4369
|
+
}
|
|
4370
|
+
const root = tokenRoot(config);
|
|
4371
|
+
const tokenFiles = selectTokenFiles(config, await ws.list());
|
|
4372
|
+
const targets = [
|
|
4373
|
+
...(payload.files ?? []).map((f) => f.path),
|
|
4374
|
+
...(payload.docs ?? []).map((d) => d.path),
|
|
4375
|
+
...payload.componentSpec ? [payload.componentSpec.specPath] : [],
|
|
4376
|
+
...payload.tokens?.edits.length ? tokenFiles.map((f) => root ? `${root}/${f}` : f) : []
|
|
4377
|
+
];
|
|
4378
|
+
if (ws.isSymlink) {
|
|
4379
|
+
const links = [];
|
|
4380
|
+
for (const p of new Set(targets)) if (await ws.isSymlink(p)) links.push(`"${p}" is a symbolic link (or inside one): Bridge never writes through links.`);
|
|
4381
|
+
if (links.length) return out("refused", "Not applied: the suggestion writes through a symbolic link.", { problems: links });
|
|
4382
|
+
}
|
|
4383
|
+
const governance = input.governance ?? { owners: config.governance.owners, rights: config.governance.rights ?? null, soloOwnerCanApproveBoth: config.governance.soloOwnerCanApproveBoth };
|
|
4384
|
+
const approvals = summarizeSuggestionApprovals(input.comments, governance, { payloadSha: parsed.sha, author: input.author });
|
|
4385
|
+
const review = suggestionReviewState(payload.tier.tier, approvals);
|
|
4386
|
+
const missing = pendingApprovals(payload.tier.tier, approvals);
|
|
4387
|
+
const approved = review === "approved";
|
|
4388
|
+
const gate = { review, missing, approvals, forced: !approved && Boolean(input.forceUnapproved), edited: parsed.edited };
|
|
4389
|
+
const ops = [];
|
|
4390
|
+
const writes = /* @__PURE__ */ new Map();
|
|
4391
|
+
const current = /* @__PURE__ */ new Map();
|
|
4392
|
+
for (const f of tokenFiles) {
|
|
4393
|
+
const r = await ws.read(root ? `${root}/${f}` : f);
|
|
4394
|
+
if (r) current.set(f, r.text);
|
|
4395
|
+
}
|
|
4396
|
+
const before = new LayeredTokenSet(config, current);
|
|
4397
|
+
let set = before;
|
|
4398
|
+
for (const e of payload.tokens?.edits ?? []) {
|
|
4399
|
+
const r = planTokenEdit(set, e, payload.tokens.changes);
|
|
4400
|
+
ops.push(r.op);
|
|
4401
|
+
notes.push(...r.notes);
|
|
4402
|
+
if (r.op.state === "apply" && r.files) {
|
|
4403
|
+
set = new LayeredTokenSet(config, r.files);
|
|
4404
|
+
}
|
|
4405
|
+
}
|
|
4406
|
+
for (const [f, text] of set.files) if (text !== before.files.get(f)) writes.set(set.repoPath(f), text);
|
|
4407
|
+
for (const f of payload.files ?? []) {
|
|
4408
|
+
if (writes.has(f.path)) {
|
|
4409
|
+
notes.push(`${f.path}: the token edits write this file, its copy in the suggestion is not used.`);
|
|
4410
|
+
continue;
|
|
4411
|
+
}
|
|
4412
|
+
const r = await planFile(ws, payload.base.sha, f);
|
|
4413
|
+
ops.push(r.op);
|
|
4414
|
+
if (r.op.state === "apply" || r.op.state === "apply-merged") writes.set(f.path, r.content ?? null);
|
|
4415
|
+
}
|
|
4416
|
+
for (const d of payload.docs ?? []) {
|
|
4417
|
+
const base = { type: "docs", path: d.path };
|
|
4418
|
+
const docs = d.after;
|
|
4419
|
+
let text;
|
|
4252
4420
|
try {
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4421
|
+
const issues = validateDocs(docs);
|
|
4422
|
+
if (issues.length) {
|
|
4423
|
+
problems.push(`${d.path}: ${issues.map((i) => i.message).join(" ")}`);
|
|
4424
|
+
continue;
|
|
4425
|
+
}
|
|
4426
|
+
const cur = await ws.read(d.path);
|
|
4427
|
+
text = docsFileText(docs, cur?.text ?? null);
|
|
4428
|
+
if (cur && text === cur.text) {
|
|
4429
|
+
ops.push({ ...base, state: "already-applied", detail: "The docs already say this." });
|
|
4430
|
+
continue;
|
|
4431
|
+
}
|
|
4432
|
+
ops.push({ ...base, state: "apply", detail: cur ? "Update the changed fields; other fields stay as they are now." : "Create the docs file." });
|
|
4433
|
+
writes.set(d.path, text);
|
|
4434
|
+
} catch (err) {
|
|
4435
|
+
problems.push(`${d.path}: the suggested docs are not a docs.json Bridge can read (${err instanceof Error ? err.message : String(err)}).`);
|
|
4256
4436
|
}
|
|
4257
4437
|
}
|
|
4258
|
-
if (
|
|
4259
|
-
|
|
4260
|
-
}
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4438
|
+
if (payload.componentSpec) {
|
|
4439
|
+
const s = payload.componentSpec;
|
|
4440
|
+
const text = `${JSON.stringify(s.spec, null, 2)}
|
|
4441
|
+
`;
|
|
4442
|
+
const cur = await ws.read(s.specPath);
|
|
4443
|
+
if (cur && cur.text === text) ops.push({ type: "component-spec", path: s.specPath, state: "already-applied", detail: "The spec file is already there." });
|
|
4444
|
+
else {
|
|
4445
|
+
ops.push({ type: "component-spec", path: s.specPath, state: "apply", detail: `Write the spec; the ${s.component} code itself is written from the brief.` });
|
|
4446
|
+
writes.set(s.specPath, text);
|
|
4447
|
+
}
|
|
4448
|
+
}
|
|
4449
|
+
const afterFiles = new Map(set.files);
|
|
4450
|
+
for (const [p, content] of writes) {
|
|
4451
|
+
const tf = tokenFileOf(config, p);
|
|
4452
|
+
if (!tf) continue;
|
|
4453
|
+
if (content === null) afterFiles.delete(tf);
|
|
4454
|
+
else afterFiles.set(tf, content);
|
|
4455
|
+
}
|
|
4456
|
+
const after = new LayeredTokenSet(config, afterFiles);
|
|
4457
|
+
const tokenChanges = diffLayered(before, after);
|
|
4458
|
+
const issueKey2 = (i) => `${i.code}|${i.path ?? ""}|${i.file ?? ""}|${i.message}`;
|
|
4459
|
+
const prevErrors = new Set(validateLayered(before).map(issueKey2));
|
|
4460
|
+
const newErrors = tokenChanges.length ? validateLayered(after).filter((i) => i.level === "error" && !prevErrors.has(issueKey2(i))) : [];
|
|
4461
|
+
let pairs = [];
|
|
4462
|
+
if (config.contrast && tokenChanges.length) {
|
|
4463
|
+
try {
|
|
4464
|
+
pairs = parseContrastChecks((await ws.read(config.contrast.path))?.text);
|
|
4465
|
+
} catch {
|
|
4466
|
+
notes.push(`${config.contrast.path} does not parse: contrast was not checked.`);
|
|
4467
|
+
}
|
|
4468
|
+
}
|
|
4469
|
+
const modes = config.contrast?.modes;
|
|
4470
|
+
const contrast = pairs.length ? contrastDelta(runLayeredContrast(before, pairs, modes), runLayeredContrast(after, pairs, modes)) : [];
|
|
4471
|
+
const newFailures = contrast.filter((d) => d.passBefore && !d.passAfter);
|
|
4472
|
+
const otherFiles = [...writes.keys()].filter((p) => !tokenFileOf(config, p));
|
|
4473
|
+
const tier = classifyRisk({ tokenChanges: tokenChanges.map((c) => ({ kind: c.kind, path: c.path, ...c.kind === "changed" ? { descriptionOnly: c.fields.length > 0 && c.fields.every((x) => x === "description") } : {} })), files: otherFiles.map((path) => ({ path })), config });
|
|
4474
|
+
const tierIncreased = writes.size > 0 && tier.tier > payload.tier.tier;
|
|
4475
|
+
const checks = { tierWhenSuggested: payload.tier.tier, tier, tierIncreased, newErrors, newFailures, contrast, tokenChanges };
|
|
4476
|
+
const rest = { gate, ops, checks, problems };
|
|
4477
|
+
if (problems.length) return out("refused", "Not applied: part of the suggestion is not valid.", rest);
|
|
4478
|
+
const conflicts = ops.filter((o) => o.state === "conflict");
|
|
4479
|
+
if (conflicts.length) {
|
|
4480
|
+
return out("conflict", `Not applied: ${conflicts.length} change(s) conflict with the code now. Ask the author to refresh the suggestion on today's code, or apply those parts by hand.`, rest);
|
|
4481
|
+
}
|
|
4482
|
+
if (!writes.size) return out("already-applied", "Already applied: the code has every change of this suggestion.", rest);
|
|
4483
|
+
if (newErrors.length || newFailures.length) {
|
|
4484
|
+
const why = [newErrors.length ? `${newErrors.length} new validation error(s)` : "", newFailures.length ? `${newFailures.length} contrast pair(s) would fail` : ""].filter(Boolean).join(" and ");
|
|
4485
|
+
return out("blocked", `Not applied: on today's code it would add ${why}. Ask the author to refresh the suggestion.`, rest);
|
|
4486
|
+
}
|
|
4487
|
+
if (tierIncreased && !input.forceUnapproved) {
|
|
4488
|
+
return out("blocked", `Not applied: on today's code it is a tier ${tier.tier} change, it was approved as tier ${payload.tier.tier}. It needs a new approval.`, rest);
|
|
4489
|
+
}
|
|
4490
|
+
if (!approved && !input.forceUnapproved) {
|
|
4491
|
+
const who = review === "changes-requested" ? `changes requested by ${approvals.changesRequested.map((l) => `@${l}`).join(", ")}` : `missing ${missing.map((m) => m === "approval" ? "an approval" : `${m} approval`).join(" and ")}`;
|
|
4492
|
+
return out("not-approved", `Not applied: it is not approved yet (${who}). Ask the owners to review it on the issue.`, rest);
|
|
4493
|
+
}
|
|
4494
|
+
const forced = gate.forced || tierIncreased && Boolean(input.forceUnapproved);
|
|
4495
|
+
return out("ready", `Ready: ${writes.size} file(s) to write${forced ? ", without every approval" : ""}.`, {
|
|
4496
|
+
...rest,
|
|
4497
|
+
gate: { ...gate, forced },
|
|
4498
|
+
writes: [...writes].map(([path, content]) => ({ path, content })).sort((a, b) => a.path.localeCompare(b.path))
|
|
4499
|
+
});
|
|
4500
|
+
}
|
|
4501
|
+
function suggestionAppliedComment(plan, where2) {
|
|
4502
|
+
return composeSuggestionAppliedComment({ ...where2, payloadSha: plan.payloadSha, ...plan.gate?.forced ? { forced: true } : {} });
|
|
4503
|
+
}
|
|
4504
|
+
function suggestionApplyTable(plan) {
|
|
4505
|
+
const label = { "already-applied": "already applied", apply: "apply", "apply-merged": "apply (merged)", conflict: "**conflict**" };
|
|
4506
|
+
const cell2 = (s) => s.replace(/\|/g, "\\|").replace(/\r?\n/g, " ");
|
|
4507
|
+
return ["| Change | Result | Detail |", "| --- | --- | --- |", ...plan.ops.map((o) => `| \`${cell2(o.path)}\` | ${label[o.state]} | ${cell2(o.detail)} |`)].join("\n");
|
|
4508
|
+
}
|
|
4509
|
+
async function createSuggestionOnGitHub(gh, payload, opts = {}) {
|
|
4510
|
+
const issue = composeSuggestionIssue(payload, { repo: gh.fullName, ...opts });
|
|
4511
|
+
const created = await gh.createIssue({ title: issue.title, body: issue.body, labels: issue.labels, assignees: issue.assignees });
|
|
4512
|
+
for (const [i, part] of issue.parts.entries()) {
|
|
4513
|
+
try {
|
|
4514
|
+
await gh.createIssueComment(created.number, part);
|
|
4515
|
+
} catch (err) {
|
|
4516
|
+
const why = err instanceof Error ? err.message : String(err);
|
|
4517
|
+
throw new GitHubError(err instanceof GitHubError ? err.status : 500, `Suggestion #${created.number} is incomplete: part ${i + 1} of ${issue.parts.length} could not be posted (${why}). Send it again, then close #${created.number}.`);
|
|
4518
|
+
}
|
|
4519
|
+
}
|
|
4520
|
+
const warnings = [];
|
|
4521
|
+
const missing = issue.labels.filter((l) => !created.labels.includes(l));
|
|
4522
|
+
if (created.labels.length && missing.length) warnings.push(`GitHub did not keep these labels on #${created.number}: ${missing.join(", ")}.`);
|
|
4523
|
+
return { number: created.number, url: created.url, sha: issue.sha, parts: issue.parts.length, warnings };
|
|
4524
|
+
}
|
|
4525
|
+
function linkedPullsOf(n, pulls) {
|
|
4526
|
+
return pulls.filter((p) => closingIssueNumbers(p.body).includes(n)).map((p) => ({ number: p.number, url: p.url, state: p.state === "open" ? "open" : "closed", merged: p.state === "merged" }));
|
|
4527
|
+
}
|
|
4528
|
+
async function listSuggestionsOnGitHub(gh, opts = {}) {
|
|
4529
|
+
const issues = await gh.listIssues({ label: SUGGESTION_LABEL, state: opts.state ?? "open", perPage: opts.perPage ?? 50 });
|
|
4530
|
+
const pulls = opts.pulls ?? (issues.length ? await gh.listPullRequests("all").catch(() => []) : []);
|
|
4531
|
+
return issues.map((i) => listItem(i, pulls));
|
|
4532
|
+
}
|
|
4533
|
+
function listItem(i, pulls) {
|
|
4534
|
+
const linkedPrs = linkedPullsOf(i.number, pulls);
|
|
4535
|
+
const cached = suggestionStateFromLabels(i.labels);
|
|
4536
|
+
const t = i.labels.map((l) => /^tier: ([123])$/.exec(l)?.[1]).find(Boolean);
|
|
4537
|
+
return {
|
|
4538
|
+
number: i.number,
|
|
4539
|
+
title: i.title,
|
|
4540
|
+
url: i.url,
|
|
4541
|
+
author: i.author,
|
|
4542
|
+
createdAt: i.createdAt,
|
|
4543
|
+
state: i.state,
|
|
4544
|
+
stateReason: i.stateReason,
|
|
4545
|
+
labels: i.labels,
|
|
4546
|
+
kind: suggestionKindFromLabels(i.labels),
|
|
4547
|
+
tier: t ? Number(t) : void 0,
|
|
4548
|
+
linkedPrs,
|
|
4549
|
+
status: suggestionStatus({ state: i.state, stateReason: i.stateReason, linkedPrs, applied: cached === "applying", review: cached === "applying" ? void 0 : cached })
|
|
4550
|
+
};
|
|
4551
|
+
}
|
|
4552
|
+
async function getSuggestionOnGitHub(gh, n, gov, opts = {}) {
|
|
4553
|
+
const [issue, comments, pulls] = await Promise.all([gh.getIssue(n), gh.listIssueComments(n), opts.pulls ? Promise.resolve(opts.pulls) : gh.listPullRequests("all").catch(() => [])]);
|
|
4554
|
+
const parsed = parseSuggestionIssue(issue.body, { comments, author: issue.author || void 0 });
|
|
4555
|
+
const linkedPrs = linkedPullsOf(n, pulls);
|
|
4556
|
+
const applied = comments.flatMap((c) => parseSuggestionAppliedComment(c.body) ?? []);
|
|
4557
|
+
const approvals = parsed.ok ? summarizeSuggestionApprovals(comments, gov, { payloadSha: parsed.sha, author: issue.author }) : void 0;
|
|
4558
|
+
const status = suggestionStatus({
|
|
4559
|
+
state: issue.state,
|
|
4560
|
+
stateReason: issue.stateReason,
|
|
4561
|
+
linkedPrs,
|
|
4562
|
+
applied: parsed.ok && applied.some((a) => a.payloadSha === parsed.sha),
|
|
4563
|
+
outdated: opts.outdated,
|
|
4564
|
+
review: parsed.ok && approvals ? suggestionReviewState(parsed.payload.tier.tier, approvals) : void 0
|
|
4565
|
+
});
|
|
4566
|
+
return { issue, parsed, comments, approvals, linkedPrs, applied, status };
|
|
4567
|
+
}
|
|
4568
|
+
async function syncSuggestionStateLabel(gh, n, labels, status) {
|
|
4569
|
+
const { add, remove } = suggestionLabelChanges(labels, status);
|
|
4570
|
+
let ok = true;
|
|
4571
|
+
for (const l of remove) {
|
|
4572
|
+
try {
|
|
4573
|
+
await gh.removeLabel(n, l);
|
|
4574
|
+
} catch {
|
|
4575
|
+
ok = false;
|
|
4576
|
+
}
|
|
4577
|
+
}
|
|
4578
|
+
if (add.length) ok = await gh.addLabels(n, add) && ok;
|
|
4579
|
+
return ok ? null : `The status label on #${n} could not be updated; the status shown in Bridge is still right.`;
|
|
4580
|
+
}
|
|
4581
|
+
async function markSuggestionsAppliedOnGitHub(gh, pr) {
|
|
4582
|
+
const applied = [];
|
|
4583
|
+
const warnings = [];
|
|
4584
|
+
for (const n of closingIssueNumbers(pr.body)) {
|
|
4585
|
+
try {
|
|
4586
|
+
const issue = await gh.getIssue(n);
|
|
4587
|
+
if (!issue.labels.includes(SUGGESTION_LABEL)) continue;
|
|
4588
|
+
const w = await syncSuggestionStateLabel(gh, n, issue.labels, "applied");
|
|
4589
|
+
if (w) warnings.push(w);
|
|
4590
|
+
else applied.push(n);
|
|
4591
|
+
} catch (err) {
|
|
4592
|
+
warnings.push(`Suggestion #${n} could not be marked applied (${err instanceof Error ? err.message : String(err)}).`);
|
|
4593
|
+
}
|
|
4594
|
+
}
|
|
4595
|
+
return { applied, warnings };
|
|
4596
|
+
}
|
|
4597
|
+
function githubSuggestionWorkspace(gh, ref) {
|
|
4598
|
+
let files;
|
|
4599
|
+
return {
|
|
4600
|
+
read: async (path) => await gh.getFile(path, ref) ?? null,
|
|
4601
|
+
readAt: async (sha, path) => await gh.readFile(path, sha).catch(() => void 0) ?? null,
|
|
4602
|
+
list: () => files ??= gh.listFiles(ref)
|
|
4603
|
+
};
|
|
4604
|
+
}
|
|
4605
|
+
function suggestionTouches(payload, config, path) {
|
|
4606
|
+
if (path === CONFIG_FILE) return true;
|
|
4607
|
+
if ((payload.files ?? []).some((f) => f.path === path)) return true;
|
|
4608
|
+
if ((payload.docs ?? []).some((d) => d.path === path)) return true;
|
|
4609
|
+
if (payload.componentSpec?.specPath === path) return true;
|
|
4610
|
+
return Boolean(payload.tokens?.edits.length) && selectTokenFiles(config, [path]).length > 0;
|
|
4611
|
+
}
|
|
4612
|
+
const MAX_COMPARE_FILES = 300;
|
|
4613
|
+
async function suggestionStalenessOnGitHub(gh, d, input) {
|
|
4614
|
+
const none = (mainSha2, mainMoved) => ({ mainSha: mainSha2, mainMoved, outdated: false, conflicts: [], plan: null });
|
|
4615
|
+
if (!d.parsed.ok || d.issue.state !== "open") return none(null, false);
|
|
4616
|
+
const payload = d.parsed.payload;
|
|
4617
|
+
const ref = input.ref ?? (payload.base.ref || input.config.repo?.defaultBranch || "main");
|
|
4618
|
+
const mainSha = await gh.getBranchSha(ref).catch(() => void 0) ?? null;
|
|
4619
|
+
if (!mainSha) return none(null, false);
|
|
4620
|
+
if (mainSha === payload.base.sha) return none(mainSha, false);
|
|
4621
|
+
const changed = await gh.compare(payload.base.sha, mainSha).catch(() => null);
|
|
4622
|
+
if (changed && changed.files.length < MAX_COMPARE_FILES && !changed.files.some((f) => suggestionTouches(payload, input.config, f.filename))) return none(mainSha, true);
|
|
4623
|
+
try {
|
|
4624
|
+
const plan = await planSuggestionApply({
|
|
4625
|
+
parsed: d.parsed,
|
|
4626
|
+
config: input.config,
|
|
4627
|
+
comments: d.comments,
|
|
4628
|
+
governance: input.governance,
|
|
4629
|
+
author: d.issue.author || null,
|
|
4630
|
+
// Only whether it still applies: approvals and a hand edit are reported apart.
|
|
4631
|
+
forceUnapproved: true,
|
|
4632
|
+
acceptEdited: true,
|
|
4633
|
+
workspace: githubSuggestionWorkspace(gh, mainSha)
|
|
4634
|
+
});
|
|
4635
|
+
const conflicts = plan.ops.filter((o) => o.state === "conflict");
|
|
4636
|
+
return { mainSha, mainMoved: true, outdated: conflicts.length > 0, conflicts, plan };
|
|
4637
|
+
} catch {
|
|
4638
|
+
return none(mainSha, true);
|
|
4639
|
+
}
|
|
4640
|
+
}
|
|
4641
|
+
function stalenessStatus(status, staleness) {
|
|
4642
|
+
return withOutdated(status, staleness?.outdated);
|
|
4643
|
+
}
|
|
4644
|
+
async function reviewSuggestionOnGitHub(gh, n, input, gov) {
|
|
4645
|
+
if (input.event === "request-changes" && !input.message?.trim()) throw new GitHubError(422, "Say what should change, in a sentence or two: the author reads it.");
|
|
4646
|
+
const before = await getSuggestionOnGitHub(gh, n, gov);
|
|
4264
4647
|
if (!before.parsed.ok) throw new GitHubError(422, `#${n} cannot be reviewed: ${before.parsed.message}`);
|
|
4265
4648
|
if (before.issue.state === "closed") throw new GitHubError(422, `#${n} is closed. Reopen it on GitHub to review it again.`);
|
|
4266
4649
|
const sha = before.parsed.sha;
|
|
@@ -4283,8 +4666,12 @@ async function linkSuggestionPrOnGitHub(gh, n, link) {
|
|
|
4283
4666
|
if (!link.pr && !link.branch && !link.commit) throw new GitHubError(422, "Say where it is applied: a pull request number, a branch or a commit.");
|
|
4284
4667
|
const issue = await gh.getIssue(n);
|
|
4285
4668
|
await gh.addComment(n, composeSuggestionAppliedComment({ by: link.by, branch: link.branch, commit: link.commit, pr: link.pr, payloadSha: link.payloadSha, forced: link.forced, at: (link.at ?? /* @__PURE__ */ new Date()).toISOString() }));
|
|
4286
|
-
const
|
|
4287
|
-
|
|
4669
|
+
const status = appliedStatus(issue, link.merged);
|
|
4670
|
+
const warning = await syncSuggestionStateLabel(gh, n, issue.labels, status);
|
|
4671
|
+
return { warnings: warning ? [warning] : [], status };
|
|
4672
|
+
}
|
|
4673
|
+
function appliedStatus(issue, merged) {
|
|
4674
|
+
return merged || issue.state === "closed" && issue.stateReason === "completed" ? "applied" : "applying";
|
|
4288
4675
|
}
|
|
4289
4676
|
async function declineSuggestionOnGitHub(gh, n, reason, by) {
|
|
4290
4677
|
const issue = await gh.getIssue(n);
|
|
@@ -4511,265 +4898,6 @@ async function commitFileOnGitHub(gh, input) {
|
|
|
4511
4898
|
if (content === cur.text) return "";
|
|
4512
4899
|
return gh.putFile({ path: input.path, content, branch: input.branch, message: input.message, sha: cur.sha });
|
|
4513
4900
|
}
|
|
4514
|
-
const SUGGESTION_APPLY_EXIT = {
|
|
4515
|
-
ready: 0,
|
|
4516
|
-
"already-applied": 0,
|
|
4517
|
-
conflict: 3,
|
|
4518
|
-
"not-approved": 1,
|
|
4519
|
-
blocked: 1,
|
|
4520
|
-
refused: 1
|
|
4521
|
-
};
|
|
4522
|
-
const same = (a, b) => canonicalJson(a) === canonicalJson(b);
|
|
4523
|
-
const show = (v) => v === void 0 ? "nothing" : typeof v === "string" ? v : JSON.stringify(v);
|
|
4524
|
-
function tokenRoot(config) {
|
|
4525
|
-
const r = config.tokens.root.replace(/\/+$/, "");
|
|
4526
|
-
return r === "." ? "" : r;
|
|
4527
|
-
}
|
|
4528
|
-
function tokenFileOf(config, path) {
|
|
4529
|
-
const root = tokenRoot(config);
|
|
4530
|
-
if (root && !path.startsWith(`${root}/`)) return null;
|
|
4531
|
-
if (!selectTokenFiles(config, [path]).length) return null;
|
|
4532
|
-
return root ? path.slice(root.length + 1) : path;
|
|
4533
|
-
}
|
|
4534
|
-
function planTokenEdit(set, e, changes) {
|
|
4535
|
-
const base = { type: "token", path: e.path, edit: e, ...e.file ? { file: e.file } : {} };
|
|
4536
|
-
const defsAt = (path) => set.defs.filter((d) => d.path === path && (!e.file || d.file === e.file));
|
|
4537
|
-
const recorded = (file, path = e.path) => changes.find((c) => c.path === path && c.file === file && (e.op === "rename" ? c.kind === "renamed" : true));
|
|
4538
|
-
const conflict = (detail, extra = {}) => ({ op: { ...base, state: "conflict", detail, ...extra }, notes: [] });
|
|
4539
|
-
const done = (detail) => ({ op: { ...base, state: "already-applied", detail }, notes: [] });
|
|
4540
|
-
let result;
|
|
4541
|
-
try {
|
|
4542
|
-
result = applyTokenEdits(set, [e]);
|
|
4543
|
-
} catch (err) {
|
|
4544
|
-
const why = err instanceof Error ? err.message : String(err);
|
|
4545
|
-
switch (e.op) {
|
|
4546
|
-
case "rename": {
|
|
4547
|
-
const hasOld = defsAt(e.path).length > 0;
|
|
4548
|
-
const hasNew = set.defs.some((d) => d.path === e.newPath);
|
|
4549
|
-
if (!hasOld && hasNew) return done(`Already renamed to ${e.newPath}.`);
|
|
4550
|
-
return conflict(hasOld && hasNew ? `Both ${e.path} and ${e.newPath} exist now: rename it by hand.` : `${e.path} no longer exists: ${why}`);
|
|
4551
|
-
}
|
|
4552
|
-
case "remove":
|
|
4553
|
-
if (!defsAt(e.path).length) return done("Already removed.");
|
|
4554
|
-
return conflict(why);
|
|
4555
|
-
case "add": {
|
|
4556
|
-
const existing = defsAt(e.path);
|
|
4557
|
-
if (!existing.length) return conflict(why);
|
|
4558
|
-
const want = (file) => {
|
|
4559
|
-
const c = recorded(file);
|
|
4560
|
-
if (c && c.after !== void 0) return c.after;
|
|
4561
|
-
return coerceValue(e.value, e.type ?? existing.find((d2) => d2.file === file)?.type);
|
|
4562
|
-
};
|
|
4563
|
-
const all = existing.every((d2) => same(d2.value, want(d2.file)));
|
|
4564
|
-
if (all) return done("Already added with the suggested value.");
|
|
4565
|
-
const d = existing.find((x) => !same(x.value, want(x.file)));
|
|
4566
|
-
return conflict(`Someone added ${e.path} with another value since.`, { file: d.file, after: want(d.file), now: d.value });
|
|
4567
|
-
}
|
|
4568
|
-
default:
|
|
4569
|
-
return conflict(defsAt(e.path).length ? why : `${e.path} no longer exists, so its value cannot be set.`);
|
|
4570
|
-
}
|
|
4571
|
-
}
|
|
4572
|
-
if (!result.touched.length) return done("Already has the suggested value.");
|
|
4573
|
-
if (e.op === "set" || e.op === "remove" || e.op === "rename") {
|
|
4574
|
-
for (const d of defsAt(e.path)) {
|
|
4575
|
-
const c = recorded(d.file, e.op === "rename" ? e.newPath ?? e.path : e.path);
|
|
4576
|
-
if (!c) continue;
|
|
4577
|
-
const okNow = same(d.value, c.before) || e.op === "set" && same(d.value, c.after);
|
|
4578
|
-
if (!okNow) return conflict(`${e.path} changed since it was suggested (now ${show(d.value)}, suggested from ${show(c.before)}).`, { file: d.file, before: c.before, after: c.after, now: d.value });
|
|
4579
|
-
}
|
|
4580
|
-
}
|
|
4581
|
-
const verb = e.op === "set" ? "Set" : e.op === "add" ? "Add" : e.op === "remove" ? "Remove" : `Rename to ${e.newPath ?? ""}`;
|
|
4582
|
-
return { op: { ...base, state: "apply", detail: `${verb} in ${result.touched.join(", ")}.` }, files: result.files, notes: result.notes };
|
|
4583
|
-
}
|
|
4584
|
-
async function planFile(ws, baseSha, f) {
|
|
4585
|
-
const base = { type: "file", path: f.path };
|
|
4586
|
-
const cur = await ws.read(f.path);
|
|
4587
|
-
const curSha = cur?.sha ?? null;
|
|
4588
|
-
const theirs = f.afterSha === null ? null : f.content ?? null;
|
|
4589
|
-
if (curSha === f.afterSha) return { op: { ...base, state: "already-applied", detail: f.afterSha === null ? "Already deleted." : "Already has the suggested content." } };
|
|
4590
|
-
if (curSha === f.beforeSha) return { op: { ...base, state: "apply", detail: f.afterSha === null ? "Delete it." : f.beforeSha === null ? "Create it." : "Replace it with the suggested content." }, content: theirs };
|
|
4591
|
-
if (f.beforeSha === null) return { op: { ...base, state: "conflict", detail: "The suggestion creates this file, but a different one exists now." } };
|
|
4592
|
-
if (!cur) return { op: { ...base, state: "conflict", detail: "The suggestion changes this file, but it was deleted since." } };
|
|
4593
|
-
if (theirs === null) return { op: { ...base, state: "conflict", detail: "The suggestion deletes this file, but it changed since." } };
|
|
4594
|
-
const original = await ws.readAt(baseSha, f.path);
|
|
4595
|
-
if (original === null || gitBlobSha(original) !== f.beforeSha || !ws.merge) {
|
|
4596
|
-
return { op: { ...base, state: "conflict", detail: original === null ? "The file changed since and its version at the suggestion base cannot be read, so it cannot be merged." : "The file changed since and cannot be merged here." } };
|
|
4597
|
-
}
|
|
4598
|
-
const merged = await ws.merge({ path: f.path, ours: cur.text, base: original, theirs });
|
|
4599
|
-
if (!merged.clean) return { op: { ...base, state: "conflict", detail: `The file changed since and the merge has ${merged.conflicts} conflict(s).`, hunks: merged.conflicts } };
|
|
4600
|
-
if (merged.text === cur.text) return { op: { ...base, state: "already-applied", detail: "Already has the suggested lines." } };
|
|
4601
|
-
return { op: { ...base, state: "apply-merged", detail: "The file changed since: merged with the suggested lines, no conflict." }, content: merged.text };
|
|
4602
|
-
}
|
|
4603
|
-
async function planSuggestionApply(input) {
|
|
4604
|
-
const { parsed, config, workspace: ws } = input;
|
|
4605
|
-
const payload = parsed.payload;
|
|
4606
|
-
const notes = [];
|
|
4607
|
-
const out = (outcome, message, rest2 = {}) => ({
|
|
4608
|
-
outcome,
|
|
4609
|
-
exitCode: SUGGESTION_APPLY_EXIT[outcome],
|
|
4610
|
-
message,
|
|
4611
|
-
payloadSha: parsed.sha,
|
|
4612
|
-
payload,
|
|
4613
|
-
gate: null,
|
|
4614
|
-
ops: [],
|
|
4615
|
-
writes: [],
|
|
4616
|
-
checks: null,
|
|
4617
|
-
problems: [],
|
|
4618
|
-
notes,
|
|
4619
|
-
...payload.componentSpec ? { brief: payload.componentSpec.brief } : {},
|
|
4620
|
-
...rest2
|
|
4621
|
-
});
|
|
4622
|
-
const problems = suggestionPathProblems(payload, config);
|
|
4623
|
-
if (payload.checks.blocked) problems.push(`It was suggested with a failing check (${payload.checks.blocked}): ask the author to fix it and send it again.`);
|
|
4624
|
-
for (const f of payload.files ?? []) {
|
|
4625
|
-
if (f.afterSha !== null && typeof f.content !== "string") problems.push(`"${f.path}" has no content in the suggestion: it is incomplete, ask the author to send it again.`);
|
|
4626
|
-
else if (f.afterSha !== null && gitBlobSha(f.content) !== f.afterSha) problems.push(`"${f.path}" does not match the content it was suggested with: someone edited it by hand.`);
|
|
4627
|
-
}
|
|
4628
|
-
if (problems.length) return out("refused", "Not applied: the suggestion asks for something Bridge never writes.", { problems: [...new Set(problems)] });
|
|
4629
|
-
if (parsed.edited && !input.acceptEdited) {
|
|
4630
|
-
return out("refused", 'Not applied: the suggestion was edited by hand since Bridge wrote it. Read the change, then apply it with "accept edited" if it is right.', {
|
|
4631
|
-
problems: ["The payload no longer matches the sha Bridge recorded."]
|
|
4632
|
-
});
|
|
4633
|
-
}
|
|
4634
|
-
const root = tokenRoot(config);
|
|
4635
|
-
const tokenFiles = selectTokenFiles(config, await ws.list());
|
|
4636
|
-
const targets = [
|
|
4637
|
-
...(payload.files ?? []).map((f) => f.path),
|
|
4638
|
-
...(payload.docs ?? []).map((d) => d.path),
|
|
4639
|
-
...payload.componentSpec ? [payload.componentSpec.specPath] : [],
|
|
4640
|
-
...payload.tokens?.edits.length ? tokenFiles.map((f) => root ? `${root}/${f}` : f) : []
|
|
4641
|
-
];
|
|
4642
|
-
if (ws.isSymlink) {
|
|
4643
|
-
const links = [];
|
|
4644
|
-
for (const p of new Set(targets)) if (await ws.isSymlink(p)) links.push(`"${p}" is a symbolic link (or inside one): Bridge never writes through links.`);
|
|
4645
|
-
if (links.length) return out("refused", "Not applied: the suggestion writes through a symbolic link.", { problems: links });
|
|
4646
|
-
}
|
|
4647
|
-
const governance = input.governance ?? { owners: config.governance.owners, rights: config.governance.rights ?? null, soloOwnerCanApproveBoth: config.governance.soloOwnerCanApproveBoth };
|
|
4648
|
-
const approvals = summarizeSuggestionApprovals(input.comments, governance, { payloadSha: parsed.sha, author: input.author });
|
|
4649
|
-
const review = suggestionReviewState(payload.tier.tier, approvals);
|
|
4650
|
-
const missing = pendingApprovals(payload.tier.tier, approvals);
|
|
4651
|
-
const approved = review === "approved";
|
|
4652
|
-
const gate = { review, missing, approvals, forced: !approved && Boolean(input.forceUnapproved), edited: parsed.edited };
|
|
4653
|
-
const ops = [];
|
|
4654
|
-
const writes = /* @__PURE__ */ new Map();
|
|
4655
|
-
const current = /* @__PURE__ */ new Map();
|
|
4656
|
-
for (const f of tokenFiles) {
|
|
4657
|
-
const r = await ws.read(root ? `${root}/${f}` : f);
|
|
4658
|
-
if (r) current.set(f, r.text);
|
|
4659
|
-
}
|
|
4660
|
-
const before = new LayeredTokenSet(config, current);
|
|
4661
|
-
let set = before;
|
|
4662
|
-
for (const e of payload.tokens?.edits ?? []) {
|
|
4663
|
-
const r = planTokenEdit(set, e, payload.tokens.changes);
|
|
4664
|
-
ops.push(r.op);
|
|
4665
|
-
notes.push(...r.notes);
|
|
4666
|
-
if (r.op.state === "apply" && r.files) {
|
|
4667
|
-
set = new LayeredTokenSet(config, r.files);
|
|
4668
|
-
}
|
|
4669
|
-
}
|
|
4670
|
-
for (const [f, text] of set.files) if (text !== before.files.get(f)) writes.set(set.repoPath(f), text);
|
|
4671
|
-
for (const f of payload.files ?? []) {
|
|
4672
|
-
if (writes.has(f.path)) {
|
|
4673
|
-
notes.push(`${f.path}: the token edits write this file, its copy in the suggestion is not used.`);
|
|
4674
|
-
continue;
|
|
4675
|
-
}
|
|
4676
|
-
const r = await planFile(ws, payload.base.sha, f);
|
|
4677
|
-
ops.push(r.op);
|
|
4678
|
-
if (r.op.state === "apply" || r.op.state === "apply-merged") writes.set(f.path, r.content ?? null);
|
|
4679
|
-
}
|
|
4680
|
-
for (const d of payload.docs ?? []) {
|
|
4681
|
-
const base = { type: "docs", path: d.path };
|
|
4682
|
-
const docs = d.after;
|
|
4683
|
-
let text;
|
|
4684
|
-
try {
|
|
4685
|
-
const issues = validateDocs(docs);
|
|
4686
|
-
if (issues.length) {
|
|
4687
|
-
problems.push(`${d.path}: ${issues.map((i) => i.message).join(" ")}`);
|
|
4688
|
-
continue;
|
|
4689
|
-
}
|
|
4690
|
-
const cur = await ws.read(d.path);
|
|
4691
|
-
text = docsFileText(docs, cur?.text ?? null);
|
|
4692
|
-
if (cur && text === cur.text) {
|
|
4693
|
-
ops.push({ ...base, state: "already-applied", detail: "The docs already say this." });
|
|
4694
|
-
continue;
|
|
4695
|
-
}
|
|
4696
|
-
ops.push({ ...base, state: "apply", detail: cur ? "Update the changed fields; other fields stay as they are now." : "Create the docs file." });
|
|
4697
|
-
writes.set(d.path, text);
|
|
4698
|
-
} catch (err) {
|
|
4699
|
-
problems.push(`${d.path}: the suggested docs are not a docs.json Bridge can read (${err instanceof Error ? err.message : String(err)}).`);
|
|
4700
|
-
}
|
|
4701
|
-
}
|
|
4702
|
-
if (payload.componentSpec) {
|
|
4703
|
-
const s = payload.componentSpec;
|
|
4704
|
-
const text = `${JSON.stringify(s.spec, null, 2)}
|
|
4705
|
-
`;
|
|
4706
|
-
const cur = await ws.read(s.specPath);
|
|
4707
|
-
if (cur && cur.text === text) ops.push({ type: "component-spec", path: s.specPath, state: "already-applied", detail: "The spec file is already there." });
|
|
4708
|
-
else {
|
|
4709
|
-
ops.push({ type: "component-spec", path: s.specPath, state: "apply", detail: `Write the spec; the ${s.component} code itself is written from the brief.` });
|
|
4710
|
-
writes.set(s.specPath, text);
|
|
4711
|
-
}
|
|
4712
|
-
}
|
|
4713
|
-
const afterFiles = new Map(set.files);
|
|
4714
|
-
for (const [p, content] of writes) {
|
|
4715
|
-
const tf = tokenFileOf(config, p);
|
|
4716
|
-
if (!tf) continue;
|
|
4717
|
-
if (content === null) afterFiles.delete(tf);
|
|
4718
|
-
else afterFiles.set(tf, content);
|
|
4719
|
-
}
|
|
4720
|
-
const after = new LayeredTokenSet(config, afterFiles);
|
|
4721
|
-
const tokenChanges = diffLayered(before, after);
|
|
4722
|
-
const issueKey2 = (i) => `${i.code}|${i.path ?? ""}|${i.file ?? ""}|${i.message}`;
|
|
4723
|
-
const prevErrors = new Set(validateLayered(before).map(issueKey2));
|
|
4724
|
-
const newErrors = tokenChanges.length ? validateLayered(after).filter((i) => i.level === "error" && !prevErrors.has(issueKey2(i))) : [];
|
|
4725
|
-
let pairs = [];
|
|
4726
|
-
if (config.contrast && tokenChanges.length) {
|
|
4727
|
-
try {
|
|
4728
|
-
pairs = parseContrastChecks((await ws.read(config.contrast.path))?.text);
|
|
4729
|
-
} catch {
|
|
4730
|
-
notes.push(`${config.contrast.path} does not parse: contrast was not checked.`);
|
|
4731
|
-
}
|
|
4732
|
-
}
|
|
4733
|
-
const modes = config.contrast?.modes;
|
|
4734
|
-
const contrast = pairs.length ? contrastDelta(runLayeredContrast(before, pairs, modes), runLayeredContrast(after, pairs, modes)) : [];
|
|
4735
|
-
const newFailures = contrast.filter((d) => d.passBefore && !d.passAfter);
|
|
4736
|
-
const otherFiles = [...writes.keys()].filter((p) => !tokenFileOf(config, p));
|
|
4737
|
-
const tier = classifyRisk({ tokenChanges: tokenChanges.map((c) => ({ kind: c.kind, path: c.path, ...c.kind === "changed" ? { descriptionOnly: c.fields.length > 0 && c.fields.every((x) => x === "description") } : {} })), files: otherFiles.map((path) => ({ path })), config });
|
|
4738
|
-
const tierIncreased = writes.size > 0 && tier.tier > payload.tier.tier;
|
|
4739
|
-
const checks = { tierWhenSuggested: payload.tier.tier, tier, tierIncreased, newErrors, newFailures, contrast, tokenChanges };
|
|
4740
|
-
const rest = { gate, ops, checks, problems };
|
|
4741
|
-
if (problems.length) return out("refused", "Not applied: part of the suggestion is not valid.", rest);
|
|
4742
|
-
const conflicts = ops.filter((o) => o.state === "conflict");
|
|
4743
|
-
if (conflicts.length) {
|
|
4744
|
-
return out("conflict", `Not applied: ${conflicts.length} change(s) conflict with the code now. Ask the author to refresh the suggestion on today's code, or apply those parts by hand.`, rest);
|
|
4745
|
-
}
|
|
4746
|
-
if (!writes.size) return out("already-applied", "Already applied: the code has every change of this suggestion.", rest);
|
|
4747
|
-
if (newErrors.length || newFailures.length) {
|
|
4748
|
-
const why = [newErrors.length ? `${newErrors.length} new validation error(s)` : "", newFailures.length ? `${newFailures.length} contrast pair(s) would fail` : ""].filter(Boolean).join(" and ");
|
|
4749
|
-
return out("blocked", `Not applied: on today's code it would add ${why}. Ask the author to refresh the suggestion.`, rest);
|
|
4750
|
-
}
|
|
4751
|
-
if (tierIncreased && !input.forceUnapproved) {
|
|
4752
|
-
return out("blocked", `Not applied: on today's code it is a tier ${tier.tier} change, it was approved as tier ${payload.tier.tier}. It needs a new approval.`, rest);
|
|
4753
|
-
}
|
|
4754
|
-
if (!approved && !input.forceUnapproved) {
|
|
4755
|
-
const who = review === "changes-requested" ? `changes requested by ${approvals.changesRequested.map((l) => `@${l}`).join(", ")}` : `missing ${missing.map((m) => m === "approval" ? "an approval" : `${m} approval`).join(" and ")}`;
|
|
4756
|
-
return out("not-approved", `Not applied: it is not approved yet (${who}). Ask the owners to review it on the issue.`, rest);
|
|
4757
|
-
}
|
|
4758
|
-
const forced = gate.forced || tierIncreased && Boolean(input.forceUnapproved);
|
|
4759
|
-
return out("ready", `Ready: ${writes.size} file(s) to write${forced ? ", without every approval" : ""}.`, {
|
|
4760
|
-
...rest,
|
|
4761
|
-
gate: { ...gate, forced },
|
|
4762
|
-
writes: [...writes].map(([path, content]) => ({ path, content })).sort((a, b) => a.path.localeCompare(b.path))
|
|
4763
|
-
});
|
|
4764
|
-
}
|
|
4765
|
-
function suggestionAppliedComment(plan, where2) {
|
|
4766
|
-
return composeSuggestionAppliedComment({ ...where2, payloadSha: plan.payloadSha, ...plan.gate?.forced ? { forced: true } : {} });
|
|
4767
|
-
}
|
|
4768
|
-
function suggestionApplyTable(plan) {
|
|
4769
|
-
const label = { "already-applied": "already applied", apply: "apply", "apply-merged": "apply (merged)", conflict: "**conflict**" };
|
|
4770
|
-
const cell2 = (s) => s.replace(/\|/g, "\\|").replace(/\r?\n/g, " ");
|
|
4771
|
-
return ["| Change | Result | Detail |", "| --- | --- | --- |", ...plan.ops.map((o) => `| \`${cell2(o.path)}\` | ${label[o.state]} | ${cell2(o.detail)} |`)].join("\n");
|
|
4772
|
-
}
|
|
4773
4901
|
function normalizeRepoPath(path) {
|
|
4774
4902
|
return path.replace(/\\/g, "/").split("/").filter((s) => s && s !== ".").join("/");
|
|
4775
4903
|
}
|
|
@@ -4845,7 +4973,7 @@ export {
|
|
|
4845
4973
|
branchWithJiraKey as Z,
|
|
4846
4974
|
titleWithJiraKey as _,
|
|
4847
4975
|
parseChangelog as a,
|
|
4848
|
-
|
|
4976
|
+
TOKEN_SETTINGS_URL as a$,
|
|
4849
4977
|
formatFigmaSpec as a0,
|
|
4850
4978
|
figmaDraftBrief as a1,
|
|
4851
4979
|
parseFigmaOrigin as a2,
|
|
@@ -4856,33 +4984,33 @@ export {
|
|
|
4856
4984
|
withFigmaOrigin as a7,
|
|
4857
4985
|
WAITING_FOR_CLAUDE_LABEL as a8,
|
|
4858
4986
|
DRAFTED_BY_CLAUDE_LABEL as a9,
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
4885
|
-
|
|
4987
|
+
DOCS_EDITOR_ORIGIN as aA,
|
|
4988
|
+
DOCS_KEYS as aB,
|
|
4989
|
+
DOCS_MCP_ORIGIN as aC,
|
|
4990
|
+
EXPLORATION_CHECK as aD,
|
|
4991
|
+
EXPLORATION_STATUS_LABEL as aE,
|
|
4992
|
+
EXPLORATION_TIER_LABEL as aF,
|
|
4993
|
+
EXPLORATION_USE_CASES as aG,
|
|
4994
|
+
PULL_STATE_PILL as aH,
|
|
4995
|
+
ProtectedBranchError as aI,
|
|
4996
|
+
REDUCED_MOTION_NOTE as aJ,
|
|
4997
|
+
REQUEST_STATUS_LABEL as aK,
|
|
4998
|
+
REVIEW_STATUS_LABEL as aL,
|
|
4999
|
+
REVIEW_STATUS_ORDER as aM,
|
|
5000
|
+
SPEC_FIELDS as aN,
|
|
5001
|
+
SUGGESTION_APPLIED_MARK as aO,
|
|
5002
|
+
SUGGESTION_APPLY_EXIT as aP,
|
|
5003
|
+
SUGGESTION_FINAL_LABELS as aQ,
|
|
5004
|
+
SUGGESTION_KINDS as aR,
|
|
5005
|
+
SUGGESTION_LABEL as aS,
|
|
5006
|
+
SUGGESTION_LIMITS as aT,
|
|
5007
|
+
SUGGESTION_REVIEW_MARK as aU,
|
|
5008
|
+
SUGGESTION_STATE_LABELS as aV,
|
|
5009
|
+
SUGGESTION_STATUS_LABEL as aW,
|
|
5010
|
+
SUGGESTION_STATUS_LABELS as aX,
|
|
5011
|
+
SUGGESTION_TOO_LARGE as aY,
|
|
5012
|
+
SuggestionError as aZ,
|
|
5013
|
+
TOKEN_REJECTED_MESSAGE as a_,
|
|
4886
5014
|
planComponentPush as aa,
|
|
4887
5015
|
figmaOriginSection as ab,
|
|
4888
5016
|
figmaBranchTitle as ac,
|
|
@@ -4895,180 +5023,194 @@ export {
|
|
|
4895
5023
|
readRepoTexts as aj,
|
|
4896
5024
|
normalizeRepoPath as ak,
|
|
4897
5025
|
isUnder as al,
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
5026
|
+
GitHubClient as am,
|
|
5027
|
+
effectiveTier as an,
|
|
5028
|
+
parseRequestIssue as ao,
|
|
5029
|
+
requestStatus as ap,
|
|
5030
|
+
listSuggestionsOnGitHub as aq,
|
|
5031
|
+
getSuggestionOnGitHub as ar,
|
|
5032
|
+
BULK_LIMIT as as,
|
|
5033
|
+
CI_STATE_LABEL as at,
|
|
5034
|
+
COLOR_FIELDS as au,
|
|
5035
|
+
COMPONENT_SPECS_VERSION as av,
|
|
5036
|
+
CONFLICT_REASON as aw,
|
|
5037
|
+
DEFAULT_COMPONENT_SPECS_FILE as ax,
|
|
5038
|
+
DEFAULT_PROTECTED_BRANCHES as ay,
|
|
5039
|
+
DOCS_CHANGE_BUMP as az,
|
|
4912
5040
|
buildReport as b,
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
|
|
5041
|
+
hasWriteAccess as b$,
|
|
5042
|
+
TRIGGER_LABELS as b0,
|
|
5043
|
+
UNRELEASED as b1,
|
|
5044
|
+
acceptDocsDraft as b2,
|
|
5045
|
+
acceptDocsText as b3,
|
|
5046
|
+
allowedMergeMethods as b4,
|
|
5047
|
+
anatomyLabel as b5,
|
|
5048
|
+
appliedStatus as b6,
|
|
5049
|
+
assertSafeBranch as b7,
|
|
5050
|
+
assignRequestOnGitHub as b8,
|
|
5051
|
+
assignVersions as b9,
|
|
5052
|
+
declineSuggestionOnGitHub as bA,
|
|
5053
|
+
defaultDocsFooter as bB,
|
|
5054
|
+
docsAuthorLine as bC,
|
|
5055
|
+
docsFileText as bD,
|
|
5056
|
+
docsProposal as bE,
|
|
5057
|
+
docsSectionShapes as bF,
|
|
5058
|
+
docsSlug as bG,
|
|
5059
|
+
docsSuggestionPayload as bH,
|
|
5060
|
+
docsText as bI,
|
|
5061
|
+
draftChangeLine as bJ,
|
|
5062
|
+
draftPushDescription as bK,
|
|
5063
|
+
draftRequestText as bL,
|
|
5064
|
+
eligibleReviewers as bM,
|
|
5065
|
+
explorationBranch as bN,
|
|
5066
|
+
explorationRules as bO,
|
|
5067
|
+
explorationShareMessage as bP,
|
|
5068
|
+
explorationSlugFromBranch as bQ,
|
|
5069
|
+
figmaCell as bR,
|
|
5070
|
+
fileModes as bS,
|
|
5071
|
+
filterHistory as bT,
|
|
5072
|
+
findOpenDocsProposal as bU,
|
|
5073
|
+
findOpenDocsProposalOnGitHub as bV,
|
|
5074
|
+
firstParagraph as bW,
|
|
5075
|
+
formatDocsJson as bX,
|
|
5076
|
+
friendlyMessage as bY,
|
|
5077
|
+
githubSuggestionWorkspace as bZ,
|
|
5078
|
+
hasSuggestionBlock as b_,
|
|
5079
|
+
base64ToUtf8 as ba,
|
|
5080
|
+
bridgeMcpInstallCommand as bb,
|
|
5081
|
+
canOverride as bc,
|
|
5082
|
+
canonicalJson as bd,
|
|
5083
|
+
changeTable as be,
|
|
5084
|
+
changesetBump as bf,
|
|
5085
|
+
classifyAuth as bg,
|
|
5086
|
+
cleanDocs as bh,
|
|
5087
|
+
clearSharedEtags as bi,
|
|
5088
|
+
closingIssueNumbers as bj,
|
|
5089
|
+
commitFileOnGitHub as bk,
|
|
5090
|
+
componentContrastPairs as bl,
|
|
5091
|
+
componentMotion as bm,
|
|
5092
|
+
componentsUsingMotionToken as bn,
|
|
5093
|
+
composeChangeset as bo,
|
|
5094
|
+
composeSuggestionAppliedComment as bp,
|
|
5095
|
+
composeSuggestionIssue as bq,
|
|
5096
|
+
composeSuggestionReviewComment as br,
|
|
5097
|
+
continueDocsProposalOnGitHub as bs,
|
|
5098
|
+
contrastDeltaTable as bt,
|
|
5099
|
+
countChanges as bu,
|
|
5100
|
+
coverageSummary as bv,
|
|
5101
|
+
cssModuleClasses as bw,
|
|
5102
|
+
declineComment as bx,
|
|
5103
|
+
declineReasonProblems as by,
|
|
5104
|
+
declineRequestOnGitHub as bz,
|
|
4977
5105
|
collectHistory as c,
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5106
|
+
sectionsNeedingReview as c$,
|
|
5107
|
+
hashSpec as c0,
|
|
5108
|
+
iconComponentName as c1,
|
|
5109
|
+
installationFor as c2,
|
|
5110
|
+
isActiveExploration as c3,
|
|
5111
|
+
isAutomationPull as c4,
|
|
5112
|
+
isDocsProposalBranch as c5,
|
|
5113
|
+
isGuardrailPath as c6,
|
|
5114
|
+
isJiraKey as c7,
|
|
5115
|
+
isMissingPermission as c8,
|
|
5116
|
+
isReleasePull as c9,
|
|
5117
|
+
parseSuggestionReviewComment as cA,
|
|
5118
|
+
parseTier as cB,
|
|
5119
|
+
patchDocsJson as cC,
|
|
5120
|
+
pendingApprovals as cD,
|
|
5121
|
+
permissionLabel as cE,
|
|
5122
|
+
planRestore as cF,
|
|
5123
|
+
planRevert as cG,
|
|
5124
|
+
planSuggestionApply as cH,
|
|
5125
|
+
planToJson as cI,
|
|
5126
|
+
prNumberFromSubject as cJ,
|
|
5127
|
+
prefillDocs as cK,
|
|
5128
|
+
proposeDocsOnGitHub as cL,
|
|
5129
|
+
propsTable as cM,
|
|
5130
|
+
pullState as cN,
|
|
5131
|
+
pushChangeRows as cO,
|
|
5132
|
+
rateLimitMessage as cP,
|
|
5133
|
+
releasePull as cQ,
|
|
5134
|
+
reopenEligibility as cR,
|
|
5135
|
+
requestBrief as cS,
|
|
5136
|
+
requestImagePath as cT,
|
|
5137
|
+
requestIssueTitle as cU,
|
|
5138
|
+
requiredApprovals as cV,
|
|
5139
|
+
requiredPermission as cW,
|
|
5140
|
+
reviewQueue as cX,
|
|
5141
|
+
reviewStatusKind as cY,
|
|
5142
|
+
reviewSuggestionOnGitHub as cZ,
|
|
5143
|
+
reviewersToAsk as c_,
|
|
5144
|
+
isTokenRejected as ca,
|
|
5145
|
+
jiraIssueFields as cb,
|
|
5146
|
+
jiraKeyOf as cc,
|
|
5147
|
+
joinRepoPath as cd,
|
|
5148
|
+
linkRequestToExplorationOnGitHub as ce,
|
|
5149
|
+
linkSuggestionPrOnGitHub as cf,
|
|
5150
|
+
linkedPullsOf as cg,
|
|
5151
|
+
loadRepoFiles as ch,
|
|
5152
|
+
lowerFirstWord as ci,
|
|
5153
|
+
markSuggestionsAppliedOnGitHub as cj,
|
|
5154
|
+
memoryEtagStore as ck,
|
|
5155
|
+
memorySource as cl,
|
|
5156
|
+
mergeGate as cm,
|
|
5157
|
+
missingPermissionMessage as cn,
|
|
5158
|
+
missingProse as co,
|
|
5159
|
+
modeLabel as cp,
|
|
5160
|
+
needsDocsReview as cq,
|
|
5161
|
+
openPulls as cr,
|
|
5162
|
+
overrideReasonProblem as cs,
|
|
5163
|
+
parseChangesetText as ct,
|
|
5164
|
+
parseJiraKey as cu,
|
|
5165
|
+
parseOverrideComment as cv,
|
|
5166
|
+
parseRepo as cw,
|
|
5167
|
+
parseStructureChange as cx,
|
|
5168
|
+
parseSuggestionAppliedComment as cy,
|
|
5169
|
+
parseSuggestionIssue as cz,
|
|
5042
5170
|
applyTokenEdits as d,
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5057
|
-
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
|
|
5070
|
-
|
|
5071
|
-
|
|
5171
|
+
sha256Hex as d0,
|
|
5172
|
+
shortVariant as d1,
|
|
5173
|
+
shortVariants as d2,
|
|
5174
|
+
specHash as d3,
|
|
5175
|
+
specsUsage as d4,
|
|
5176
|
+
stalenessStatus as d5,
|
|
5177
|
+
subjectTitle as d6,
|
|
5178
|
+
suggestionAllowedRoots as d7,
|
|
5179
|
+
suggestionAppliedComment as d8,
|
|
5180
|
+
suggestionApplyTable as d9,
|
|
5181
|
+
validateIntegrations as dA,
|
|
5182
|
+
variantName as dB,
|
|
5183
|
+
versionTimeline as dC,
|
|
5184
|
+
webStorageEtagStore as dD,
|
|
5185
|
+
withAuthorLine as dE,
|
|
5186
|
+
withOutdated as dF,
|
|
5187
|
+
withRequestExploration as dG,
|
|
5188
|
+
withoutJiraKey as dH,
|
|
5189
|
+
suggestionIssueTitle as da,
|
|
5190
|
+
suggestionKindFromLabels as db,
|
|
5191
|
+
suggestionLabelChanges as dc,
|
|
5192
|
+
suggestionLabels as dd,
|
|
5193
|
+
suggestionPathProblems as de,
|
|
5194
|
+
suggestionPayloadSha as df,
|
|
5195
|
+
suggestionReviewState as dg,
|
|
5196
|
+
suggestionShapeProblems as dh,
|
|
5197
|
+
suggestionStalenessOnGitHub as di,
|
|
5198
|
+
suggestionStateLabel as dj,
|
|
5199
|
+
suggestionSubject as dk,
|
|
5200
|
+
suggestionTouches as dl,
|
|
5201
|
+
summarizeApprovals as dm,
|
|
5202
|
+
summarizeCi as dn,
|
|
5203
|
+
summarizeSuggestionApprovals as dp,
|
|
5204
|
+
syncSuggestionStateLabel as dq,
|
|
5205
|
+
titleOf as dr,
|
|
5206
|
+
toChangeSummary as ds,
|
|
5207
|
+
tokenCssVar as dt,
|
|
5208
|
+
tokenFingerprint as du,
|
|
5209
|
+
tokenHistory as dv,
|
|
5210
|
+
unsafePathProblem as dw,
|
|
5211
|
+
utf8ToBase64 as dx,
|
|
5212
|
+
validateComponentSpec as dy,
|
|
5213
|
+
validateDocs as dz,
|
|
5072
5214
|
buildHistoryEntry as e,
|
|
5073
5215
|
coerceValue as f,
|
|
5074
5216
|
checkLayeredPair as g,
|
|
@@ -5092,4 +5234,4 @@ export {
|
|
|
5092
5234
|
explorationNote as y,
|
|
5093
5235
|
explorationNotePath as z
|
|
5094
5236
|
};
|
|
5095
|
-
//# sourceMappingURL=repo-source-
|
|
5237
|
+
//# sourceMappingURL=repo-source-CRfhLO87.js.map
|