@cellajs/cli 0.6.1 → 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 +271 -9
- package/dist/a11y.d.ts +80 -0
- package/dist/a11y.js +309 -0
- package/dist/a11y.js.map +1 -0
- package/dist/adapters-CxHOtWog.d.ts +16 -0
- package/dist/bench.d.ts +179 -0
- package/dist/bench.js +1480 -0
- package/dist/bench.js.map +1 -0
- package/dist/config.js.map +1 -1
- package/dist/drive.d.ts +82 -0
- package/dist/drive.js +141 -0
- package/dist/drive.js.map +1 -0
- package/dist/index.js +6442 -882
- package/dist/index.js.map +1 -1
- package/dist/pentest.d.ts +39 -0
- package/dist/pentest.js +32 -0
- package/dist/pentest.js.map +1 -0
- package/dist/screenshots.d.ts +63 -0
- package/dist/screenshots.js +104 -0
- package/dist/screenshots.js.map +1 -0
- package/dist/style.d.ts +92 -0
- package/dist/style.js +268 -0
- package/dist/style.js.map +1 -0
- package/package.json +29 -1
package/README.md
CHANGED
|
@@ -1,42 +1,70 @@
|
|
|
1
1
|
# @cellajs/cli
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
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
|
|
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
|
-
##
|
|
20
|
+
## Commands
|
|
17
21
|
|
|
18
|
-
|
|
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 [
|
|
55
|
+
pnpm cella [command] [options]
|
|
34
56
|
```
|
|
35
57
|
|
|
36
|
-
Per-
|
|
58
|
+
Per-command help: `pnpm cella <command> --help`.
|
|
37
59
|
|
|
38
|
-
|
|
|
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 };
|