@abide/abide 0.117.0 → 0.117.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @abide/abide
2
2
 
3
+ ## 0.117.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 05ba855: a ceiling naming no bench fails the run, and a bench file sits beside any code it times (RULEBOOK 23.22, 43.32-43.34) ([`d5f3d3c`](https://github.com/briancray/abide/commit/d5f3d3cc6368057f8437559aafc7113abd2b3269))
8
+ - 05ba855: a refusal and the starter's notes say a lib/ or public/ fence may sit in any directory above the file (RULEBOOK 23.22, 23.23, 37.1) ([`d9d95df`](https://github.com/briancray/abide/commit/d9d95df07fb8cb92a295ffc9a316da9b5bc6141e))
9
+ - 9e24653: `runBenches` (`@abide/harness/runner`) and the `harness-bench` command run every `*.bench.ts` under an app's `src`, print each arm's time and the claim between them, and fail the run where an arm goes over its `CEILINGS` entry or a ceiling names no arm (RULEBOOK 43.29-43.33); `abide scaffold` writes a `bench` script that runs it, and the starter's agent notes say to measure with the harness (RULEBOOK 38.124)
10
+
3
11
  ## 0.117.0
4
12
 
5
13
  ### Minor Changes
package/docs/REGISTRY.md CHANGED
@@ -677,6 +677,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
677
677
  | `**/lib/**` | The app's code that is no address: components and modules. | 23.15, 23.16, 23.18, 23.22, 37.1 |
678
678
  | `**/lib/**/<name>.server.<extension>` | A server file: source the browser never loads. | 16.29, 23.23, 34.20, 36.10 |
679
679
  | `**/<name>.test.ts` | A test file: one of the app's tests, wherever it lies. | 23.22, 38.109 |
680
+ | `**/<name>.bench.ts` | A bench file: benches `bun run bench` runs, wherever it lies. | 23.22, 43.29 |
680
681
  | `**/<name>.d.ts` | A declaration file: types for the checker, wherever it lies. | 23.22 |
681
682
  | `**/public/**` | The files answered as authored, at their path below that directory. | 23.15, 23.16, 23.18, 36.11, 36.12, 36.13, 36.14, 36.15, 36.17, 21.12, 21.13, 21.14, 38.18, 38.19 |
682
683
  | `src/public/favicon.ico` | The file answered at `/favicon.ico`. | 36.11 |
@@ -824,13 +825,19 @@ rows a runner prints, and an adapter for an abide app.
824
825
  | `inScope` | `(item: RowItem, scope: string) => boolean` | Whether an item falls in a `--scope`. | 43.26 |
825
826
  | `matchesFilter` | `(item: RowItem, filter: string) => boolean` | Whether an item matches a `--filter`. | 43.26 |
826
827
 
827
- ## `@abide/harness/runner` — runners that print rows
828
+ ## `@abide/harness/runner` — runners that print rows, and the bench runner
828
829
 
829
830
  | Name | Signature | Meaning | Rules |
830
831
  | --- | --- | --- | --- |
831
832
  | `rowsCommand` | `<Item extends RowItem, Row>(items: Item[], run: (selected: Item[], emit: (row: Row) => void) => Promise<void>) => Promise<void>` | A runner's command line: its catalog under `--list`, else the rows of what it selects. | 43.26 |
832
833
  | `testRows` | `(suites: TestSuite[], cwd: string) => Promise<void>` | `rowsCommand` over the test files of `suites`, one `TestRow` per file. | 43.27 |
833
834
  | `TestSuite` | `{ section: string; root: string; runner: 'bun'; pattern?: string } \| { section: string; root: string; runner: 'playwright'; projects: string[]; pattern?: string }` | One directory of test files, listed under one section. | 43.27 |
835
+ | `runBenches` | `(root: string, only?: string) => Promise<{ file: string; name: string; case: string; n: number; floor: number; arms: Record<string, number>; claim: string; over: string[] }[]>` | Every bench under `root`'s `src`, run, with each arm's time per operation and the ceilings it went over. | 43.29, 43.30, 43.31, 43.32, 43.34 |
836
+ | `CEILINGS` | `Record<string, Record<string, number>>` | A bench file's export naming the most each arm of each of its benches may take, in milliseconds. | 43.29, 43.31, 43.32 |
837
+
838
+ | Name | Meaning | Rules |
839
+ | --- | --- | --- |
840
+ | `harness-bench [word]` | The command that runs `runBenches` over the directory it runs in and prints each bench. | 43.33, 43.34 |
834
841
 
