@abide/abide 0.109.0 → 0.110.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/docs/abide.md CHANGED
@@ -305,20 +305,21 @@ A file's path below `src` is its address. Nothing else names a route.
305
305
  | `src/contacts/index.abide` | `/contacts` |
306
306
  | `src/contacts/listContacts.ts` | `GET /contacts/listContacts`, the handler its default export declares |
307
307
  | `src/contacts/lib/ContactList.abide` | no address: a component the pages import |
308
- | `src/contacts/tests/ContactList.test.ts` | no address: what `bun test` runs |
308
+ | `src/contacts/lib/ContactList.test.ts` | no address: what `bun test` runs |
309
309
 
310
- Code that is not an address goes in a directory that never is. The directory also decides where the code runs:
310
+ Code that is not an address goes in a directory that never is, and a file's name decides where it runs:
311
311
 
312
- | Directory | What goes in it | Where it runs |
312
+ | Path | What goes in it | Where it runs |
313
313
  | --- | --- | --- |
314
- | `src/**` | the app | both sides, unless its directory says otherwise |
314
+ | `src/**` | the app | both sides, unless its name says otherwise |
315
315
  | `src/**/` | pages, layouts and handler files, routed by their path | a page on both sides; a handler on the server, with a call in its place in the browser |
316
316
  | `**/lib/**` | components and modules | wherever they are imported |
317
- | `**/server/**` | the database, secrets, anything the browser never loads | the server alone; a page importing it is a compile error |
318
- | `**/tests/**` | tests | `bun test`, never served |
317
+ | `**/lib/**/<name>.server.<extension>` | the database, secrets, anything the browser never loads | the server alone; a page importing it is a compile error |
318
+ | `**/<name>.test.ts` | tests, beside what they test | `bun test`, never served |
319
+ | `**/<name>.d.ts` | types, beside what they type | nowhere; the checker reads them |
319
320
  | `**/public/**` | files answered as they are | nowhere; each is sent at its path |
320
321
 
321
- Any other file beside the pages fails the build, so listing a directory lists its routes. The CRM keeps its contacts in `src/contacts/server/CONTACTS.ts`, and only the handler reads it.
322
+ Any other file beside the pages fails the build, so listing a directory lists its routes. The CRM keeps its contacts in `src/contacts/lib/CONTACTS.server.ts`, and only the handler reads it.
322
323
 
323
324
  ## A page reads a handler like a value
324
325
 
@@ -341,7 +342,7 @@ const contacts = reactive(() => listContacts())
341
342
 
342
343
  ```ts src/contacts/listContacts.ts
343
344
  import { GET } from '@abide/abide/server'
344
- import { CONTACTS } from './server/CONTACTS.ts'
345
+ import { CONTACTS } from './lib/CONTACTS.server.ts'
345
346
 
346
347
  export default GET(async () => {
347
348
  // A slow database, so the wait is on screen.
@@ -350,7 +351,7 @@ export default GET(async () => {
350
351
  })
351
352
  ```
352
353
 
353
- ```ts src/contacts/tests/ContactList.test.ts
354
+ ```ts src/contacts/lib/ContactList.test.ts
354
355
  import { expect, test } from 'bun:test'
355
356
  import listContacts from '../listContacts.ts'
356
357
 
