@bridge-ds/core 0.13.0 → 0.15.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/README.md CHANGED
@@ -83,7 +83,8 @@ function composeProposalBody(input); function changeTable(changes); function con
83
83
  ```
84
84
 
85
85
  `@bridge-ds/core/node` (Node only): `git`, `isGitRepo`, `currentBranch`, `branchExists`, `showFile`, `listFilesAt`,
86
- `originRepo`, `commitToNewLocalBranch` (plumbing commit on a new branch; working tree, index and checkout untouched).
86
+ `originRepo`, `commitToNewLocalBranch` (plumbing commit on a new branch; working tree, index and checkout untouched),
87
+ `fsSource(root, ref?)` and `localRepoRef(root)` (a `RepoSource` over a checkout, see repo model).
87
88
 
88
89
  ## types
89
90
 
@@ -299,6 +300,29 @@ normalizeStorybookUrl(pasted): string; // https only (http on localhost), drop
299
300
  storybookIframeUrl(base, id, { viewMode?, globals?, args? }); storybookManagerUrl(base, id, viewMode?); storybookIndexUrls(base);
300
301
  ```
301
302
 
303
+ ## repo model (repo-source.ts, repo-model.ts, token-rows.ts, contrast-model.ts, component-scan.ts)
304
+
305
+ Everything the docs site and Studio read from a design system repo, from one `RepoSource`, so the build (a local
306
+ checkout) and the browser (any team's repo on GitHub) run the same code. `bridge.config.json` is parsed once; every
307
+ folder comes from it (`tokens.root` and tier folders, `contrast.path`, `components.dir|index|specs`,
308
+ `tokens.usage`), with this repo's former paths as defaults (`REPO_MODEL_DEFAULTS`). Repo content never throws: a bad
309
+ config, docs.json or alias, or a file the source refuses as too large (413), lands in `problems`; a rate limit or
310
+ network error still throws so the caller can retry.
311
+
312
+ ```ts
313
+ interface RepoSource { ref: { owner; repo; sha }; list(prefix?): Promise<string[]>; text(path): Promise<string | null> }
314
+ memorySource(files, ref?); readRepoTexts(source, paths, concurrency?, onError?); // fsSource(root, ref?) in @bridge-ds/core/node
315
+ // gitHubSource(client, sha, opts?) (repo-source-github.ts) is the same RepoSource over GitHub; every list() is sorted.
316
+ loadRepoModel(source, { components?, href?, concurrency? }): Promise<RepoModel>;
317
+ // RepoModel: { ref, configText, config, site: SiteRepo, problems, tokens: { root, files, rows, usage },
318
+ // contrast: { path, specs, pairs }, components: { dir, index, families, list } | null, specs: { path, file, coverage } }
319
+ siteRepoFromConfig(config, raw?, fallback?); parseModelConfig(text);
320
+ buildTokenRows(config, readText, listFiles); tokenRowIndex(rows); tokenRowAxes(config, readText?); tokenRowFiles(config);
321
+ parseContrastSpecs(text); evaluateContrastPairs(specs, rows); hexContrastRatio(a, b);
322
+ scanComponents(input); parseComponentTsx(name, src); parseComponentCss(src, labels, describeVar);
323
+ parseComponentStories(src); parseComponentFamilies(indexSrc, componentsRel?); resolveInheritedTokens(list);
324
+ ```
325
+
302
326
  ## history (history.ts, history-github.ts, history-demo.ts)
303
327
 
304
328
  What changed in the tokens, when, who changed it and why, and how to undo it safely.
@@ -129,6 +129,11 @@
129
129
  ]
130
130
  }
131
131
  },
132
+ "usage": {
133
+ "type": "string",
134
+ "description": "Optional notes per token group ({ \"color.action\": \"Buttons and links\" }), relative to root. Bridge reads usage.json under root when this is not set.",
135
+ "minLength": 1
136
+ },
132
137
  "primitives": {
133
138
  "type": "string",
134
139
  "description": "Flat form: primitive tier directory."
@@ -678,6 +683,18 @@
678
683
  }
679
684
  }
680
685
  },