835
842
  ## `@abide/harness/abide` — the adapter for an abide app
836
843
 
@@ -849,7 +856,7 @@ rows a runner prints, and an adapter for an abide app.
849
856
 
850
857
  | Command | Meaning | Rules |
851
858
  | --- | --- | --- |
852
- | `abide scaffold <name>` | Writes a starter project. | 38.1, 38.42, 38.103, 38.104, 38.106, 38.112, 38.122 |
859
+ | `abide scaffold <name>` | Writes a starter project. | 38.1, 38.42, 38.103, 38.104, 38.106, 38.112, 38.122, 38.124 |
853
860
  | `abide run <file> [args…]` | Runs a script under the abide runtime. | 38.2, 38.57 |
854
861
  | `abide check [dir…]` | The type-checker over a `.abide` tree. | 38.3, 38.31, 38.32, 38.33, 38.40, 38.105, 38.123 |
855
862
  | `abide routes [--json]` | Every address the app answers, the file answering it, and where each hook is filed. | 38.108, 38.123 |
package/docs/RULEBOOK.md CHANGED
@@ -1549,7 +1549,7 @@ error naming the mount.
1549
1549
  `layout.abide`, an `error.abide` and the root's `launcher.abide`.
1550
1550
 
1551
1551
  23.22 A file of the app's own directly in a directory under no fence that is neither a `.abide`, a
1552
- handler file, a test file nor a declaration file MUST be a build error naming the file and a `lib` directory, save one
1552
+ handler file, a test file, a bench file nor a declaration file MUST be a build error naming the file and a `lib` directory, save one
1553
1553
  beside the `package.json` of a root that holds it, nor a `.ts`.
1554
1554
 
1555
1555
  23.23 A server file of the app's own under no `lib` directory MUST be a build error naming the file
@@ -2784,6 +2784,8 @@ answered in the test's process by the handler the component's server module decl
2784
2784
  is outside the range abide's `engines.bun` names, and the refusal MUST name that bun's version and
2785
2785
  the range.
2786
2786
 
2787
+ 38.124 The project `abide scaffold` writes MUST carry a `bench` script that runs `harness-bench`.
2788
+
2787
2789
  # 39. Head and view transitions
2788
2790
 
2789
2791
  39.1 A `<head>` element in a `.abide` file MUST contribute its children to the document head.
@@ -3198,6 +3200,20 @@ runner wrote no report as `broken` rather than as a result.
3198
3200
  43.28 The DOM `@abide/harness/preload` registers MUST leave the runtime's own `fetch`, `Request`,
3199
3201
  `Response`, `Headers`, `Blob`, `File`, `FormData`, `AbortController` and `AbortSignal` the globals.
3200
3202
 
3203
+ 43.29 `runBenches` MUST run, as one bench each, every function a bench file exports other than
3204
+ `CEILINGS`.
3205
+
3206
+ 43.30 `runBenches` MUST NOT report a bench's A/A control as one of its arms.
3207
+
3208
+ 43.31 `runBenches` MUST report an arm whose time per operation is over its `CEILINGS` entry.
3209
+
3210
+ 43.32 `runBenches` MUST report a `CEILINGS` entry naming an arm its bench does not have, and one
3211
+ naming a bench its file does not export.
3212
+
3213
+ 43.33 `harness-bench` MUST exit non-zero where `runBenches` reports anything under 43.31 or 43.32.
3214
+
3215
+ 43.34 `runBenches` given `only` MUST run only the bench files whose path below `root` holds it.
3216
+
3201
3217
  # 44. The assistant
3202
3218
 
3203
3219
  44.1 `assistant` MUST answer only through a handler that yields it, and MUST NOT mount an address
