@bridge-ds/core 0.6.0 → 0.8.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 +69 -0
- package/dist/branch-actions.d.ts +70 -0
- package/dist/combine-branches.d.ts +41 -0
- package/dist/{component-spec-GFVEE6Fc.js → component-spec-SKuywZqr.js} +366 -184
- package/dist/component-spec-SKuywZqr.js.map +1 -0
- package/dist/component-spec.d.ts +114 -0
- package/dist/config.d.ts +4 -0
- package/dist/docs-proposal.d.ts +154 -0
- package/dist/explorations-github.d.ts +2 -0
- package/dist/github-client.d.ts +89 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +1280 -298
- package/dist/index.js.map +1 -1
- package/dist/jira.d.ts +15 -0
- package/dist/library-update.d.ts +145 -0
- package/dist/node/component-specs.d.ts +2 -0
- package/dist/node/index.d.ts +1 -0
- package/dist/node/index.js +301 -5
- package/dist/node/index.js.map +1 -1
- package/dist/node/spec-motion.d.ts +66 -0
- package/dist/products.d.ts +65 -0
- package/dist/review-gate.d.ts +251 -0
- package/dist/share-branch.d.ts +85 -0
- package/dist/try-in-product.d.ts +16 -0
- package/package.json +1 -1
- package/dist/component-spec-GFVEE6Fc.js.map +0 -1
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { SpecEasing, SpecMotionNote, SpecTransition } from '../component-spec';
|
|
2
|
+
import { type CssRule } from './spec-css';
|
|
3
|
+
/** A CSS custom property's token, with its value resolved in the default modes (durations and easings). */
|
|
4
|
+
export interface MotionToken {
|
|
5
|
+
path: string;
|
|
6
|
+
type: string;
|
|
7
|
+
value?: unknown;
|
|
8
|
+
}
|
|
9
|
+
/** One entry of a `transition` list: the property, and its duration, easing and delay as written. */
|
|
10
|
+
export interface TransitionEntry {
|
|
11
|
+
property: string;
|
|
12
|
+
duration: string;
|
|
13
|
+
easing: string;
|
|
14
|
+
delay: string;
|
|
15
|
+
}
|
|
16
|
+
/** `var(--x)` replaced by the value of a non-token custom property the element has, recursively. */
|
|
17
|
+
export declare function substituteVars(raw: string, computed: Map<string, string>, tokens: Map<string, MotionToken>, depth?: number): string;
|
|
18
|
+
/**
|
|
19
|
+
* The transitions an element declares: the `transition` shorthand and the longhands (transition-property, -duration,
|
|
20
|
+
* -timing-function, -delay; shorter lists repeat, as in CSS). Local custom properties are substituted first.
|
|
21
|
+
*/
|
|
22
|
+
export declare function parseTransitions(computed: Map<string, string>, tokens: Map<string, MotionToken>): TransitionEntry[];
|
|
23
|
+
/** A duration as milliseconds, with its token: "var(--motion-duration-fast)" -> 150 (motion.duration.fast). */
|
|
24
|
+
export declare function resolveDuration(raw: string, tokens: Map<string, MotionToken>): {
|
|
25
|
+
ms: number;
|
|
26
|
+
token?: string;
|
|
27
|
+
} | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* An easing Figma can play, with its token: a cubic bezier (token or `cubic-bezier()`, or a CSS keyword) is played
|
|
30
|
+
* exactly as Figma's custom bezier; linear as linear. steps() has no Figma easing: linear, recorded as approximated.
|
|
31
|
+
*/
|
|
32
|
+
export declare function resolveEasing(raw: string, tokens: Map<string, MotionToken>): {
|
|
33
|
+
easing: SpecEasing;
|
|
34
|
+
token?: string;
|
|
35
|
+
approximated?: string;
|
|
36
|
+
} | undefined;
|
|
37
|
+
/** The property a longhand belongs to, without its side: border-top-color -> border-color, padding-left -> padding. */
|
|
38
|
+
export declare const shorthandOf: (prop: string) => string;
|
|
39
|
+
/** The transition entry that animates `prop`: same name, `all`, or a shorthand of it (border-color for border-top-color). */
|
|
40
|
+
export declare function entryFor(prop: string, entries: TransitionEntry[]): TransitionEntry | undefined;
|
|
41
|
+
/** CSS properties whose value differs between two computed styles of a part (custom properties substituted). */
|
|
42
|
+
export declare function changedProperties(from: Map<string, string> | undefined, to: Map<string, string> | undefined, tokens: Map<string, MotionToken>): string[];
|
|
43
|
+
/**
|
|
44
|
+
* The transition Figma plays when a component goes from one variant to another: for every part, the CSS properties that
|
|
45
|
+
* change, animated by the destination state's `transition` (as in CSS). One duration and easing per reaction in
|
|
46
|
+
* Figma: the longest animated change, recorded as approximated when parts animate at other speeds or with a delay.
|
|
47
|
+
* Null when nothing that changes is animated in code (an instant change).
|
|
48
|
+
*/
|
|
49
|
+
export declare function transitionBetween(from: Map<string, Map<string, string>>, to: Map<string, Map<string, string>>, tokens: Map<string, MotionToken>): SpecTransition | null;
|
|
50
|
+
/**
|
|
51
|
+
* Motion a Figma prototype cannot play, for the parts a set draws (`classes`: their "<module>|<class>" names):
|
|
52
|
+
* keyframe animations (a spinner, a shimmer), the keyboard focus ring, and opening or closing (a popup's enter motion,
|
|
53
|
+
* an accordion panel). `view` is the set's view: a popup drawn open, or a trigger without its popup.
|
|
54
|
+
*/
|
|
55
|
+
export declare function motionNotes(rules: CssRule[], classes: Set<string>, tokens: Map<string, MotionToken>, view?: 'popup' | 'without-popup', elements?: string[][]): SpecMotionNote[];
|
|
56
|
+
/**
|
|
57
|
+
* The code's transitions when a part opens and when it closes, for the elements with open-state rules (`elements`, each
|
|
58
|
+
* the "<module>|<class>" names one element carries): the
|
|
59
|
+
* destination state's `transition` animates, as in CSS. Opening ends in the base rules plus the open rules
|
|
60
|
+
* ([data-open], [data-panel-open]...); closing ends in the base rules plus [data-ending-style] / [data-closed]. The
|
|
61
|
+
* properties are the ones those rules change. One transition per direction: the longest, as for interactions.
|
|
62
|
+
*/
|
|
63
|
+
export declare function openingTransitions(rules: CssRule[], elements: string[][], tokens: Map<string, MotionToken>): {
|
|
64
|
+
enter: SpecTransition | null;
|
|
65
|
+
exit: SpecTransition | null;
|
|
66
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export interface ProductConfig {
|
|
2
|
+
/** Stable id: lowercase letters, digits and dashes ("admin"). The `product: <key>` label names it. */
|
|
3
|
+
key: string;
|
|
4
|
+
/** What people read ("Admin demo"). */
|
|
5
|
+
name: string;
|
|
6
|
+
/** "owner/name" when the product lives in another GitHub repository. */
|
|
7
|
+
repo?: string;
|
|
8
|
+
/** Folder of this repository, repo-relative ("apps/admin"). */
|
|
9
|
+
path?: string;
|
|
10
|
+
/** Preview URL pattern, same placeholders as previews.apps: {branch} {rawBranch} {pr} {sha} {sha7}. */
|
|
11
|
+
preview?: string;
|
|
12
|
+
/** react, vue, angular, svelte, astro, other. */
|
|
13
|
+
framework?: string;
|
|
14
|
+
/** The product a branch belongs to when nothing else matches: the library. */
|
|
15
|
+
default?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/** What a config without `products` means: the Admin demo first, then the library (the default). */
|
|
18
|
+
export declare const DEFAULT_PRODUCTS: ProductConfig[];
|
|
19
|
+
/** Pull request labels that pin a branch to a product ("product: admin"). */
|
|
20
|
+
export declare const PRODUCT_LABEL_PREFIX = "product: ";
|
|
21
|
+
export declare const productLabel: (key: string) => string;
|
|
22
|
+
/**
|
|
23
|
+
* Labels and a body line for new work started for a product (a branch, an exploration, a request): the
|
|
24
|
+
* `product: <key>` label and "**Product:** <name>". The default product adds nothing, since productOfBranch lands
|
|
25
|
+
* an unlabeled branch there. Studio, the CLI and the MCP server all write these.
|
|
26
|
+
*/
|
|
27
|
+
export declare function productMarks(products: readonly ProductConfig[], p: ProductConfig): {
|
|
28
|
+
labels: string[];
|
|
29
|
+
line: string;
|
|
30
|
+
};
|
|
31
|
+
/** The configured products, or DEFAULT_PRODUCTS when the config lists none. */
|
|
32
|
+
export declare function productsOf(config: {
|
|
33
|
+
products?: ProductConfig[] | undefined;
|
|
34
|
+
} | null | undefined): ProductConfig[];
|
|
35
|
+
/** The product marked `default: true`, else the last one listed. */
|
|
36
|
+
export declare function defaultProduct(products: readonly ProductConfig[]): ProductConfig;
|
|
37
|
+
/** A product by key or name, whatever the case. */
|
|
38
|
+
export declare function findProduct(products: readonly ProductConfig[], keyOrName: string | null | undefined): ProductConfig | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* The product a branch belongs to: a `product: <key>` label naming a known product wins; else the first product
|
|
41
|
+
* (other than the default) whose folder holds one of the changed files; else the default product. Files not read
|
|
42
|
+
* yet (null) land on the default, since every branch of the repository starts from the library.
|
|
43
|
+
*/
|
|
44
|
+
export declare function productOfBranch(products: readonly ProductConfig[], branch: {
|
|
45
|
+
files?: readonly string[] | null | undefined;
|
|
46
|
+
labels?: readonly (string | {
|
|
47
|
+
name: string;
|
|
48
|
+
})[] | null | undefined;
|
|
49
|
+
}): ProductConfig;
|
|
50
|
+
/**
|
|
51
|
+
* The product a place belongs to: a product whose `repo` is the current repository (when that is not the design
|
|
52
|
+
* system repository itself); else the product whose folder holds `relPath` (the deepest one); else the default.
|
|
53
|
+
*/
|
|
54
|
+
export declare function productForPlace(products: readonly ProductConfig[], place: {
|
|
55
|
+
relPath?: string | null | undefined;
|
|
56
|
+
repo?: string | null | undefined;
|
|
57
|
+
designSystemRepo?: string | null | undefined;
|
|
58
|
+
}): {
|
|
59
|
+
product: ProductConfig;
|
|
60
|
+
how: 'repo' | 'path' | 'default';
|
|
61
|
+
};
|
|
62
|
+
/** What is wrong with a `products` value, each problem saying how to fix it. Empty: valid (or absent). */
|
|
63
|
+
export declare function productProblems(raw: unknown): string[];
|
|
64
|
+
/** Whether two product lists say the same thing (toConfigJson leaves the default list out). */
|
|
65
|
+
export declare function sameProducts(a: readonly ProductConfig[], b: readonly ProductConfig[]): boolean;
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import type { OverridePolicy } from './config';
|
|
2
|
+
import { type ShipPolicy } from './rights';
|
|
3
|
+
import { type RiskTier } from './risk';
|
|
4
|
+
export interface LabelLike {
|
|
5
|
+
name: string;
|
|
6
|
+
}
|
|
7
|
+
/** Tier from labels (`tier-3`, `tier: 3`, `risk:3`), the PR template checkbox (`- [x] Tier 3`) or a `Risk tier: 3` line. Highest wins. */
|
|
8
|
+
export declare function parseTier(labels: LabelLike[], body: string | null | undefined): RiskTier | null;
|
|
9
|
+
/** What GitHub returns about a pull request's lifecycle. `merged_at` is set once merged; `state` is then "closed". */
|
|
10
|
+
export interface PullStateLike {
|
|
11
|
+
state?: string;
|
|
12
|
+
draft?: boolean;
|
|
13
|
+
merged?: boolean;
|
|
14
|
+
merged_at?: string | null;
|
|
15
|
+
}
|
|
16
|
+
export type PullState = 'merged' | 'closed' | 'draft' | 'open';
|
|
17
|
+
/** Merged wins over closed (GitHub reports merged pull requests as closed), closed over draft, draft over open. */
|
|
18
|
+
export declare function pullState(p: PullStateLike): PullState;
|
|
19
|
+
/** Label and pill tone of each state, for list and detail views. */
|
|
20
|
+
export declare const PULL_STATE_PILL: Record<PullState, {
|
|
21
|
+
label: string;
|
|
22
|
+
cls: string;
|
|
23
|
+
}>;
|
|
24
|
+
/** Pull requests still waiting for review (open or draft): merged and closed ones never count as open reviews. */
|
|
25
|
+
export declare function openPulls<P extends PullStateLike>(pulls: P[]): P[];
|
|
26
|
+
export interface CiRunLike {
|
|
27
|
+
name: string;
|
|
28
|
+
status: 'queued' | 'in_progress' | 'completed' | string;
|
|
29
|
+
conclusion: string | null;
|
|
30
|
+
html_url?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface CiStatusLike {
|
|
33
|
+
context: string;
|
|
34
|
+
state: 'success' | 'failure' | 'error' | 'pending' | string;
|
|
35
|
+
target_url?: string | null;
|
|
36
|
+
}
|
|
37
|
+
export type CiState = 'pass' | 'fail' | 'pending' | 'none';
|
|
38
|
+
export interface CiSummary {
|
|
39
|
+
state: CiState;
|
|
40
|
+
total: number;
|
|
41
|
+
passed: number;
|
|
42
|
+
failed: {
|
|
43
|
+
name: string;
|
|
44
|
+
url?: string;
|
|
45
|
+
}[];
|
|
46
|
+
pending: {
|
|
47
|
+
name: string;
|
|
48
|
+
url?: string;
|
|
49
|
+
}[];
|
|
50
|
+
}
|
|
51
|
+
/** Combines check runs and commit statuses into one verdict. Any failure wins over pending. */
|
|
52
|
+
export declare function summarizeCi(runs: CiRunLike[], statuses?: CiStatusLike[]): CiSummary;
|
|
53
|
+
export interface ReviewLike {
|
|
54
|
+
user: {
|
|
55
|
+
login: string;
|
|
56
|
+
} | null;
|
|
57
|
+
state: 'APPROVED' | 'CHANGES_REQUESTED' | 'COMMENTED' | 'DISMISSED' | 'PENDING' | string;
|
|
58
|
+
submitted_at?: string | null;
|
|
59
|
+
}
|
|
60
|
+
export interface ApprovalSummary {
|
|
61
|
+
approvers: string[];
|
|
62
|
+
changesRequested: string[];
|
|
63
|
+
design: boolean;
|
|
64
|
+
tech: boolean;
|
|
65
|
+
}
|
|
66
|
+
/** Latest decisive review per person (comments do not reset an approval), then owner coverage. */
|
|
67
|
+
export declare function summarizeApprovals(reviews: ReviewLike[], owners: {
|
|
68
|
+
design: string[];
|
|
69
|
+
tech: string[];
|
|
70
|
+
}, author?: string): ApprovalSummary;
|
|
71
|
+
export type MissingApproval = 'design approval' | 'tech approval' | 'approval';
|
|
72
|
+
export interface MergeGate {
|
|
73
|
+
allowed: boolean;
|
|
74
|
+
reasons: string[];
|
|
75
|
+
/** True when the only failing reasons are missing approvals and the viewer is in `governance.override.allowed`. */
|
|
76
|
+
overridable: boolean;
|
|
77
|
+
/** Approvals still missing, in the words the override comment uses. */
|
|
78
|
+
missing: MissingApproval[];
|
|
79
|
+
/** Whether the viewer holds the right to ship (true when no rule singles anyone out or the viewer is unknown). */
|
|
80
|
+
canShip: boolean;
|
|
81
|
+
}
|
|
82
|
+
/** Whether `login` may use the owner override. Case-insensitive, a leading @ is ignored. */
|
|
83
|
+
export declare function canOverride(login: string | null | undefined, policy: OverridePolicy | null | undefined): boolean;
|
|
84
|
+
/**
|
|
85
|
+
* Whether the Merge button is enabled, and why not. Mirrors docs/GOVERNANCE.md.
|
|
86
|
+
* The owner override only ever covers missing approvals: CI failing or running, changes requested, a draft,
|
|
87
|
+
* a closed pull request or a merge conflict always block, for everyone.
|
|
88
|
+
*/
|
|
89
|
+
export declare function mergeGate(input: {
|
|
90
|
+
tier: RiskTier | null;
|
|
91
|
+
ci: CiSummary;
|
|
92
|
+
approvals: ApprovalSummary;
|
|
93
|
+
draft: boolean;
|
|
94
|
+
mergeable?: boolean | null;
|
|
95
|
+
state: string;
|
|
96
|
+
merged_at?: string | null;
|
|
97
|
+
/** Login of the connected viewer (GitHub /user). */
|
|
98
|
+
viewer?: string | null;
|
|
99
|
+
override?: OverridePolicy | null;
|
|
100
|
+
/** A running exploration (label `exploration`): never merged as is, whatever else is green. */
|
|
101
|
+
exploration?: boolean;
|
|
102
|
+
/** Who may ship (shipPolicy from governance.rights). null or absent: anyone with write access. */
|
|
103
|
+
ship?: ShipPolicy | null;
|
|
104
|
+
}): MergeGate;
|
|
105
|
+
/** The gate reason of a conflict, in the words a designer reads. Detected by identity, never by regex. */
|
|
106
|
+
export declare const CONFLICT_REASON = "It conflicts with main: resolve it with Update from main on the branch.";
|
|
107
|
+
export declare const OVERRIDE_HEADING = "**Bridge owner override**";
|
|
108
|
+
/** Problem with an override reason, or null when it is accepted. */
|
|
109
|
+
export declare function overrideReasonProblem(reason: string, policy: OverridePolicy): string | null;
|
|
110
|
+
/**
|
|
111
|
+
* The audit comment posted on the pull request before an override merge. Stable format: History and the PR
|
|
112
|
+
* timeline find it by its first line, `**Bridge owner override**`, and read it back with parseOverrideComment.
|
|
113
|
+
*/
|
|
114
|
+
export declare function overrideComment(login: string, reason: string, missing: MissingApproval[]): string;
|
|
115
|
+
/** The comment body after GitHub refused the merge, so the audit trail does not claim a merge that never happened. */
|
|
116
|
+
export declare function overrideRefusedComment(body: string, message: string): string;
|
|
117
|
+
export interface OverrideRecord {
|
|
118
|
+
login: string;
|
|
119
|
+
reason: string;
|
|
120
|
+
missing: string[];
|
|
121
|
+
merged: boolean;
|
|
122
|
+
}
|
|
123
|
+
/** Reads an override comment back; null for any other comment. */
|
|
124
|
+
export declare function parseOverrideComment(body: string | null | undefined): OverrideRecord | null;
|
|
125
|
+
export type MergeMethod = 'squash' | 'merge' | 'rebase';
|
|
126
|
+
/** Merge methods the repo allows, squash first. Unknown settings (no push access) count as allowed. */
|
|
127
|
+
export declare function allowedMergeMethods(repo: {
|
|
128
|
+
allow_squash_merge?: boolean;
|
|
129
|
+
allow_merge_commit?: boolean;
|
|
130
|
+
allow_rebase_merge?: boolean;
|
|
131
|
+
} | null | undefined): MergeMethod[];
|
|
132
|
+
/** Whether the viewer can write to the repo, from `permissions` on GET /repos/{owner}/{repo} with their token. */
|
|
133
|
+
export declare function hasWriteAccess(perms: {
|
|
134
|
+
admin?: boolean;
|
|
135
|
+
maintain?: boolean;
|
|
136
|
+
push?: boolean;
|
|
137
|
+
} | null | undefined): boolean;
|
|
138
|
+
export type ReviewStatusKind = 'merged' | 'closed' | 'draft' | 'ci-failing' | 'changes-requested' | 'ci-running' | 'needs-design' | 'needs-tech' | 'needs-approval' | 'conflict' | 'checking' | 'ready' | 'unknown';
|
|
139
|
+
/**
|
|
140
|
+
* Sort order for a "needs you" list, most urgent first: what blocks the merge, then what waits for an approval,
|
|
141
|
+
* then what is ready, then running checks and unknown ones, drafts and finished ones last. Lower comes first.
|
|
142
|
+
*/
|
|
143
|
+
export declare const REVIEW_STATUS_ORDER: Record<ReviewStatusKind, number>;
|
|
144
|
+
/**
|
|
145
|
+
* One status chip for a pull request, from real data. Same rules and order as the pull request header
|
|
146
|
+
* (lib/changes needFromDetail): lifecycle, draft, CI failing, changes requested, merge conflict, CI running, then
|
|
147
|
+
* missing approvals (Tier 3 by default), then ready. `mergeable` null means GitHub has not computed it yet: the chip
|
|
148
|
+
* reads "Checking" instead of an approval or "Ready to ship", so a list never says something the detail contradicts.
|
|
149
|
+
* Undefined means it was not read: no conflict known. `ready` is returned only when mergeGate allows the merge.
|
|
150
|
+
* Unknown CI or reviews (null, still loading or failed) never read as ready.
|
|
151
|
+
*/
|
|
152
|
+
export declare function reviewStatusKind(input: {
|
|
153
|
+
state?: string;
|
|
154
|
+
draft?: boolean;
|
|
155
|
+
merged_at?: string | null;
|
|
156
|
+
ci: CiSummary | null;
|
|
157
|
+
approvals: ApprovalSummary | null;
|
|
158
|
+
tier: RiskTier | null;
|
|
159
|
+
mergeable?: boolean | null;
|
|
160
|
+
}): ReviewStatusKind;
|
|
161
|
+
export interface QueuePull extends PullStateLike {
|
|
162
|
+
state: string;
|
|
163
|
+
labels?: ({
|
|
164
|
+
name?: string;
|
|
165
|
+
} | string)[];
|
|
166
|
+
head: {
|
|
167
|
+
ref: string;
|
|
168
|
+
};
|
|
169
|
+
user?: {
|
|
170
|
+
login: string;
|
|
171
|
+
type?: string;
|
|
172
|
+
} | null;
|
|
173
|
+
}
|
|
174
|
+
/** The changesets release pull request ("Version packages"), on a changeset-release/* branch. */
|
|
175
|
+
export declare const isReleasePull: (p: Pick<QueuePull, "head">) => boolean;
|
|
176
|
+
/** Opened by GitHub automation, not by a person: the release pull request or anything github-actions[bot] opened. */
|
|
177
|
+
export declare function isAutomationPull(p: Pick<QueuePull, 'head' | 'user'>): boolean;
|
|
178
|
+
/** The open pull requests that wait for a review, in the order given. Every count in the Studio comes from here. */
|
|
179
|
+
export declare function reviewQueue<P extends QueuePull>(pulls: P[]): P[];
|
|
180
|
+
/** The open release pull request, if the changesets action opened one. */
|
|
181
|
+
export declare function releasePull<P extends QueuePull>(pulls: P[]): P | null;
|
|
182
|
+
/**
|
|
183
|
+
* The approval a change needs, the same rule everywhere: the tier the author set (label, PR template checkbox or
|
|
184
|
+
* "Risk tier" line) or the one the shared classifier computes from the changed files, highest wins. Without files
|
|
185
|
+
* (not read yet) only a label counts, and a missing label stays unknown.
|
|
186
|
+
*/
|
|
187
|
+
export declare function effectiveTier(labels: LabelLike[], body: string | null | undefined, files: {
|
|
188
|
+
filename: string;
|
|
189
|
+
status?: string;
|
|
190
|
+
}[] | null): RiskTier | null;
|
|
191
|
+
export type ReopenEligibility =
|
|
192
|
+
/** The branch is still there: the change can be reopened as it was. */
|
|
193
|
+
{
|
|
194
|
+
kind: 'reopen';
|
|
195
|
+
}
|
|
196
|
+
/** The branch is gone: nothing to reopen, but its token edits can start a new change. */
|
|
197
|
+
| {
|
|
198
|
+
kind: 'gone';
|
|
199
|
+
}
|
|
200
|
+
/** Not closed, or not the viewer's to reopen, or not known yet. */
|
|
201
|
+
| {
|
|
202
|
+
kind: 'none';
|
|
203
|
+
};
|
|
204
|
+
/**
|
|
205
|
+
* Whether the viewer can reopen a closed change: only a change closed without shipping, only its author or an owner
|
|
206
|
+
* (governance.owners, both roles), and only someone GitHub lets write (write access, or the author). `branch` is what
|
|
207
|
+
* GitHub said about the head branch: there, removed, or not read.
|
|
208
|
+
*/
|
|
209
|
+
export declare function reopenEligibility(x: {
|
|
210
|
+
state: PullState;
|
|
211
|
+
viewer: string | null;
|
|
212
|
+
author: string;
|
|
213
|
+
owners: string[];
|
|
214
|
+
canWrite: boolean;
|
|
215
|
+
branch: 'exists' | 'gone' | 'unknown';
|
|
216
|
+
}): ReopenEligibility;
|
|
217
|
+
export type ApprovalRole = 'design' | 'tech' | 'approval';
|
|
218
|
+
/** The approvals a tier needs, as mergeGate counts them (Tier 2 and 3: design and tech; Tier 1: one approval). */
|
|
219
|
+
export declare function requiredApprovals(tier: RiskTier | null): ApprovalRole[];
|
|
220
|
+
/** The approvals still missing on a branch, from its tier and the reviews read (empty when either is unknown). */
|
|
221
|
+
export declare function pendingApprovals(tier: RiskTier | null | undefined, approvals: ApprovalSummary | null | undefined): ApprovalRole[];
|
|
222
|
+
/**
|
|
223
|
+
* Who can still give an approval: the owners listed for the role (both lists for a plain approval) minus the author
|
|
224
|
+
* and anyone who already approved. `open`: the config lists nobody for the role, so any reviewer but the author counts.
|
|
225
|
+
*/
|
|
226
|
+
export declare function eligibleReviewers(role: ApprovalRole, p: {
|
|
227
|
+
owners: {
|
|
228
|
+
design: string[];
|
|
229
|
+
tech: string[];
|
|
230
|
+
};
|
|
231
|
+
author: string;
|
|
232
|
+
approvers?: string[] | undefined;
|
|
233
|
+
}): {
|
|
234
|
+
open: boolean;
|
|
235
|
+
logins: string[];
|
|
236
|
+
listed: string[];
|
|
237
|
+
};
|
|
238
|
+
/**
|
|
239
|
+
* "Ask someone else to review": the other people who can give the missing approvals, never the viewer or the author.
|
|
240
|
+
* Empty when nobody else can (the caller says to review it or to add a reviewer in Settings).
|
|
241
|
+
*/
|
|
242
|
+
export declare function reviewersToAsk(x: {
|
|
243
|
+
tier: RiskTier | null;
|
|
244
|
+
approvals: ApprovalSummary;
|
|
245
|
+
owners: {
|
|
246
|
+
design: string[];
|
|
247
|
+
tech: string[];
|
|
248
|
+
};
|
|
249
|
+
author: string;
|
|
250
|
+
viewer: string | null | undefined;
|
|
251
|
+
}): string[];
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/** The dialog's explanation, in designer words. */
|
|
2
|
+
export declare const SHARE_EXPLAIN = "Product teams can install this branch in their app before it is merged, and remove it any time.";
|
|
3
|
+
export type ChangeLifecycle = 'open' | 'draft' | 'merged' | 'closed';
|
|
4
|
+
type Source = 'pkg.pr.new' | 'release' | 'other';
|
|
5
|
+
export type ShareState =
|
|
6
|
+
/** The command for the latest update is posted. */
|
|
7
|
+
{
|
|
8
|
+
kind: 'ready';
|
|
9
|
+
command: string;
|
|
10
|
+
source: Source;
|
|
11
|
+
}
|
|
12
|
+
/** No command for the latest update yet. */
|
|
13
|
+
| {
|
|
14
|
+
kind: 'building';
|
|
15
|
+
}
|
|
16
|
+
/** The workflow posted for the latest update but could not publish. */
|
|
17
|
+
| {
|
|
18
|
+
kind: 'failed';
|
|
19
|
+
}
|
|
20
|
+
/** The change touches none of the preview packages: there is nothing to install. */
|
|
21
|
+
| {
|
|
22
|
+
kind: 'no-packages';
|
|
23
|
+
}
|
|
24
|
+
/** Shipped or closed. `stillWorks` from where the preview lived; null when no preview was ever posted. */
|
|
25
|
+
| {
|
|
26
|
+
kind: 'shipped' | 'closed';
|
|
27
|
+
stillWorks: boolean | null;
|
|
28
|
+
command: string | null;
|
|
29
|
+
};
|
|
30
|
+
/** `@bridge-ds/react` -> `packages/react/`, the folder the workflow builds it from (PACKAGE_DIRS). */
|
|
31
|
+
export declare const packageDir: (name: string) => string;
|
|
32
|
+
/** True when a changed file is inside one of the preview packages. */
|
|
33
|
+
export declare function touchesPackages(files: readonly string[], packages: readonly string[]): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Whether a change offers sharing at all: never from a fork (the workflow cannot publish from one) or when the
|
|
36
|
+
* repository has no preview packages configured.
|
|
37
|
+
*/
|
|
38
|
+
export declare const canShare: (x: {
|
|
39
|
+
fromFork: boolean;
|
|
40
|
+
packages: readonly string[];
|
|
41
|
+
}) => boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Where the preview of a change stands, from its lifecycle, its changed files and the sticky preview comment. A
|
|
44
|
+
* comment about an older update than the head means the new one is still being prepared: its command is never shown.
|
|
45
|
+
*/
|
|
46
|
+
export declare function shareState(x: {
|
|
47
|
+
lifecycle: ChangeLifecycle;
|
|
48
|
+
files: readonly string[];
|
|
49
|
+
packages: readonly string[];
|
|
50
|
+
comment: string | null | undefined;
|
|
51
|
+
headSha: string;
|
|
52
|
+
}): ShareState;
|
|
53
|
+
/**
|
|
54
|
+
* Whether the change page shows the action: always while the change is open or a draft (the dialog says why there is
|
|
55
|
+
* nothing to install yet), and after it ships or closes only when a preview was posted that a team may have installed.
|
|
56
|
+
*/
|
|
57
|
+
export declare const showShare: (lifecycle: ChangeLifecycle, s: ShareState) => boolean;
|
|
58
|
+
/** The dialog's description: the explanation while the change can still be tried, where it stands once finished. */
|
|
59
|
+
export declare const shareDescription: (s: ShareState) => string;
|
|
60
|
+
/** How a product goes back to the released packages. */
|
|
61
|
+
export declare const stableCommand: (packages: readonly string[]) => string;
|
|
62
|
+
/** The one sentence the dialog shows under its explanation, in designer words. Null when the message says it all. */
|
|
63
|
+
export declare function stateSentence(s: ShareState): string | null;
|
|
64
|
+
export interface ShareMessageInput {
|
|
65
|
+
title: string;
|
|
66
|
+
state: ShareState;
|
|
67
|
+
packages: readonly string[];
|
|
68
|
+
/** "Try it in the product": the admin preview with this change, when GitHub reports one. */
|
|
69
|
+
tryUrl?: string | null | undefined;
|
|
70
|
+
/** The change on GitHub, for the developer. */
|
|
71
|
+
url: string;
|
|
72
|
+
}
|
|
73
|
+
export interface ShareMessage {
|
|
74
|
+
subject: string;
|
|
75
|
+
body: string;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* The ready-to-paste message for a developer of a product team: the change, the preview install command, where to
|
|
79
|
+
* see it first, how long the preview lasts and how to go back. Plain text, for a chat or an email. Null when there is
|
|
80
|
+
* nothing to install and nothing to undo (still building, failed, no packages, or never posted).
|
|
81
|
+
*/
|
|
82
|
+
export declare function shareMessage(x: ShareMessageInput): ShareMessage | null;
|
|
83
|
+
/** "Open in email": a mailto link with the subject and body. Nothing is sent by Studio. */
|
|
84
|
+
export declare const mailtoHref: (m: ShareMessage) => string;
|
|
85
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type BranchPreviewSources, type PreviewApps } from './previews';
|
|
2
|
+
export interface ProductScreen {
|
|
3
|
+
label: string;
|
|
4
|
+
route: string;
|
|
5
|
+
tenant: string;
|
|
6
|
+
theme: string;
|
|
7
|
+
}
|
|
8
|
+
/** The admin preview of a change, or null when GitHub reports none (or the build failed, or it comes from a fork). */
|
|
9
|
+
export declare function productPreview(apps: PreviewApps, ctx: {
|
|
10
|
+
branch: string;
|
|
11
|
+
pr: number;
|
|
12
|
+
sha: string;
|
|
13
|
+
fromFork?: boolean;
|
|
14
|
+
}, sources: BranchPreviewSources): string | null;
|
|
15
|
+
/** The preview with the toolbar on, at that screen (shop and theme in the query, the hash route ra-core reads), else home. */
|
|
16
|
+
export declare function tryInProductUrl(preview: string, pr: number, screen: ProductScreen | null): string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bridge-ds/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Bridge core: bridge.config.json, DTCG token loading (tiers and layered modes), alias resolution, WCAG contrast, diffs, minimal-diff edits, PR body, the shared risk tier classifier, changesets and a fetch-based GitHub client. No Figma API, no DOM; Node-only helpers (git, reading a product folder) under @bridge-ds/core/node; the adoption audit under @bridge-ds/core/audit.",
|
|
6
6
|
"license": "MIT",
|