686
+ "library": {
687
+ "type": "object",
688
+ "additionalProperties": false,
689
+ "description": "The team's hosted Bridge Library (/library/<owner>/<repo>/), read from the bridge-data snapshot.",
690
+ "properties": {
691
+ "public": {
692
+ "type": "boolean",
693
+ "default": false,
694
+ "description": "true lets search engines index the hosted Library of a public repository. Default false: every Library page says noindex."
695
+ }
696
+ }
697
+ },
681
698
  "visualImpact": {
682
699
  "type": "object",
683
700
  "additionalProperties": false,
@@ -1,6 +1,6 @@
1
- import { i as isAuditable, a as auditFile } from "../report-BeWNSjPk.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-BeWNSjPk.js";
3
- import { p as pieceId, a as proposedTokenPath, t as tokenLabel } from "../prod-branch-6ftQWwdu.js";
1
+ import { i as isAuditable, a as auditFile } from "../report-DCPfvvoq.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-DCPfvvoq.js";
3
+ import { p as pieceId, a as proposedTokenPath, t as tokenLabel } from "../prod-branch-BjXFIz74.js";
4
4
  const lines = (text) => text.split(/\r?\n/);
5
5
  function lineBag(text) {
6
6
  const bag = /* @__PURE__ */ new Map();
@@ -0,0 +1,158 @@
1
+ import type { ProseKey } from './docs-draft';
2
+ import type { TokenRowTier } from './token-rows';
3
+ export type ExampleNode = string | {
4
+ c: string;
5
+ p?: Record<string, unknown>;
6
+ children?: ExampleNode[];
7
+ };
8
+ export interface GuidanceItem {
9
+ title: string;
10
+ text: string;
11
+ example?: ExampleNode;
12
+ }
13
+ /** <Name>.docs.json. */
14
+ export interface ComponentDocs {
15
+ summary: string;
16
+ /** Navigation group. Optional; components without one are listed under "Core". */
17
+ category?: string;
18
+ /** Lifecycle shown on the component page and in the nav: stable (default), beta, deprecated. */
19
+ status?: 'stable' | 'beta' | 'deprecated';
20
+ preview?: ExampleNode;
21
+ whenToUse: string[];
22
+ whenNotToUse: string[];
23
+ dos: GuidanceItem[];
24
+ donts: GuidanceItem[];
25
+ accessibility: string[];
26
+ contentGuidelines?: string[];
27
+ anatomy?: Record<string, string>;
28
+ /** "claude" when an AI drafted the prose (docs-draft Action, bridge_draft_docs). */
29
+ draftedBy?: string;
30
+ /** Prose sections Claude drafted that wait for review, when a draft covered only some (missing: the whole file). */
31
+ draftedSections?: ProseKey[];
32
+ /** True while drafted prose waits for a reviewer. */
33
+ needsReview?: boolean;
34
+ }
35
+ export interface ComponentProp {
36
+ name: string;
37
+ type: string;
38
+ required: boolean;
39
+ defaultValue?: string;
40
+ description?: string;
41
+ }
42
+ /** What the scan knows about a CSS variable: the token it is, and an optional docs link (the caller's routing). */
43
+ export interface ComponentVar {
44
+ name: string;
45
+ href?: string;
46
+ known: boolean;
47
+ path?: string;
48
+ kind?: TokenRowTier;
49
+ }
50
+ export interface ComponentCssRule {
51
+ selector: string;
52
+ /** First class in the selector, used for the anatomy label. */
53
+ className: string;
54
+ label: string;
55
+ vars: ComponentVar[];
56
+ }
57
+ export interface ScannedComponent {
58
+ name: string;
59
+ slug: string;
60
+ /** Repo-relative folder. */
61
+ dir: string;
62
+ /** Repo-relative files (they may not all exist). */
63
+ files: {
64
+ tsx: string;
65
+ css: string;
66
+ stories: string;
67
+ docs: string;
68
+ };
69
+ description?: string;
70
+ props: ComponentProp[];
71
+ inherits?: string;
72
+ omitted: string[];
73
+ subcomponents: string[];
74
+ rules: ComponentCssRule[];
75
+ tokensUsed: {
76
+ name: string;
77
+ href?: string;
78
+ known: boolean;
79
+ kind?: string;
80
+ }[];
81
+ /** Paths of the tokens its own stylesheet reads, in every tier. */
82
+ tokenPaths: string[];
83
+ /**
84
+ * Tokens it reads through another component's stylesheet it imports (IconButton imports Button.module.css), by
85
+ * that component, without the ones its own stylesheet already reads. Followed transitively.
86
+ */
87
+ inherited: {
88
+ from: string;
89
+ slug: string;
90
+ paths: string[];
91
+ }[];
92
+ /** Own and inherited token paths: what the component actually paints with. */
93
+ allTokenPaths: string[];
94
+ stories: {
95
+ name: string;
96
+ id: string;
97
+ }[];
98
+ docs: ComponentDocs | null;
99
+ /** Family from docs.json `category`, else from the grouped exports of the barrel file. */
100
+ category: string;
101
+ }
102
+ /** "IconButton" -> "icon-button". */
103
+ export declare const componentSlug: (s: string) => string;
104
+ /**
105
+ * Families in the order the barrel file lists them ("// Actions" then its exports). `componentsRel` is the components
106
+ * folder relative to the barrel's folder ("components"); exports from elsewhere are not members.
107
+ */
108
+ export declare function parseComponentFamilies(indexSrc: string, componentsRel?: string): {
109
+ name: string;
110
+ members: string[];
111
+ }[];
112
+ /** Props (with defaults and JSDoc), the native element it extends, its description and sub-components, from <Name>.tsx. */
113
+ export declare function parseComponentTsx(name: string, src: string): {
114
+ props: ComponentProp[];
115
+ inherits: string | undefined;
116
+ omitted: string[];
117
+ description: string | undefined;
118
+ subcomponents: string[];
119
+ };
120
+ /**
121
+ * Minimal CSS reader: selector -> var(--*) list, labelled with the docs.json anatomy. Skips @keyframes, flattens
122
+ * @media. `describe` says what a variable is (token path, tier, docs link); undefined: not a token.
123
+ */
124
+ export declare function parseComponentCss(src: string, labels: Record<string, string>, describe: (cssVar: string) => Omit<ComponentVar, 'name' | 'known'> | undefined): ComponentCssRule[];
125
+ /** Story names and Storybook ids of a <Name>.stories.tsx (CSF with a `title`). */
126
+ export declare function parseComponentStories(src: string): {
127
+ name: string;
128
+ id: string;
129
+ }[];
130
+ /**
131
+ * Fills `inherited` and `allTokenPaths` from the stylesheets each component imports from another component,
132
+ * transitively (A imports B's stylesheet, B imports C's: A inherits from B and C). Removes `reuses`.
133
+ */
134
+ export declare function resolveInheritedTokens(list: (Pick<ScannedComponent, 'name' | 'slug' | 'tokenPaths' | 'inherited' | 'allTokenPaths'> & {
135
+ reuses?: string[];
136
+ })[]): void;
137
+ export interface ComponentScanInput {
138
+ /** Components folder, repo-relative (bridge.config.json components.dir). */
139
+ dir: string;
140
+ /** Every repo-relative file under `dir` (RepoSource.list(dir)). */
141
+ files: string[];
142
+ /** Body of a repo-relative file, undefined when missing. */
143
+ readText: (path: string) => string | undefined;
144
+ /** Barrel file body (components.index), for families. Undefined: no families. */
145
+ indexText?: string | undefined;
146
+ /** Components folder relative to the barrel's folder. Default "components". */
147
+ indexRel?: string;
148
+ docsSuffix?: string;
149
+ styleSuffix?: string;
150
+ /** What a CSS variable is (token path, tier, docs link). Undefined: not a token. */
151
+ describeVar: (cssVar: string) => Omit<ComponentVar, 'name' | 'known'> | undefined;
152
+ /** Problems found (a docs.json that does not parse) are reported here instead of failing the scan. */
153
+ problems?: string[];
154
+ }
155
+ /**
156
+ * Every component of the folder: a sub-folder <Name> holding <Name>.tsx. Sorted by family (barrel order), then name.
157
+ */
158
+ export declare function scanComponents(input: ComponentScanInput): ScannedComponent[];
package/dist/config.d.ts CHANGED
@@ -17,6 +17,11 @@ export interface StorybookConfig {
17
17
  /** Root URL (where index.json and iframe.html are served), https only, no trailing slash. */
18
18
  url: string;
19
19
  }
20
+ /** The team's hosted Library (/library/<owner>/<repo>/). */
21
+ export interface LibraryConfig {
22
+ /** True lets search engines index the hosted Library of a public repository. Default false: noindex. */
23
+ public?: boolean;
24
+ }
20
25
  export interface RepoRef {
21
26
  owner: string;
22
27
  name: string;
@@ -48,6 +53,8 @@ export interface TokenConfig extends TokenPaths {
48
53
  /** Tier name -> directories or files relative to root. First match wins. */
49
54
  tiers: Record<string, string[]>;
50
55
  layers: TokenLayer[];
56
+ /** Optional notes per token group ({ "color.action": "Buttons and links" }), relative to root. Default usage.json when present. */
57
+ usage?: string | undefined;
51
58
  }
52
59
  export interface ModeAxis {
53
60
  values: string[];
@@ -176,6 +183,8 @@ export interface BridgeConfig {
176
183
  changes: ChangesMode;
177
184
  /** The Storybook set in the file. Read it through storybookUrl(), which falls back to previews.production.storybook. */
178
185
  storybook?: StorybookConfig | undefined;
186
+ /** The hosted Library. Read it through libraryIsPublic(). */
187
+ library?: LibraryConfig | undefined;
179
188
  }
180
189
  type Partialish<T> = {
181
190
  [K in keyof T]?: T[K] | undefined;
@@ -198,6 +207,7 @@ export interface BridgeConfigInput {
198
207
  files: string;
199
208
  optional?: boolean;
200
209
  })[];
210
+ usage?: string;
201
211
  };
202
212
  modes?: (Partial<Omit<ModeConfig, 'defaults'>> & {
203
213
  defaults?: ModeConfig['defaults'];
@@ -240,6 +250,7 @@ export interface BridgeConfigInput {
240
250
  storybook?: {
241
251
  url: string;
242
252
  };
253
+ library?: LibraryConfig;
243
254
  }
244
255
  export declare const DEFAULT_TOKEN_PATHS: TokenPaths;
245
256
  export declare const DEFAULT_GOVERNANCE: GovernanceConfig;
@@ -264,6 +275,12 @@ export declare const changesMode: (config: Pick<BridgeConfig, "changes"> | null
264
275
  * when neither is set. No trailing slash.
265
276
  */
266
277
  export declare function storybookUrl(config: Pick<BridgeConfig, 'storybook' | 'previews'> | null | undefined): string | undefined;
278
+ /**
279
+ * True when the team opted in to search engines for its hosted Library (`"library": { "public": true }`). Off by
280
+ * default: a Library page under the Bridge domain shows content Bridge does not control, so it is noindex unless the
281
+ * team asks.
282
+ */
283
+ export declare const libraryIsPublic: (config: Pick<BridgeConfig, "library"> | null | undefined) => boolean;
267
284
  /** Plain-JSON form of a config, in the canonical shape (what `bridge init` writes). */
268
285
  export declare function toConfigJson(config: BridgeConfig, schema?: string): Record<string, unknown>;
269
286
  export {};
@@ -0,0 +1,43 @@
1
+ import type { TokenRow, TokenRows } from './token-rows';
2
+ export interface ContrastPairSpec {
3
+ fg: string;
4
+ bg: string;
5
+ usage: 'text' | 'ui';
6
+ /** Minimum ratio the build enforces for this pair. */
7
+ min: number;
8
+ }
9
+ export interface ContrastModeResult {
10
+ fgHex: string;
11
+ bgHex: string;
12
+ /** Rounded down to 2 decimals, so 4.496 never displays as a passing 4.50. */
13
+ ratio: number;
14
+ aa: boolean;
15
+ /** null when AAA is not defined (non-text UI). */
16
+ aaa: boolean | null;
17
+ }
18
+ export interface ContrastPairResult extends ContrastPairSpec {
19
+ fgToken: TokenRow;
20
+ bgToken: TokenRow;
21
+ /** Default brand, per theme. */
22
+ modes: Record<string, ContrastModeResult>;
23
+ brands: Record<string, Record<string, ContrastModeResult>>;
24
+ }
25
+ /** WCAG thresholds: body text 4.5:1 (AA) and 7:1 (AAA); UI parts and graphics 3:1 (AA, 1.4.11). */
26
+ export declare const CONTRAST_THRESHOLDS: {
27
+ readonly text: {
28
+ readonly aa: 4.5;
29
+ readonly aaa: 7;
30
+ };
31
+ readonly ui: {
32
+ readonly aa: 3;
33
+ readonly aaa: null;
34
+ };
35
+ };
36
+ /** Relative luminance of a #rrggbb color. */
37
+ export declare function hexLuminance(hex: string): number;
38
+ /** Contrast ratio of two opaque #rrggbb colors. */
39
+ export declare function hexContrastRatio(a: string, b: string): number;
40
+ /** The pairs of a contrast checks file ({ "pairs": [[fg, bg, min] | { fg, bg, min }] }). Empty when there is no file. */
41
+ export declare function parseContrastSpecs(text: string | null | undefined): ContrastPairSpec[];
42
+ /** Resolves pair specs against the token rows in every brand and theme. Throws on a pair naming a missing token. */
43
+ export declare function evaluateContrastPairs(specs: ContrastPairSpec[], rows: Pick<TokenRows, 'all' | 'brands' | 'themes' | 'defaultBrand'>): ContrastPairResult[];
@@ -60,7 +60,9 @@ export interface RepoInfo {
60
60
  private: boolean;
61
61
  permissions: {
62
62
  admin?: boolean;
63
+ maintain?: boolean;
63
64
  push?: boolean;
65
+ triage?: boolean;
64
66
  pull?: boolean;
65
67
  } | undefined;
66
68
  }
@@ -69,6 +71,8 @@ export interface TreeEntry {
69
71
  type: 'blob' | 'tree' | 'commit';
70
72
  sha: string;
71
73
  mode: string;
74
+ /** Bytes, for blobs (GitHub sends it in tree listings). */
75
+ size?: number | undefined;
72
76
  }
73
77
  export interface CompareResult {
74
78
  status: string;
@@ -123,7 +127,119 @@ export interface ClientOptions {
123
127
  * the MCP server config"). Default: "add a GitHub token".
124
128
  */
125
129
  tokenHint?: string;
130
+ /**
131
+ * Where conditional GET responses are kept (ETag + body). Default: one in-memory store shared by every client in
132
+ * this process or page, keyed by the token's fingerprint, so two accounts never share an entry. `false` turns
133
+ * conditional requests off. A 304 answer does not count against GitHub's primary rate limit.
134
+ */
135
+ etags?: EtagStore | false;
136
+ /**
137
+ * Who the cached responses belong to, when a stable name is known (the signed-in login). Default: a fingerprint of
138
+ * the token, which changes when the token is refreshed.
139
+ */
140
+ etagScope?: string;
141
+ /**
142
+ * When GitHub's hourly limit is used up and a cached response exists for that request, return it instead of
143
+ * throwing. Off by default: the caller then knows the data may be stale only from `rateLimit`.
144
+ */
145
+ serveStaleOnRateLimit?: boolean;
146
+ }
147
+ /** One cached conditional response: what GitHub sent with this ETag. */
148
+ export interface EtagEntry {
149
+ etag: string;
150
+ body: unknown;
151
+ /** When it was stored or last confirmed by a 304 (ms since epoch). */
152
+ at: number;
153
+ }
154
+ /** Storage for conditional requests. Keys never contain the token. */
155
+ export interface EtagStore {
156
+ get(key: string): EtagEntry | undefined;
157
+ set(key: string, entry: EtagEntry): void;
158
+ /** Forgets every entry (sign-out). */
159
+ clear(): void;
160
+ }
161
+ /** An in-memory ETag store that keeps the `max` most recently used entries. */
162
+ export declare function memoryEtagStore(max?: number): EtagStore;
163
+ /** The subset of the Web Storage API (localStorage, sessionStorage) an ETag store needs. */
164
+ export interface StorageLike {
165
+ getItem(key: string): string | null;
166
+ setItem(key: string, value: string): void;
167
+ removeItem(key: string): void;
168
+ key(index: number): string | null;
169
+ readonly length: number;
170
+ }
171
+ /**
172
+ * An ETag store over Web Storage, so conditional requests survive a page load. Every access is guarded: private
173
+ * windows, quota errors and blocked storage turn into cache misses, never into failures. Entries live under
174
+ * `prefix` (default "bridge.gh.etag:"); `clear()` removes only those. Bodies of private repositories land here
175
+ * too, so a sign-out must call `clear()`.
176
+ */
177
+ export declare function webStorageEtagStore(storage: StorageLike, opts?: {
178
+ prefix?: string;
179
+ maxEntryBytes?: number;
180
+ }): EtagStore;
181
+ /** Clears the default in-memory ETag store every client shares (sign-out, account switch). */
182
+ export declare function clearSharedEtags(): void;
183
+ /** A short, non-reversible name for a token, used in cache keys instead of the token itself (two FNV-1a passes). */
184
+ export declare function tokenFingerprint(token: string): string;
185
+ /** GitHub's rate-limit headers from the last API response. */
186
+ export interface RateLimitInfo {
187
+ limit: number | undefined;
188
+ remaining: number | undefined;
189
+ resetsAt: Date | undefined;
190
+ /** "core", "search", "graphql"... */
191
+ resource: string | undefined;
192
+ }
193
+ /** What the conditional request cache did, for tests and diagnostics. */
194
+ export interface EtagStats {
195
+ /** Requests sent with If-None-Match. */
196
+ conditional: number;
197
+ /** 304 answers served from the cache (free for the primary rate limit). */
198
+ notModified: number;
199
+ /** Cached bodies returned because the hourly limit was used up (serveStaleOnRateLimit). */
200
+ stale: number;
201
+ }
202
+ /** An installation of a GitHub App the signed-in user can reach (GET /user/installations). */
203
+ export interface Installation {
204
+ id: number;
205
+ account: {
206
+ login: string;
207
+ type: string;
208
+ } | null;
209
+ /** "all" repositories of the account, or "selected" ones. */
210
+ repositorySelection: 'all' | 'selected' | string;
211
+ appSlug: string | undefined;
212
+ }
213
+ /** One repository of an installation (GET /user/installations/{id}/repositories). */
214
+ export interface InstallationRepo {
215
+ owner: string;
216
+ name: string;
217
+ fullName: string;
218
+ private: boolean;
219
+ }
220
+ /** One installation of the GitHub App, as GET /user/installations lists it (raw or mapped). */
221
+ export interface InstallationLike {
222
+ id: number;
223
+ account?: {
224
+ login?: string;
225
+ } | null;
226
+ repository_selection?: 'all' | 'selected' | string;
227
+ repositorySelection?: 'all' | 'selected' | string;
126
228
  }
229
+ /**
230
+ * Whether the signed-in account reaches a repository of `owner` through the app: 'yes' when an installation on the
231
+ * owner covers all its repositories, 'check' (with its id) when it covers selected ones (list them to know), 'no'
232
+ * when there is none. Same contract as the site's `installationFor` (lib/github-signin.ts).
233
+ */
234
+ export declare function installationFor(list: InstallationLike[], owner: string): {
235
+ reach: 'yes';
236
+ id: number;
237
+ } | {
238
+ reach: 'check';
239
+ id: number;
240
+ } | {
241
+ reach: 'no';
242
+ };
127
243
  /**
128
244
  * The sentence for GitHub's exhausted hourly limit. With a token, the token's own limit is used up: wait. Without one,
129
245
  * the shared limit for anonymous reads is (60 an hour per address): say how to get a higher one.
@@ -199,7 +315,14 @@ export declare class GitHubClient {
199
315
  private readonly blobCache;
200
316
  private readonly api;
201
317
  private readonly protectedBranches;
318
+ private readonly etags;
319
+ private readonly etagScope;
320
+ private rate;
321
+ /** What the conditional request cache did for this client. */
322
+ readonly etagStats: EtagStats;
202
323
  constructor(repo: string, token: string, fetchImpl?: FetchLike, retry?: RetryPolicy, options?: ClientOptions);
324
+ /** GitHub's rate-limit headers from the last API response, or undefined before any response carried them. */
325
+ get rateLimit(): RateLimitInfo | undefined;
203
326
  /** "owner/name". */
204
327
  get fullName(): string;
205
328
  /** Whether a token was given (without one, every write is refused before any request). */
@@ -209,7 +332,13 @@ export declare class GitHubClient {
209
332
  private assertToken;
210
333
  private get repoPath();
211
334
  private request;
335
+ private noteRateLimit;
212
336
  private once;
337
+ /**
338
+ * Every item of a paged listing (`per_page=100`), at most 10 pages. `pick` takes the items out of one page (the
339
+ * installation endpoints wrap them in an object with `total_count`).
340
+ */
341
+ private paged;
213
342
  getRepo(): Promise<RepoInfo>;
214
343
  /** Commit sha a branch points to, or undefined when the branch does not exist. */
215
344
  getBranchSha(branch: string): Promise<string | undefined>;
@@ -408,6 +537,43 @@ export declare class GitHubClient {
408
537
  * hourly API limit); with one, through the contents API (private repos work).
409
538
  */
410
539
  readFile(path: string, ref: string): Promise<string | undefined>;
540
+ /**
541
+ * Text of a file from the raw host (raw.githubusercontent.com), sent without any header so the browser makes a
542
+ * simple CORS request (no preflight) and nothing counts against the API limit. Works for public repositories only:
543
+ * a private file answers 404 there, like a missing one. Undefined on 404.
544
+ */
545
+ readRawFile(path: string, ref: string): Promise<string | undefined>;
546
+ /**
547
+ * Text of a file at a ref through the contents API with the raw media type: the way to read a private file (the raw
548
+ * host cannot take the token without a preflight). Counts against the API limit; conditional when cached.
549
+ * Undefined when the file does not exist.
550
+ */
551
+ getRawText(path: string, ref: string): Promise<string | undefined>;
552
+ /**
553
+ * Text of a blob by its sha through the git blobs API with the raw media type: how a signed-in reader loads a file
554
+ * listed by a tree (the raw host refuses any request that carries a token). A blob sha names its content, so the
555
+ * answer is cached for the client's life and never revalidated. Undefined on 404.
556
+ */
557
+ getBlobRaw(sha: string): Promise<string | undefined>;
558
+ /**
559
+ * Installations of the signing-in GitHub App that the signed-in user can reach (GET /user/installations, every
560
+ * page). Needs a user-to-server token of that same App: a personal or OAuth token is refused (403), and each App
561
+ * sees only its own installations. GitHub lists only installations on accounts and repositories the user has
562
+ * explicit permission on: a public repository where the user is not a collaborator is never listed, installed or
563
+ * not. (GET /repos/{o}/{r}/installation would say, but it needs the App's private key, which Bridge never holds.)
564
+ */
565
+ listUserInstallations(): Promise<Installation[]>;
566
+ /** Repositories of one installation the signed-in user can reach (GET /user/installations/{id}/repositories, every page). */
567
+ listInstallationRepos(installationId: number): Promise<InstallationRepo[]>;
568
+ /**
569
+ * The App installation that gives the signed-in user this repository (this client's by default): its id, or null
570
+ * when the App is not installed there or the user cannot reach it. One call when the installation covers all of
571
+ * the owner's repositories, two (paged) when it covers selected ones.
572
+ */
573
+ findInstallation(owner?: string, repo?: string): Promise<{
574
+ id: number;
575
+ selection: 'all' | 'selected';
576
+ } | null>;
411
577
  /** The repository's default branch. */
412
578
  getDefaultBranch(): Promise<string>;
413
579
  /** Every file path at a ref (branch, tag or sha), one API call. */
package/dist/index.d.ts CHANGED
@@ -64,3 +64,11 @@ export * from './suggestion-review';
64
64
  export * from './suggestions-github';
65
65
  export * from './suggestion-apply';
66
66
  export * from './storybook';
67
+ export * from './repo-source';
68
+ export * from './token-rows';
69
+ export * from './contrast-model';
70
+ export * from './component-scan';
71
+ export * from './repo-model';
72
+ export * from './repo-source-github';
73
+ export * from './repo-access';
74
+ export * from './snapshot';