package/docs/abide.md CHANGED
@@ -196,6 +196,7 @@ at its address with `.md` on the end, and the links below point at those files.
196
196
  - `rowsCommand` — `reference/harness-runner/rowsCommand.md`
197
197
  - `testRows` — `reference/harness-runner/testRows.md`
198
198
  - `TestSuite` — `reference/harness-runner/types/TestSuite.md`
199
+ - `runBenches` — `reference/harness-runner/runBenches.md`
199
200
  - `measure` — `reference/harness-abide/measure.md`
200
201
  - `AbideWork` — `reference/harness-abide/types/AbideWork.md`
201
202
  - CLI — `reference/cli.md`
@@ -6258,6 +6259,9 @@ function pipeline(count: number): () => void {
6258
6259
  }
6259
6260
  }
6260
6261
 
6262
+ // The most an advance may take among 1,000 deals, in milliseconds.
6263
+ export const CEILINGS = { priced: { 'among 1,000': 1 } }
6264
+
6261
6265
  /** Advancing one deal among 100, and among 1,000. */
6262
6266
  export function priced(): Batched {
6263
6267
  return batch({
@@ -6285,8 +6289,13 @@ A millisecond describes the machine it was taken on. A ratio between two sizes o
6285
6289
  | `emulatedDom` | whether this process's DOM is an emulator |
6286
6290
  | `time` | `batch` with `emulated` filled in, refusing to run while the counters are armed |
6287
6291
  | `THRESHOLDS` | a frame and an interaction, the two lines a cost is judged against |
6292
+ | `runBenches` | runs every bench under `src`, and reports each arm over its ceiling |
6293
+ | `CEILINGS` | the most each arm of a bench may take, in milliseconds, by bench and then by arm |
6294
+ | `harness-bench` | the `bench` script: `runBenches` printed, failing where a ceiling is not held |
6295
+
6296
+ `batch` throws on a duration whose op touched an emulated DOM, and on a sample taken in a parallel test worker. That is why the bench builds no component: it times the derived value the component reads. Run the benches with `bun run bench`, never inside `bun test`.
6288
6297
 
6289
- `batch` throws on a duration whose op touched an emulated DOM, and on a sample taken in a parallel test worker. That is why the bench builds no component: it times the derived value the component reads. Run a bench under `bun run`, never inside `bun test`.
6298
+ The `bench` script is `harness-bench`, which hands `runBenches` every `*.bench.ts` under `src`, wherever one sits beside the code it times, and prints each arm's time and the claim between them. A bench file's `CEILINGS` names the most each arm may take, in milliseconds, and the run fails where an arm takes more.
6290
6299
 
6291
6300
  ## Find which layer a click spends its time in
6292
6301
 
@@ -6806,6 +6815,7 @@ Arrive here knowing which name you want. Each page is read off the registry: the
6806
6815
  | [`rowsCommand`](/reference/harness-runner/rowsCommand.md) | `@abide/harness/runner` |
6807
6816
  | [`testRows`](/reference/harness-runner/testRows.md) | `@abide/harness/runner` |
6808
6817
  | [`TestSuite`](/reference/harness-runner/types/TestSuite.md) | `@abide/harness/runner` |
6818
+ | [`runBenches`](/reference/harness-runner/runBenches.md) | `@abide/harness/runner` |
6809
6819
  | [`measure`](/reference/harness-abide/measure.md) | `@abide/harness/abide` |
6810
6820
  | [`AbideWork`](/reference/harness-abide/types/AbideWork.md) | `@abide/harness/abide` |
6811
6821
 
@@ -9133,6 +9143,7 @@ Configuration: what is written in place rather than imported.
9133
9143
  | `**/lib/**` | The app's code that is no address: components and modules. | [23.15](/rulebook#23.15), [23.16](/rulebook#23.16), [23.18](/rulebook#23.18), [23.22](/rulebook#23.22), [37.1](/rulebook#37.1) |
9134
9144
  | `**/lib/**/<name>.server.<extension>` | A server file: source the browser never loads. | [16.29](/rulebook#16.29), [23.23](/rulebook#23.23), [34.20](/rulebook#34.20), [36.10](/rulebook#36.10) |
9135
9145
  | `**/<name>.test.ts` | A test file: one of the app's tests, wherever it lies. | [23.22](/rulebook#23.22), [38.109](/rulebook#38.109) |
9146
+ | `**/<name>.bench.ts` | A bench file: benches `bun run bench` runs, wherever it lies. | [23.22](/rulebook#23.22), [43.29](/rulebook#43.29) |
9136
9147
  | `**/<name>.d.ts` | A declaration file: types for the checker, wherever it lies. | [23.22](/rulebook#23.22) |
9137
9148
  | `**/public/**` | The files answered as authored, at their path below that directory. | [23.15](/rulebook#23.15), [23.16](/rulebook#23.16), [23.18](/rulebook#23.18), [36.11](/rulebook#36.11), [36.12](/rulebook#36.12), [36.13](/rulebook#36.13), [36.14](/rulebook#36.14), [36.15](/rulebook#36.15), [36.17](/rulebook#36.17), [21.12](/rulebook#21.12), [21.13](/rulebook#21.13), [21.14](/rulebook#21.14), [38.18](/rulebook#38.18), [38.19](/rulebook#38.19) |
9138
9149
  | `src/public/favicon.ico` | The file answered at `/favicon.ico`. | [36.11](/rulebook#36.11) |
@@ -9633,6 +9644,12 @@ The measurement harness: what is written in place rather than imported.
9633
9644
  | --- | --- | --- |
9634
9645
  | `preload = ["@abide/harness/preload"]` | The `[test]` preload that registers a DOM with the measure lane's patches installed. | [43.4](/rulebook#43.4), [43.28](/rulebook#43.28) |
9635
9646
 
9647
+ ## `@abide/harness/runner` — runners that print rows, and the bench runner
9648
+
9649
+ | Name | Meaning | Rules |
9650
+ | --- | --- | --- |
9651
+ | `harness-bench [word]` | The command that runs `runBenches` over the directory it runs in and prints each bench. | [43.33](/rulebook#43.33), [43.34](/rulebook#43.34) |
9652
+
9636
9653
  ## `@abide/harness/abide` — the adapter for an abide app
9637
9654
 
9638
9655
  | Name | Meaning | Rules |
@@ -10281,11 +10298,12 @@ The measurement harness: what is written in place rather than imported.
10281
10298
 
10282
10299
  `import { rowsCommand } from '@abide/harness/runner'`
10283
10300
 
10284
- ## `@abide/harness/runner` — runners that print rows
10301
+ ## `@abide/harness/runner` — runners that print rows, and the bench runner
10285
10302
 
10286
10303
  | Name | Signature | Meaning | Rules |
10287
10304
  | --- | --- | --- | --- |
10288
10305
  | `rowsCommand` | `<Item extends RowItem, Row>(items: Item[], run: (selected: Item[], emit: (row: Row) => void) => Promise<void>) => Promise<void>` | A runner's command line: its catalog under `--list`, else the rows of what it selects. | [43.26](/rulebook#43.26) |
10306
+ | `CEILINGS` | `Record<string, Record<string, number>>` | A bench file's export naming the most each arm of each of its benches may take, in milliseconds. | [43.29](/rulebook#43.29), [43.31](/rulebook#43.31), [43.32](/rulebook#43.32) |
10289
10307
 
10290
10308
  ## Taught in
10291
10309
 
@@ -10299,7 +10317,7 @@ The measurement harness: what is written in place rather than imported.
10299
10317
 
10300
10318
  `import { testRows } from '@abide/harness/runner'`
10301
10319
 
10302
- ## `@abide/harness/runner` — runners that print rows
10320
+ ## `@abide/harness/runner` — runners that print rows, and the bench runner
10303
10321
 
10304
10322
  | Name | Signature | Meaning | Rules |
10305
10323
  | --- | --- | --- | --- |
@@ -10317,7 +10335,7 @@ The measurement harness: what is written in place rather than imported.
10317
10335
 
10318
10336
  `import type { TestSuite } from '@abide/harness/runner'`
10319
10337
 
10320
- ## `@abide/harness/runner` — runners that print rows
10338
+ ## `@abide/harness/runner` — runners that print rows, and the bench runner
10321
10339
 
10322
10340
  | Name | Signature | Meaning | Rules |
10323
10341
  | --- | --- | --- | --- |
@@ -10329,6 +10347,24 @@ The measurement harness: what is written in place rather than imported.
10329
10347
 
10330
10348
  ---
10331
10349
 
10350
+ <!-- reference/harness-runner/runBenches.md -->
10351
+
10352
+ # `runBenches`
10353
+
10354
+ `import { runBenches } from '@abide/harness/runner'`
10355
+
10356
+ ## `@abide/harness/runner` — runners that print rows, and the bench runner
10357
+
10358
+ | Name | Signature | Meaning | Rules |
10359
+ | --- | --- | --- | --- |
10360
+ | `runBenches` | `(root: string, only?: string) => Promise<{ file: string; name: string; case: string; n: number; floor: number; arms: Record<string, number>; claim: string; over: string[] }[]>` | Every bench under `root`'s `src`, run, with each arm's time per operation and the ceilings it went over. | [43.29](/rulebook#43.29), [43.30](/rulebook#43.30), [43.31](/rulebook#43.31), [43.32](/rulebook#43.32), [43.34](/rulebook#43.34) |
10361
+
10362
+ ## Taught in
10363
+
10364
+ - [Test, measure, ship](/guide/test-measure-and-ship.md)
10365
+
10366
+ ---
10367
+
10332
10368
  <!-- reference/harness-abide/measure.md -->
10333
10369
 
10334
10370
  # `measure`
@@ -10375,7 +10411,7 @@ CLI: what is written in place rather than imported.
10375
10411
 
10376
10412
  | Command | Meaning | Rules |
10377
10413
  | --- | --- | --- |
10378
- | `abide scaffold <name>` | Writes a starter project. | [38.1](/rulebook#38.1), [38.42](/rulebook#38.42), [38.103](/rulebook#38.103), [38.104](/rulebook#38.104), [38.106](/rulebook#38.106), [38.112](/rulebook#38.112), [38.122](/rulebook#38.122) |
10414
+ | `abide scaffold <name>` | Writes a starter project. | [38.1](/rulebook#38.1), [38.42](/rulebook#38.42), [38.103](/rulebook#38.103), [38.104](/rulebook#38.104), [38.106](/rulebook#38.106), [38.112](/rulebook#38.112), [38.122](/rulebook#38.122), [38.124](/rulebook#38.124) |
10379
10415
  | `abide run <file> [args…]` | Runs a script under the abide runtime. | [38.2](/rulebook#38.2), [38.57](/rulebook#38.57) |
10380
10416
  | `abide check [dir…]` | The type-checker over a `.abide` tree. | [38.3](/rulebook#38.3), [38.31](/rulebook#38.31), [38.32](/rulebook#38.32), [38.33](/rulebook#38.33), [38.40](/rulebook#38.40), [38.105](/rulebook#38.105), [38.123](/rulebook#38.123) |
10381
10417
  | `abide routes [--json]` | Every address the app answers, the file answering it, and where each hook is filed. | [38.108](/rulebook#38.108), [38.123](/rulebook#38.123) |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abide/abide",
3
- "version": "0.117.0",
3
+ "version": "0.117.1",
4
4
  "type": "module",
5
5
  "description": "Isomorphic type-safe framework for reactive async interfaces for humans and machines",
6
6
  "license": "MIT",
@@ -20,8 +20,14 @@ before writing code, rather than guessing from Svelte, Vue or React. It installs
20
20
  Where the read is meant, `// abide-ignore` and the clause the warning cites, on the line above
21
21
  (`// abide-ignore 14.15`), silences that one (RULEBOOK 14.19); a read hoisted out of its place
22
22
  only to quiet a warning is the bug it named.
23
- - `bun run test` — runs `bun test`. `src/getGreeting.test.ts` is the pattern: a test imports a
24
- handler or a `.abide` file and calls it.
23
+ - `bun run test` — runs `bun test`. `src/getGreeting.test.ts` is the pattern for a handler: a test
24
+ imports it and calls it. `src/index.test.ts` is the pattern for a page: `mount` it, then count
25
+ the work a click did with `measure`, both from `@abide/harness/abide` (RULEBOOK 38.119, 43.23).
26
+ - `bun run bench` — runs `harness-bench`: every `*.bench.ts` under `src/`, kept beside the code it
27
+ times. Each export is a function returning `batch()` from `@abide/harness/report`, an op at two
28
+ sizes; a `CEILINGS` export, `{ [bench]: { [arm]: milliseconds } }`, names the most each arm may
29
+ take, and an arm over it, or a ceiling naming no bench or arm, fails the run (RULEBOOK 43.29,
30
+ 43.31, 43.32, 43.33).
25
31
  - `bun run dev` — serves the app on a local port and rebuilds when a file changes.
26
32
  - `bun run build` then `bun run start` — the production build, and the app served from it.
27
33
 
@@ -35,10 +41,15 @@ before writing code, rather than guessing from Svelte, Vue or React. It installs
35
41
  scripts, the CLI and MCP alike.
36
42
  - A `.abide` cannot export. A `<script server>` in one holds that file's private handlers
37
43
  (`const save = POST(…)`), callable by that file alone, and its hooks (`onStart`, `middleware`, …).
38
- - A directory holds addresses, tests, `.d.ts` declarations and two fenced directories alone: `lib/` for components and
39
- modules, and `public/` for files served as they are (`src/blog/public/cover.png` answers
40
- `/blog/cover.png`). Anything else beside the pages is a build error.
41
- - A test is a `.test.ts`, kept beside the file it tests — a page, a handler, or a module in `lib/`.
44
+ - A directory holds addresses, tests, benches, `.d.ts` declarations and two fenced directories alone: `lib/` for components and
45
+ modules, and `public/` for files served as they are. Anything else beside the pages is a build error.
46
+ - Either fence may sit in any directory, at the root or beside the pages that use it.
47
+ `src/lib/` and `src/invoices/lib/` import alike (`#src/…`); the one difference is a lifecycle
48
+ hook a `lib/` file calls, filed at the route of the directory holding that `lib/` (RULEBOOK 37.1).
49
+ A `public/` file answers at the route of the directory holding it: `src/public/logo.png` answers
50
+ `/logo.png`, and `src/blog/public/cover.png` answers `/blog/cover.png`.
51
+ - A test is a `.test.ts`, and a bench a `.bench.ts`, kept beside the file it tests or times — a
52
+ page, a handler, or a module in `lib/` (RULEBOOK 23.22).
42
53
  A browser test (playwright) is a `.test.ts` too, under `e2e/` beside `src/`: `bun test` reads
43
54
  `src/` alone (`root` in `bunfig.toml`), so it never runs one.
44
55
  - A module the browser never loads is named `<name>.server.ts` and lives in `lib/`
@@ -50,6 +61,13 @@ before writing code, rather than guessing from Svelte, Vue or React. It installs
50
61
 
51
62
  ## Habits
52
63
 
64
+ - Measure with `@abide/harness`, not a hand-written timer, bench runner or Chrome DevTools Protocol
65
+ script. Its three lanes: `measure` from `@abide/harness/abide` is the DOM work and wakes an op
66
+ did, under `bun test`; `batch` from `@abide/harness/report` is time, in a bench; `traceOp` and
67
+ `shares` from `@abide/harness/engine` are what Chromium did, from a playwright test. The page
68
+ `guide/test-measure-and-ship.md` in `abide.md`, and "The measurement harness" in `REGISTRY.md`,
69
+ document it.
70
+
53
71
  - Export a `Reactive` itself, never wrapped in an object: an `.abide` file reads and writes it by
54
72
  name (RULEBOOK 31.1), and one reached through a property is left to `x()` and `x.set(v)` (31.7).
55
73
  - Model output and fetched HTML are untrusted. Never hand them to `raw()` (RULEBOOK 32.2)
@@ -62,6 +62,8 @@ function filesFor(name: string): Record<string, string> {
62
62
  routes: 'abide routes',
63
63
  // 38.112 — the starter's own test, under the 38.109 preload.
64
64
  test: 'bun test',
65
+ // 38.124 — every `*.bench.ts` under `src`, timed, and failed over its `CEILINGS`.
66
+ bench: 'harness-bench',
65
67
  },
66
68
  // ONE ALIAS FOR THE SOURCE, and two targets for it, because an extensionless
67
69
  // specifier is how an import is written and only bun probes for the extension:
@@ -74,7 +76,8 @@ function filesFor(name: string): Record<string, string> {
74
76
  dependencies: { '@abide/abide': ABIDE_DEPENDENCY },
75
77
  // `Bun`, `bun:sqlite` and the rest of the runtime a server module reaches for are
76
78
  // typed by these, and `abide check` reports every one untyped without them. The
77
- // harness is what the starter's tests measure the work a click does with (38.119).
79
+ // harness is what the starter's tests measure the work a click does with (38.119), and
80
+ // what `bench` runs (38.124).
78
81
  devDependencies: {
79
82
  '@abide/harness': HARNESS_DEPENDENCY,
80
83
  '@types/bun': 'latest',
@@ -23,6 +23,7 @@ import {
23
23
  DIRECTORY_FILES,
24
24
  isDeclarationFile,
25
25
  isServerFile,
26
+ isBenchFile,
26
27
  isTestFile,
27
28
  placeOf,
28
29
  routeOf,
@@ -111,7 +112,7 @@ function walk(
111
112
  refusals.at(
112
113
  path,
113
114
  new Error(
114
- `abide: ${path} is a server file under no lib directory. Move it into a lib directory beside it. RULEBOOK 23.23.`,
115
+ `abide: ${path} is a server file under no lib directory. Move it into a lib directory, beside it or in any directory above it. RULEBOOK 23.23.`,
115
116
  ),
116
117
  )
117
118
  continue
@@ -119,14 +120,19 @@ function walk(
119
120
  const abide = path.endsWith('.abide')
120
121
  if (abide) files.abide.push(path)
121
122
  if (place === 'address') {
122
- if (isTestFile(path) || isDeclarationFile(path)) continue
123
+ if (
124
+ isTestFile(path) ||
125
+ isBenchFile(path) ||
126
+ isDeclarationFile(path)
127
+ )
128
+ continue
123
129
  if (!abide && !isModule(path)) {
124
130
  if (packaged && relative === '' && !path.endsWith('.ts'))
125
131
  continue
126
132
  refusals.at(
127
133
  path,
128
134
  new Error(
129
- `abide: ${path} is directly in an address directory, which holds pages, layouts, error pages, handler files, test files and declaration files alone. Move it into a lib directory beside it. RULEBOOK 23.22.`,
135
+ `abide: ${path} is directly in an address directory, which holds pages, layouts, error pages, handler files, test files, bench files and declaration files alone. Move it into a lib directory, beside it or in any directory above it. RULEBOOK 23.22.`,
130
136
  ),
131
137
  )
132
138
  continue
@@ -152,9 +158,14 @@ function walk(
152
158
  }
153
159
  }
154
160
 
155
- /** Whether `path` names a module the app runs — neither a declaration nor a test. */
161
+ /** Whether `path` names a module the app runs — neither a declaration, a test nor a bench. */
156
162
  function isModule(path: string): boolean {
157
- return path.endsWith('.ts') && !isDeclarationFile(path) && !isTestFile(path)
163
+ return (
164
+ path.endsWith('.ts') &&
165
+ !isDeclarationFile(path) &&
166
+ !isTestFile(path) &&
167
+ !isBenchFile(path)
168
+ )
158
169
  }
159
170
 
160
171
  /** The refusal of `source` where `route` lies in the framework's own namespace, or `undefined`. 23.17. */
@@ -109,6 +109,11 @@ export function isTestFile(path: string): boolean {
109
109
  return path.endsWith('.test.ts')
110
110
  }
111
111
 
112
+ /** Whether `path` names a bench file, which `bun run bench` reads and the app never runs. */
113
+ export function isBenchFile(path: string): boolean {
114
+ return path.endsWith('.bench.ts')
115
+ }
116
+
112
117
  /** Whether `path` names a declaration file, which the checker reads and nothing runs. */
113
118
  export function isDeclarationFile(path: string): boolean {
114
119
  return path.endsWith('.d.ts')