@bridge-ds/core 0.19.0 → 0.20.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/LICENSE ADDED
@@ -0,0 +1,17 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Florian Gouloubi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
6
+ documentation files (the "Software"), to deal in the Software without restriction, including without limitation
7
+ the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
8
+ to permit persons to whom the Software is furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of
11
+ the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
14
+ THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
16
+ CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
17
+ IN THE SOFTWARE.
@@ -458,6 +458,11 @@
458
458
  "description": "For a design system run by one person. When true and the owners resolve to exactly one person who holds both roles, that person's approval counts as the design and the tech approval, so Tier 2 and Tier 3 changes can be approved. The author of a change still never approves it. Default false: one person never covers both approvals alone.",
459
459
  "default": false
460
460
  },
461
+ "leadsPublishTier1": {
462
+ "type": "boolean",
463
+ "description": "Leads can publish small changes without review. When true (or absent), team admins and the reviewers of the matching domain (design for docs and guidance, tech for code-only Tier 1) publish a Tier 1 change directly: Bridge opens the pull request, waits for the checks and merges it, recording who published it. A failing check sends it to review instead. Tier 2 and 3 are unchanged. Changing it is a Tier 3 change (bridge.config.json is a guardrail file).",
464
+ "default": true
465
+ },
461
466
  "rights": {
462
467
  "type": "object",
463
468
  "additionalProperties": false,
@@ -1,6 +1,6 @@
1
- import { i as isAuditable, a as auditFile } from "../report-DFPKygfk.js";
2
- import { A, b, c, C, D, d, F, I, L, e, M, N, f, g, h, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, B, E, G, H, J, K, O, P, Q, R, S, T, U, V, W, X, Y, Z } from "../report-DFPKygfk.js";
3
- import { p as pieceId, a as proposedTokenPath, t as tokenLabel } from "../prod-branch-Bdx_2bRQ.js";
1
+ import { i as isAuditable, a as auditFile } from "../report-CE-jHAuw.js";
2
+ import { A, b, c, C, D, d, F, I, L, e, M, N, f, g, h, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, B, E, G, H, J, K, O, P, Q, R, S, T, U, V, W, X, Y, Z } from "../report-CE-jHAuw.js";
3
+ import { p as pieceId, a as proposedTokenPath, t as tokenLabel } from "../prod-branch-CJZwMfVD.js";
4
4
  const lines = (text) => text.split(/\r?\n/);
5
5
  function lineBag(text) {
6
6
  const bag = /* @__PURE__ */ new Map();
package/dist/config.d.ts CHANGED
@@ -141,6 +141,11 @@ export interface GovernanceConfig {
141
141
  * person's approval counts for design and tech. Default false: one person never covers both approvals alone.
142
142
  */
143
143
  soloOwnerCanApproveBoth?: boolean | undefined;
144
+ /**
145
+ * Team admins and the reviewers of the matching domain publish a Tier 1 change without review (docs/GOVERNANCE.md,
146
+ * "Tier 1 publish by leads"). Absent counts as on (leadsPublishTier1On); false turns it off.
147
+ */
148
+ leadsPublishTier1?: boolean | undefined;
144
149
  }
145
150
  /** `bridge audit` settings: how product code that consumes the design system is measured. */
146
151
  export interface AuditConfig {
@@ -132,7 +132,18 @@ export declare function buildFidelityReport(input: {
132
132
  file?: string;
133
133
  now?: Date;
134
134
  }): FidelityReport;
135
- /** One variant's verdict: "Matches the code" or "Differs from the code: 12% of pixels, size 120x40 vs 118x40". */
135
+ /**
136
+ * A size difference past the tolerance, in designer words: "height 89 in Figma vs 48 in code". It says more than the
137
+ * share of pixels when the two images do not have the same size (a 1% pixel score can hide a set twice as tall).
138
+ */
139
+ export declare function sizeGap(s: {
140
+ figma: Size;
141
+ code: Size;
142
+ }, tolerance?: number): string | undefined;
143
+ /**
144
+ * One variant's verdict: "Matches the code", "Differs from the code: 12% of pixels", or the size first when the size
145
+ * differs: "Differs from the code: height 89 in Figma vs 48 in code, 1.4% of pixels".
146
+ */
136
147
  export declare function fidelityVerdict(v: Pick<FidelityVariantResult, 'status' | 'percent' | 'size' | 'reason'>): string;
137
148
  /** One component's line, from its worst variant. */
138
149
  export declare function fidelityComponentLine(c: FidelityComponentResult): string;
@@ -0,0 +1,103 @@
1
+ import { type BridgeConfig } from './config';
2
+ import { type FixAttempt, type FixVerdict, type GapCaseForInput, type TriedVariant } from './figma-fix-loop';
3
+ import type { FigmaFix } from './figma-fixes';
4
+ import type { GapCase } from './gap-case';
5
+ /** Where `bridge specs --measure` writes, from the repo root. */
6
+ export declare const DEFAULT_MEASURED_SPECS_FILE = "measured-specs.json";
7
+ /** The label every figma-fixes.json change carries, next to drafted-by-claude. */
8
+ export declare const FIGMA_FIXES_LABEL = "figma-fixes";
9
+ /** Asks Claude one prompt, returns the reply text. Runners pass the team's key; tests pass a fake. */
10
+ export type FixAsk = (prompt: string) => Promise<string>;
11
+ /** Where a loop stands between rounds: plain JSON, so a server can keep it between requests. */
12
+ export interface FixLoopState {
13
+ component: string;
14
+ /** Rounds done. */
15
+ round: number;
16
+ attempts: FixAttempt[];
17
+ /** The closest fixes so far (verdict closer), with their score and the referee's summary. */
18
+ best?: {
19
+ fixes: unknown[];
20
+ score: number;
21
+ summary: string;
22
+ } | undefined;
23
+ /** No round left, or every difference resolved. */
24
+ done: boolean;
25
+ }
26
+ export interface FixLoopRound {
27
+ round: number;
28
+ fixes: unknown[];
29
+ /** invalid: the fixes broke the schema; undefined: no fixes could be read from the reply. */
30
+ verdict?: FixVerdict | 'invalid' | undefined;
31
+ summary: string;
32
+ }
33
+ export declare const startFixLoop: (component: string) => FixLoopState;
34
+ /**
35
+ * One round: Claude proposes fixes from the gap case and what the earlier rounds gave, the values referee tries them,
36
+ * and the state keeps the closest. `ask` may throw (the network, the key): the state is then unchanged.
37
+ */
38
+ export declare function fixLoopRound(input: GapCaseForInput, state: FixLoopState, ask: FixAsk, gap?: GapCase): Promise<{
39
+ state: FixLoopState;
40
+ round: FixLoopRound;
41
+ }>;
42
+ /** What the loop proposes once done: the closest fixes, or a status fix (Differs, with the reason) when none came closer. */
43
+ export declare function fixLoopOutcome(gap: GapCase, state: FixLoopState, existingIds?: string[]): {
44
+ kind: 'fixes' | 'status';
45
+ fixes: unknown[];
46
+ summary: string;
47
+ };
48
+ /** The branch a component's fixes go on (a number is added when it is taken): bridge/figma-fixes-text-input. */
49
+ export declare const figmaFixesBranchStem: (config: BridgeConfig, component: string) => string;
50
+ export interface FigmaFixesProposalInput extends GapCaseForInput {
51
+ /** The fixes to propose (fix objects; the repo's fixes with the same ids are replaced). */
52
+ proposed: unknown[];
53
+ /** One or two sentences a reviewer reads first. */
54
+ summary: string;
55
+ config: BridgeConfig;
56
+ /** figma-fixes.json path in the repo. */
57
+ fixesPath: string;
58
+ /** Who drafted it, for the body and the commit: "Bridge MCP (source: mcp)". */
59
+ tool: string;
60
+ /** The `source:` label: mcp, cli, studio. */
61
+ source: string;
62
+ }
63
+ export interface FigmaFixesReport {
64
+ component: string;
65
+ verdict: FixVerdict;
66
+ summary: string;
67
+ variants: TriedVariant[];
68
+ fixes: string[];
69
+ tier: number;
70
+ prTitle: string;
71
+ }
72
+ export type FigmaFixesProposal = {
73
+ status: 'blocked';
74
+ verdict: FixVerdict;
75
+ why: string;
76
+ detail: string;
77
+ component: string;
78
+ } | {
79
+ status: 'ready';
80
+ report: FigmaFixesReport;
81
+ /** The accepted fixes as validated. */
82
+ fixes: FigmaFix[];
83
+ labels: string[];
84
+ prBody: string;
85
+ fileContents: {
86
+ path: string;
87
+ content: string;
88
+ }[];
89
+ title: string;
90
+ message: string;
91
+ /** Tier 2 and above open as a draft. */
92
+ draft: boolean;
93
+ /** The default branch it targets. */
94
+ base: string;
95
+ branchStem: string;
96
+ };
97
+ /**
98
+ * The change for review of proposed fixes. The values referee runs again first: a fix that moves the component further
99
+ * from the code is refused, and part or story fixes that move nothing are refused (propose a status fix), unless they
100
+ * size, place or lay out a part: the values referee can't judge layout, Check fidelity decides. Throws a sentence when
101
+ * the fixes are invalid or the summary is not one.
102
+ */
103
+ export declare function figmaFixesProposal(input: FigmaFixesProposalInput): FigmaFixesProposal;
@@ -1,5 +1,5 @@
1
1
  import { type ComponentSpec, type SpecField, type SpecPart, type SpecValue, type SpecVariant } from './component-spec';
2
- import type { FidelityComponentResult, Size } from './fidelity';
2
+ import { type FidelityComponentResult, type Size } from './fidelity';
3
3
  import { type MeasuredBox, type MeasuredSpec, type MeasuredSpecsFile, type MeasuredSpecsStory, type MeasuredState } from './measured-spec';
4
4
  export declare const GAP_CASE_VERSION = 1;
5
5
  /** How a story reaches a variant: at rest, a forced pseudo state, or disabled (an arg). */
@@ -114,7 +114,6 @@ export declare function storyState(s: Pick<MeasuredSpecsStory, 'state' | 'props'
114
114
  export declare function storyForVariant(spec: Pick<ComponentSpec, 'axes'>, props: Record<string, string>, stories: MeasuredSpecsStory[], shot?: {
115
115
  file: string;
116
116
  }): MeasuredSpecsStory | undefined;
117
- /** A part in words: the measure's ids are class names ("cds--toggle__switch" -> "toggle switch"). */
118
117
  export declare function partWords(part: Pick<SpecPart, 'id' | 'name'> | undefined, id?: string): string;
119
118
  /** Token path -> value, from the measure: components[].resolved, then the token harvest (by CSS name). */
120
119
  export declare function measuredResolver(file: Pick<MeasuredSpecsFile, 'components' | 'tokens'>): (path: string) => string | number | undefined;
package/dist/index.d.ts CHANGED
@@ -53,7 +53,11 @@ export * from './figma-fixes';
53
53
  export * from './measured-only';
54
54
  export * from './css-color';
55
55
  export * from './figma-fix-loop';
56
+ export * from './figma-fix-run';
57
+ export * from './measure-location';
58
+ export * from './measure-workflow';
56
59
  export * from './review-gate';
60
+ export * from './tier1-publish';
57
61
  export * from './library-update';
58
62
  export * from './branch-actions';
59
63
  export * from './jira';