@@ -401,7 +402,7 @@ async function requestPage() {
401
402
  </ul>
402
403
  ```
403
404
 
404
- ```ts src/contacts/tests/PageTiming.test.ts
405
+ ```ts src/contacts/lib/PageTiming.test.ts
405
406
  import { expect, test } from 'bun:test'
406
407
  import { createApp } from '@abide/abide/server'
407
408
 
@@ -440,11 +441,11 @@ let starred = reactive(false)
440
441
  </div>
441
442
  ```
442
443
 
443
- ```ts src/contacts/tests/StarButton.test.ts
444
+ ```ts src/contacts/lib/StarButton.test.ts
444
445
  import { expect, test } from 'bun:test'
445
446
  import { flushSync } from '@abide/abide'
446
447
  import { mount } from '@abide/harness/abide'
447
- import StarButton from '../lib/StarButton.abide'
448
+ import StarButton from './StarButton.abide'
448
449
 
449
450
  test('the click writes starred and the button follows', async () => {
450
451
  using _button = await mount(StarButton({}))
@@ -492,11 +493,11 @@ let saved = reactive<string>()
492
493
  </div>
493
494
  ```
494
495
 
495
- ```ts src/contacts/tests/ContactNote.test.ts
496
+ ```ts src/contacts/lib/ContactNote.test.ts
496
497
  import { expect, test } from 'bun:test'
497
498
  import { flushSync } from '@abide/abide'
498
499
  import { mount } from '@abide/harness/abide'
499
- import ContactNote from '../lib/ContactNote.abide'
500
+ import ContactNote from './ContactNote.abide'
500
501
 
501
502
  const savedNote = () => document.querySelectorAll('strong')[3]?.textContent
502
503
 
@@ -564,11 +565,11 @@ let email = reactive('ada@example.com')
564
565
  </ul>
565
566
  ```
566
567
 
567
- ```ts src/profile/tests/ContactFields.test.ts
568
+ ```ts src/profile/lib/ContactFields.test.ts
568
569
  import { expect, test } from 'bun:test'
569
570
  import { flushSync } from '@abide/abide'
570
571
  import { mount } from '@abide/harness/abide'
571
- import ContactFields from '../lib/ContactFields.abide'
572
+ import ContactFields from './ContactFields.abide'
572
573
 
573
574
  test('typing writes the value, and the value fills the field', async () => {
574
575
  using _fields = await mount(ContactFields({}))
@@ -627,11 +628,11 @@ let phone = reactive('', {
627
628
  </label>
628
629
  ```
629
630
 
630
- ```ts src/profile/tests/PhoneField.test.ts
631
+ ```ts src/profile/lib/PhoneField.test.ts
631
632
  import { expect, test } from 'bun:test'
632
633
  import { flushSync } from '@abide/abide'
633
634
  import { mount } from '@abide/harness/abide'
634
- import PhoneField from '../lib/PhoneField.abide'
635
+ import PhoneField from './PhoneField.abide'
635
636
 
636
637
  test('the record keeps the digits, and the field shows them back', async () => {
637
638
  using _phone = await mount(PhoneField({}))
@@ -676,11 +677,11 @@ const filled = reactive(() => [name, phone].filter(Boolean).length)
676
677
  </ul>
677
678
  ```
678
679
 
679
- ```ts src/profile/tests/ProfileSummary.test.ts
680
+ ```ts src/profile/lib/ProfileSummary.test.ts
680
681
  import { expect, test } from 'bun:test'
681
682
  import { flushSync } from '@abide/abide'
682
683
  import { measure, mount } from '@abide/harness/abide'
683
- import ProfileSummary from '../lib/ProfileSummary.abide'
684
+ import ProfileSummary from './ProfileSummary.abide'
684
685
 
685
686
  const shown = () =>
686
687
  [...document.querySelectorAll('strong')].map((one) => one.textContent)
@@ -736,11 +737,11 @@ let contact = reactive({
736
737
  </ul>
737
738
  ```
738
739
 
739
- ```ts src/profile/tests/AddressField.test.ts
740
+ ```ts src/profile/lib/AddressField.test.ts
740
741
  import { expect, test } from 'bun:test'
741
742
  import { flushSync } from '@abide/abide'
742
743
  import { mount } from '@abide/harness/abide'
743
- import AddressField from '../lib/AddressField.abide'
744
+ import AddressField from './AddressField.abide'
744
745
 
745
746
  test('a write to the city reaches the record it sits in', async () => {
746
747
  using _address = await mount(AddressField({}))
@@ -798,11 +799,11 @@ let tags = reactive(['investor', 'speaker', 'mentor'])
798
799
  </div>
799
800
  ```
800
801
 
801
- ```ts src/profile/tests/Tags.test.ts
802
+ ```ts src/profile/lib/Tags.test.ts
802
803
  import { expect, test } from 'bun:test'
803
804
  import { flushSync } from '@abide/abide'
804
805
  import { measure, mount } from '@abide/harness/abide'
805
- import Tags from '../lib/Tags.abide'
806
+ import Tags from './Tags.abide'
806
807
 
807
808
  test('removing a tag removes its row and rewrites no other', async () => {
808
809
  using _tags = await mount(Tags({}))
@@ -841,11 +842,11 @@ let followUp = reactive(false)
841
842
  {/if}
842
843
  ```
843
844
 
844
- ```ts src/profile/tests/FollowUp.test.ts
845
+ ```ts src/profile/lib/FollowUp.test.ts
845
846
  import { expect, test } from 'bun:test'
846
847
  import { flushSync } from '@abide/abide'
847
848
  import { mount } from '@abide/harness/abide'
848
- import FollowUp from '../lib/FollowUp.abide'
849
+ import FollowUp from './FollowUp.abide'
849
850
 
850
851
  const dateField = () => document.querySelector('input[type="date"]')
851
852
 
@@ -896,11 +897,11 @@ const { name } = props<{ name: string }>()
896
897
  </span>
897
898
  ```
898
899
 
899
- ```ts src/profile/tests/ContactCard.test.ts
900
+ ```ts src/profile/lib/ContactCard.test.ts
900
901
  import { expect, test } from 'bun:test'
901
902
  import { flushSync } from '@abide/abide'
902
903
  import { measure, mount } from '@abide/harness/abide'
903
- import ContactCard from '../lib/ContactCard.abide'
904
+ import ContactCard from './ContactCard.abide'
904
905
 
905
906
  test('the avatar follows its name prop, and is not rebuilt', async () => {
906
907
  using _card = await mount(ContactCard({}))
@@ -965,10 +966,10 @@ import LeadBadge from './LeadBadge.abide'
965
966
  </style>
966
967
  ```
967
968
 
968
- ```ts src/profile/tests/StageBadges.test.ts
969
+ ```ts src/profile/lib/StageBadges.test.ts
969
970
  import { expect, test } from 'bun:test'
970
971
  import { mount } from '@abide/harness/abide'
971
- import StageBadges from '../lib/StageBadges.abide'
972
+ import StageBadges from './StageBadges.abide'
972
973
 
973
974
  // The scope token: an attribute every rule in a `<style>` requires.
974
975
  const tokensOf = (element: Element) =>
@@ -1034,11 +1035,11 @@ let calls = reactive(0)
1034
1035
  <button onclick={() => (calls += 1)}>Log a call</button></li>
1035
1036
  ```
1036
1037
 
1037
- ```ts src/profile/tests/CallLog.test.ts
1038
+ ```ts src/profile/lib/CallLog.test.ts
1038
1039
  import { expect, test } from 'bun:test'
1039
1040
  import { flushSync } from '@abide/abide'
1040
1041
  import { mount } from '@abide/harness/abide'
1041
- import CallLog from '../lib/CallLog.abide'
1042
+ import CallLog from './CallLog.abide'
1042
1043
 
1043
1044
  const rows = () =>
1044
1045
  [...document.querySelectorAll('li')].map((row) =>
@@ -1119,7 +1120,7 @@ const deal = reactive(() => getDeal({ id }))
1119
1120
  ```ts src/deals/getDeal.ts
1120
1121
  import { refuse } from '@abide/abide'
1121
1122
  import { GET } from '@abide/abide/server'
1122
- import { DEALS } from './server/DEALS.ts'
1123
+ import { DEALS } from './lib/DEALS.server.ts'
1123
1124
 
1124
1125
  /** Answers at /deals/getDeal?id=…; the argument type is the schema. */
1125
1126
  export default GET(
@@ -1132,7 +1133,7 @@ export default GET(
1132
1133
  )
1133
1134
  ```
1134
1135
 
1135
- ```ts src/deals/tests/DealDetail.test.ts
1136
+ ```ts src/deals/lib/DealDetail.test.ts
1136
1137
  import { expect, test } from 'bun:test'
1137
1138
  import getDeal from '../getDeal.ts'
1138
1139
 
@@ -1179,7 +1180,7 @@ const notes = reactive(() => getNotes({ id: 'd1' }))
1179
1180
  </ul>
1180
1181
  ```
1181
1182
 
1182
- ```ts src/deals/tests/DealNotes.test.ts
1183
+ ```ts src/deals/lib/DealNotes.test.ts
1183
1184
  import { expect, test } from 'bun:test'
1184
1185
  import { createApp } from '@abide/abide/server'
1185
1186
 
@@ -1255,7 +1256,7 @@ async function add() {
1255
1256
  ```ts src/deals/createDeal.ts
1256
1257
  import { jsonSchema } from '@abide/abide'
1257
1258
  import { POST } from '@abide/abide/server'
1258
- import { DEALS } from './server/DEALS.ts'
1259
+ import { DEALS } from './lib/DEALS.server.ts'
1259
1260
 
1260
1261
  type NewDeal = { name: string; company: string; amount: number }
1261
1262
 
@@ -1286,7 +1287,7 @@ export default POST(
1286
1287
  )
1287
1288
  ```
1288
1289
 
1289
- ```ts src/deals/tests/NewDeal.test.ts
1290
+ ```ts src/deals/lib/NewDeal.test.ts
1290
1291
  import { expect, test } from 'bun:test'
1291
1292
  import { createApp } from '@abide/abide/server'
1292
1293
 
@@ -1375,7 +1376,7 @@ async function move(stage: string) {
1375
1376
  ```ts src/deals/moveDeal.ts
1376
1377
  import { jsonSchema, refuse } from '@abide/abide'
1377
1378
  import { PATCH } from '@abide/abide/server'
1378
- import { DEALS } from './server/DEALS.ts'
1379
+ import { DEALS } from './lib/DEALS.server.ts'
1379
1380
 
1380
1381
  // A refusal with a name and typed data, which the page can match.
1381
1382
  const dealClosed = refuse.named(
@@ -1398,7 +1399,7 @@ export default PATCH(
1398
1399
  )
1399
1400
  ```
1400
1401
 
1401
- ```ts src/deals/tests/DealStage.test.ts
1402
+ ```ts src/deals/lib/DealStage.test.ts
1402
1403
  import { expect, test } from 'bun:test'
1403
1404
  import moveDeal from '../moveDeal.ts'
1404
1405
 
@@ -1441,7 +1442,7 @@ import exportDeals from '../exportDeals.ts'
1441
1442
 
1442
1443
  ```ts src/deals/exportDeals.ts
1443
1444
  import { GET } from '@abide/abide/server'
1444
- import { DEALS } from './server/DEALS.ts'
1445
+ import { DEALS } from './lib/DEALS.server.ts'
1445
1446
 
1446
1447
  /** A Blob answers with its own type: this one is a CSV file. */
1447
1448
  export default GET(
@@ -1455,7 +1456,7 @@ export default GET(
1455
1456
  )
1456
1457
  ```
1457
1458
 
1458
- ```ts src/deals/tests/ExportDeals.test.ts
1459
+ ```ts src/deals/lib/ExportDeals.test.ts
1459
1460
  import { expect, test } from 'bun:test'
1460
1461
  import exportDeals from '../exportDeals.ts'
1461
1462
 
@@ -1537,7 +1538,7 @@ const total = reactive(() => {
1537
1538
 
1538
1539
  ```ts src/deals/listDeals.ts
1539
1540
  import { GET } from '@abide/abide/server'
1540
- import { DEALS } from './server/DEALS.ts'
1541
+ import { DEALS } from './lib/DEALS.server.ts'
1541
1542
 
1542
1543
  export default GET(
1543
1544
  async () => {
@@ -1549,7 +1550,7 @@ export default GET(
1549
1550
  )
1550
1551
  ```
1551
1552
 
1552
- ```ts src/deals/tests/Pipeline.test.ts
1553
+ ```ts src/deals/lib/Pipeline.test.ts
1553
1554
  import { expect, test } from 'bun:test'
1554
1555
  import { reactive, refresh } from '@abide/abide'
1555
1556
  import listDeals from '../listDeals.ts'
@@ -1656,8 +1657,8 @@ const totals = reactive(() => getStage({ stage }))
1656
1657
 
1657
1658
  ```ts src/pipeline/getStage.ts
1658
1659
  import { GET } from '@abide/abide/server'
1659
- import { DEALS } from './server/DEALS.ts'
1660
- import { LOADS } from './server/LOADS.ts'
1660
+ import { DEALS } from './lib/DEALS.server.ts'
1661
+ import { LOADS } from './lib/LOADS.server.ts'
1661
1662
 
1662
1663
  /** Takes args, so it is keyed: one entry per stage. */
1663
1664
  export default GET(
@@ -1678,12 +1679,12 @@ export default GET(
1678
1679
  )
1679
1680
  ```
1680
1681
 
1681
- ```ts src/pipeline/tests/StageTiles.test.ts
1682
+ ```ts src/pipeline/lib/StageTiles.test.ts
1682
1683
  import { expect, test } from 'bun:test'
1683
1684
  import { mount } from '@abide/harness/abide'
1684
1685
  import getStage from '../getStage.ts'
1685
- import StageTiles from '../lib/StageTiles.abide'
1686
- import { LOADS } from '../server/LOADS.ts'
1686
+ import StageTiles from './StageTiles.abide'
1687
+ import { LOADS } from './LOADS.server.ts'
1687
1688
 
1688
1689
  function pick(index: number, stage: string): void {
1689
1690
  const select = document.querySelectorAll('select')[index]
@@ -1747,8 +1748,8 @@ const reloading = reactive(() => forecast.busy() && forecast.ready())
1747
1748
 
1748
1749
  ```ts src/pipeline/getForecast.ts
1749
1750
  import { GET } from '@abide/abide/server'
1750
- import { DEALS } from './server/DEALS.ts'
1751
- import { LOADS } from './server/LOADS.ts'
1751
+ import { DEALS } from './lib/DEALS.server.ts'
1752
+ import { LOADS } from './lib/LOADS.server.ts'
1752
1753
 
1753
1754
  // How likely a deal in each stage is to close.
1754
1755
  const ODDS: Record<string, number> = {
@@ -1773,12 +1774,12 @@ export default GET(
1773
1774
  )
1774
1775
  ```
1775
1776
 
1776
- ```ts src/pipeline/tests/Forecast.test.ts
1777
+ ```ts src/pipeline/lib/Forecast.test.ts
1777
1778
  import { expect, test } from 'bun:test'
1778
1779
  import { flushSync } from '@abide/abide'
1779
1780
  import { mount } from '@abide/harness/abide'
1780
- import Forecast from '../lib/Forecast.abide'
1781
- import { LOADS } from '../server/LOADS.ts'
1781
+ import Forecast from './Forecast.abide'
1782
+ import { LOADS } from './LOADS.server.ts'
1782
1783
 
1783
1784
  function press(label: string): void {
1784
1785
  for (const button of document.querySelectorAll('button'))
@@ -1868,7 +1869,7 @@ const totals = reactive(() => getStage({ stage }))
1868
1869
 
1869
1870
  ```ts src/pipeline/winDeal.ts
1870
1871
  import { POST } from '@abide/abide/server'
1871
- import { DEALS } from './server/DEALS.ts'
1872
+ import { DEALS } from './lib/DEALS.server.ts'
1872
1873
 
1873
1874
  /** Moves the first open lead to won, and answers with its id. */
1874
1875
  export default POST(
@@ -1881,12 +1882,12 @@ export default POST(
1881
1882
  )
1882
1883
  ```
1883
1884
 
1884
- ```ts src/pipeline/tests/StageBoard.test.ts
1885
+ ```ts src/pipeline/lib/StageBoard.test.ts
1885
1886
  import { expect, test } from 'bun:test'
1886
1887
  import { mount } from '@abide/harness/abide'
1887
- import { STAGES } from '../lib/STAGES.ts'
1888
- import StageBoard from '../lib/StageBoard.abide'
1889
- import { LOADS } from '../server/LOADS.ts'
1888
+ import { STAGES } from './STAGES.ts'
1889
+ import StageBoard from './StageBoard.abide'
1890
+ import { LOADS } from './LOADS.server.ts'
1890
1891
 
1891
1892
  function counted(): number[] {
1892
1893
  return STAGES.map((stage) => LOADS[stage] ?? 0)
@@ -1944,8 +1945,8 @@ const found = reactive(() => searchDeals({ q }), { debounce: 300 })
1944
1945
 
1945
1946
  ```ts src/pipeline/searchDeals.ts
1946
1947
  import { GET } from '@abide/abide/server'
1947
- import { DEALS } from './server/DEALS.ts'
1948
- import { LOADS } from './server/LOADS.ts'
1948
+ import { DEALS } from './lib/DEALS.server.ts'
1949
+ import { LOADS } from './lib/LOADS.server.ts'
1949
1950
 
1950
1951
  /** The deals whose name holds the query, ignoring case. */
1951
1952
  export default GET(
@@ -1963,11 +1964,11 @@ export default GET(
1963
1964
  )
1964
1965
  ```
1965
1966
 
1966
- ```ts src/pipeline/tests/DealSearch.test.ts
1967
+ ```ts src/pipeline/lib/DealSearch.test.ts
1967
1968
  import { expect, test } from 'bun:test'
1968
1969
  import { mount } from '@abide/harness/abide'
1969
- import DealSearch from '../lib/DealSearch.abide'
1970
- import { LOADS } from '../server/LOADS.ts'
1970
+ import DealSearch from './DealSearch.abide'
1971
+ import { LOADS } from './LOADS.server.ts'
1971
1972
 
1972
1973
  function type(text: string): void {
1973
1974
  const input = document.querySelector('input')
@@ -2048,7 +2049,7 @@ function applyNextUpdate() {
2048
2049
 
2049
2050
  ```ts src/pipeline/listDeals.ts
2050
2051
  import { GET } from '@abide/abide/server'
2051
- import { DEALS } from './server/DEALS.ts'
2052
+ import { DEALS } from './lib/DEALS.server.ts'
2052
2053
 
2053
2054
  /** Every open deal, as a list. */
2054
2055
  export default GET(
@@ -2061,11 +2062,11 @@ export default GET(
2061
2062
  )
2062
2063
  ```
2063
2064
 
2064
- ```ts src/pipeline/tests/DealFeed.test.ts
2065
+ ```ts src/pipeline/lib/DealFeed.test.ts
2065
2066
  import { expect, test } from 'bun:test'
2066
2067
  import { flushSync } from '@abide/abide'
2067
2068
  import { measure, mount } from '@abide/harness/abide'
2068
- import DealFeed from '../lib/DealFeed.abide'
2069
+ import DealFeed from './DealFeed.abide'
2069
2070
 
2070
2071
  test('an update rewrites one amount and builds nothing', async () => {
2071
2072
  using _feed = await mount(DealFeed({}))
@@ -2151,11 +2152,11 @@ const deal = reactive(() =>
2151
2152
  </ul>
2152
2153
  ```
2153
2154
 
2154
- ```ts src/pipeline/tests/DealPicker.test.ts
2155
+ ```ts src/pipeline/lib/DealPicker.test.ts
2155
2156
  import { expect, test } from 'bun:test'
2156
2157
  import { flushSync } from '@abide/abide'
2157
2158
  import { mount } from '@abide/harness/abide'
2158
- import DealPicker from '../lib/DealPicker.abide'
2159
+ import DealPicker from './DealPicker.abide'
2159
2160
 
2160
2161
  test('picking a deal in the list shows it in the detail panel', async () => {
2161
2162
  using _picker = await mount(DealPicker({}))
@@ -2213,8 +2214,8 @@ watch(() => {
2213
2214
 
2214
2215
  ```ts src/pipeline/saveNote.ts
2215
2216
  import { POST } from '@abide/abide/server'
2216
- import { LOADS } from './server/LOADS.ts'
2217
- import { NOTES } from './server/NOTES.ts'
2217
+ import { LOADS } from './lib/LOADS.server.ts'
2218
+ import { NOTES } from './lib/NOTES.server.ts'
2218
2219
 
2219
2220
  /** Saves the note on a deal. */
2220
2221
  export default POST(
@@ -2226,12 +2227,12 @@ export default POST(
2226
2227
  )
2227
2228
  ```
2228
2229
 
2229
- ```ts src/pipeline/tests/DealNote.test.ts
2230
+ ```ts src/pipeline/lib/DealNote.test.ts
2230
2231
  import { expect, test } from 'bun:test'
2231
2232
  import { mount } from '@abide/harness/abide'
2232
- import DealNote from '../lib/DealNote.abide'
2233
- import { LOADS } from '../server/LOADS.ts'
2234
- import { NOTES } from '../server/NOTES.ts'
2233
+ import DealNote from './DealNote.abide'
2234
+ import { LOADS } from './LOADS.server.ts'
2235
+ import { NOTES } from './NOTES.server.ts'
2235
2236
 
2236
2237
  test('a burst of typing is saved once, after it stops', async () => {
2237
2238
  using _note = await mount(DealNote({}))
@@ -2323,7 +2324,7 @@ export function preferenceStore(name: string): Store<string> {
2323
2324
 
2324
2325
  ```ts src/pipeline/getPreference.ts
2325
2326
  import { GET } from '@abide/abide/server'
2326
- import { PREFERENCES } from './server/PREFERENCES.ts'
2327
+ import { PREFERENCES } from './lib/PREFERENCES.server.ts'
2327
2328
 
2328
2329
  /** A saved preference, or `null` where none was saved. */
2329
2330
  export default GET(
@@ -2338,7 +2339,7 @@ export default GET(
2338
2339
 
2339
2340
  ```ts src/pipeline/setPreference.ts
2340
2341
  import { POST } from '@abide/abide/server'
2341
- import { PREFERENCES } from './server/PREFERENCES.ts'
2342
+ import { PREFERENCES } from './lib/PREFERENCES.server.ts'
2342
2343
 
2343
2344
  /** Saves a preference. */
2344
2345
  export default POST(
@@ -2349,12 +2350,12 @@ export default POST(
2349
2350
  )
2350
2351
  ```
2351
2352
 
2352
- ```ts src/pipeline/tests/PipelineView.test.ts
2353
+ ```ts src/pipeline/lib/PipelineView.test.ts
2353
2354
  import { expect, test } from 'bun:test'
2354
2355
  import { flushSync } from '@abide/abide'
2355
2356
  import { mount } from '@abide/harness/abide'
2356
- import PipelineView from '../lib/PipelineView.abide'
2357
- import { PREFERENCES } from '../server/PREFERENCES.ts'
2357
+ import PipelineView from './PipelineView.abide'
2358
+ import { PREFERENCES } from './PREFERENCES.server.ts'
2358
2359
 
2359
2360
  function chosen(): string | undefined {
2360
2361
  const tab = document.querySelector('[aria-selected="true"]')
@@ -2458,11 +2459,11 @@ const last = reactive(() => activity.at(-1))
2458
2459
  </ul>
2459
2460
  ```
2460
2461
 
2461
- ```ts src/activity/tests/QuickLog.test.ts
2462
+ ```ts src/activity/lib/QuickLog.test.ts
2462
2463
  import { expect, test } from 'bun:test'
2463
2464
  import { flushSync } from '@abide/abide'
2464
2465
  import { mount } from '@abide/harness/abide'
2465
- import QuickLog from '../lib/QuickLog.abide'
2466
+ import QuickLog from './QuickLog.abide'
2466
2467
 
2467
2468
  test('a reader the logger never names follows what it logs', async () => {
2468
2469
  using _log = await mount(QuickLog({}))
@@ -2524,11 +2525,11 @@ export function logActivity(contact: string, kind: string): void {
2524
2525
  }
2525
2526
  ```
2526
2527
 
2527
- ```ts src/activity/tests/RecentActivity.test.ts
2528
+ ```ts src/activity/lib/RecentActivity.test.ts
2528
2529
  import { expect, test } from 'bun:test'
2529
2530
  import { flushSync } from '@abide/abide'
2530
2531
  import { mount } from '@abide/harness/abide'
2531
- import RecentActivity from '../lib/RecentActivity.abide'
2532
+ import RecentActivity from './RecentActivity.abide'
2532
2533
 
2533
2534
  test('logging past three adds one entry and drops the oldest', async () => {
2534
2535
  using _recent = await mount(RecentActivity({}))
@@ -2620,7 +2621,7 @@ export default POST(
2620
2621
  )
2621
2622
  ```
2622
2623
 
2623
- ```ts src/activity/tests/AccountFeed.test.ts
2624
+ ```ts src/activity/lib/AccountFeed.test.ts
2624
2625
  import { expect, test } from 'bun:test'
2625
2626
  import { createApp } from '@abide/abide/server'
2626
2627
 
@@ -2702,7 +2703,7 @@ export default GET(
2702
2703
  )
2703
2704
  ```
2704
2705
 
2705
- ```ts src/activity/tests/ActivityHistory.test.ts
2706
+ ```ts src/activity/lib/ActivityHistory.test.ts
2706
2707
  import { expect, test } from 'bun:test'
2707
2708
  import olderActivity from '../olderActivity.ts'
2708
2709
 
@@ -2774,7 +2775,7 @@ export default GET(() => bytes(csvOf()), {
2774
2775
  })
2775
2776
  ```
2776
2777
 
2777
- ```ts src/activity/tests/CsvExport.test.ts
2778
+ ```ts src/activity/lib/CsvExport.test.ts
2778
2779
  import { expect, test } from 'bun:test'
2779
2780
  import { createApp } from '@abide/abide/server'
2780
2781
 
@@ -2839,7 +2840,7 @@ export default GET(() => sse(MOVES), {
2839
2840
  })
2840
2841
  ```
2841
2842
 
2842
- ```ts src/activity/tests/Wallboard.test.ts
2843
+ ```ts src/activity/lib/Wallboard.test.ts
2843
2844
  import { expect, test } from 'bun:test'
2844
2845
  import { createApp } from '@abide/abide/server'
2845
2846
 
@@ -2963,7 +2964,7 @@ const company = reactive(() => getCompany({ id: route.params.id }))
2963
2964
  </style>
2964
2965
  ```
2965
2966
 
2966
- ```ts src/companies/tests/CompanyPage.test.ts
2967
+ ```ts src/companies/CompanyPage.test.ts
2967
2968
  import { expect, test } from 'bun:test'
2968
2969
  import { createApp } from '@abide/abide/server'
2969
2970
 
@@ -3004,7 +3005,7 @@ import { url } from '@abide/abide'
3004
3005
  ```abide src/companies/layout.abide
3005
3006
  <script server>
3006
3007
  import { onError } from '@abide/abide/server'
3007
- import { tracker } from './server/tracker.ts'
3008
+ import { tracker } from './lib/tracker.server.ts'
3008
3009
 
3009
3010
  onError((error) => tracker.capture(error))
3010
3011
  </script>
@@ -3025,7 +3026,7 @@ let note = reactive('')
3025
3026
  <slot/>
3026
3027
  ```
3027
3028
 
3028
- ```ts src/companies/tests/Layout.test.ts
3029
+ ```ts src/companies/Layout.test.ts
3029
3030
  import { expect, test } from 'bun:test'
3030
3031
  import { createApp } from '@abide/abide/server'
3031
3032
 
@@ -3093,7 +3094,7 @@ let draft = reactive('')
3093
3094
  </ul>
3094
3095
  ```
3095
3096
 
3096
- ```ts src/companies/tests/CompanyTabs.test.ts
3097
+ ```ts src/companies/lib/CompanyTabs.test.ts
3097
3098
  import { expect, test } from 'bun:test'
3098
3099
  import { createApp } from '@abide/abide/server'
3099
3100
 
@@ -3169,7 +3170,7 @@ const company = reactive(() => getCompany({ id: route.params.id }))
3169
3170
  </style>
3170
3171
  ```
3171
3172
 
3172
- ```ts src/companies/tests/ViewTransition.test.ts
3173
+ ```ts src/companies/ViewTransition.test.ts
3173
3174
  import { expect, test } from 'bun:test'
3174
3175
  import { createApp } from '@abide/abide/server'
3175
3176
 
@@ -3243,7 +3244,7 @@ function followTitle(element: Element) {
3243
3244
  ```abide src/companies/layout.abide
3244
3245
  <script server>
3245
3246
  import { onError } from '@abide/abide/server'
3246
- import { tracker } from './server/tracker.ts'
3247
+ import { tracker } from './lib/tracker.server.ts'
3247
3248
 
3248
3249
  onError((error) => tracker.capture(error))
3249
3250
  </script>
@@ -3288,7 +3289,7 @@ const company = reactive(() => getCompany({ id: route.params.id }))
3288
3289
  </style>
3289
3290
  ```
3290
3291
 
3291
- ```ts src/companies/tests/Head.test.ts
3292
+ ```ts src/companies/Head.test.ts
3292
3293
  import { expect, test } from 'bun:test'
3293
3294
  import { createApp } from '@abide/abide/server'
3294
3295
 
@@ -3356,7 +3357,7 @@ const company = reactive(() => getCompany({ id: route.params.id }))
3356
3357
 
3357
3358
  ```ts src/companies/getCompany.ts
3358
3359
  import { GET, notFound } from '@abide/abide/server'
3359
- import { COMPANIES } from './server/COMPANIES.ts'
3360
+ import { COMPANIES } from './lib/COMPANIES.server.ts'
3360
3361
 
3361
3362
  /** Answers at /companies/getCompany; a company's page reads it. */
3362
3363
  export default GET(
@@ -3372,7 +3373,7 @@ export default GET(
3372
3373
  )
3373
3374
  ```
3374
3375
 
3375
- ```ts src/companies/tests/Streaming.test.ts
3376
+ ```ts src/companies/Streaming.test.ts
3376
3377
  import { expect, test } from 'bun:test'
3377
3378
  import { createApp } from '@abide/abide/server'
3378
3379
 
@@ -3431,7 +3432,7 @@ const { failure } = props<{ failure: Failed<string, unknown> }>()
3431
3432
 
3432
3433
  ```ts src/companies/getCompany.ts
3433
3434
  import { GET, notFound } from '@abide/abide/server'
3434
- import { COMPANIES } from './server/COMPANIES.ts'
3435
+ import { COMPANIES } from './lib/COMPANIES.server.ts'
3435
3436
 
3436
3437
  /** Answers at /companies/getCompany; a company's page reads it. */
3437
3438
  export default GET(
@@ -3447,7 +3448,7 @@ export default GET(
3447
3448
  )
3448
3449
  ```
3449
3450
 
3450
- ```ts src/companies/tests/ErrorPage.test.ts
3451
+ ```ts src/companies/ErrorPage.test.ts
3451
3452
  import { expect, test } from 'bun:test'
3452
3453
  import { createApp } from '@abide/abide/server'
3453
3454
 
@@ -3514,7 +3515,7 @@ export default POST(
3514
3515
  ```abide src/companies/layout.abide
3515
3516
  <script server>
3516
3517
  import { onError } from '@abide/abide/server'
3517
- import { tracker } from './server/tracker.ts'
3518
+ import { tracker } from './lib/tracker.server.ts'
3518
3519
 
3519
3520
  onError((error) => tracker.capture(error))
3520
3521
  </script>
@@ -3535,7 +3536,7 @@ let note = reactive('')
3535
3536
  <slot/>
3536
3537
  ```
3537
3538
 
3538
- ```ts src/companies/tests/ExportButton.test.ts
3539
+ ```ts src/companies/lib/ExportButton.test.ts
3539
3540
  import { expect, test } from 'bun:test'
3540
3541
  import { createApp } from '@abide/abide/server'
3541
3542
 
@@ -3576,7 +3577,7 @@ import previewCompany from '../previewCompany.ts'
3576
3577
  ```ts src/companies/previewCompany.ts
3577
3578
  import { GET, notFound, page, render } from '@abide/abide/server'
3578
3579
  import CompanySheet from './lib/CompanySheet.abide'
3579
- import { COMPANIES } from './server/COMPANIES.ts'
3580
+ import { COMPANIES } from './lib/COMPANIES.server.ts'
3580
3581
 
3581
3582
  /** Answers at /companies/previewCompany: an account sheet, as its own page. */
3582
3583
  export default GET(
@@ -3610,7 +3611,7 @@ const { company } = props<{
3610
3611
  </html>
3611
3612
  ```
3612
3613
 
3613
- ```ts src/companies/tests/SheetPreview.test.ts
3614
+ ```ts src/companies/lib/SheetPreview.test.ts
3614
3615
  import { expect, test } from 'bun:test'
3615
3616
  import { createApp } from '@abide/abide/server'
3616
3617
 
@@ -3686,7 +3687,7 @@ let email = reactive('maya@crm.example')
3686
3687
  ```ts src/account/signIn.ts
3687
3688
  import { principal, refuse } from '@abide/abide'
3688
3689
  import { onPrincipal, POST } from '@abide/abide/server'
3689
- import { TEAM } from './server/TEAM.ts'
3690
+ import { TEAM } from './lib/TEAM.server.ts'
3690
3691
 
3691
3692
  /** Seals the member's id into the cookie, and nothing else. */
3692
3693
  export default POST(
@@ -3713,7 +3714,7 @@ import { POST } from '@abide/abide/server'
3713
3714
  export default POST(() => principal.clear(), { description: 'Signs out.' })
3714
3715
  ```
3715
3716
 
3716
- ```ts src/account/tests/SignIn.test.ts
3717
+ ```ts src/account/lib/SignIn.test.ts
3717
3718
  import { expect, test } from 'bun:test'
3718
3719
  import { principal } from '@abide/abide'
3719
3720
  import { createApp } from '@abide/abide/server'
@@ -3783,7 +3784,7 @@ import SignInAs from './SignInAs.abide'
3783
3784
  ```ts src/account/signIn.ts
3784
3785
  import { principal, refuse } from '@abide/abide'
3785
3786
  import { onPrincipal, POST } from '@abide/abide/server'
3786
- import { TEAM } from './server/TEAM.ts'
3787
+ import { TEAM } from './lib/TEAM.server.ts'
3787
3788
 
3788
3789
  /** Seals the member's id into the cookie, and nothing else. */
3789
3790
  export default POST(
@@ -3803,7 +3804,7 @@ onPrincipal((claims) => {
3803
3804
  })
3804
3805
  ```
3805
3806
 
3806
- ```ts src/account/tests/Badge.test.ts
3807
+ ```ts src/account/lib/Badge.test.ts
3807
3808
  import { expect, test } from 'bun:test'
3808
3809
  import { principal } from '@abide/abide'
3809
3810
  import { createApp } from '@abide/abide/server'
@@ -3830,7 +3831,7 @@ test('the cookie holds an id, and the request reads the member', async () => {
3830
3831
 
3831
3832
  `onPrincipal` is handed the claims and returns your half of the caller. It runs on the server, at most once per request, where the team records are. So the cookie stays small, and a rename shows on the next request rather than at the next sign-in. Filed in any handler file or `.abide`, it resolves the caller for every route.
3832
3833
 
3833
- What it returns is read as `principal.resolved`, typed by `Principal`. Declare your fields on that interface, as the app's `server/TEAM.ts` does beside the team it reads. abide's own three fields are `authenticated`, `expiresAt` and `error`, and they win any collision, so a resolver cannot sign a caller in by returning `authenticated: true`. A resolver that throws fills `error` and leaves the caller signed out.
3834
+ What it returns is read as `principal.resolved`, typed by `Principal`. Declare your fields on that interface, as the app's `lib/TEAM.server.ts` does beside the team it reads. abide's own three fields are `authenticated`, `expiresAt` and `error`, and they win any collision, so a resolver cannot sign a caller in by returning `authenticated: true`. A resolver that throws fills `error` and leaves the caller signed out.
3834
3835
 
3835
3836
  The tabs on this card, the deal-value card and the export card are `lib/SignInAs.abide`, which calls the same `signIn` handler.
3836
3837
 
@@ -3860,7 +3861,7 @@ const density = reactive(() => cookies().get('density') ?? 'comfortable')
3860
3861
  </ul>
3861
3862
  ```
3862
3863
 
3863
- ```ts src/account/tests/Density.test.ts
3864
+ ```ts src/account/lib/Density.test.ts
3864
3865
  import { expect, test } from 'bun:test'
3865
3866
  import { createApp } from '@abide/abide/server'
3866
3867
 
@@ -3929,7 +3930,7 @@ export default GET(
3929
3930
  )
3930
3931
  ```
3931
3932
 
3932
- ```ts src/account/tests/ContactCard.test.ts
3933
+ ```ts src/account/lib/ContactCard.test.ts
3933
3934
  import { expect, test } from 'bun:test'
3934
3935
  import { createApp } from '@abide/abide/server'
3935
3936
  import getContact from '../getContact.ts'
@@ -3989,7 +3990,7 @@ async function exportAll() {
3989
3990
  ```ts src/account/exportContacts.ts
3990
3991
  import { principal, refuse } from '@abide/abide'
3991
3992
  import { POST } from '@abide/abide/server'
3992
- import { CONTACTS } from '../contacts/server/CONTACTS.ts'
3993
+ import { CONTACTS } from '../contacts/lib/CONTACTS.server.ts'
3993
3994
 
3994
3995
  export default POST(async () => CONTACTS, {
3995
3996
  description: 'Every contact, for an admin to export.',
@@ -4004,7 +4005,7 @@ export default POST(async () => CONTACTS, {
4004
4005
  })
4005
4006
  ```
4006
4007
 
4007
- ```ts src/account/tests/ExportContacts.test.ts
4008
+ ```ts src/account/lib/ExportContacts.test.ts
4008
4009
  import { expect, test } from 'bun:test'
4009
4010
  import { createApp } from '@abide/abide/server'
4010
4011
  import exportContacts from '../exportContacts.ts'
@@ -4109,7 +4110,7 @@ export default GET(() => ({ account: 'A-1042', balance: '$2,140.00' }), {
4109
4110
  })
4110
4111
  ```
4111
4112
 
4112
- ```ts src/account/tests/PartnerStatement.test.ts
4113
+ ```ts src/account/lib/PartnerStatement.test.ts
4113
4114
  import { expect, test } from 'bun:test'
4114
4115
  import { createApp } from '@abide/abide/server'
4115
4116
 
@@ -4201,7 +4202,7 @@ export default GET(
4201
4202
  )
4202
4203
  ```
4203
4204
 
4204
- ```ts src/account/tests/SalesReport.test.ts
4205
+ ```ts src/account/lib/SalesReport.test.ts
4205
4206
  import { expect, test } from 'bun:test'
4206
4207
  import salesReport from '../salesReport.ts'
4207
4208
 
@@ -4271,7 +4272,7 @@ async function sync() {
4271
4272
  ```ts src/account/syncContact.ts
4272
4273
  import { refuse } from '@abide/abide'
4273
4274
  import { POST, reachable } from '@abide/abide/server'
4274
- import { billingOrigin } from './server/billing.ts'
4275
+ import { billingOrigin } from './lib/billing.server.ts'
4275
4276
 
4276
4277
  /** Refuses at once while billing is down, rather than waiting on every call. */
4277
4278
  export default POST(
@@ -4291,7 +4292,7 @@ export default POST(
4291
4292
  )
4292
4293
  ```
4293
4294
 
4294
- ```ts src/account/tests/SyncContact.test.ts
4295
+ ```ts src/account/lib/SyncContact.test.ts
4295
4296
  import { expect, test } from 'bun:test'
4296
4297
  import { reachable } from '@abide/abide/server'
4297
4298
 
@@ -4370,7 +4371,7 @@ export default POST(
4370
4371
  )
4371
4372
  ```
4372
4373
 
4373
- ```ts src/account/tests/AddContact.test.ts
4374
+ ```ts src/account/lib/AddContact.test.ts
4374
4375
  import { expect, test } from 'bun:test'
4375
4376
  import { createApp } from '@abide/abide/server'
4376
4377
 
@@ -4447,7 +4448,7 @@ middleware(csp())
4447
4448
  </html>
4448
4449
  ```
4449
4450
 
4450
- ```ts src/account/tests/Widget.test.ts
4451
+ ```ts src/account/lib/Widget.test.ts
4451
4452
  import { expect, test } from 'bun:test'
4452
4453
  import { createApp } from '@abide/abide/server'
4453
4454
 
@@ -4528,7 +4529,7 @@ export default GET(() => ({ retries: config().RETRIES }), {
4528
4529
  RETRIES=5
4529
4530
  ```
4530
4531
 
4531
- ```ts src/ops/tests/Retries.test.ts
4532
+ ```ts src/ops/lib/Retries.test.ts
4532
4533
  import { expect, test } from 'bun:test'
4533
4534
  import { createApp } from '@abide/abide/server'
4534
4535
 
@@ -4596,7 +4597,7 @@ const times = reactive(() => getPool())
4596
4597
 
4597
4598
  ```ts src/ops/getPool.ts
4598
4599
  import { GET, onStart } from '@abide/abide/server'
4599
- import { pool } from './server/pool.ts'
4600
+ import { pool } from './lib/pool.server.ts'
4600
4601
 
4601
4602
  // Connect before the app answers anything; note when it came up.
4602
4603
  onStart(async (start) => {
@@ -4611,7 +4612,7 @@ export default GET(() => ({ connectedAt: pool.connectedAt, upAt: pool.upAt }), {
4611
4612
  })
4612
4613
  ```
4613
4614
 
4614
- ```ts src/ops/tests/Pool.test.ts
4615
+ ```ts src/ops/lib/Pool.test.ts
4615
4616
  import { expect, test } from 'bun:test'
4616
4617
  import { createApp } from '@abide/abide/server'
4617
4618
 
@@ -4662,13 +4663,13 @@ const sync = log.named('sync')
4662
4663
  </ul>
4663
4664
  ```
4664
4665
 
4665
- ```ts src/ops/tests/Console.test.ts
4666
+ ```ts src/ops/lib/Console.test.ts
4666
4667
  import { expect, test } from 'bun:test'
4667
4668
  import { flushSync } from '@abide/abide'
4668
4669
  import { createApp } from '@abide/abide/server'
4669
4670
  import { mount } from '@abide/harness/abide'
4670
4671
  import manifest from '../../../package.json' with { type: 'json' }
4671
- import Console from '../lib/Console.abide'
4672
+ import Console from './Console.abide'
4672
4673
 
4673
4674
  test('DEBUG writes the billing line and not the poll', async () => {
4674
4675
  // Booting resolves the app's name, which every namespace starts with.
@@ -4780,7 +4781,7 @@ export const billing = {
4780
4781
  }
4781
4782
  ```
4782
4783
 
4783
- ```ts src/ops/tests/Sync.test.ts
4784
+ ```ts src/ops/lib/Sync.test.ts
4784
4785
  import { expect, test } from 'bun:test'
4785
4786
  import { createApp } from '@abide/abide/server'
4786
4787
 
@@ -4831,7 +4832,7 @@ With `timing`, a span joins `server-timing` under its name, so the name has to b
4831
4832
  ```abide src/ops/lib/Health.abide
4832
4833
  <script server>
4833
4834
  import { onHealth } from '@abide/abide/server'
4834
- import { pool } from '../server/pool.ts'
4835
+ import { pool } from './pool.server.ts'
4835
4836
 
4836
4837
  // Lets a page read `account.database` as a string.
4837
4838
  declare module '@abide/abide' {
@@ -4855,7 +4856,7 @@ const account = health()
4855
4856
  </ul>
4856
4857
  ```
4857
4858
 
4858
- ```ts src/ops/tests/Health.test.ts
4859
+ ```ts src/ops/lib/Health.test.ts
4859
4860
  import { expect, test } from 'bun:test'
4860
4861
  import { createApp } from '@abide/abide/server'
4861
4862
 
@@ -4899,11 +4900,11 @@ import { online } from '@abide/abide'
4899
4900
  </ul>
4900
4901
  ```
4901
4902
 
4902
- ```ts src/ops/tests/OfflineBanner.test.ts
4903
+ ```ts src/ops/lib/OfflineBanner.test.ts
4903
4904
  import { expect, test } from 'bun:test'
4904
4905
  import { flushSync } from '@abide/abide'
4905
4906
  import { mount } from '@abide/harness/abide'
4906
- import OfflineBanner from '../lib/OfflineBanner.abide'
4907
+ import OfflineBanner from './OfflineBanner.abide'
4907
4908
 
4908
4909
  test('the banner shows while the browser is offline, and goes', async () => {
4909
4910
  using _banner = await mount(OfflineBanner({}))
@@ -4990,7 +4991,7 @@ async function readDocument() {
4990
4991
 
4991
4992
  ```ts src/assistant/searchContacts.ts
4992
4993
  import { GET } from '@abide/abide/server'
4993
- import { CONTACTS } from '../contacts/server/CONTACTS.ts'
4994
+ import { CONTACTS } from '../contacts/lib/CONTACTS.server.ts'
4994
4995
 
4995
4996
  /** One handler: an OpenAPI operation, an MCP tool and a command. */
4996
4997
  export default GET(
@@ -5000,7 +5001,7 @@ export default GET(
5000
5001
  )
5001
5002
  ```
5002
5003
 
5003
- ```ts src/assistant/tests/OpenApiOperation.test.ts
5004
+ ```ts src/assistant/lib/OpenApiOperation.test.ts
5004
5005
  import { expect, test } from 'bun:test'
5005
5006
  import { createApp } from '@abide/abide/server'
5006
5007
 
@@ -5089,7 +5090,7 @@ async function callAsAModel() {
5089
5090
 
5090
5091
  ```ts src/assistant/searchContacts.ts
5091
5092
  import { GET } from '@abide/abide/server'
5092
- import { CONTACTS } from '../contacts/server/CONTACTS.ts'
5093
+ import { CONTACTS } from '../contacts/lib/CONTACTS.server.ts'
5093
5094
 
5094
5095
  /** One handler: an OpenAPI operation, an MCP tool and a command. */
5095
5096
  export default GET(
@@ -5099,7 +5100,7 @@ export default GET(
5099
5100
  )
5100
5101
  ```
5101
5102
 
5102
- ```ts src/assistant/tests/ModelToolCall.test.ts
5103
+ ```ts src/assistant/lib/ModelToolCall.test.ts
5103
5104
  import { expect, test } from 'bun:test'
5104
5105
  import { createApp } from '@abide/abide/server'
5105
5106
 
@@ -5179,7 +5180,7 @@ async function check() {
5179
5180
 
5180
5181
  ```ts src/assistant/exportContacts.ts
5181
5182
  import { GET } from '@abide/abide/server'
5182
- import { CONTACTS } from '../contacts/server/CONTACTS.ts'
5183
+ import { CONTACTS } from '../contacts/lib/CONTACTS.server.ts'
5183
5184
 
5184
5185
  // Every contact at once: for the export button, not for a model.
5185
5186
  export default GET(() => CONTACTS, {
@@ -5188,7 +5189,7 @@ export default GET(() => CONTACTS, {
5188
5189
  })
5189
5190
  ```
5190
5191
 
5191
- ```ts src/assistant/tests/WithheldExport.test.ts
5192
+ ```ts src/assistant/lib/WithheldExport.test.ts
5192
5193
  import { expect, test } from 'bun:test'
5193
5194
  import { createApp } from '@abide/abide/server'
5194
5195
 
@@ -5306,7 +5307,7 @@ export default POST(
5306
5307
  )
5307
5308
  ```
5308
5309
 
5309
- ```ts src/assistant/tests/AssistantChat.test.ts
5310
+ ```ts src/assistant/lib/AssistantChat.test.ts
5310
5311
  import { expect, test } from 'bun:test'
5311
5312
  import { createApp } from '@abide/abide/server'
5312
5313
 
@@ -5446,7 +5447,7 @@ export default POST(
5446
5447
  )
5447
5448
  ```
5448
5449
 
5449
- ```ts src/assistant/tests/ClaudeApiRequest.test.ts
5450
+ ```ts src/assistant/lib/ClaudeApiRequest.test.ts
5450
5451
  import { expect, spyOn, test } from 'bun:test'
5451
5452
  import { createApp } from '@abide/abide/server'
5452
5453
 
@@ -5577,7 +5578,7 @@ export default POST(
5577
5578
  )
5578
5579
  ```
5579
5580
 
5580
- ```ts src/assistant/tests/ClaudeCodeBridge.test.ts
5581
+ ```ts src/assistant/lib/ClaudeCodeBridge.test.ts
5581
5582
  import { expect, test } from 'bun:test'
5582
5583
  import { createApp } from '@abide/abide/server'
5583
5584
 
@@ -5703,7 +5704,7 @@ const summary = reactive(() => getSummary())
5703
5704
 
5704
5705
  ```ts src/reports/getSummary.ts
5705
5706
  import { GET } from '@abide/abide/server'
5706
- import { DEALS } from './server/DEALS.ts'
5707
+ import { DEALS } from './lib/DEALS.server.ts'
5707
5708
 
5708
5709
  /** Answers at /reports/getSummary; the summary tiles and their test read it. */
5709
5710
  export default GET(
@@ -5721,7 +5722,7 @@ export default GET(
5721
5722
  )
5722
5723
  ```
5723
5724
 
5724
- ```ts src/reports/tests/PipelineSummary.test.ts
5725
+ ```ts src/reports/lib/PipelineSummary.test.ts
5725
5726
  import { expect, test } from 'bun:test'
5726
5727
  import { createApp } from '@abide/abide/server'
5727
5728
 
@@ -5749,7 +5750,7 @@ test('the summary answers in process as it does on the wire', async () => {
5749
5750
 
5750
5751
  Hold the app with `await using` and it stops when the test ends, a failed one included. An app nobody stops leaves open whatever `onStart` opened, and the suite hangs with no report at all.
5751
5752
 
5752
- A file under `**/tests/**` has no address, so a test sits beside the feature it tests and is never served.
5753
+ A `.test.ts` has no address, so a test sits beside the file it tests and is never served.
5753
5754
 
5754
5755
  ## A test mounts a component and counts what a click did
5755
5756
 
@@ -5798,11 +5799,11 @@ export function closedTotals(days: number) {
5798
5799
  }
5799
5800
  ```
5800
5801
 
5801
- ```ts src/reports/tests/ClosedTiles.test.ts
5802
+ ```ts src/reports/lib/ClosedTiles.test.ts
5802
5803
  import { expect, test } from 'bun:test'
5803
5804
  import { flushSync } from '@abide/abide'
5804
5805
  import { measure, mount } from '@abide/harness/abide'
5805
- import ClosedTiles from '../lib/ClosedTiles.abide'
5806
+ import ClosedTiles from './ClosedTiles.abide'
5806
5807
 
5807
5808
  test('a range switch rewrites two numbers and builds nothing', async () => {
5808
5809
  using _tiles = await mount(ClosedTiles({}))
@@ -5837,6 +5838,8 @@ The DOM under `bun test` comes from one line in `bunfig.toml`, which the scaffol
5837
5838
  ```toml bunfig.toml
5838
5839
  # A DOM to mount into and measure, and `.abide` files as components.
5839
5840
  [test]
5841
+ # Bun's tests are under src; e2e holds the browser's, which playwright runs.
5842
+ root = "src"
5840
5843
  preload = ["@abide/harness/abide/preload"]
5841
5844
  ```
5842
5845
 
@@ -5909,12 +5912,12 @@ export function summarizeStage(stage: string) {
5909
5912
  }
5910
5913
  ```
5911
5914
 
5912
- ```ts src/reports/tests/StageTiles.test.ts
5915
+ ```ts src/reports/lib/StageTiles.test.ts
5913
5916
  import { expect, test } from 'bun:test'
5914
5917
  import { flushSync } from '@abide/abide'
5915
5918
  import { mount } from '@abide/harness/abide'
5916
5919
  import { measure, publishedBy } from '@abide/harness/measure'
5917
- import StageTiles from '../lib/StageTiles.abide'
5920
+ import StageTiles from './StageTiles.abide'
5918
5921
 
5919
5922
  test('three tiles read one summary, worked out once per switch', async () => {
5920
5923
  using _tiles = await mount(StageTiles({}))
@@ -5976,13 +5979,13 @@ export const money = {
5976
5979
  }
5977
5980
  ```
5978
5981
 
5979
- ```ts src/reports/tests/WonDeals.test.ts
5982
+ ```ts src/reports/lib/WonDeals.test.ts
5980
5983
  import { expect, spyOn } from 'bun:test'
5981
5984
  import { flushSync } from '@abide/abide'
5982
5985
  import { mount } from '@abide/harness/abide'
5983
5986
  import { gate } from '@abide/harness/gate'
5984
- import { money } from '../lib/money.ts'
5985
- import WonDeals from '../lib/WonDeals.abide'
5987
+ import { money } from './money.ts'
5988
+ import WonDeals from './WonDeals.abide'
5986
5989
 
5987
5990
  gate(
5988
5991
  'a won deal is formatted without building a formatter',
@@ -6090,11 +6093,11 @@ export function advance(deals: Deal[], index: number): Deal[] {
6090
6093
  }
6091
6094
  ```
6092
6095
 
6093
- ```ts src/reports/tests/Forecast.test.ts
6096
+ ```ts src/reports/lib/Forecast.test.ts
6094
6097
  import { expect, test } from 'bun:test'
6095
6098
  import { flushSync } from '@abide/abide'
6096
6099
  import { mount } from '@abide/harness/abide'
6097
- import Forecast from '../lib/Forecast.abide'
6100
+ import Forecast from './Forecast.abide'
6098
6101
 
6099
6102
  test('advancing a deal moves the forecast by its weighted amount', async () => {
6100
6103
  using _forecast = await mount(Forecast({}))
@@ -6107,10 +6110,10 @@ test('advancing a deal moves the forecast by its weighted amount', async () => {
6107
6110
  })
6108
6111
  ```
6109
6112
 
6110
- ```ts src/reports/tests/Forecast.bench.ts
6113
+ ```ts src/reports/lib/Forecast.bench.ts
6111
6114
  import { reactive } from '@abide/abide'
6112
6115
  import { type Batched, batch } from '@abide/harness/report'
6113
- import { advance, type Deal, forecast } from '../lib/forecast.ts'
6116
+ import { advance, type Deal, forecast } from './forecast.ts'
6114
6117
 
6115
6118
  // The forecast as the card keeps it, over a pipeline of `count` deals.
6116
6119
  function pipeline(count: number): () => void {
@@ -6209,7 +6212,7 @@ export function topFive(by: 'amount' | 'idle') {
6209
6212
  }
6210
6213
  ```
6211
6214
 
6212
- ```ts src/reports/tests/TopDeals.e2e.ts
6215
+ ```ts e2e/TopDeals.test.ts
6213
6216
  import { shares, traceOp } from '@abide/harness/engine'
6214
6217
  import { expect, test } from '@playwright/test'
6215
6218
 
@@ -6286,12 +6289,12 @@ export function dealsOf(count: number): ExportedDeal[] {
6286
6289
  }
6287
6290
  ```
6288
6291
 
6289
- ```ts src/reports/tests/DealsExport.test.ts
6292
+ ```ts src/reports/lib/DealsExport.test.ts
6290
6293
  import { expect, test } from 'bun:test'
6291
6294
  import { render } from '@abide/abide/server'
6292
6295
  import { bytes } from '@abide/harness/server'
6293
- import DealsExport from '../lib/DealsExport.abide'
6294
- import { dealsOf } from '../lib/dealsOf.ts'
6296
+ import DealsExport from './DealsExport.abide'
6297
+ import { dealsOf } from './dealsOf.ts'
6295
6298
 
6296
6299
  const exportOf = async (count: number) =>
6297
6300
  bytes(await render(DealsExport({ deals: dealsOf(count) })))
@@ -8842,9 +8845,10 @@ Configuration: what is written in place rather than imported.
8842
8845
  | Path | Meaning | Rules |
8843
8846
  | --- | --- | --- |
8844
8847
  | `src/**` | The app's source. | [36.10](/rulebook#36.10), [38.25](/rulebook#38.25) |
8845
- | `**/server/**` | Source the browser never loads. | [16.29](/rulebook#16.29), [23.15](/rulebook#23.15), [23.16](/rulebook#23.16), [23.18](/rulebook#23.18), [36.10](/rulebook#36.10) |
8846
- | `**/lib/**` | The app's code that is no address: components and modules, on both sides. | [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) |
8847
- | `**/tests/**` | The app's tests. | [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), [38.109](/rulebook#38.109) |
8848
+ | `**/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) |
8849
+ | `**/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) |
8850
+ | `**/<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) |
8851
+ | `**/<name>.d.ts` | A declaration file: types for the checker, wherever it lies. | [23.22](/rulebook#23.22) |
8848
8852
  | `**/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) |
8849
8853
  | `src/public/favicon.ico` | The file answered at `/favicon.ico`. | [36.11](/rulebook#36.11) |
8850
8854
  | `src/public/robots.txt` | The file answered at `/robots.txt`. | [36.11](/rulebook#36.11) |