@bridge-ds/mcp 0.3.3 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -1
- package/dist/branches.d.ts +440 -0
- package/dist/{chunk-7HYTZVRW.js → chunk-MXMMPO7F.js} +2250 -37
- package/dist/{chunk-K3HY24MZ.js → chunk-QTO4EP6V.js} +336 -1
- package/dist/explorations.d.ts +5 -0
- package/dist/index.js +2 -2
- package/dist/lib.d.ts +2 -0
- package/dist/lib.js +7 -3
- package/dist/ops.d.ts +70 -1
- package/dist/requests.d.ts +2 -0
- package/dist/{src-V653Z7JC.js → src-DHJOM5PQ.js} +7 -145
- package/dist/studio.d.ts +113 -0
- package/package.json +3 -3
|
@@ -391,6 +391,7 @@ var TeamDirectory = class {
|
|
|
391
391
|
var OWNER_GROUPS = ["design", "tech"];
|
|
392
392
|
var RIGHTS = ["approveDesign", "approveTech", "ship"];
|
|
393
393
|
var DEFAULT_RIGHTS = { approveDesign: ["design"], approveTech: ["tech"], ship: ["tech"] };
|
|
394
|
+
var norm2 = (x) => x.trim().toLowerCase().replace(/^@/, "");
|
|
394
395
|
var uniq = (xs) => [...new Set(xs)];
|
|
395
396
|
function normalizeRights(input) {
|
|
396
397
|
const pick = (r) => {
|
|
@@ -426,6 +427,16 @@ function shipPolicy(owners, rights, teams) {
|
|
|
426
427
|
const logins = expandEntries(entries, teams);
|
|
427
428
|
return { logins, groups: [...rights.ship], entries, ...teams ? { teams } : {} };
|
|
428
429
|
}
|
|
430
|
+
function unreadableShipTeams(policy) {
|
|
431
|
+
if (!policy?.teams) return [];
|
|
432
|
+
const keys = new Set((policy.entries ?? []).map((e) => e.trim().toLowerCase().replace(/^@?/, "@")));
|
|
433
|
+
return unreadableTeams(policy.teams).filter((t) => keys.has(t.key));
|
|
434
|
+
}
|
|
435
|
+
function canShip(login, policy) {
|
|
436
|
+
if (!policy) return true;
|
|
437
|
+
if (!login || login.includes("/")) return false;
|
|
438
|
+
return policy.logins.map(norm2).includes(norm2(login));
|
|
439
|
+
}
|
|
429
440
|
function groupsPhrase(groups) {
|
|
430
441
|
const g = OWNER_GROUPS.filter((x) => groups.includes(x));
|
|
431
442
|
if (g.length === 1) return `the ${g[0]} owner`;
|
|
@@ -446,6 +457,105 @@ function visualImpactProblems(raw) {
|
|
|
446
457
|
return out;
|
|
447
458
|
}
|
|
448
459
|
|
|
460
|
+
// ../core/src/products.ts
|
|
461
|
+
var DEFAULT_PRODUCTS = [
|
|
462
|
+
{ key: "admin", name: "Admin demo", path: "apps/admin", framework: "react" },
|
|
463
|
+
{ key: "library", name: "Library", default: true }
|
|
464
|
+
];
|
|
465
|
+
var PRODUCT_LABEL_PREFIX = "product: ";
|
|
466
|
+
var productLabel = (key) => `${PRODUCT_LABEL_PREFIX}${key}`;
|
|
467
|
+
function productMarks(products, p) {
|
|
468
|
+
if (p.key === defaultProduct(products).key) return { labels: [], line: "" };
|
|
469
|
+
return { labels: [productLabel(p.key)], line: `**Product:** ${p.name}` };
|
|
470
|
+
}
|
|
471
|
+
function productsOf(config) {
|
|
472
|
+
const list2 = config?.products;
|
|
473
|
+
return Array.isArray(list2) && list2.length ? list2 : DEFAULT_PRODUCTS;
|
|
474
|
+
}
|
|
475
|
+
function defaultProduct(products) {
|
|
476
|
+
const list2 = products.length ? products : DEFAULT_PRODUCTS;
|
|
477
|
+
return list2.find((p) => p.default) ?? list2[list2.length - 1];
|
|
478
|
+
}
|
|
479
|
+
function findProduct(products, keyOrName) {
|
|
480
|
+
const k = (keyOrName ?? "").trim().toLowerCase();
|
|
481
|
+
if (!k) return void 0;
|
|
482
|
+
return products.find((p) => p.key.toLowerCase() === k) ?? products.find((p) => p.name.toLowerCase() === k);
|
|
483
|
+
}
|
|
484
|
+
var cleanPath = (p) => p.trim().replace(/^\.?\/+|\/+$/g, "");
|
|
485
|
+
function productOfBranch(products, branch) {
|
|
486
|
+
const fallback = defaultProduct(products);
|
|
487
|
+
for (const l of branch.labels ?? []) {
|
|
488
|
+
const name = typeof l === "string" ? l : l?.name;
|
|
489
|
+
if (typeof name !== "string" || !name.toLowerCase().startsWith(PRODUCT_LABEL_PREFIX)) continue;
|
|
490
|
+
const hit = findProduct(products, name.slice(PRODUCT_LABEL_PREFIX.length));
|
|
491
|
+
if (hit) return hit;
|
|
492
|
+
}
|
|
493
|
+
const files = branch.files ?? [];
|
|
494
|
+
for (const p of products) {
|
|
495
|
+
if (p === fallback || !p.path) continue;
|
|
496
|
+
const prefix = `${cleanPath(p.path)}/`;
|
|
497
|
+
if (prefix !== "/" && files.some((f) => f.startsWith(prefix))) return p;
|
|
498
|
+
}
|
|
499
|
+
return fallback;
|
|
500
|
+
}
|
|
501
|
+
function productForPlace(products, place) {
|
|
502
|
+
const repo = place.repo?.trim().toLowerCase();
|
|
503
|
+
if (repo && repo !== place.designSystemRepo?.trim().toLowerCase()) {
|
|
504
|
+
const hit = products.find((p) => p.repo?.toLowerCase() === repo);
|
|
505
|
+
if (hit) return { product: hit, how: "repo" };
|
|
506
|
+
}
|
|
507
|
+
const rel = cleanPath(place.relPath ?? "");
|
|
508
|
+
if (rel && rel !== ".") {
|
|
509
|
+
let best;
|
|
510
|
+
for (const p of products) {
|
|
511
|
+
if (!p.path) continue;
|
|
512
|
+
const dir = cleanPath(p.path);
|
|
513
|
+
if (dir && (rel === dir || rel.startsWith(`${dir}/`)) && (!best || dir.length > cleanPath(best.path).length)) best = p;
|
|
514
|
+
}
|
|
515
|
+
if (best) return { product: best, how: "path" };
|
|
516
|
+
}
|
|
517
|
+
return { product: defaultProduct(products), how: "default" };
|
|
518
|
+
}
|
|
519
|
+
var KEY = /^[a-z0-9][a-z0-9-]*$/;
|
|
520
|
+
var REPO = /^[\w.-]+\/[\w.-]+$/;
|
|
521
|
+
var FIELDS = ["key", "name", "repo", "path", "preview", "framework", "default"];
|
|
522
|
+
function productProblems(raw) {
|
|
523
|
+
if (raw === void 0) return [];
|
|
524
|
+
if (!Array.isArray(raw)) return ['"products" must be a list of { "key", "name" } objects'];
|
|
525
|
+
if (!raw.length) return ['"products" must list at least one product, or be left out to use the defaults'];
|
|
526
|
+
const problems = [];
|
|
527
|
+
const seen = /* @__PURE__ */ new Set();
|
|
528
|
+
let defaults = 0;
|
|
529
|
+
raw.forEach((p, i) => {
|
|
530
|
+
const at = `"products[${i}]`;
|
|
531
|
+
if (!p || typeof p !== "object" || Array.isArray(p)) {
|
|
532
|
+
problems.push(`${at}" must be an object with a "key" and a "name"`);
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
const o = p;
|
|
536
|
+
for (const k of Object.keys(o)) if (!FIELDS.includes(k)) problems.push(`${at}.${k}" is not a known field (${FIELDS.join(", ")})`);
|
|
537
|
+
if (typeof o.key !== "string" || !KEY.test(o.key)) problems.push(`${at}.key" must be lowercase letters, digits and dashes, such as "admin"`);
|
|
538
|
+
else if (seen.has(o.key)) problems.push(`${at}.key" ("${o.key}") is used twice: give each product its own key`);
|
|
539
|
+
else seen.add(o.key);
|
|
540
|
+
if (typeof o.name !== "string" || !o.name.trim()) problems.push(`${at}.name" must be the name people read, such as "Admin demo"`);
|
|
541
|
+
if (o.repo !== void 0 && (typeof o.repo !== "string" || !REPO.test(o.repo))) problems.push(`${at}.repo" must be "owner/name"`);
|
|
542
|
+
if (o.path !== void 0) {
|
|
543
|
+
const bad = typeof o.path !== "string" || !o.path.trim() || o.path.startsWith("/") || o.path.split("/").includes("..");
|
|
544
|
+
if (bad) problems.push(`${at}.path" must be a folder relative to the repository root, without a leading "/" or "..", such as "apps/admin"`);
|
|
545
|
+
}
|
|
546
|
+
if (o.preview !== void 0 && (typeof o.preview !== "string" || !/^https?:\/\//.test(o.preview))) problems.push(`${at}.preview" must be an http(s) URL template, such as "https://app-git-{branch}.vercel.app"`);
|
|
547
|
+
if (o.framework !== void 0 && (typeof o.framework !== "string" || !o.framework.trim())) problems.push(`${at}.framework" must be a name such as "react", "vue" or "other"`);
|
|
548
|
+
if (o.default !== void 0 && typeof o.default !== "boolean") problems.push(`${at}.default" must be true or false`);
|
|
549
|
+
if (o.default === true) defaults++;
|
|
550
|
+
});
|
|
551
|
+
if (defaults > 1) problems.push('"products" marks more than one product as default: keep "default": true on the library only');
|
|
552
|
+
return problems;
|
|
553
|
+
}
|
|
554
|
+
function sameProducts(a, b) {
|
|
555
|
+
const norm3 = (l) => JSON.stringify(l.map((p) => FIELDS.map((f) => p[f] ?? null)));
|
|
556
|
+
return norm3(a) === norm3(b);
|
|
557
|
+
}
|
|
558
|
+
|
|
449
559
|
// ../core/src/config.ts
|
|
450
560
|
var CONFIG_FILE = "bridge.config.json";
|
|
451
561
|
var DEFAULT_OVERRIDE = { allowed: [], requireReason: true, minReasonLength: 20 };
|
|
@@ -586,6 +696,7 @@ function defineConfig(input) {
|
|
|
586
696
|
} : {},
|
|
587
697
|
...g.rights ? { rights: normalizeRights(g.rights) } : {}
|
|
588
698
|
},
|
|
699
|
+
products: input.products?.length ? input.products.map((p) => ({ ...p })) : DEFAULT_PRODUCTS.map((p) => ({ ...p })),
|
|
589
700
|
...input.audit ? { audit: { ...input.audit } } : {},
|
|
590
701
|
...input.visualImpact ? { visualImpact: { mode: input.visualImpact.mode === "on-demand" ? "on-demand" : "auto" } } : {},
|
|
591
702
|
...input.integrations ? { integrations: { ...input.integrations } } : {},
|
|
@@ -708,6 +819,7 @@ function loadConfig(source) {
|
|
|
708
819
|
}
|
|
709
820
|
}
|
|
710
821
|
problems.push(...visualImpactProblems(d.visualImpact));
|
|
822
|
+
problems.push(...productProblems(d.products));
|
|
711
823
|
const audit = d.audit;
|
|
712
824
|
if (audit !== void 0) {
|
|
713
825
|
if (!isObj(audit)) problems.push('"audit" must be an object');
|
|
@@ -754,7 +866,8 @@ function toConfigJson(config, schema = "./packages/core/bridge.config.schema.jso
|
|
|
754
866
|
governance: config.governance,
|
|
755
867
|
...config.audit ? { audit: config.audit } : {},
|
|
756
868
|
...config.previews ? { previews: config.previews } : {},
|
|
757
|
-
...config.visualImpact ? { visualImpact: config.visualImpact } : {}
|
|
869
|
+
...config.visualImpact ? { visualImpact: config.visualImpact } : {},
|
|
870
|
+
...config.products && !sameProducts(config.products, DEFAULT_PRODUCTS) ? { products: config.products } : {}
|
|
758
871
|
};
|
|
759
872
|
}
|
|
760
873
|
|
|
@@ -1317,6 +1430,49 @@ function fieldChanges(a, b) {
|
|
|
1317
1430
|
if (!sameScopes(sa, sb)) out.push({ field: "scopes", before: sa, after: sb });
|
|
1318
1431
|
return out;
|
|
1319
1432
|
}
|
|
1433
|
+
function sameToken(a, b) {
|
|
1434
|
+
if (!a || !b) return a === b;
|
|
1435
|
+
return fieldChanges(a, b).length === 0;
|
|
1436
|
+
}
|
|
1437
|
+
function sideChange(from, to) {
|
|
1438
|
+
if (!from && !to) return void 0;
|
|
1439
|
+
if (!from) return { kind: "added", fields: [] };
|
|
1440
|
+
if (!to) return { kind: "removed", fields: [] };
|
|
1441
|
+
const fields = fieldChanges(from, to);
|
|
1442
|
+
if (!fields.length) return void 0;
|
|
1443
|
+
return { kind: fields.some((f) => f.field === "path") ? "renamed" : "changed", fields };
|
|
1444
|
+
}
|
|
1445
|
+
function threeWayDiff(base, figma, head) {
|
|
1446
|
+
const ids = /* @__PURE__ */ new Set([...base?.keys() ?? [], ...figma.keys(), ...head.keys()]);
|
|
1447
|
+
const result = { local: [], remote: [], conflicts: [], unchanged: 0 };
|
|
1448
|
+
for (const id of [...ids].sort()) {
|
|
1449
|
+
const b = base?.get(id);
|
|
1450
|
+
const f = figma.get(id);
|
|
1451
|
+
const h = head.get(id);
|
|
1452
|
+
const local = sideChange(b, f);
|
|
1453
|
+
const remote = sideChange(b, h);
|
|
1454
|
+
if (!local && !remote || f && h && sameToken(f, h) || !f && !h) {
|
|
1455
|
+
result.unchanged++;
|
|
1456
|
+
continue;
|
|
1457
|
+
}
|
|
1458
|
+
const entry = { id, path: (f ?? h ?? b).path, base: b, figma: f, head: h, local, remote, conflict: false };
|
|
1459
|
+
if (local && !remote) {
|
|
1460
|
+
entry.path = (f ?? b).path;
|
|
1461
|
+
result.local.push(entry);
|
|
1462
|
+
} else if (remote && !local) {
|
|
1463
|
+
entry.path = (h ?? b).path;
|
|
1464
|
+
result.remote.push(entry);
|
|
1465
|
+
} else {
|
|
1466
|
+
entry.conflict = true;
|
|
1467
|
+
result.conflicts.push(entry);
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
const byPath = (a, b) => a.path.localeCompare(b.path);
|
|
1471
|
+
result.local.sort(byPath);
|
|
1472
|
+
result.remote.sort(byPath);
|
|
1473
|
+
result.conflicts.sort(byPath);
|
|
1474
|
+
return result;
|
|
1475
|
+
}
|
|
1320
1476
|
|
|
1321
1477
|
// ../core/src/risk.ts
|
|
1322
1478
|
var RISK_LABELS = {
|
|
@@ -1551,6 +1707,164 @@ function composeProposalBody(args) {
|
|
|
1551
1707
|
].join("\n");
|
|
1552
1708
|
}
|
|
1553
1709
|
|
|
1710
|
+
// ../core/src/component-spec.ts
|
|
1711
|
+
var COMPONENT_SPECS_VERSION = 1;
|
|
1712
|
+
var DEFAULT_COMPONENT_SPECS_FILE = "packages/react/component-specs.json";
|
|
1713
|
+
function coverageSummary(file) {
|
|
1714
|
+
const list2 = file.coverage ?? file.components.map((c) => ({ name: c.name, status: "generated" }));
|
|
1715
|
+
const generated = list2.filter((c) => c.status === "generated").length;
|
|
1716
|
+
const drafted = list2.filter((c) => c.status === "drafted").length;
|
|
1717
|
+
const layout = list2.filter((c) => c.status === "layout").length;
|
|
1718
|
+
return { generated, drafted, inFigma: generated + drafted, visual: list2.length - layout, layout, total: list2.length, missing: list2.filter((c) => c.status === "unsupported") };
|
|
1719
|
+
}
|
|
1720
|
+
var variantKeyOf = (props) => Object.keys(props).sort().map((k) => `${k}=${props[k]}`).join(", ") || "Default";
|
|
1721
|
+
function parseComponentSpecs(text) {
|
|
1722
|
+
let data;
|
|
1723
|
+
try {
|
|
1724
|
+
data = JSON.parse(text);
|
|
1725
|
+
} catch {
|
|
1726
|
+
throw new Error("component-specs.json is not valid JSON.");
|
|
1727
|
+
}
|
|
1728
|
+
const f = data;
|
|
1729
|
+
if (!f || typeof f !== "object" || !Array.isArray(f.components)) throw new Error('component-specs.json has no "components" list.');
|
|
1730
|
+
if (f.version !== COMPONENT_SPECS_VERSION) throw new Error(`component-specs.json is version ${String(f.version)}; this tool reads version ${COMPONENT_SPECS_VERSION}.`);
|
|
1731
|
+
return f;
|
|
1732
|
+
}
|
|
1733
|
+
function figmaCell(entry) {
|
|
1734
|
+
if (!entry) return void 0;
|
|
1735
|
+
if (entry.status === "generated") return { label: "Yes" };
|
|
1736
|
+
if (entry.status === "drafted") return { label: "Drafted", title: "Drafted with Claude where the code cannot be read on its own: in Figma, check it against the code." };
|
|
1737
|
+
if (entry.status === "layout") return { label: "Auto layout", title: "A layout helper: nothing to draw, use auto layout in Figma." };
|
|
1738
|
+
return { label: "Not yet", ...entry.reason ? { title: `Not supported yet: ${entry.reason}` } : {} };
|
|
1739
|
+
}
|
|
1740
|
+
|
|
1741
|
+
// ../core/src/fidelity-references.ts
|
|
1742
|
+
var FIGMA_REFERENCES_BRANCH = "bridge-visual";
|
|
1743
|
+
var FIGMA_REFERENCES_DIR = "figma-references";
|
|
1744
|
+
var FIGMA_REFERENCES_MANIFEST = `${FIGMA_REFERENCES_DIR}/manifest.json`;
|
|
1745
|
+
var FIGMA_REFERENCE_STORY = "internal-figma-reference--variant";
|
|
1746
|
+
var FIGMA_FILL_WIDTH_TOKEN = "size.80";
|
|
1747
|
+
var STATE_PSEUDO = {
|
|
1748
|
+
default: "rest",
|
|
1749
|
+
rest: "rest",
|
|
1750
|
+
enabled: "rest",
|
|
1751
|
+
hover: "hover",
|
|
1752
|
+
hovered: "hover",
|
|
1753
|
+
pressed: "active",
|
|
1754
|
+
active: "active",
|
|
1755
|
+
focus: "focus-visible",
|
|
1756
|
+
focused: "focus-visible",
|
|
1757
|
+
"focus visible": "focus-visible",
|
|
1758
|
+
disabled: "disabled"
|
|
1759
|
+
};
|
|
1760
|
+
var iconName = (component) => component.replace(/^Icon\//, "");
|
|
1761
|
+
var exportName = (name) => name.replace(/[^A-Za-z0-9]+/g, "");
|
|
1762
|
+
function planReference(spec, variant, file) {
|
|
1763
|
+
if (spec.view === "popup") return { ok: false, reason: "drawn open in Figma; the code image shows it closed" };
|
|
1764
|
+
const props = { ...spec.fixed ?? {} };
|
|
1765
|
+
let pseudo;
|
|
1766
|
+
for (const axis of spec.axes) {
|
|
1767
|
+
const value = variant.props[axis.name];
|
|
1768
|
+
if (value === void 0) return { ok: false, reason: `the variant has no ${axis.name} value` };
|
|
1769
|
+
if (axis.prop) {
|
|
1770
|
+
props[axis.prop] = value === "true" ? true : value === "false" ? false : value;
|
|
1771
|
+
continue;
|
|
1772
|
+
}
|
|
1773
|
+
if (axis.name === "Checked") {
|
|
1774
|
+
props.checked = value === "true";
|
|
1775
|
+
if (value === "mixed") props.indeterminate = true;
|
|
1776
|
+
continue;
|
|
1777
|
+
}
|
|
1778
|
+
if (axis.name.toLowerCase() !== "state") return { ok: false, reason: `${axis.name} has no matching option in code` };
|
|
1779
|
+
const s = STATE_PSEUDO[value.toLowerCase()];
|
|
1780
|
+
if (!s) return { ok: false, reason: `the ${value} state has no code equivalent to render` };
|
|
1781
|
+
if (s === "disabled") props.disabled = true;
|
|
1782
|
+
else if (s !== "rest") pseudo = s;
|
|
1783
|
+
}
|
|
1784
|
+
const root = spec.parts.find((p) => !p.parent);
|
|
1785
|
+
if (!root) return { ok: false, reason: "the spec has no root layer" };
|
|
1786
|
+
const children = (id) => spec.parts.filter((p) => p.parent === id);
|
|
1787
|
+
const textOf = (part) => {
|
|
1788
|
+
const swapTo = variant.slots?.[part.id] ?? part.swap?.default;
|
|
1789
|
+
if (part.swap && swapTo) {
|
|
1790
|
+
const slot = file?.slots?.find((s) => s.name === swapTo);
|
|
1791
|
+
if (!slot) return { reason: `${swapTo} is not in the specs` };
|
|
1792
|
+
if (slot.kind !== "text" || !slot.text) return { reason: `Figma shows a placeholder (${swapTo}) that code has no example of` };
|
|
1793
|
+
return slot.text.default;
|
|
1794
|
+
}
|
|
1795
|
+
const prop = spec.properties.find((p) => p.name === part.text && p.type === "TEXT");
|
|
1796
|
+
return typeof prop?.default === "string" ? prop.default : part.name;
|
|
1797
|
+
};
|
|
1798
|
+
let text;
|
|
1799
|
+
const icons = {};
|
|
1800
|
+
for (const prop of spec.properties) {
|
|
1801
|
+
if (prop.type === "TEXT") {
|
|
1802
|
+
const hidden = spec.properties.some((b) => b.type === "BOOLEAN" && b.prop === prop.prop && b.default === false);
|
|
1803
|
+
if (hidden && prop.prop) continue;
|
|
1804
|
+
if (prop.prop === "children") text = String(prop.default);
|
|
1805
|
+
else if (prop.prop) props[prop.prop] = String(prop.default);
|
|
1806
|
+
continue;
|
|
1807
|
+
}
|
|
1808
|
+
const iconPart = spec.parts.find((p) => p.kind === "icon" && p.visible === prop.name);
|
|
1809
|
+
if (prop.default === true && !iconPart && prop.prop && props[prop.prop] === void 0 && spec.example?.p?.[prop.prop] === void 0) props[prop.prop] = true;
|
|
1810
|
+
}
|
|
1811
|
+
for (const part of spec.parts) {
|
|
1812
|
+
if (part.kind !== "icon" || !part.swap) continue;
|
|
1813
|
+
const shown = part.visible ? spec.properties.find((p) => p.name === part.visible)?.default === true : true;
|
|
1814
|
+
if (!shown) continue;
|
|
1815
|
+
if (!part.swap.prop) return { ok: false, reason: `the ${part.name} icon has no matching option in code` };
|
|
1816
|
+
icons[part.swap.prop] = iconName(variant.slots?.[part.id] ?? part.swap.default);
|
|
1817
|
+
}
|
|
1818
|
+
let parts;
|
|
1819
|
+
const loose = spec.properties.filter((p) => p.type === "TEXT" && !p.prop);
|
|
1820
|
+
const frames = children(root.id).filter((p) => p.kind === "frame");
|
|
1821
|
+
const compound = text === void 0 && frames.length > 0 && frames.every((f) => spec.parts.some((p) => p.parent === f.id && p.kind === "text" && (p.swap || p.text === f.name)));
|
|
1822
|
+
if (compound) {
|
|
1823
|
+
parts = [];
|
|
1824
|
+
for (const f of frames) {
|
|
1825
|
+
const t = spec.parts.find((p) => p.parent === f.id && p.kind === "text");
|
|
1826
|
+
const value = textOf(t);
|
|
1827
|
+
if (typeof value !== "string") return { ok: false, reason: value.reason };
|
|
1828
|
+
parts.push({ component: `${exportName(spec.name)}${exportName(f.name)}`, text: value });
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
for (const p of loose) {
|
|
1832
|
+
if (p.name === "Value") continue;
|
|
1833
|
+
const part = spec.parts.find((x) => x.text === p.name);
|
|
1834
|
+
if (compound && part && part.parent !== root.id) continue;
|
|
1835
|
+
if (part && /placeholder$/.test(part.id)) {
|
|
1836
|
+
props.placeholder = String(p.default);
|
|
1837
|
+
continue;
|
|
1838
|
+
}
|
|
1839
|
+
return { ok: false, reason: `the ${p.name} text has no matching option in code` };
|
|
1840
|
+
}
|
|
1841
|
+
const example = spec.example;
|
|
1842
|
+
const exampleChildren = example?.children?.length ? example.children : void 0;
|
|
1843
|
+
return {
|
|
1844
|
+
ok: true,
|
|
1845
|
+
render: {
|
|
1846
|
+
component: exportName(spec.view || spec.fixed || example ? example?.c ?? spec.component ?? spec.name : spec.name),
|
|
1847
|
+
props: { ...example?.p ?? {}, ...props },
|
|
1848
|
+
...exampleChildren ? { children: exampleChildren } : {},
|
|
1849
|
+
...text !== void 0 && !exampleChildren ? { text } : {},
|
|
1850
|
+
...parts && !exampleChildren ? { parts } : {},
|
|
1851
|
+
...Object.keys(icons).length ? { icons } : {},
|
|
1852
|
+
...pseudo ? { pseudo } : {},
|
|
1853
|
+
...root.fill ? { width: FIGMA_FILL_WIDTH_TOKEN } : {}
|
|
1854
|
+
}
|
|
1855
|
+
};
|
|
1856
|
+
}
|
|
1857
|
+
function referenceImagePath(component, key) {
|
|
1858
|
+
const slug = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
1859
|
+
const file = key.split(",").map((kv) => slug(kv)).filter(Boolean).join("__");
|
|
1860
|
+
return `${slug(component)}/${file || "default"}.png`;
|
|
1861
|
+
}
|
|
1862
|
+
function planReferences(file) {
|
|
1863
|
+
return file.components.flatMap(
|
|
1864
|
+
(spec) => spec.variants.map((v) => ({ component: spec.name, specHash: spec.hash, key: variantKeyOf(v.props), plan: planReference(spec, v, file) }))
|
|
1865
|
+
);
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1554
1868
|
// ../core/src/node/git-local.ts
|
|
1555
1869
|
import { execFile } from "node:child_process";
|
|
1556
1870
|
import { mkdtemp, rm } from "node:fs/promises";
|
|
@@ -1661,6 +1975,7 @@ async function identityEnv(root) {
|
|
|
1661
1975
|
|
|
1662
1976
|
export {
|
|
1663
1977
|
__export,
|
|
1978
|
+
previewUrl,
|
|
1664
1979
|
previewLinks,
|
|
1665
1980
|
readPreviewComment,
|
|
1666
1981
|
findPreviewComment,
|
|
@@ -1673,8 +1988,18 @@ export {
|
|
|
1673
1988
|
DEFAULT_RIGHTS,
|
|
1674
1989
|
approverEntries,
|
|
1675
1990
|
shipPolicy,
|
|
1991
|
+
unreadableShipTeams,
|
|
1992
|
+
canShip,
|
|
1676
1993
|
whoCanShipSentence,
|
|
1994
|
+
productMarks,
|
|
1995
|
+
productsOf,
|
|
1996
|
+
defaultProduct,
|
|
1997
|
+
findProduct,
|
|
1998
|
+
productOfBranch,
|
|
1999
|
+
productForPlace,
|
|
1677
2000
|
CONFIG_FILE,
|
|
2001
|
+
DEFAULT_OVERRIDE,
|
|
2002
|
+
DEFAULT_TOKEN_PATHS,
|
|
1678
2003
|
loadConfig,
|
|
1679
2004
|
tokensPath,
|
|
1680
2005
|
protectedBranchesOf,
|
|
@@ -1686,6 +2011,7 @@ export {
|
|
|
1686
2011
|
renderedColor,
|
|
1687
2012
|
contrastPair,
|
|
1688
2013
|
parseContrastChecks,
|
|
2014
|
+
threeWayDiff,
|
|
1689
2015
|
RISK_LABELS,
|
|
1690
2016
|
classifyRisk,
|
|
1691
2017
|
aliasOf,
|
|
@@ -1707,6 +2033,15 @@ export {
|
|
|
1707
2033
|
DEFAULT_PROTECTED_BRANCHES,
|
|
1708
2034
|
ProtectedBranchError,
|
|
1709
2035
|
assertSafeBranch,
|
|
2036
|
+
DEFAULT_COMPONENT_SPECS_FILE,
|
|
2037
|
+
coverageSummary,
|
|
2038
|
+
parseComponentSpecs,
|
|
2039
|
+
figmaCell,
|
|
2040
|
+
FIGMA_REFERENCES_BRANCH,
|
|
2041
|
+
FIGMA_REFERENCES_MANIFEST,
|
|
2042
|
+
FIGMA_REFERENCE_STORY,
|
|
2043
|
+
referenceImagePath,
|
|
2044
|
+
planReferences,
|
|
1710
2045
|
git,
|
|
1711
2046
|
isGitRepo,
|
|
1712
2047
|
currentBranch,
|
package/dist/explorations.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ export interface StartExplorationInput {
|
|
|
11
11
|
/** Where it is started from (the MCP tool says mcp, the CLI says cli), and the AI agent behind it when known. */
|
|
12
12
|
source?: ExplorationSource | undefined;
|
|
13
13
|
agent?: string | undefined;
|
|
14
|
+
/** The product it is tried in (key or name, bridge.config.json "products"). Default: the product of the current folder or repo. */
|
|
15
|
+
product?: string | undefined;
|
|
14
16
|
dryRun?: boolean | undefined;
|
|
15
17
|
}
|
|
16
18
|
/**
|
|
@@ -20,6 +22,7 @@ export interface StartExplorationInput {
|
|
|
20
22
|
*/
|
|
21
23
|
export declare function startExploration(ctx: BridgeContext, a: StartExplorationInput): Promise<{
|
|
22
24
|
branch: string;
|
|
25
|
+
product: string;
|
|
23
26
|
slug: string;
|
|
24
27
|
base: string;
|
|
25
28
|
title: string;
|
|
@@ -46,6 +49,7 @@ export declare function startExploration(ctx: BridgeContext, a: StartExploration
|
|
|
46
49
|
}[];
|
|
47
50
|
next: string;
|
|
48
51
|
branch: string;
|
|
52
|
+
product: string;
|
|
49
53
|
slug: string;
|
|
50
54
|
base: string;
|
|
51
55
|
title: string;
|
|
@@ -60,6 +64,7 @@ export declare function startExploration(ctx: BridgeContext, a: StartExploration
|
|
|
60
64
|
commit: string;
|
|
61
65
|
next: string;
|
|
62
66
|
branch: string;
|
|
67
|
+
product: string;
|
|
63
68
|
slug: string;
|
|
64
69
|
base: string;
|
|
65
70
|
title: string;
|
package/dist/index.js
CHANGED
package/dist/lib.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ export * as brand from './brand.js';
|
|
|
11
11
|
export * as requests from './requests.js';
|
|
12
12
|
export * as explorations from './explorations.js';
|
|
13
13
|
export * as drafts from './drafts.js';
|
|
14
|
+
export * as branches from './branches.js';
|
|
15
|
+
export * as studio from './studio.js';
|
|
14
16
|
export { DOCS_DRAFT_MODEL, anthropicReplyText, anthropicRequest, formatDocsJson, mergeDocsDraft, missingProse, parseDocsReply } from '@bridge-ds/core';
|
|
15
17
|
export interface ServeOptions extends ContextOptions {
|
|
16
18
|
}
|
package/dist/lib.js
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
anthropicReplyText,
|
|
9
9
|
anthropicRequest,
|
|
10
10
|
auditSnippet,
|
|
11
|
+
branches_exports,
|
|
11
12
|
brand_exports,
|
|
12
13
|
core,
|
|
13
14
|
createServer,
|
|
@@ -22,13 +23,14 @@ import {
|
|
|
22
23
|
optionsFromArgv,
|
|
23
24
|
parseDocsReply,
|
|
24
25
|
requests_exports,
|
|
25
|
-
serveStdio
|
|
26
|
-
|
|
26
|
+
serveStdio,
|
|
27
|
+
studio_exports
|
|
28
|
+
} from "./chunk-MXMMPO7F.js";
|
|
27
29
|
import {
|
|
28
30
|
CONFIG_FILE,
|
|
29
31
|
loadConfig,
|
|
30
32
|
toConfigJson
|
|
31
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-QTO4EP6V.js";
|
|
32
34
|
export {
|
|
33
35
|
BridgeContext,
|
|
34
36
|
CONFIG_FILE,
|
|
@@ -40,6 +42,7 @@ export {
|
|
|
40
42
|
anthropicReplyText,
|
|
41
43
|
anthropicRequest,
|
|
42
44
|
auditSnippet,
|
|
45
|
+
branches_exports as branches,
|
|
43
46
|
brand_exports as brand,
|
|
44
47
|
core,
|
|
45
48
|
createServer,
|
|
@@ -56,5 +59,6 @@ export {
|
|
|
56
59
|
parseDocsReply,
|
|
57
60
|
requests_exports as requests,
|
|
58
61
|
serveStdio,
|
|
62
|
+
studio_exports as studio,
|
|
59
63
|
toConfigJson
|
|
60
64
|
};
|
package/dist/ops.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type ScaffoldInput } from './components.js';
|
|
2
|
-
import type { BridgeContext } from './context.js';
|
|
3
2
|
import { type BridgeConfig, type GitHubClient } from '@bridge-ds/core';
|
|
4
3
|
import { type Combo, type PairInput, type TokenEdit, type TokenSet } from './core-adapter.js';
|
|
4
|
+
import { BridgeContext } from './context.js';
|
|
5
5
|
/** Accepts `color.text.muted`, `--color-text-muted` or `var(--color-text-muted)`. */
|
|
6
6
|
export declare function normalizeTokenPath(set: TokenSet, input: string): string;
|
|
7
7
|
export declare function getConfig(ctx: BridgeContext): Promise<{
|
|
@@ -274,7 +274,75 @@ export declare function proposeTokenChange(ctx: BridgeContext, a: {
|
|
|
274
274
|
summary?: string;
|
|
275
275
|
dryRun?: boolean;
|
|
276
276
|
branch?: string | undefined;
|
|
277
|
+
pr?: number | string | undefined;
|
|
278
|
+
jira?: string | undefined;
|
|
279
|
+
product?: string | undefined;
|
|
277
280
|
}): Promise<{
|
|
281
|
+
status: string;
|
|
282
|
+
pr: number;
|
|
283
|
+
message: string;
|
|
284
|
+
} | {
|
|
285
|
+
hint: string;
|
|
286
|
+
pr: number;
|
|
287
|
+
branch: string;
|
|
288
|
+
tier: 1 | 2 | 3;
|
|
289
|
+
changes: {
|
|
290
|
+
kind: "added" | "changed" | "renamed" | "removed";
|
|
291
|
+
path: string;
|
|
292
|
+
file: string;
|
|
293
|
+
before: string | undefined;
|
|
294
|
+
after: string | undefined;
|
|
295
|
+
}[];
|
|
296
|
+
contrast: {
|
|
297
|
+
moved: number;
|
|
298
|
+
newFailures: import("@bridge-ds/core").ContrastDelta[];
|
|
299
|
+
};
|
|
300
|
+
notes: string[];
|
|
301
|
+
status: string;
|
|
302
|
+
why: string;
|
|
303
|
+
newErrors: import("@bridge-ds/core").LayerIssue[];
|
|
304
|
+
message?: undefined;
|
|
305
|
+
} | {
|
|
306
|
+
files: string[];
|
|
307
|
+
pr: number;
|
|
308
|
+
branch: string;
|
|
309
|
+
tier: 1 | 2 | 3;
|
|
310
|
+
changes: {
|
|
311
|
+
kind: "added" | "changed" | "renamed" | "removed";
|
|
312
|
+
path: string;
|
|
313
|
+
file: string;
|
|
314
|
+
before: string | undefined;
|
|
315
|
+
after: string | undefined;
|
|
316
|
+
}[];
|
|
317
|
+
contrast: {
|
|
318
|
+
moved: number;
|
|
319
|
+
newFailures: import("@bridge-ds/core").ContrastDelta[];
|
|
320
|
+
};
|
|
321
|
+
notes: string[];
|
|
322
|
+
status: string;
|
|
323
|
+
message?: undefined;
|
|
324
|
+
} | {
|
|
325
|
+
commit: string;
|
|
326
|
+
url: string;
|
|
327
|
+
said: string;
|
|
328
|
+
pr: number;
|
|
329
|
+
branch: string;
|
|
330
|
+
tier: 1 | 2 | 3;
|
|
331
|
+
changes: {
|
|
332
|
+
kind: "added" | "changed" | "renamed" | "removed";
|
|
333
|
+
path: string;
|
|
334
|
+
file: string;
|
|
335
|
+
before: string | undefined;
|
|
336
|
+
after: string | undefined;
|
|
337
|
+
}[];
|
|
338
|
+
contrast: {
|
|
339
|
+
moved: number;
|
|
340
|
+
newFailures: import("@bridge-ds/core").ContrastDelta[];
|
|
341
|
+
};
|
|
342
|
+
notes: string[];
|
|
343
|
+
status: string;
|
|
344
|
+
message?: undefined;
|
|
345
|
+
} | {
|
|
278
346
|
status: string;
|
|
279
347
|
message: string;
|
|
280
348
|
} | {
|
|
@@ -501,6 +569,7 @@ export declare function proposeTokenChange(ctx: BridgeContext, a: {
|
|
|
501
569
|
url: string;
|
|
502
570
|
labels: string[];
|
|
503
571
|
draft: boolean;
|
|
572
|
+
product: string;
|
|
504
573
|
files: string[];
|
|
505
574
|
prBody: string;
|
|
506
575
|
title: string;
|
package/dist/requests.d.ts
CHANGED
|
@@ -68,6 +68,8 @@ export interface CreateRequestInput {
|
|
|
68
68
|
source?: RequestSource | undefined;
|
|
69
69
|
/** Exploration branch to implement it on (`explore/<slug>`), instead of a pull request to main. */
|
|
70
70
|
exploration?: string | undefined;
|
|
71
|
+
/** The product it is for (key or name, bridge.config.json "products"). Default: the product of the current folder or repo. */
|
|
72
|
+
product?: string | undefined;
|
|
71
73
|
dryRun?: boolean | undefined;
|
|
72
74
|
}
|
|
73
75
|
export declare function createRequest(ctx: BridgeContext, a: CreateRequestInput): Promise<{
|