@bridge-ds/core 0.2.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/bridge.config.schema.json +74 -5
- package/dist/audit/index.js +1 -1
- package/dist/change-drafts.d.ts +29 -0
- package/dist/{component-spec-B0V_D3gM.js → component-spec-cvrSvIZj.js} +628 -140
- package/dist/component-spec-cvrSvIZj.js.map +1 -0
- package/dist/config.d.ts +16 -2
- package/dist/css-overrides.d.ts +11 -0
- package/dist/docs-ai.d.ts +116 -0
- package/dist/docs-draft.d.ts +30 -4
- package/dist/draft-exchange.d.ts +135 -14
- package/dist/explorations-github.d.ts +43 -5
- package/dist/explorations.d.ts +40 -28
- package/dist/github-client.d.ts +125 -2
- package/dist/index.d.ts +10 -0
- package/dist/index.js +1955 -647
- 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/previews-github.d.ts +22 -0
- package/dist/previews.d.ts +102 -0
- package/dist/protection-github.d.ts +25 -0
- package/dist/protection.d.ts +139 -0
- package/dist/{report-tNzTriGF.js → report-Bf2HEr9t.js} +2 -2
- package/dist/{report-tNzTriGF.js.map → report-Bf2HEr9t.js.map} +1 -1
- package/dist/requests-github.d.ts +21 -0
- package/dist/requests.d.ts +16 -1
- package/dist/rights.d.ts +63 -0
- package/dist/teams.d.ts +149 -0
- package/dist/token-combine.d.ts +78 -0
- package/dist/token-merge.d.ts +72 -0
- package/package.json +1 -1
- package/dist/component-spec-B0V_D3gM.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as collectRefs, i as isAlias, b as aliasTarget, h as hexToRgba, p as parseColor, v as valuesEqual, f as formatValue, L as LayeredTokenSet,
|
|
1
|
+
import { a as collectRefs, i as isAlias, b as aliasTarget, h as hexToRgba, p as parseColor, v as valuesEqual, f as formatValue, L as LayeredTokenSet, d as aliasOf, r as rgbaToDtcg, c as comboKey, e as rgbaToHex, g as globToRegExp, j as validateLayered } from "./layered-BnbmToGs.js";
|
|
2
2
|
const EXT_ID = "com.dsbridge.id";
|
|
3
3
|
const EXT_SCOPES = "com.figma.scopes";
|
|
4
4
|
const PRIMITIVE_MODE = "value";
|
|
@@ -100,12 +100,12 @@ function previewComment(input) {
|
|
|
100
100
|
}
|
|
101
101
|
lines.push("");
|
|
102
102
|
const ctx = { branch: input.branch, pr: input.pr, sha: input.sha };
|
|
103
|
-
const
|
|
103
|
+
const previews = input.previews ?? resolveBranchPreviews(input.apps, ctx);
|
|
104
104
|
lines.push("### Previews", "");
|
|
105
|
-
if (
|
|
105
|
+
if (previews.length) {
|
|
106
106
|
lines.push("| App | Preview |", "| --- | --- |");
|
|
107
|
-
for (const
|
|
108
|
-
const docs =
|
|
107
|
+
for (const p of previews) lines.push(`| ${p.label} | ${p.url ?? "Link in the Vercel comment on this pull request"} |`);
|
|
108
|
+
const docs = previews.find((p) => p.app === "docs")?.url;
|
|
109
109
|
if (docs) {
|
|
110
110
|
lines.push("");
|
|
111
111
|
lines.push(`Tokens only, no reinstall: open your staging with \`?bridge-tokens=${new URL("tokens.css", docs).href}\` (the staging app must allow this origin, see docs/PREVIEWS.md).`);
|
|
@@ -115,6 +115,449 @@ function previewComment(input) {
|
|
|
115
115
|
}
|
|
116
116
|
return lines.join("\n") + "\n";
|
|
117
117
|
}
|
|
118
|
+
function installCommandFromPreviewComment(body) {
|
|
119
|
+
if (!body || !body.trimStart().startsWith(PREVIEW_COMMENT_MARKER)) return null;
|
|
120
|
+
const m = /```(?:sh|bash|shell)[^\n]*\n([\s\S]*?)```/.exec(body);
|
|
121
|
+
if (!m) return null;
|
|
122
|
+
const command = m[1].split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#")).join(" ").replace(/\s*\\\s+/g, " ").replace(/\s+/g, " ").trim();
|
|
123
|
+
return command || null;
|
|
124
|
+
}
|
|
125
|
+
function readPreviewComment(body) {
|
|
126
|
+
if (!body || !body.trimStart().startsWith(PREVIEW_COMMENT_MARKER)) return null;
|
|
127
|
+
const command = installCommandFromPreviewComment(body);
|
|
128
|
+
const sha7 = /Snapshot of `([0-9a-f]{7,40})`/.exec(body)?.[1]?.slice(0, 7) ?? null;
|
|
129
|
+
const source = !command ? "none" : command.includes("https://pkg.pr.new/") ? "pkg.pr.new" : /\/releases\/download\/preview-pr-\d+\//.test(command) ? "release" : "other";
|
|
130
|
+
return { command, sha7, source };
|
|
131
|
+
}
|
|
132
|
+
function findPreviewComment(comments) {
|
|
133
|
+
return [...comments].reverse().find((c) => !!c.body && c.body.trimStart().startsWith(PREVIEW_COMMENT_MARKER));
|
|
134
|
+
}
|
|
135
|
+
const httpsUrl = (raw) => {
|
|
136
|
+
if (!raw || typeof raw !== "string") return null;
|
|
137
|
+
try {
|
|
138
|
+
const u = new URL(/^https?:\/\//i.test(raw) ? raw : `https://${raw}`);
|
|
139
|
+
return u.protocol === "https:" || u.protocol === "http:" ? u.href : null;
|
|
140
|
+
} catch {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
const decodeBase64 = (b64) => {
|
|
145
|
+
const bin = atob(b64.replace(/\s/g, ""));
|
|
146
|
+
const bytes = new Uint8Array(bin.length);
|
|
147
|
+
for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
|
|
148
|
+
return new TextDecoder("utf-8").decode(bytes);
|
|
149
|
+
};
|
|
150
|
+
const isVercelComment = (body) => !!body && /^\[vc\]: #[^:\s]*:[A-Za-z0-9+/=]+/m.test(body);
|
|
151
|
+
function parseVercelComment(body) {
|
|
152
|
+
if (!body) return null;
|
|
153
|
+
const m = /^\[vc\]: #[^:\s]*:([A-Za-z0-9+/=]+)/m.exec(body);
|
|
154
|
+
if (!m) return null;
|
|
155
|
+
let data;
|
|
156
|
+
try {
|
|
157
|
+
data = JSON.parse(decodeBase64(m[1]));
|
|
158
|
+
} catch {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
if (!data || !Array.isArray(data.projects)) return null;
|
|
162
|
+
return data.projects.filter((p) => p && typeof p.name === "string").map((p) => ({ name: p.name, previewUrl: httpsUrl(typeof p.previewUrl === "string" ? p.previewUrl : null), status: typeof p.nextCommitStatus === "string" ? p.nextCommitStatus : null }));
|
|
163
|
+
}
|
|
164
|
+
function findVercelComment(comments) {
|
|
165
|
+
return [...comments].reverse().find((c) => isVercelComment(c.body));
|
|
166
|
+
}
|
|
167
|
+
function templateHostPrefix(template) {
|
|
168
|
+
if (!template) return null;
|
|
169
|
+
const host = /^https?:\/\/([^/?#]+)/i.exec(template)?.[1];
|
|
170
|
+
if (!host) return null;
|
|
171
|
+
return host.split(".")[0].split("{")[0].toLowerCase();
|
|
172
|
+
}
|
|
173
|
+
function appForProject(project, apps) {
|
|
174
|
+
const name = project.trim().toLowerCase();
|
|
175
|
+
if (!name) return null;
|
|
176
|
+
const keys = Object.keys(PREVIEW_APP_LABELS);
|
|
177
|
+
const prefixes = keys.map((app) => [app, templateHostPrefix(apps[app])]);
|
|
178
|
+
const exact = prefixes.find(([, p]) => p === `${name}-git-` || p === name);
|
|
179
|
+
if (exact) return exact[0];
|
|
180
|
+
const loose = prefixes.filter(([, p]) => p !== null && p.startsWith(`${name}-`));
|
|
181
|
+
return loose.length === 1 ? loose[0][0] : null;
|
|
182
|
+
}
|
|
183
|
+
function projectOfLabel(label) {
|
|
184
|
+
const m = /^\s*(?:preview|vercel)\s*[\u2013\u2014-]\s*(.+?)\s*$/i.exec(label);
|
|
185
|
+
return m ? m[1] : null;
|
|
186
|
+
}
|
|
187
|
+
const STATE_OF = {
|
|
188
|
+
success: "ready",
|
|
189
|
+
inactive: "ready",
|
|
190
|
+
deployed: "ready",
|
|
191
|
+
ready: "ready",
|
|
192
|
+
ignored: "ready",
|
|
193
|
+
// Vercel skipped the build (nothing changed for this app): the branch alias serves the last one.
|
|
194
|
+
pending: "building",
|
|
195
|
+
queued: "building",
|
|
196
|
+
in_progress: "building",
|
|
197
|
+
building: "building",
|
|
198
|
+
initializing: "building",
|
|
199
|
+
failure: "failed",
|
|
200
|
+
error: "failed",
|
|
201
|
+
failed: "failed",
|
|
202
|
+
canceled: "failed"
|
|
203
|
+
};
|
|
204
|
+
const stateOf = (s) => STATE_OF[(s ?? "").toLowerCase()] ?? "unknown";
|
|
205
|
+
const isInspectorUrl = (u) => /^https:\/\/(?:www\.)?vercel\.com\//i.test(u);
|
|
206
|
+
function resolveBranchPreviews(apps, ctx, sources = {}) {
|
|
207
|
+
const seen = /* @__PURE__ */ new Map();
|
|
208
|
+
const at = (app) => seen.get(app) ?? (seen.set(app, {}), seen.get(app));
|
|
209
|
+
const offer = (app, url, urlSource, state, stateRank) => {
|
|
210
|
+
if (!app) return;
|
|
211
|
+
const s = at(app);
|
|
212
|
+
if (url && !s.url) Object.assign(s, { url, urlSource });
|
|
213
|
+
if (state !== "unknown" && (s.stateRank === void 0 || stateRank < s.stateRank)) Object.assign(s, { state, stateRank });
|
|
214
|
+
};
|
|
215
|
+
const contexts = /* @__PURE__ */ new Set();
|
|
216
|
+
for (const st of sources.statuses ?? []) {
|
|
217
|
+
if (contexts.has(st.context)) continue;
|
|
218
|
+
contexts.add(st.context);
|
|
219
|
+
const project = projectOfLabel(st.context);
|
|
220
|
+
if (!project) continue;
|
|
221
|
+
const url = httpsUrl(st.target_url);
|
|
222
|
+
offer(appForProject(project, apps), url && !isInspectorUrl(url) ? url : null, "status", stateOf(st.state), 0);
|
|
223
|
+
}
|
|
224
|
+
for (const d of sources.deployments ?? []) {
|
|
225
|
+
const project = projectOfLabel(d.environment);
|
|
226
|
+
const app = project ? appForProject(project, apps) : null;
|
|
227
|
+
if (!app) continue;
|
|
228
|
+
const url = httpsUrl(d.environmentUrl);
|
|
229
|
+
const s = at(app);
|
|
230
|
+
if (url && s.urlSource !== "deployment") Object.assign(s, { url, urlSource: "deployment" });
|
|
231
|
+
const state = stateOf(d.state);
|
|
232
|
+
if (state !== "unknown" && (s.stateRank === void 0 || 1 < s.stateRank)) Object.assign(s, { state, stateRank: 1 });
|
|
233
|
+
}
|
|
234
|
+
for (const p of parseVercelComment(findVercelComment(sources.comments ?? [])?.body) ?? []) offer(appForProject(p.name, apps), p.previewUrl, "vercel-comment", stateOf(p.status), 2);
|
|
235
|
+
const out = [];
|
|
236
|
+
for (const app of Object.keys(PREVIEW_APP_LABELS)) {
|
|
237
|
+
if (!apps[app]) continue;
|
|
238
|
+
const s = seen.get(app) ?? {};
|
|
239
|
+
const url = s.url ?? previewUrl(apps[app], ctx);
|
|
240
|
+
const source = s.url ? s.urlSource : url ? "template" : null;
|
|
241
|
+
out.push({ app, label: PREVIEW_APP_LABELS[app], url, state: s.state ?? "unknown", source });
|
|
242
|
+
}
|
|
243
|
+
return out;
|
|
244
|
+
}
|
|
245
|
+
const previewLinksOf = (previews) => previews.filter((p) => !!p.url).map((p) => ({ app: p.app, label: p.label, url: p.url }));
|
|
246
|
+
const previewsSettled = (previews) => previews.every((p) => !!p.url && (p.state === "ready" || p.state === "failed"));
|
|
247
|
+
const LOGIN = /^[A-Za-z0-9](?:[A-Za-z0-9]|-(?=[A-Za-z0-9])){0,38}$/;
|
|
248
|
+
const SLUG = /^[A-Za-z0-9][A-Za-z0-9._-]{0,99}$/;
|
|
249
|
+
function parseOwnerEntry(raw) {
|
|
250
|
+
if (typeof raw !== "string") return null;
|
|
251
|
+
const s = raw.trim().replace(/^@/, "");
|
|
252
|
+
const slash = s.indexOf("/");
|
|
253
|
+
if (slash < 0) return LOGIN.test(s) ? { kind: "person", login: s } : null;
|
|
254
|
+
const org = s.slice(0, slash);
|
|
255
|
+
const slug = s.slice(slash + 1);
|
|
256
|
+
return LOGIN.test(org) && SLUG.test(slug) ? { kind: "team", org, slug } : null;
|
|
257
|
+
}
|
|
258
|
+
function formatOwnerEntry(e) {
|
|
259
|
+
return e.kind === "team" ? `@${e.org}/${e.slug}` : e.login;
|
|
260
|
+
}
|
|
261
|
+
const isTeamEntry = (raw) => parseOwnerEntry(raw)?.kind === "team";
|
|
262
|
+
const teamKey = (org, slug) => `@${org.toLowerCase()}/${slug.toLowerCase()}`;
|
|
263
|
+
function teamKeyOf(raw) {
|
|
264
|
+
const e = parseOwnerEntry(raw);
|
|
265
|
+
return e?.kind === "team" ? teamKey(e.org, e.slug) : void 0;
|
|
266
|
+
}
|
|
267
|
+
function ownerEntryProblems(raw) {
|
|
268
|
+
if (raw === void 0) return [];
|
|
269
|
+
const isObj2 = (x) => Boolean(x) && typeof x === "object" && !Array.isArray(x);
|
|
270
|
+
if (!isObj2(raw)) return ['"governance.owners" must be { "design": [people or teams], "tech": [people or teams] }'];
|
|
271
|
+
const out = [];
|
|
272
|
+
for (const [k, v] of Object.entries(raw)) {
|
|
273
|
+
if (k !== "design" && k !== "tech") {
|
|
274
|
+
out.push(`"governance.owners.${k}" is not an owner group (design, tech)`);
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
if (!Array.isArray(v)) {
|
|
278
|
+
out.push(`"governance.owners.${k}" must be a list of people or teams`);
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
v.forEach((x, i) => {
|
|
282
|
+
if (typeof x !== "string" || !parseOwnerEntry(x))
|
|
283
|
+
out.push(`"governance.owners.${k}[${i}]" (${JSON.stringify(x)}) is not a GitHub login or team. Write a person as "peterbono" or "@peterbono", a team as "@org/team-slug".`);
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
return out;
|
|
287
|
+
}
|
|
288
|
+
function teamUnreadableMessage(team, reason) {
|
|
289
|
+
const name = `@${team.org}/${team.slug}`;
|
|
290
|
+
switch (reason) {
|
|
291
|
+
case "personal-account":
|
|
292
|
+
return `${name} cannot be used: ${team.org} is a personal GitHub account, and teams only exist in organizations. List people instead, or move the repository to an organization.`;
|
|
293
|
+
case "missing-permission":
|
|
294
|
+
return `The members of ${name} cannot be read: the GitHub token needs read:org (classic token) or Members: Read on the ${team.org} organization (fine-grained token).`;
|
|
295
|
+
case "no-token":
|
|
296
|
+
return `The members of ${name} cannot be read without a working GitHub token. Connect GitHub with a token that has read:org.`;
|
|
297
|
+
case "not-found":
|
|
298
|
+
return `${name} was not found. Check the team slug on GitHub, or give the token read:org so it can see the team.`;
|
|
299
|
+
case "unreachable":
|
|
300
|
+
return `The members of ${name} cannot be read right now: GitHub did not answer. Try again in a moment.`;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
function teamsIn(owners) {
|
|
304
|
+
const all = Array.isArray(owners) ? owners : [...owners.design ?? [], ...owners.tech ?? []];
|
|
305
|
+
const out = /* @__PURE__ */ new Map();
|
|
306
|
+
for (const raw of all) {
|
|
307
|
+
const e = parseOwnerEntry(raw);
|
|
308
|
+
if (e?.kind === "team") out.set(teamKey(e.org, e.slug), { key: teamKey(e.org, e.slug), org: e.org, slug: e.slug });
|
|
309
|
+
}
|
|
310
|
+
return [...out.values()];
|
|
311
|
+
}
|
|
312
|
+
const norm$1 = (x) => x.trim().toLowerCase().replace(/^@/, "");
|
|
313
|
+
function expandEntries(entries, teams) {
|
|
314
|
+
const out = [];
|
|
315
|
+
const seen = /* @__PURE__ */ new Set();
|
|
316
|
+
const add = (x) => {
|
|
317
|
+
const k = norm$1(x);
|
|
318
|
+
if (k && !seen.has(k)) {
|
|
319
|
+
seen.add(k);
|
|
320
|
+
out.push(x.replace(/^@(?=[^/]+$)/, ""));
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
for (const raw of entries) {
|
|
324
|
+
const e = parseOwnerEntry(raw);
|
|
325
|
+
if (!e) continue;
|
|
326
|
+
if (e.kind === "person") add(e.login);
|
|
327
|
+
else {
|
|
328
|
+
const state = teams?.[teamKey(e.org, e.slug)];
|
|
329
|
+
if (state?.status === "ok") state.members.forEach(add);
|
|
330
|
+
else add(teamKey(e.org, e.slug));
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return out;
|
|
334
|
+
}
|
|
335
|
+
function expandOwners(owners, teams) {
|
|
336
|
+
return { design: expandEntries(owners.design, teams), tech: expandEntries(owners.tech, teams) };
|
|
337
|
+
}
|
|
338
|
+
const people = (n) => `${n} ${n === 1 ? "person" : "people"}`;
|
|
339
|
+
function entryLabel(raw, teams) {
|
|
340
|
+
const e = parseOwnerEntry(raw);
|
|
341
|
+
if (!e) return raw;
|
|
342
|
+
if (e.kind === "person") return `@${e.login}`;
|
|
343
|
+
const state = teams?.[teamKey(e.org, e.slug)];
|
|
344
|
+
const name = `@${e.org}/${e.slug}`;
|
|
345
|
+
return state?.status === "ok" ? `${name} (${people(state.members.length)})` : name;
|
|
346
|
+
}
|
|
347
|
+
function entriesFor(entries, logins, teams) {
|
|
348
|
+
const want = new Set(logins.map(norm$1));
|
|
349
|
+
const out = [];
|
|
350
|
+
for (const raw of entries) {
|
|
351
|
+
const e = parseOwnerEntry(raw);
|
|
352
|
+
if (!e) continue;
|
|
353
|
+
if (e.kind === "person") {
|
|
354
|
+
if (want.has(norm$1(e.login))) out.push(entryLabel(raw, teams));
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
const state = teams?.[teamKey(e.org, e.slug)];
|
|
358
|
+
if (state?.status !== "ok" || state.members.some((m) => want.has(norm$1(m)))) out.push(entryLabel(raw, teams));
|
|
359
|
+
}
|
|
360
|
+
return [...new Set(out)];
|
|
361
|
+
}
|
|
362
|
+
function unreadableTeams(teams) {
|
|
363
|
+
return Object.values(teams ?? {}).filter((t) => t.status === "unreadable");
|
|
364
|
+
}
|
|
365
|
+
class TeamDirectory {
|
|
366
|
+
constructor(reader, opts = {}) {
|
|
367
|
+
this.reader = reader;
|
|
368
|
+
this.ttl = opts.ttlMs ?? 5 * 6e4;
|
|
369
|
+
this.now = opts.now ?? Date.now;
|
|
370
|
+
}
|
|
371
|
+
reader;
|
|
372
|
+
teams = /* @__PURE__ */ new Map();
|
|
373
|
+
accounts = /* @__PURE__ */ new Map();
|
|
374
|
+
memberships = /* @__PURE__ */ new Map();
|
|
375
|
+
ttl;
|
|
376
|
+
now;
|
|
377
|
+
accountType(org) {
|
|
378
|
+
const k = org.toLowerCase();
|
|
379
|
+
let p = this.accounts.get(k);
|
|
380
|
+
if (!p) {
|
|
381
|
+
p = this.reader.getAccountType(org).catch(() => null);
|
|
382
|
+
this.accounts.set(k, p);
|
|
383
|
+
}
|
|
384
|
+
return p;
|
|
385
|
+
}
|
|
386
|
+
/** The reason a read of `org` failed, as a state. A 404 on a personal account says so; a 403 is a missing permission. */
|
|
387
|
+
async failure(ref, err) {
|
|
388
|
+
const e = err ?? {};
|
|
389
|
+
let reason;
|
|
390
|
+
if (!this.reader.hasToken) reason = await this.accountType(ref.org) === "User" ? "personal-account" : "no-token";
|
|
391
|
+
else if (e.status === 401) reason = "no-token";
|
|
392
|
+
else if (e.status === 403 || e.auth === "missing-permission") reason = "missing-permission";
|
|
393
|
+
else if (e.status === 404) reason = await this.accountType(ref.org) === "User" ? "personal-account" : "not-found";
|
|
394
|
+
else reason = "unreachable";
|
|
395
|
+
return { ...ref, status: "unreadable", reason, message: teamUnreadableMessage(ref, reason) };
|
|
396
|
+
}
|
|
397
|
+
/** Members of one team, or why they cannot be read. Cached. */
|
|
398
|
+
team(org, slug) {
|
|
399
|
+
const ref = { key: teamKey(org, slug), org, slug };
|
|
400
|
+
const hit = this.teams.get(ref.key);
|
|
401
|
+
if (hit && this.now() - hit.at < this.ttl) return hit.value;
|
|
402
|
+
const value = (async () => {
|
|
403
|
+
if (!this.reader.hasToken) return this.failure(ref, void 0);
|
|
404
|
+
try {
|
|
405
|
+
const members = await this.reader.listTeamMembers(org, slug);
|
|
406
|
+
return { ...ref, status: "ok", members };
|
|
407
|
+
} catch (err) {
|
|
408
|
+
return this.failure(ref, err);
|
|
409
|
+
}
|
|
410
|
+
})();
|
|
411
|
+
this.teams.set(ref.key, { at: this.now(), value });
|
|
412
|
+
void value.then((s) => {
|
|
413
|
+
if (s.status === "unreadable" && s.reason === "unreachable") this.teams.delete(ref.key);
|
|
414
|
+
});
|
|
415
|
+
return value;
|
|
416
|
+
}
|
|
417
|
+
/** Every team named in the owner lists, read in parallel. Empty object when no team is named (no request). */
|
|
418
|
+
async resolve(owners) {
|
|
419
|
+
const refs = teamsIn(owners);
|
|
420
|
+
const states = await Promise.all(refs.map((r) => this.team(r.org, r.slug)));
|
|
421
|
+
return Object.fromEntries(states.map((s) => [s.key, s]));
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Whether `login` is an active member of one team: from the cached member list when there is one, else one
|
|
425
|
+
* membership read (GET /orgs/{org}/teams/{slug}/memberships/{user}). An unreadable team returns its state.
|
|
426
|
+
*/
|
|
427
|
+
async isMember(org, slug, login) {
|
|
428
|
+
const ref = { key: teamKey(org, slug), org, slug };
|
|
429
|
+
const cached = this.teams.get(ref.key);
|
|
430
|
+
if (cached && this.now() - cached.at < this.ttl) {
|
|
431
|
+
const s = await cached.value;
|
|
432
|
+
if (s.status === "ok") return s.members.some((m) => norm$1(m) === norm$1(login));
|
|
433
|
+
}
|
|
434
|
+
const k = `${ref.key}#${norm$1(login)}`;
|
|
435
|
+
const hit = this.memberships.get(k);
|
|
436
|
+
if (hit && this.now() - hit.at < this.ttl) return hit.value;
|
|
437
|
+
const value = (async () => {
|
|
438
|
+
if (!this.reader.hasToken) return this.failure(ref, void 0);
|
|
439
|
+
try {
|
|
440
|
+
return await this.reader.getTeamMembership(org, slug, login) === "active";
|
|
441
|
+
} catch (err) {
|
|
442
|
+
return this.failure(ref, err);
|
|
443
|
+
}
|
|
444
|
+
})();
|
|
445
|
+
this.memberships.set(k, { at: this.now(), value });
|
|
446
|
+
return value;
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Whether `login` holds a right given to `entries` (people and teams): a person entry matches directly, a team
|
|
450
|
+
* through its membership. Teams are only read when no person entry matches.
|
|
451
|
+
*/
|
|
452
|
+
async memberOfAny(entries, login) {
|
|
453
|
+
const unreadable = [];
|
|
454
|
+
const parsed = entries.map(parseOwnerEntry).filter((e) => Boolean(e));
|
|
455
|
+
if (parsed.some((e) => e.kind === "person" && norm$1(e.login) === norm$1(login))) return { member: true, unreadable };
|
|
456
|
+
for (const e of parsed) {
|
|
457
|
+
if (e.kind !== "team") continue;
|
|
458
|
+
const r = await this.isMember(e.org, e.slug, login);
|
|
459
|
+
if (r === true) return { member: true, unreadable };
|
|
460
|
+
if (typeof r === "object" && r.status === "unreadable") unreadable.push(r);
|
|
461
|
+
}
|
|
462
|
+
return { member: false, unreadable };
|
|
463
|
+
}
|
|
464
|
+
/** The organization's teams for a picker, or why they cannot be listed (a personal account has none). */
|
|
465
|
+
async orgTeams(org) {
|
|
466
|
+
if (await this.accountType(org) === "User") return { status: "unreadable", reason: "personal-account" };
|
|
467
|
+
if (!this.reader.hasToken) return { status: "unreadable", reason: "no-token" };
|
|
468
|
+
try {
|
|
469
|
+
return { status: "ok", teams: await this.reader.listOrgTeams(org) };
|
|
470
|
+
} catch (err) {
|
|
471
|
+
const s = await this.failure({ key: teamKey(org, "*"), org, slug: "*" }, err);
|
|
472
|
+
return { status: "unreadable", reason: s.status === "unreadable" ? s.reason : "unreachable" };
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
/** Drops every cached read (after the token changes). */
|
|
476
|
+
clear() {
|
|
477
|
+
this.teams.clear();
|
|
478
|
+
this.accounts.clear();
|
|
479
|
+
this.memberships.clear();
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
const OWNER_GROUPS = ["design", "tech"];
|
|
483
|
+
const RIGHTS = ["approveDesign", "approveTech", "ship"];
|
|
484
|
+
const RIGHT_LABEL = {
|
|
485
|
+
approveDesign: "Approve design",
|
|
486
|
+
approveTech: "Approve tech",
|
|
487
|
+
ship: "Ship"
|
|
488
|
+
};
|
|
489
|
+
const DEFAULT_RIGHTS = { approveDesign: ["design"], approveTech: ["tech"], ship: ["tech"] };
|
|
490
|
+
const norm = (x) => x.trim().toLowerCase().replace(/^@/, "");
|
|
491
|
+
const uniq = (xs) => [...new Set(xs)];
|
|
492
|
+
function normalizeRights(input) {
|
|
493
|
+
const pick = (r) => {
|
|
494
|
+
const v = input?.[r];
|
|
495
|
+
if (!Array.isArray(v)) return [...DEFAULT_RIGHTS[r]];
|
|
496
|
+
return OWNER_GROUPS.filter((g) => v.includes(g));
|
|
497
|
+
};
|
|
498
|
+
return { approveDesign: pick("approveDesign"), approveTech: pick("approveTech"), ship: pick("ship") };
|
|
499
|
+
}
|
|
500
|
+
function rightsProblems(raw) {
|
|
501
|
+
if (raw === void 0) return [];
|
|
502
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return ['"governance.rights" must be { "approveDesign": [groups], "approveTech": [groups], "ship": [groups] }'];
|
|
503
|
+
const out = [];
|
|
504
|
+
for (const [k, v] of Object.entries(raw)) {
|
|
505
|
+
if (!RIGHTS.includes(k)) out.push(`"governance.rights.${k}" is not a right (${RIGHTS.join(", ")})`);
|
|
506
|
+
else if (!Array.isArray(v) || v.length === 0 || !v.every((g) => OWNER_GROUPS.includes(g)))
|
|
507
|
+
out.push(`"governance.rights.${k}" must list one or more of "design", "tech"`);
|
|
508
|
+
}
|
|
509
|
+
return out;
|
|
510
|
+
}
|
|
511
|
+
function holdersOf(right, owners, rights) {
|
|
512
|
+
const groups = (rights ?? DEFAULT_RIGHTS)[right];
|
|
513
|
+
return uniq(groups.flatMap((g) => owners[g] ?? []));
|
|
514
|
+
}
|
|
515
|
+
function approverLists(owners, rights, teams) {
|
|
516
|
+
const entries = approverEntries(owners, rights);
|
|
517
|
+
return teams === void 0 ? entries : expandOwners(entries, teams);
|
|
518
|
+
}
|
|
519
|
+
function approverEntries(owners, rights) {
|
|
520
|
+
if (!rights) return { design: [...owners.design], tech: [...owners.tech] };
|
|
521
|
+
return { design: holdersOf("approveDesign", owners, rights), tech: holdersOf("approveTech", owners, rights) };
|
|
522
|
+
}
|
|
523
|
+
function shipPolicy(owners, rights, teams) {
|
|
524
|
+
if (!rights) return null;
|
|
525
|
+
const entries = holdersOf("ship", owners, rights);
|
|
526
|
+
if (!entries.length) return null;
|
|
527
|
+
const logins = expandEntries(entries, teams);
|
|
528
|
+
return { logins, groups: [...rights.ship], entries, ...teams ? { teams } : {} };
|
|
529
|
+
}
|
|
530
|
+
function unreadableShipTeams(policy) {
|
|
531
|
+
if (!policy?.teams) return [];
|
|
532
|
+
const keys = new Set((policy.entries ?? []).map((e) => e.trim().toLowerCase().replace(/^@?/, "@")));
|
|
533
|
+
return unreadableTeams(policy.teams).filter((t) => keys.has(t.key));
|
|
534
|
+
}
|
|
535
|
+
function canShip(login, policy) {
|
|
536
|
+
if (!policy) return true;
|
|
537
|
+
if (!login || login.includes("/")) return false;
|
|
538
|
+
return policy.logins.map(norm).includes(norm(login));
|
|
539
|
+
}
|
|
540
|
+
function groupsPhrase(groups) {
|
|
541
|
+
const g = OWNER_GROUPS.filter((x) => groups.includes(x));
|
|
542
|
+
if (g.length === 1) return `the ${g[0]} owner`;
|
|
543
|
+
return `a ${g.join(" or ")} owner`;
|
|
544
|
+
}
|
|
545
|
+
function whoCanShipSentence(policy) {
|
|
546
|
+
const names = (policy.entries ?? policy.logins).map((l) => entryLabel(l, policy.teams)).join(", ");
|
|
547
|
+
return `Only ${groupsPhrase(policy.groups)} can ship this${names ? ` (${names})` : ""}.`;
|
|
548
|
+
}
|
|
549
|
+
const DEFAULT_VISUAL_IMPACT = { mode: "auto" };
|
|
550
|
+
const VISUAL_CHECK_LABEL = "visual-check";
|
|
551
|
+
function visualImpactProblems(raw) {
|
|
552
|
+
if (raw === void 0) return [];
|
|
553
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return ['"visualImpact" must be { "mode": "auto" | "on-demand" }'];
|
|
554
|
+
const out = [];
|
|
555
|
+
for (const [k, v] of Object.entries(raw)) {
|
|
556
|
+
if (k !== "mode") out.push(`"visualImpact.${k}" is not a setting (mode)`);
|
|
557
|
+
else if (v !== "auto" && v !== "on-demand") out.push('"visualImpact.mode" must be "auto" or "on-demand"');
|
|
558
|
+
}
|
|
559
|
+
return out;
|
|
560
|
+
}
|
|
118
561
|
const CONFIG_FILE = "bridge.config.json";
|
|
119
562
|
const DEFAULT_OVERRIDE = { allowed: [], requireReason: true, minReasonLength: 20 };
|
|
120
563
|
const DEFAULT_TOKEN_PATHS = {
|
|
@@ -251,9 +694,11 @@ function defineConfig(input) {
|
|
|
251
694
|
requireReason: g.override.requireReason ?? DEFAULT_OVERRIDE.requireReason,
|
|
252
695
|
minReasonLength: g.override.minReasonLength ?? DEFAULT_OVERRIDE.minReasonLength
|
|
253
696
|
}
|
|
254
|
-
} : {}
|
|
697
|
+
} : {},
|
|
698
|
+
...g.rights ? { rights: normalizeRights(g.rights) } : {}
|
|
255
699
|
},
|
|
256
700
|
...input.audit ? { audit: { ...input.audit } } : {},
|
|
701
|
+
...input.visualImpact ? { visualImpact: { mode: input.visualImpact.mode === "on-demand" ? "on-demand" : "auto" } } : {},
|
|
257
702
|
...input.integrations ? { integrations: { ...input.integrations } } : {},
|
|
258
703
|
...input.previews ? {
|
|
259
704
|
previews: {
|
|
@@ -360,9 +805,7 @@ function loadConfig(source) {
|
|
|
360
805
|
else {
|
|
361
806
|
for (const k of ["protectedBranches", "labels", "guardrails"]) if (gov[k] !== void 0 && !(Array.isArray(gov[k]) && gov[k].every((x) => typeof x === "string"))) problems.push(`"governance.${k}" must be a list of strings`);
|
|
362
807
|
if (gov.branchPrefix !== void 0 && !isStr(gov.branchPrefix)) problems.push('"governance.branchPrefix" must be a non-empty string');
|
|
363
|
-
|
|
364
|
-
if (o !== void 0 && (!isObj(o) || ["design", "tech"].some((k) => o[k] !== void 0 && !(Array.isArray(o[k]) && o[k].every((x) => typeof x === "string")))))
|
|
365
|
-
problems.push('"governance.owners" must be { "design": [logins], "tech": [logins] }');
|
|
808
|
+
problems.push(...ownerEntryProblems(gov.owners));
|
|
366
809
|
const ov = gov.override;
|
|
367
810
|
if (ov !== void 0) {
|
|
368
811
|
if (!isObj(ov)) problems.push('"governance.override" must be an object');
|
|
@@ -372,8 +815,10 @@ function loadConfig(source) {
|
|
|
372
815
|
if (ov.minReasonLength !== void 0 && !(Number.isInteger(ov.minReasonLength) && ov.minReasonLength >= 0)) problems.push('"governance.override.minReasonLength" must be a whole number, 0 or more');
|
|
373
816
|
}
|
|
374
817
|
}
|
|
818
|
+
problems.push(...rightsProblems(gov.rights));
|
|
375
819
|
}
|
|
376
820
|
}
|
|
821
|
+
problems.push(...visualImpactProblems(d.visualImpact));
|
|
377
822
|
const audit = d.audit;
|
|
378
823
|
if (audit !== void 0) {
|
|
379
824
|
if (!isObj(audit)) problems.push('"audit" must be an object');
|
|
@@ -419,7 +864,8 @@ function toConfigJson(config, schema = "./packages/core/bridge.config.schema.jso
|
|
|
419
864
|
cssVar: config.cssVar,
|
|
420
865
|
governance: config.governance,
|
|
421
866
|
...config.audit ? { audit: config.audit } : {},
|
|
422
|
-
...config.previews ? { previews: config.previews } : {}
|
|
867
|
+
...config.previews ? { previews: config.previews } : {},
|
|
868
|
+
...config.visualImpact ? { visualImpact: config.visualImpact } : {}
|
|
423
869
|
};
|
|
424
870
|
}
|
|
425
871
|
class JsonParseError extends Error {
|
|
@@ -2480,141 +2926,183 @@ function parseComponentSpecs(text) {
|
|
|
2480
2926
|
}
|
|
2481
2927
|
const tokenCssVar = (path, prefix = "--", separator = "-") => `${prefix}${path.split(".").join(separator)}`;
|
|
2482
2928
|
export {
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2929
|
+
tokensPath as $,
|
|
2930
|
+
flattenTokens as A,
|
|
2931
|
+
serializeTokenFile as B,
|
|
2486
2932
|
COMPONENT_SPECS_VERSION as C,
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2933
|
+
removeToken as D,
|
|
2934
|
+
upsertToken as E,
|
|
2935
|
+
threeWayDiff as F,
|
|
2936
|
+
emptyTokenFile as G,
|
|
2937
|
+
parseTokenFile as H,
|
|
2938
|
+
EXT_ID as I,
|
|
2939
|
+
EXT_SCOPES as J,
|
|
2940
|
+
resolve as K,
|
|
2941
|
+
moveToken as L,
|
|
2942
|
+
effectiveScopes as M,
|
|
2943
|
+
sameScopes as N,
|
|
2944
|
+
defaultScopes as O,
|
|
2945
|
+
readTokenAt as P,
|
|
2946
|
+
modeFile as Q,
|
|
2947
|
+
defaultFile as R,
|
|
2502
2948
|
SINGLE_MODE as S,
|
|
2503
2949
|
TIER_NAMES as T,
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2950
|
+
DEFAULT_PROTECTED_BRANCHES as U,
|
|
2951
|
+
ProtectedBranchError as V,
|
|
2952
|
+
ConfigError as W,
|
|
2953
|
+
DEFAULT_TOKEN_PATHS as X,
|
|
2954
|
+
applyTokenEdits as Y,
|
|
2955
|
+
buildHistoryEntry as Z,
|
|
2956
|
+
resolveBranchPreviews as _,
|
|
2511
2957
|
assertSafeBranch as a,
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2958
|
+
contextFor as a$,
|
|
2959
|
+
coerceValue as a0,
|
|
2960
|
+
toConfigJson as a1,
|
|
2961
|
+
parseContrastChecks as a2,
|
|
2962
|
+
checkLayeredPair as a3,
|
|
2963
|
+
classifyRisk as a4,
|
|
2964
|
+
formatChangeset as a5,
|
|
2965
|
+
RISK_LABELS as a6,
|
|
2966
|
+
parseJson as a7,
|
|
2967
|
+
serializeJson as a8,
|
|
2968
|
+
plainEqual as a9,
|
|
2969
|
+
RISK_TIER as aA,
|
|
2970
|
+
SPEC_FIELDS as aB,
|
|
2971
|
+
TIER_ORDER as aC,
|
|
2972
|
+
TeamDirectory as aD,
|
|
2973
|
+
UNRELEASED as aE,
|
|
2974
|
+
VISUAL_CHECK_LABEL as aF,
|
|
2975
|
+
affectedContrast as aG,
|
|
2976
|
+
appForProject as aH,
|
|
2977
|
+
appUrl as aI,
|
|
2978
|
+
approverEntries as aJ,
|
|
2979
|
+
approverLists as aK,
|
|
2980
|
+
assignVersions as aL,
|
|
2981
|
+
baseBranchOf as aM,
|
|
2982
|
+
branchName as aN,
|
|
2983
|
+
branchSlug as aO,
|
|
2984
|
+
canShip as aP,
|
|
2985
|
+
changeKind as aQ,
|
|
2986
|
+
changeTable as aR,
|
|
2987
|
+
changedModes as aS,
|
|
2988
|
+
changesetBump as aT,
|
|
2989
|
+
classifyLayered as aU,
|
|
2990
|
+
clearRawDeep as aV,
|
|
2991
|
+
commitMessage as aW,
|
|
2992
|
+
composeChangeset as aX,
|
|
2993
|
+
composePrBody as aY,
|
|
2994
|
+
composeProposalBody as aZ,
|
|
2995
|
+
composite as a_,
|
|
2996
|
+
toPlain as aa,
|
|
2997
|
+
getEntry as ab,
|
|
2998
|
+
fromPlain as ac,
|
|
2999
|
+
previewLinks as ad,
|
|
3000
|
+
slugify as ae,
|
|
3001
|
+
readPreviewComment as af,
|
|
3002
|
+
findPreviewComment as ag,
|
|
3003
|
+
previewsSettled as ah,
|
|
3004
|
+
AAA_MIN as ai,
|
|
3005
|
+
AA_MIN as aj,
|
|
3006
|
+
BULK_LIMIT as ak,
|
|
3007
|
+
CONTRAST_CELL_MAX as al,
|
|
3008
|
+
CONTRAST_CHECKS_FILE as am,
|
|
3009
|
+
DEFAULT_COMPONENT_SPECS_FILE as an,
|
|
3010
|
+
DEFAULT_GOVERNANCE as ao,
|
|
3011
|
+
DEFAULT_OVERRIDE as ap,
|
|
3012
|
+
DEFAULT_RIGHTS as aq,
|
|
3013
|
+
DEFAULT_VISUAL_IMPACT as ar,
|
|
3014
|
+
JsonParseError as as,
|
|
3015
|
+
OWNER_GROUPS as at,
|
|
3016
|
+
PREVIEW_APP_LABELS as au,
|
|
3017
|
+
PREVIEW_COMMENT_MARKER as av,
|
|
3018
|
+
PREVIEW_PLACEHOLDERS as aw,
|
|
3019
|
+
PRIMITIVE_MODE as ax,
|
|
3020
|
+
RIGHTS as ay,
|
|
3021
|
+
RIGHT_LABEL as az,
|
|
2576
3022
|
parseChangelog as b,
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
3023
|
+
tarballAssetName as b$,
|
|
3024
|
+
contrastDelta as b0,
|
|
3025
|
+
contrastDeltaTable as b1,
|
|
3026
|
+
contrastLevel as b2,
|
|
3027
|
+
contrastPair as b3,
|
|
3028
|
+
contrastRatio as b4,
|
|
3029
|
+
countChanges as b5,
|
|
3030
|
+
defaultContext as b6,
|
|
3031
|
+
defaultTitle as b7,
|
|
3032
|
+
defineConfig as b8,
|
|
3033
|
+
detectLayout as b9,
|
|
3034
|
+
luminance as bA,
|
|
3035
|
+
modeLabel as bB,
|
|
3036
|
+
nodesEqual as bC,
|
|
3037
|
+
normalizeRights as bD,
|
|
3038
|
+
overrideKey as bE,
|
|
3039
|
+
ownerEntryProblems as bF,
|
|
3040
|
+
pairKey as bG,
|
|
3041
|
+
parseChangesetText as bH,
|
|
3042
|
+
parseComponentSpecs as bI,
|
|
3043
|
+
parseOwnerEntry as bJ,
|
|
3044
|
+
parseVercelComment as bK,
|
|
3045
|
+
planRestore as bL,
|
|
3046
|
+
planRevert as bM,
|
|
3047
|
+
planToJson as bN,
|
|
3048
|
+
previewComment as bO,
|
|
3049
|
+
previewLinksOf as bP,
|
|
3050
|
+
previewUrl as bQ,
|
|
3051
|
+
projectOfLabel as bR,
|
|
3052
|
+
protectedBranchesOf as bS,
|
|
3053
|
+
renderedColor as bT,
|
|
3054
|
+
rightsProblems as bU,
|
|
3055
|
+
runLayeredContrast as bV,
|
|
3056
|
+
sameToken as bW,
|
|
3057
|
+
shipPolicy as bX,
|
|
3058
|
+
snapshotUrl as bY,
|
|
3059
|
+
specHash as bZ,
|
|
3060
|
+
summarizeTokenChange as b_,
|
|
3061
|
+
diffLayered as ba,
|
|
3062
|
+
entriesFor as bb,
|
|
3063
|
+
entryLabel as bc,
|
|
3064
|
+
expandEntries as bd,
|
|
3065
|
+
expandOwners as be,
|
|
3066
|
+
fieldChanges as bf,
|
|
3067
|
+
fileModes as bg,
|
|
3068
|
+
filterHistory as bh,
|
|
3069
|
+
findToken as bi,
|
|
3070
|
+
findVercelComment as bj,
|
|
3071
|
+
firstParagraph as bk,
|
|
3072
|
+
formatNumber as bl,
|
|
3073
|
+
formatOwnerEntry as bm,
|
|
3074
|
+
getChild as bn,
|
|
3075
|
+
groupsPhrase as bo,
|
|
3076
|
+
holdersOf as bp,
|
|
3077
|
+
indexByPath as bq,
|
|
3078
|
+
inheritedTypeAt as br,
|
|
3079
|
+
installCommand as bs,
|
|
3080
|
+
installCommandFromPreviewComment as bt,
|
|
3081
|
+
isGuardrail as bu,
|
|
3082
|
+
isTeamEntry as bv,
|
|
3083
|
+
isTokenFile as bw,
|
|
3084
|
+
isVercelComment as bx,
|
|
3085
|
+
kindForMin as by,
|
|
3086
|
+
layeredImpact as bz,
|
|
2617
3087
|
collectHistory as c,
|
|
3088
|
+
tarballUrl as c0,
|
|
3089
|
+
teamKey as c1,
|
|
3090
|
+
teamKeyOf as c2,
|
|
3091
|
+
teamUnreadableMessage as c3,
|
|
3092
|
+
teamsIn as c4,
|
|
3093
|
+
titleOf as c5,
|
|
3094
|
+
toChangeSummary as c6,
|
|
3095
|
+
toMap as c7,
|
|
3096
|
+
tokenBump as c8,
|
|
3097
|
+
tokenHistory as c9,
|
|
3098
|
+
tokenNode as ca,
|
|
3099
|
+
unreadableShipTeams as cb,
|
|
3100
|
+
unreadableTeams as cc,
|
|
3101
|
+
valueIn as cd,
|
|
3102
|
+
variantName as ce,
|
|
3103
|
+
versionTimeline as cf,
|
|
3104
|
+
visualImpactProblems as cg,
|
|
3105
|
+
whoCanShipSentence as ch,
|
|
2618
3106
|
buildReport as d,
|
|
2619
3107
|
prNumberFromSubject as e,
|
|
2620
3108
|
CONFIG_FILE as f,
|
|
@@ -2639,4 +3127,4 @@ export {
|
|
|
2639
3127
|
contextLabel as y,
|
|
2640
3128
|
pathId as z
|
|
2641
3129
|
};
|
|
2642
|
-
//# sourceMappingURL=component-spec-
|
|
3130
|
+
//# sourceMappingURL=component-spec-cvrSvIZj.js.map
|