@bridge-ds/core 0.6.0 → 0.7.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.
@@ -171,6 +171,59 @@ export interface SpecVector {
171
171
  svg: string;
172
172
  strokes: SpecValue[];
173
173
  }
174
+ /**
175
+ * A prototype trigger, Figma's names. ON_HOVER "While hovering" and ON_PRESS "While pressing" go back when the pointer
176
+ * leaves or lets go; ON_CLICK "On click" stays. A set with a Checked axis uses MOUSE_ENTER / MOUSE_LEAVE for hover, so a
177
+ * click that changes Checked is not undone when the pointer leaves.
178
+ */
179
+ export type SpecTrigger = 'ON_HOVER' | 'ON_PRESS' | 'ON_CLICK' | 'MOUSE_ENTER' | 'MOUSE_LEAVE';
180
+ /** Designer names of the triggers, as Figma's prototype panel shows them. */
181
+ export declare const TRIGGER_LABELS: Record<SpecTrigger, string>;
182
+ /** An easing Figma can play: linear, or a cubic bezier with the exact points of the code's curve. */
183
+ export interface SpecEasing {
184
+ type: 'LINEAR' | 'CUSTOM_CUBIC_BEZIER';
185
+ /** CUSTOM_CUBIC_BEZIER: x1, y1, x2, y2. */
186
+ bezier?: [number, number, number, number] | undefined;
187
+ }
188
+ /** The code's CSS transition for one change, resolved from the motion tokens. Played with Smart animate. */
189
+ export interface SpecTransition {
190
+ /** Milliseconds. */
191
+ duration: number;
192
+ /** Token behind the duration ("motion.duration.instant"), when it is one. */
193
+ durationToken?: string | undefined;
194
+ easing: SpecEasing;
195
+ /** Token behind the easing ("motion.easing.standard"), when it is one. */
196
+ easingToken?: string | undefined;
197
+ /** CSS properties the code animates in this change ("background-color"). */
198
+ properties: string[];
199
+ /** Why Figma plays it slightly differently from the code (one speed for parts that move at several), when it does. */
200
+ approximated?: string | undefined;
201
+ }
202
+ /**
203
+ * A prototype interaction between variants of the set, read from the CSS: on `trigger`, every variant whose `axis` is
204
+ * `from` changes to the variant with `to` instead (other axes kept). `variants` narrows it to those variant keys
205
+ * (variantKeyOf) when it does not apply to all of them. `transition` null: the code changes it instantly.
206
+ */
207
+ export interface SpecInteraction {
208
+ trigger: SpecTrigger;
209
+ axis: string;
210
+ from: string;
211
+ to: string;
212
+ variants?: string[] | undefined;
213
+ transition: SpecTransition | null;
214
+ }
215
+ /** Motion the code has that a Figma prototype cannot play (a looping spinner, keyboard focus, a popup opening). */
216
+ export interface SpecMotionNote {
217
+ /** What moves, in plain words ("Looping animation spin, 700 ms"). */
218
+ what: string;
219
+ /** Why Figma does not play it. */
220
+ reason: string;
221
+ /** A keyframe animation: the token behind its duration ("motion.duration.spin"), when it is one. */
222
+ durationToken?: string | undefined;
223
+ /** Opening and closing: the code's transition when it opens (enter) and when it closes (exit), when it has one. */
224
+ enter?: SpecTransition | undefined;
225
+ exit?: SpecTransition | undefined;
226
+ }
174
227
  /** A declaration, prop or part that has no Figma equivalent in this version, with the reason. */
