@cellajs/cli 0.6.2 → 0.6.4

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
@@ -1,42 +1,70 @@
1
1
  # @cellajs/cli
2
2
 
3
- Upgrade your app with upstream [cella template](https://github.com/cellajs/cella) changes while preserving your
4
- customizations.
3
+ The command line of apps built from the [cella template](https://github.com/cellajs/cella): a testbed and the tools that
4
+ run on it (load tests, an accessibility audit, the marketing screenshots), the style check, and upgrades from upstream
5
+ cella that keep your customizations.
5
6
 
6
7
  ## Usage
7
8
 
8
- From your monorepo root, run `pnpm cella` for the interactive menu, or call a service directly:
9
+ From your monorepo root, run `pnpm cella` for the interactive menu, or call a command directly:
9
10
 
10
11
  ```bash
12
+ pnpm cella testbed
13
+ pnpm cella bench
14
+ pnpm cella a11y
11
15
  pnpm cella analyze
12
16
  pnpm cella upgrade
13
17
  pnpm cella audit
14
18
  ```
15
19
 
16
- ## Services
20
+ ## Commands
17
21
 
18
- | Service | Description |
22
+ Develop, security and gate commands run without `cella.config.ts`; upstream commands read it first.
23
+
24
+ | Develop | Description |
19
25
  |---------|-------------|
26
+ | `testbed` | A throwaway copy of the app on its own seeded database and ports, beside `pnpm dev` (see [Testbed](#testbed)) |
27
+ | `bench` | Load-test the running app with its scenarios and check what the stack did with the load (see [Bench](#bench)) |
28
+ | `a11y` | Audit the app against WCAG 2.2 AA on a testbed and keep the conformance ledger (see [a11y](#a11y)) |
29
+ | `screenshots` | Shoot the app's marketing screenshots on a testbed (see [Screenshots](#screenshots)) |
30
+ | `i18n` | Translation status, unused keys and renames across the locale files and the code (see [Translations](#translations)) |
31
+
32
+ | Security | Description |
33
+ |----------|-------------|
34
+ | `audit` | Update outdated packages and runtime pins, then rebuild the security overrides from a fresh audit; report the security fixes this machine's Node, pnpm and gh miss |
35
+ | `pentest` | Scan a throwaway testbed of the app: a cross-tenant access probe, then Nuclei and ZAP (see [Pentest](#pentest)) |
36
+
37
+ | Upstream | Description |
38
+ |----------|-------------|
20
39
  | `analyze` | Dry run to see what would change on upgrade. It leaves your files and branches alone but updates git metadata: it fetches upstream and writes `refs/cella/last-upgrade` and a graft for the upgrade base |
21
40
  | `upgrade` | Merge upstream changes onto a fresh branch, merge package.json and open a squash-merge PR into `main` |
22
41
  | `align` | List the upstream alignments this app has not handled yet, read them, run their codemods, record them |
23
- | `audit` | Update outdated packages and runtime pins, then rebuild the security overrides from a fresh audit; report the security fixes this machine's Node, pnpm and gh miss |
24
42
  | `stats` | Count files by category and workspace package; with `--since <ref>`, count what the current branch changed, by kind of file |
25
43
  | `forks` * | Run the upgrade inside local fork repositories |
26
44
  | `contributions` * | Pull and adopt changes from local forks |
27
45
 
28
46
  \* `forks` and `contributions` only appear when you have `forks` configured in `cella.config.ts`.
29
47
 
48
+ | Gate | Description |
49
+ |------|-------------|
50
+ | `style` | The style check: terminology, docs, comments and frontend code; exits 1 on any finding (see [Style](#style)). Scripts and CI run it, so the menu leaves it out |
51
+
30
52
  ## CLI options
31
53
 
32
54
  ```bash
33
- pnpm cella [service] [options]
55
+ pnpm cella [command] [options]
34
56
  ```
35
57
 
36
- Per-service help: `pnpm cella <service> --help`.
58
+ Per-command help: `pnpm cella <command> --help`.
37
59
 
38
- | Service | Useful options |
60
+ | Command | Useful options |
39
61
  |---------|----------------|
62
+ | testbed | `stop`, `--fresh`, `-- <command>` |
63
+ | bench | `[scenario]`, `--all`, `--short`, `--isolated`, `--skip-seed` |
64
+ | a11y | `report`, `decide`, `--states <ids>`, `--workers <n>`, `--email <address>`, `--publish`, `--draft`, `--file <path>` |
65
+ | screenshots | `<ids...>`, `--out <dir>`, `--email <address>` |
66
+ | i18n | `unused`, `rename <ns:old> <ns:new>`, `--json` |
67
+ | pentest | `report`, `--only <probe,nuclei,zap>`, `--json` |
40
68
  | analyze | `--list`, `--json`, `--scope <all\|risk\|protected>`, `--all`, `--track <release\|branch>`, `--ref <ref>`, `--unpinned` |
41
69
  | upgrade | `--track <release\|branch>`, `--ref <ref>`, `--keep-config`, `--unpinned` |
42
70
  | align | `--all`, `--json`, `--show <id>`, `--extract <id>`, `--run <id> [-- <codemod args>]`, `--mark <ids...>` |
@@ -44,12 +72,246 @@ Per-service help: `pnpm cella <service> --help`.
44
72
  | forks | `--fork <name>`, `--keep-config` |
45
73
  | contributions | `--fork <name>`, `--json` |
46
74
  | stats | `--since <ref>`, `--md`, `-V, --verbose` |
75
+ | style | `[paths...]` |
47
76
 
48
77
  | Global flag | Description |
49
78
  |-------------|-------------|
50
79
  | `-v, --version` | Output the current version |
51
80
  | `-h, --help` | Display help for a command |
52
81
 
82
+ ## Testbed
83
+
84
+ `pnpm cella testbed` starts a copy of the app on a database of its own and on ports beside the ones `pnpm dev` uses:
85
+ the backend, the workers, the relay and a built frontend. A new database is seeded once and kept, and every start
86
+ migrates it, so a branch with migrations works too. The testbed holds until ctrl-c, which stops its servers and its
87
+ database container.
88
+
89
+ | Command | What it does |
90
+ |---------|--------------|
91
+ | `pnpm cella testbed` | Start a testbed and hold it |
92
+ | `pnpm cella testbed -- <command>` | Run a command against it: the one this checkout holds, or one started for the command and stopped after. The command gets the testbed's ports (`DEV_PORT_OFFSET`) and database URLs in its environment |
93
+ | `pnpm cella testbed --fresh` | Start from an empty database, seeded again |
94
+ | `pnpm cella testbed stop` | Stop the running testbed, or the servers one left behind after a crash |
95
+
96
+ One testbed runs per app on a machine, since every checkout of the app shares its database: a second start, from
97
+ another worktree too, says which checkout holds it. The servers write to `testbed.log` in `.git/cella-testbed/`; a
98
+ start that fails prints the end of that log. The testbed needs Docker and an app whose `backend/compose.yaml` has the
99
+ `db_testbed` service.
100
+
101
+ ## Tools and their adapters
102
+
103
+ A tool's engine ships in this package, and what is app-specific sits in the app's `cella/` folder as an adapter: a base
104
+ `cella/<tool>.ts` that comes with each upgrade and describes the template's UI, plus an optional `cella/<tool>.app.ts`
105
+ that no upgrade touches, holding only what the app adds, removes (`remove`, by id) or replaces (an entry with a base
106
+ id). Without the second file the base runs alone. Both are checked at load, so an adapter that does not fit the
107
+ installed CLI fails in seconds, naming the key. They import the app's SDK and run under the app's own `tsx`.
108
+
109
+ Heavy runtimes come from the app: Playwright from `frontend`, with its Chromium installed once through
110
+ `pnpm --filter frontend exec playwright install chromium`. Results stay in the git directory, `.git/cella-<tool>/` in
111
+ the main checkout and the worktree's own in a linked one, so they never show in `git status`.
112
+
113
+ ## Bench
114
+
115
+ `pnpm cella bench` load-tests the app with the scenarios in `backend/bench/scenarios/`. New virtual users arrive on
116
+ schedule whether or not earlier ones are done, a live line every five seconds shows what the last interval did, and
117
+ the summary ends in checks: the responses the stack rejected, failed users, latency thresholds, the scenario's own
118
+ counters, and the app's checks of what the stack recorded. A passing full run is saved as the scenario's baseline and
119
+ the next run compares with it.
120
+
121
+ | Command | What it does |
122
+ |---------|--------------|
123
+ | `pnpm cella bench` | Pick a scenario from a list, then run it |
124
+ | `pnpm cella bench <scenario>` | Run one scenario |
125
+ | `pnpm cella bench --all` | Run every scenario in turn, one line each, with a cooldown between them and a table at the end |
126
+ | `pnpm cella bench --short` | A smoke run: one user per role, no thresholds and no baseline |
127
+ | `pnpm cella bench --isolated` | Run on a [testbed](#testbed), the one this checkout holds or one started for the run |
128
+
129
+ A run measures the stack this checkout runs with `pnpm dev`, and one run at a time holds it. `--skip-seed` keeps the
130
+ bench data of the last run. Baselines, logs and locks live
131
+ in `cella-bench/` in the git directory every worktree shares, so `git status` never shows them.
132
+
133
+ The app's side is `backend/bench/run.ts`, run with the backend's `tsx` so it imports backend modules: it calls
134
+ `runBench` from `@cellajs/cli/bench` with the app's preflight, seed and checks around each scenario. A scenario is a
135
+ module that default-exports `defineScenario({...})` from the same import; counter and histogram names are typed, so a
136
+ misspelled one fails the typecheck.
137
+
138
+ ## a11y
139
+
140
+ `pnpm cella a11y` audits the app against WCAG 2.2 Level A and AA and fills the conformance ledger,
141
+ `cella/a11y-ledger.json`. On a testbed it opens every page and state of the scope once, scans it with axe, runs browser
142
+ checks that axe cannot do, and reviews the code for the rest. Each of the 55 criteria gets its evidence and, where the
143
+ evidence is complete, a decision. What needs a person goes on a checklist, and a person's decision survives the next
144
+ run. The audit takes three to four minutes on top of the testbed's start.
145
+
146
+ | Command | What it does |
147
+ |---------|--------------|
148
+ | `pnpm cella a11y` | Run the full audit: axe, browser probes and code checks |
149
+ | `pnpm cella a11y --states sign-in,org-members` | Audit some states only; writes `ledger-partial.json` in the results folder, never the ledger |
150
+ | `pnpm cella a11y --workers 2` | States audited at the same time (default 4); lower it on a busy machine |
151
+ | `pnpm cella a11y report` | Render the ledger as a draft VPAT and a checklist for the manual pass |
152
+ | `pnpm cella a11y report --publish` | Put the report where the app serves it and print what the statement needs; refuses a draft |
153
+ | `pnpm cella a11y report --publish --draft` | The same while rows still need a person: the statement calls the results provisional |
154
+ | `pnpm cella a11y decide --file decisions.json` | Record a reviewer's decisions (an agent's or a person's) with their evidence |
155
+
156
+ The audit signs in as the seeded admin (`ADMIN_EMAIL` of `backend/.env`) with a minted session and signs it out when it
157
+ ends. A visit that fails (a page that redirects, a check that cannot run) is reported, the command exits with an error,
158
+ and the run writes `ledger-partial.json`: an incomplete run never replaces the ledger. The failed visit leaves
159
+ `failure-<mode>.png` and `failure-<mode>.json` in the state's folder under `review/`: what the page showed, its
160
+ console errors and the steps that ran on it before.
161
+
162
+ **How a run works.** Each state gets one visit in light mode, which runs every check on that one page, and a second in
163
+ dark mode, which measures contrast only. A check is a function of an open page that returns findings, and all findings
164
+ turn into evidence in one place after every visit.
165
+
166
+ **What it writes.** The ledger, committed: one row per criterion with its status, the remarks for the report, the
167
+ evidence and the checks still open. It carries the product's name (the root package name), and a ledger of another
168
+ product is never read, so a new app starts its own with its first full run. In the results folder: raw findings for
169
+ fixing (`axe.json`, with every failing element), the draft VPAT and the manual checklist, and a review packet per state.
170
+
171
+ **How a row is decided.** Each criterion names its checks: axe, a browser probe, a code check or a manual one. Any
172
+ failure makes the row Partially Supports. A row becomes Supports or Not Applicable only when all its checks ran and none
173
+ of them is manual. Everything else stays open until a reviewer decides it. Contrast is measured in both color modes,
174
+ with increased contrast turned on, which the report states: axe decides most text, and what it cannot decide (over a
175
+ gradient, an image or a pseudo-element) is measured from the pixels, as is the edge of form controls.
176
+
177
+ **Reviewers.** Every full run leaves a packet per state under `review/`: a screenshot, the accessibility tree, facts
178
+ about the page and the Tab order. A reviewer judges the open criteria from those and records each decision with
179
+ `decide`. An agent's decision is stored as `decidedBy: "agent"` and marked in the draft report, provisional until a
180
+ person confirms it (`--by human`). A decision survives later runs; if the audit later finds a failure on a row a
181
+ reviewer called Supports, the row goes back to Partially Supports, and the checklist lists decided rows whose evidence
182
+ names a file changed since. `report --publish` refuses while rows are open or decided by an agent only, unless
183
+ `--draft` publishes them as provisional. The cella template's `a11y-review` skill is the procedure around this loop.
184
+
185
+ **The scope.** `cella/a11y.ts` lists the template's pages and states, following steps 2 and 3 of W3C's WCAG-EM: every
186
+ page type, plus overlays such as dialogs, sheets and menus. A `{name}` in a path is filled by the resolver of that name,
187
+ which reads the API as the audit user. A state that exists only at one width carries its own browser settings, and
188
+ `forms` marks the states whose form the form probes submit. `cella/a11y.app.ts` adds the app's own.
189
+
190
+ A short script can open one state of the scope, signed in, to check a fix: `openState('<id>', { context })` from
191
+ `@cellajs/cli/a11y`, run from the app's root through `pnpm cella testbed -- node <script>`.
192
+
193
+ ## Screenshots
194
+
195
+ `pnpm cella screenshots` shoots the app's marketing screenshots on a testbed, a light and a dark file each, over the
196
+ files the app ships, and prints each file's pixel and byte size. Ids pick some shots (`pnpm cella screenshots settings`),
197
+ `--out <dir>` writes a trial run elsewhere. On the testbed it also turns MFA on for the shot user and a share of their
198
+ members, so the pages show both states; nobody can sign in with those factors. WebP is written lossless by the system
199
+ `cwebp` encoder, checked before the testbed starts. `cella/screenshots.ts` lists the template's shots and devices, and
200
+ `cella/screenshots.app.ts` the app's own. The cella template's `screenshots` skill is the rubric for judging them.
201
+
202
+ ## Drive library
203
+
204
+ `@cellajs/cli/drive` is the browser layer the tools share, for an app's own scripts and skill drivers:
205
+ `loadPlaywright()` (the app's frontend copy), `mintSession(email)` and `signOut(session)` (a session for any seeded
206
+ user, through the backend's mint script), `newContext(browser, { session, uiStore })`, `settle(page)` and
207
+ `openPage(context, url)`. A script run through `pnpm cella testbed -- node <script>` mints its sessions on the testbed's
208
+ database and gets its URLs.
209
+
210
+ ## Translations
211
+
212
+ `pnpm cella i18n` reads `locales/<language>/<file>.json` the way the app loads them: `locales/namespaces.json` names the
213
+ runtime namespaces that merge several files (`c` is `common.json` with `app.json` over it), and every other file is a
214
+ namespace of its own. English holds every key, and each other language is measured against it.
215
+
216
+ | Command | What it does |
217
+ |---------|--------------|
218
+ | `pnpm cella i18n` | Per namespace and language: missing keys, keys English lacks, translations equal to English, interpolations and tags that differ from English, plural forms a language lacks or never uses, and the keys a later file of a namespace overrides. Exits 1 on a key English lacks or a mismatch; a missing translation falls back to English and only counts |
219
+ | `pnpm cella i18n unused` | The keys no code names, in this app and in the `forks` of `cella.config.ts` that exist on this machine, so a template key only an app uses stays |
220
+ | `pnpm cella i18n rename <ns:old> <ns:new>` | Renames a key and its plural forms in every language and every `'ns:old'` literal in the code, then sorts the changed locale files with the app's own Biome. A key moves to another namespace too, unless that namespace merges several files |
221
+
222
+ The code is read with the app's own TypeScript (the root `typescript` package), so a key built from a value whose
223
+ type is a union of literals, such as `` t(`c:${role}`) ``, counts as a use of each key it can build. A template whose
224
+ values are open, such as an index, is reported by its pattern (`about:why.title_*`) as unverified, and the keys it
225
+ may build are never listed as unused. A rename lists those templates and every key without its namespace passed to
226
+ `t()`, since no literal rewrite reaches them. `--json` prints any job's full report.
227
+
228
+ ## Pentest
229
+
230
+ `pnpm cella pentest` is a security check to run before a release, against a copy of your own app on this machine and
231
+ nothing else. It starts a testbed on a new database, signs in as seeded users, runs three checks and removes the
232
+ database after, since the scans write rows and run into the rate limits. It refuses to start while a testbed runs.
233
+
234
+ | Check | What it does |
235
+ |-------|--------------|
236
+ | `probe` | Calls every read route scoped to a tenant, an organization or a user with the ids of an organization, once as its admin and once as an outsider with no membership in its tenant. Any answer to the outsider other than 403 or 404 is a finding |
237
+ | `nuclei` | Nuclei's templates for misconfigurations and exposures against the testbed's origin, signed in |
238
+ | `zap` | ZAP's API scan from the app's OpenAPI spec: passive rules, then an active scan capped at a few minutes, signed in |
239
+
240
+ | Command | What it does |
241
+ |---------|--------------|
242
+ | `pnpm cella pentest` | Run all three checks |
243
+ | `pnpm cella pentest --only probe` | Run only the checks named, comma-separated |
244
+ | `pnpm cella pentest report` | Print the last run's findings; `--json` prints the report as JSON |
245
+
246
+ A run fails (exit code 1) on a finding of high or critical severity, and every read the probe lets through to the
247
+ outsider is one. The scanners run as Docker images pinned by digest, pulled on first use, on the host network so
248
+ they reach the testbed by its own origin. Results stay in `.git/cella-pentest/`, out of the repository, because the
249
+ scanners' output can hold the sessions, which the run signs out at the end.
250
+
251
+ The app describes itself in `cella/pentest.ts`, synced from the template: the query that picks the seeded users, the
252
+ operations a scan must skip because they end its session or delete what it needs, the guards that mark a route as
253
+ scoped, and what fills each route's ids. `cella/pentest.app.ts` (optional, the app's own) adds the app's own skips and ids
254
+ or overrides the rest. A route whose ids nothing fills is listed as not probed, so the report says what the probe did
255
+ not reach.
256
+
257
+ ## Style
258
+
259
+ `pnpm cella style` is the style check an app runs as a gate: `pnpm lint:fix` and `pnpm check` call it, and CI runs it
260
+ on every PR. It reads every file git lists, untracked ones it does not ignore included, prints each finding as
261
+ `file:line:column [rule] "term": message` and exits 1 on any. Paths limit the files the in-file rules read:
262
+ `pnpm cella style frontend/src`. It parses with the TypeScript of the app's root package and compiles classes with the
263
+ frontend's Tailwind, so the CLI ships neither.
264
+
265
+ An app lists its exceptions in `shared/config/vocabulary-allowlist.ts`, typed from `@cellajs/cli/style`:
266
+
267
+ | Key | What it exempts |
268
+ |-----|-----------------|
269
+ | `files`, `prefixes` | Paths that may carry the CLI's source-control term, such as a generated icon name list |
270
+ | `proseExclude` | Path prefixes the comment and doc rules skip, such as reference code the app keeps but does not maintain |
271
+ | `markerClasses` | Class names kept as hooks with no Tailwind utility or stylesheet rule behind them, each with its reason |
272
+
273
+ The `alignment` rule reads only what the template has. It checks the shape of every folder in `cella/alignments/`,
274
+ which `cella align` reads and an app never has. In a repo with `cella/alignments/_TEMPLATE.md` and no upgrade
275
+ manifest, it also asks a branch that changes an app-owned path template code reads from for an alignment, or for an
276
+ `Alignment: none, <reason>` line in a commit message.
277
+
278
+ | Rule | Fails on | Where |
279
+ | --- | --- | --- |
280
+ | `em-dash` | an em dash | source and config files, strings, JSON values and `.env*` files included; Markdown and MDX prose |
281
+ | `contrast-phrase` | `instead`, `rather than`, `as opposed to` | comments |
282
+ | `change-history` | `previously`, `formerly`, `used to`, `originally` | comments |
283
+ | `review-conversation` | `maybe`, `perhaps`, `we should`, `we could`, `we might`, `was considered` | comments |
284
+ | `concrete-language` | `invariant` | comments, Markdown and MDX prose |
285
+ | `load-bearing`, `authoritative` | the word the rule is named after | comments, Markdown and MDX prose, outside `infra/` and `cella/alignments/` |
286
+ | `detached-long-comment` | a comment of more than three prose lines outside a statement and not directly above a declaration; a licence header passes | source and config files |
287
+ | `code-in-link` | inline code in a link label | Markdown and MDX |
288
+ | `source-control-term` | the CLI's source-control term | file paths and text, outside the sync markers and the allowlisted files |
289
+ | `product-name` | `cella_*`, `Cella*`, `_cella-*`, `cellajs.com` | `backend/src`, `shared/src` and `frontend/src`, outside tests, marketing and content |
290
+ | `component-declaration` | a component declared as an arrow function, or a helper such as `memo` wrapping an unnamed render function | frontend `.tsx` sources |
291
+ | `store-selector` | a zustand store called without a selector | frontend sources |
292
+ | `route-context-selector` | `useRouteContext()` without `select` | frontend sources |
293
+ | `button-press-cancel` | `active:translate-y-0` on a `Button`, which leaves the press nudge in place | frontend sources |
294
+ | `form-no-validate` | a form react-hook-form validates without `noValidate` | frontend sources |
295
+ | `reorder-grip` | a lucide grip icon in a file that imports no `ReorderHandle` or `reorderColumn` | frontend sources outside drag previews and the editor's side menu |
296
+ | `memo-dependency-unread` | a `useMemo` or `useCallback` dependency its callback never reads | frontend sources |
297
+ | `product-cache-write` | a query-cache call that clears the refetch mark where synced product rows live; the keepers in `~/query/basic/cache-mutations` replace it | `frontend/src/query/`, product modules and range-fetch registrars |
298
+ | `tailwind-class` | a class that compiles to no CSS and that nothing selects, a variant on a plain CSS class, a stylesheet Tailwind cannot load | `frontend/src` scripts outside tests, and the Tailwind entry stylesheet |
299
+ | `text-color` | a text token with an opacity modifier outside `disabled:`, a palette gray | frontend sources; palette grays pass in the docs module and the JSON viewer theme |
300
+ | `token-contrast` | a token pair below its WCAG ratio, a theme without `--background` or `--edge-raised` | `frontend/src/styling/tailwind.css` |
301
+ | `stamp-clock` | `new Date().toISOString()` | `backend/src`, outside tests, mock builders and the two stamp helpers |
302
+ | `stamp-pair` | an object that writes `updatedAt` with `updatedBy`, or `deletedAt` with `deletedBy`, from a moment written by hand | `backend/src`, outside tests, mock builders and the two stamp helpers |
303
+ | `alignment` | an alignment folder off its shape; in the template, a changed app-owned path with no alignment or waiver | `cella/alignments/`; the branch against `origin/main` |
304
+ | `path-argument` | a path argument that matches no file | the paths `pnpm cella style` takes |
305
+
306
+ `@cellajs/cli/style` also exports the class-string helpers the template's skill scripts read with, behind
307
+ `openRepository(<repo root>)`.
308
+
309
+ ## Trying a change in an app
310
+
311
+ A change to a tool reaches apps with a CLI release. To try one on an app first, run `pnpm build` here and put this
312
+ checkout in place of the app's package: `rm -rf <app>/node_modules/@cellajs/cli && ln -s "$PWD" <app>/node_modules/@cellajs/cli`.
313
+ An install in the app puts the published package back, so link again after every `pnpm install`.
314
+
53
315
  ## Configuration
54
316
 
55
317
  Upgrade behavior lives in `cella.config.ts` at your monorepo root (a sensible default ships with new
package/dist/a11y.d.ts ADDED
@@ -0,0 +1,80 @@
1
+ import { P as Prepare, a as Placeholders } from './adapters-CxHOtWog.js';
2
+ export { S as SignedIn } from './adapters-CxHOtWog.js';
3
+ export { settle } from './drive.js';
4
+ import { Page, BrowserContextOptions, BrowserContext } from 'playwright';
5
+ export { BrowserContextOptions, Page } from 'playwright';
6
+
7
+ type Opener = (page: Page) => Promise<void>;
8
+ /** A form probe a state runs on its page. */
9
+ type FormCheck =
10
+ /** Submitting the form empty identifies each error in text (3.3.1). */
11
+ 'required'
12
+ /** Inputs for the user's own details declare their purpose (1.3.5). */
13
+ | 'personal'
14
+ /** Sign-in fields accept autofill and pasted text (3.3.8). */
15
+ | 'sign-in';
16
+ /** A page or state of the audit scope. */
17
+ interface A11yState {
18
+ id: string;
19
+ /** Path on the frontend; a `{name}` placeholder takes the value its resolver returns. */
20
+ path: string;
21
+ auth: boolean;
22
+ /** Brings the page into the state to audit, such as an open dialog. Runs after the page has loaded. */
23
+ open?: Opener;
24
+ /** `open` leaves an overlay (dialog, sheet, menu) open that holds focus until it closes. */
25
+ overlay?: true;
26
+ /** Browser settings of this state's visits, such as a viewport the state only exists at. */
27
+ context?: BrowserContextOptions;
28
+ forms?: FormCheck[];
29
+ }
30
+ /** `cella/a11y.ts`: the template's scope, synced with the UI it describes. */
31
+ interface A11yBase {
32
+ /** Brings the audit user into the state the pages expect, before the first visit. */
33
+ prepare?: Prepare;
34
+ /** Values for the `{name}` placeholders of the paths, read as the audit user before the first visit. */
35
+ placeholders?: Placeholders;
36
+ /** What the code checks search, as `git grep` pathspecs from the app's root. */
37
+ sources: string[];
38
+ /** The sample of pages and states the audit covers (WCAG-EM steps 2 and 3), in report order. */
39
+ scope: A11yState[];
40
+ }
41
+ /** `cella/a11y.app.ts`, optional: what an app adds to the base, removes from it or overrides. */
42
+ interface A11yApp {
43
+ /** Runs after the base's. */
44
+ prepare?: Prepare;
45
+ /** Joins the base's; a name in both takes this one. */
46
+ placeholders?: Placeholders;
47
+ /** Base state ids the app does not have. */
48
+ remove?: string[];
49
+ /** States of the app's own; one with a base state's id replaces it in place. */
50
+ add?: A11yState[];
51
+ }
52
+ /**
53
+ * The audit measures the app with increased contrast turned on, which is what raises the edge tokens to 3:1. It is a
54
+ * user setting, not the default, so every report built from a run says so: the ledger records it and the report prints
55
+ * it. `prepare` stores it for the audit user, since `/me` overwrites the seeded UI store on every authenticated page.
56
+ */
57
+ declare const auditContrast: "more";
58
+ declare const defineA11yBase: (base: A11yBase) => A11yBase;
59
+ declare const defineA11yApp: (app: A11yApp) => A11yApp;
60
+
61
+ type Mode = 'light' | 'dark';
62
+ interface OpenStateOptions {
63
+ mode?: Mode;
64
+ /** Over the state's own browser settings, such as a phone or `{ reducedMotion: 'no-preference' }`. */
65
+ context?: BrowserContextOptions;
66
+ /** Default `ADMIN_EMAIL`, which a testbed hands the command it runs. */
67
+ email?: string;
68
+ }
69
+ /**
70
+ * Opens a state of the app's audit scope by id, signed in: the start of a short script that checks a fix in the
71
+ * running app. Run the script from the app's root through `pnpm cella testbed -- node <script>`; `close` ends it.
72
+ * @throws when the scope has no state of that id, or no email is given and `ADMIN_EMAIL` is unset
73
+ */
74
+ declare function openState(id: string, { mode, context: overrides, email }?: OpenStateOptions): Promise<{
75
+ page: Page;
76
+ context: BrowserContext;
77
+ close: () => Promise<void>;
78
+ }>;
79
+
80
+ export { type A11yApp, type A11yBase, type A11yState, type FormCheck, Placeholders, Prepare, auditContrast, defineA11yApp, defineA11yBase, openState };