175
228
  export interface SpecUnmapped {
176
229
  part: string;
@@ -209,6 +262,10 @@ export interface ComponentSpec {
209
262
  parts: SpecPart[];
210
263
  variants: SpecVariant[];
211
264
  unmapped: SpecUnmapped[];
265
+ /** Prototype interactions between the variants (hover, press, click to check), with the code's motion. */
266
+ interactions?: SpecInteraction[] | undefined;
267
+ /** Motion of the code a Figma prototype cannot play. */
268
+ motion?: SpecMotionNote[] | undefined;
212
269
  }
213
270
  /** An element of a code example: component export name, literal props, children (texts or elements). */
214
271
  export interface SpecExample {
@@ -275,7 +332,64 @@ export declare function hashSpec(spec: Omit<ComponentSpec, 'hash'> | ComponentSp
275
332
  export declare const variantName: (props: Record<string, string>) => string;
276
333
  /** Variant key independent of axis order (same as the plugin's signature keys): sorted "Size=md, Variant=primary". */
277
334
  export declare const variantKeyOf: (props: Record<string, string>) => string;
335
+ /** What a set's description says about reduced motion: tokens set every duration to 0 under prefers-reduced-motion. */
336
+ export declare const REDUCED_MOTION_NOTE = "In code, reduced motion turns this off.";
337
+ /** One prototype reaction: on `trigger`, the variant `fromKey` changes to `toKey`, with the code's transition. */
338
+ export interface MotionReaction {
339
+ trigger: SpecTrigger;
340
+ /** Figma's name for the trigger ("While hovering"). */
341
+ label: string;
342
+ from: Record<string, string>;
343
+ to: Record<string, string>;
344
+ /** variantKeyOf(from) and variantKeyOf(to). */
345
+ fromKey: string;
346
+ toKey: string;
347
+ transition: SpecTransition | null;
348
+ }
349
+ /** A component set's motion and interactions, read from its spec: for Figma prototypes, docs and reviews. */
350
+ export interface ComponentMotion {
351
+ name: string;
352
+ /** Every reaction between variants, one per variant it starts from. */
353
+ reactions: MotionReaction[];
354
+ /** The interactions as the spec lists them (one per trigger and change), with the number of variants each covers. */
355
+ triggers: (SpecInteraction & {
356
+ label: string;
357
+ count: number;
358
+ })[];
359
+ /** Motion tokens the transitions use ("motion.duration.instant", "motion.easing.standard"), sorted. */
360
+ tokens: string[];
361
+ /** CSS properties that animate ("background-color"), sorted. */
362
+ properties: string[];
363
+ /** Where Figma plays the motion differently from the code. */
364
+ approximations: string[];
365
+ /** Motion of the code a Figma prototype cannot play. */
366
+ cannotPlay: SpecMotionNote[];
367
+ /** Opening and closing (a popup, a panel): the code's enter and exit transitions, when it has them. */
368
+ opening: {
369
+ enter: SpecTransition | null;
370
+ exit: SpecTransition | null;
371
+ } | undefined;
372
+ /** REDUCED_MOTION_NOTE when the component animates in code, else undefined. */
373
+ reducedMotion: string | undefined;
374
+ }
375
+ /**
376
+ * The motion of a component set from its spec: every reaction between its variants (trigger, from and to variants, the
377
+ * transition with duration and easing resolved from the motion tokens), the tokens and properties involved, what Figma
378
+ * approximates or cannot play, and the reduced motion note. Pure: works in the plugin, the browser and Node.
379
+ */
380
+ export declare function componentMotion(spec: Pick<ComponentSpec, 'name' | 'variants' | 'interactions' | 'motion'>): ComponentMotion;
381
+ /**
382
+ * Sets whose interactions use a motion token, directly or through an alias: `chainOf(path)` gives the tokens a path
383
+ * resolves through (resolve(...).chain), so a change to a primitive ("duration.75") finds the sets using
384
+ * "motion.duration.instant". Without it, only direct uses count. Sorted set names.
385
+ */
386
+ export declare function componentsUsingMotionToken(specs: Pick<ComponentSpecsFile, 'components'>, tokenPath: string, chainOf?: (path: string) => string[]): string[];
278
387
  /** Parses component-specs.json; throws with a readable message when it is not a specs file of a known version. */
279
388
  export declare function parseComponentSpecs(text: string): ComponentSpecsFile;
280
389
  /** CSS custom property of a token path, with the repo convention (`cssVar` in bridge.config.json): "--" + path joined by "-". */
281
390
  export declare const tokenCssVar: (path: string, prefix?: string, separator?: string) => string;
391
+ /** How a component stands in Figma, in Studio's words (the "In Figma" column): a short word and, for the ones not in Figma, the reason as a tooltip. Undefined: no data. */
392
+ export declare function figmaCell(entry: SpecCoverage | undefined): {
393
+ label: string;
394
+ title?: string;
395
+ } | undefined;
package/dist/config.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { IntegrationsConfig } from './requests';
2
2
  import { type PreviewApps, type PreviewsConfig } from './previews';
3
3
  import { type Rights, type VisualImpactConfig } from './rights';
4
+ import { type ProductConfig } from './products';
4
5
  /** File name Bridge looks for at the repository root. */
5
6
  export declare const CONFIG_FILE = "bridge.config.json";
6
7
  export interface RepoRef {
@@ -151,6 +152,8 @@ export interface BridgeConfig {
151
152
  integrations?: IntegrationsConfig | undefined;
152
153
  /** Visual before and after on pull requests: automatic (default) or on demand (label `visual-check`). */
153
154
  visualImpact?: VisualImpactConfig | undefined;
155
+ /** Apps that consume the design system, the default one being the library (see products.ts). Default DEFAULT_PRODUCTS. */
156
+ products: ProductConfig[];
154
157
  }
155
158
  type Partialish<T> = {
156
159
  [K in keyof T]?: T[K] | undefined;
@@ -210,6 +213,7 @@ export interface BridgeConfigInput {
210
213
  };
211
214
  integrations?: IntegrationsConfig;
212
215
  visualImpact?: Partial<VisualImpactConfig>;
216
+ products?: ProductConfig[];
213
217
  }
214
218
  export declare const DEFAULT_TOKEN_PATHS: TokenPaths;
215
219
  export declare const DEFAULT_GOVERNANCE: GovernanceConfig;
@@ -0,0 +1,82 @@
1
+ import { type ComponentDocsJson } from './docs-draft';
2
+ import type { GitHubClient } from './github-client';
3
+ import { type RiskResult } from './risk';
4
+ /** One component's docs.json, before (null: a new file) and after, at its repo path. */
5
+ export interface DocsFileEdit {
6
+ path: string;
7
+ component: string;
8
+ slug: string;
9
+ before: string | null;
10
+ after: string;
11
+ }
12
+ /** Who wrote the docs, for the pull request body and its source label. */
13
+ export interface DocsOrigin {
14
+ /** "in the Bridge docs editor". */
15
+ where: string;
16
+ footer: string;
17
+ label: string;
18
+ }
19
+ export declare const DOCS_EDITOR_ORIGIN: DocsOrigin;
20
+ export declare const DOCS_MCP_ORIGIN: DocsOrigin;
21
+ export interface DocsIssue {
22
+ field: string;
23
+ message: string;
24
+ }
25
+ /** What blocks proposing a docs.json: a summary, no empty list items, no em dash (Bridge voice). */
26
+ export declare function validateDocs(d: ComponentDocsJson): DocsIssue[];
27
+ /** Drops empty list items and trims text, the way the editor saves. */
28
+ export declare function cleanDocs(d: ComponentDocsJson): ComponentDocsJson;
29
+ /** docs.json in the editor's canonical form: cleaned, in the Button.docs.json key order. Used to compare. */
30
+ export declare const docsText: (d: ComponentDocsJson) => string;
31
+ /** docs.json text the editor writes: cleaned, as a minimal diff of the file on main (or the branch) when there is one. */
32
+ export declare const docsFileText: (d: ComponentDocsJson, original: string | null) => string;
33
+ export interface DocsProposal {
34
+ branch: string;
35
+ title: string;
36
+ body: string;
37
+ files: {
38
+ path: string;
39
+ content: string;
40
+ }[];
41
+ risk: RiskResult;
42
+ bump: 'patch';
43
+ labels: string[];
44
+ }
45
+ /** One pull request for the docs edits of a draft: branch, title, body, files with a patch changeset, the tier. */
46
+ export declare function docsProposal(edits: DocsFileEdit[], opts: {
47
+ title?: string | undefined;
48
+ notes?: string | undefined;
49
+ date: Date;
50
+ packageName?: string | undefined;
51
+ changesetDir?: string | undefined;
52
+ origin?: DocsOrigin | undefined;
53
+ }): DocsProposal;
54
+ /**
55
+ * Opens one pull request with the docs edits from the default branch, the Jira key first in the title and the branch
56
+ * name (Jira's GitHub integration links them). Returns its number, link and branch. Labels are best effort.
57
+ */
58
+ export declare function proposeDocsOnGitHub(gh: GitHubClient, edits: DocsFileEdit[], opts: {
59
+ base: string;
60
+ title?: string | undefined;
61
+ notes?: string | undefined;
62
+ jira?: string | null | undefined;
63
+ date?: Date | undefined;
64
+ origin?: DocsOrigin | undefined;
65
+ extraLabels?: string[] | undefined;
66
+ commitNote?: string | undefined;
67
+ }): Promise<{
68
+ number: number;
69
+ url: string;
70
+ branch: string;
71
+ title: string;
72
+ }>;
73
+ /**
74
+ * Rewrites one file on an open branch in one commit (`next` gets the current text). Returns the commit sha, or ''
75
+ * when nothing changed. The Library editor saves an edited docs.json and accepts a Claude draft this way.
76
+ */
77
+ export declare function commitFileOnGitHub(gh: GitHubClient, input: {
78
+ path: string;
79
+ branch: string;
80
+ message: string;
81
+ next: (text: string) => string;
82
+ }): Promise<string>;
@@ -437,6 +437,7 @@ export declare class GitHubClient {
437
437
  getPullRequest(n: number): Promise<PullRequest & PullRequestSummary & {
438
438
  body: string;
439
439
  mergeable: boolean | null;
440
+ requestedReviewers: string[];
440
441
  }>;
441
442
  listPullRequestFiles(n: number): Promise<PullRequestFile[]>;
442
443
  updatePullRequest(n: number, patch: {
@@ -478,6 +479,94 @@ export declare class GitHubClient {
478
479
  };
479
480
  }): Promise<CheckRun>;
480
481
  removeLabel(issueNumber: number, label: string): Promise<void>;
482
+ /** The account the token belongs to (GET /user). Needs a token. */
483
+ getViewer(): Promise<{
484
+ login: string;
485
+ }>;
486
+ /** The repository's merge settings and the viewer's permissions (unknown settings count as allowed). */
487
+ getMergeSettings(): Promise<{
488
+ allow_squash_merge?: boolean;
489
+ allow_merge_commit?: boolean;
490
+ allow_rebase_merge?: boolean;
491
+ permissions?: {
492
+ admin?: boolean;
493
+ maintain?: boolean;
494
+ push?: boolean;
495
+ };
496
+ }>;
497
+ /**
498
+ * Merges a pull request, only at `sha` (GitHub refuses when the branch moved meanwhile). The repository's branch
499
+ * rules still apply: GitHub refuses a merge they block, whatever Bridge says.
500
+ */
501
+ mergePullRequest(n: number, input: {
502
+ method: 'squash' | 'merge' | 'rebase';
503
+ sha: string;
504
+ commitMessage?: string | undefined;
505
+ }): Promise<{
506
+ sha: string;
507
+ merged: boolean;
508
+ }>;
509
+ /** GitHub's "Update branch": merges the base into the pull request, only when its head is still `expectedHeadSha`. */
510
+ updatePullRequestBranch(n: number, expectedHeadSha: string): Promise<void>;
511
+ /** Posts a comment on an issue or pull request and returns its id and link. */
512
+ createIssueComment(n: number, body: string): Promise<{
513
+ id: number;
514
+ url: string;
515
+ }>;
516
+ /** Replaces the text of a comment. */
517
+ updateIssueComment(id: number, body: string): Promise<void>;
518
+ /** Submits a review on a given commit (the head the reviewer saw); the body may be empty for an approval. */
519
+ submitReview(n: number, input: {
520
+ event: 'COMMENT' | 'APPROVE' | 'REQUEST_CHANGES';
521
+ body?: string | undefined;
522
+ commitId?: string | undefined;
523
+ }): Promise<Review>;
524
+ /** Line comments of a pull request (review threads), oldest first. */
525
+ listReviewComments(n: number): Promise<{
526
+ id: number;
527
+ body: string;
528
+ author: string;
529
+ path: string;
530
+ line: number | null;
531
+ inReplyTo: number | null;
532
+ url: string;
533
+ createdAt: string;
534
+ }[]>;
535
+ /** Asks these people to review a pull request. */
536
+ requestReviewers(n: number, logins: string[]): Promise<void>;
537
+ /** Takes these people off the requested reviewers. */
538
+ removeRequestedReviewers(n: number, logins: string[]): Promise<void>;
539
+ /** Replies in the thread of a line comment. */
540
+ replyToReviewComment(n: number, commentId: number, body: string): Promise<{
541
+ id: number;
542
+ url: string;
543
+ }>;
544
+ /**
545
+ * What `head` adds on top of `base`, with what library sync needs: the shared start (merge base), the base
546
+ * commit and its tree, how far behind, and the files with their previous names.
547
+ */
548
+ compareDetailed(base: string, head: string): Promise<{
549
+ behindBy: number;
550
+ aheadBy: number;
551
+ mergeBase: string | null;
552
+ baseSha: string | null;
553
+ baseTree: string | null;
554
+ files: {
555
+ filename: string;
556
+ status: string;
557
+ previous_filename?: string | undefined;
558
+ }[];
559
+ }>;
560
+ /** A tree on top of `baseTree` from entries with their own file mode (blob sha, inline content, or sha null to delete). */
561
+ createTreeEntries(baseTree: string, entries: ({
562
+ path: string;
563
+ mode: string;
564
+ sha: string | null;
565
+ } | {
566
+ path: string;
567
+ mode: string;
568
+ content: string;
569
+ })[]): Promise<string>;
481
570
  }
482
571
  export declare function friendlyMessage(status: number, detail: string, info?: GitHubErrorInfo): string;
483
572
  export interface RepoFiles {
package/dist/index.d.ts CHANGED
@@ -45,3 +45,12 @@ export * from './requests-github';
45
45
  export * from './component-spec';
46
46
  export * from './fidelity';
47
47
  export * from './fidelity-references';
48
+ export * from './review-gate';
49
+ export * from './library-update';
50
+ export * from './branch-actions';
51
+ export * from './jira';
52
+ export * from './docs-proposal';
53
+ export * from './share-branch';
54
+ export * from './try-in-product';
55
+ export * from './combine-branches';
56
+ export * from './products';