@abide/abide 0.108.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/CHANGELOG.md +12 -0
- package/docs/REGISTRY.md +6 -5
- package/docs/RULEBOOK.md +21 -13
- package/docs/abide.md +165 -159
- package/package.json +1 -1
- package/scaffold/AGENTS.md +12 -6
- package/scaffold/src/{tests/getGreeting.test.ts → getGreeting.test.ts} +1 -1
- package/scaffold/src/{tests/index.test.ts → index.test.ts} +1 -1
- package/src/compiler/appLayout.ts +5 -15
- package/src/compiler/compileApp.ts +0 -2
- package/src/compiler/handlersUnder.ts +1 -1
- package/src/compiler/scaffold.ts +5 -5
- package/src/server/abideModules.ts +0 -5
- package/src/server/appTree.ts +38 -34
- package/src/server/claudeCode.ts +17 -5
- package/src/server/clientBundle.ts +2 -2
- package/src/server/fenceServer.ts +25 -31
- package/src/server/placement.ts +27 -51
- package/src/server/serveDev.ts +0 -1
- package/src/shared/RESERVED_DIRECTORIES.ts +7 -20
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/
|
|
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
|
|
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
|
-
|
|
|
312
|
+
| Path | What goes in it | Where it runs |
|
|
313
313
|
| --- | --- | --- |
|
|
314
|
-
| `src/**` | the app | both sides, unless its
|
|
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
|
|
318
|
-
|
|
|
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/
|
|
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 './
|
|
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/
|
|
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/
|
|
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/
|
|
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 '
|
|
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/
|
|
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 '
|
|
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/
|
|
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 '
|
|
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/
|
|
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 '
|
|
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/
|
|
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 '
|
|
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/
|
|
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 '
|
|
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/
|
|
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 '
|
|
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/
|
|
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 '
|
|
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/
|
|
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 '
|
|
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/
|
|
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 '
|
|
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/
|
|
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 '
|
|
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 './
|
|
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/
|
|
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/
|
|
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 './
|
|
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/
|
|
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 './
|
|
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/
|
|
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 './
|
|
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/
|
|
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 './
|
|
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/
|
|
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 './
|
|
1660
|
-
import { LOADS } from './
|
|
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/
|
|
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 '
|
|
1686
|
-
import { LOADS } from '
|
|
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 './
|
|
1751
|
-
import { LOADS } from './
|
|
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/
|
|
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 '
|
|
1781
|
-
import { LOADS } from '
|
|
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 './
|
|
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/
|
|
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 '
|
|
1888
|
-
import StageBoard from '
|
|
1889
|
-
import { LOADS } from '
|
|
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 './
|
|
1948
|
-
import { LOADS } from './
|
|
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/
|
|
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 '
|
|
1970
|
-
import { LOADS } from '
|
|
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 './
|
|
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/
|
|
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 '
|
|
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/
|
|
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 '
|
|
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 './
|
|
2217
|
-
import { NOTES } from './
|
|
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/
|
|
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 '
|
|
2233
|
-
import { LOADS } from '
|
|
2234
|
-
import { NOTES } from '
|
|
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 './
|
|
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 './
|
|
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/
|
|
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 '
|
|
2357
|
-
import { PREFERENCES } from '
|
|
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/
|
|
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 '
|
|
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/
|
|
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 '
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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 './
|
|
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/
|
|
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/
|
|
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/
|
|
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 './
|
|
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/
|
|
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 './
|
|
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/
|
|
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 './
|
|
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/
|
|
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 './
|
|
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/
|
|
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 './
|
|
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/
|
|
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 './
|
|
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/
|
|
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 './
|
|
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/
|
|
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 `
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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 './
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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 './
|
|
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/
|
|
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/
|
|
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 '
|
|
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/
|
|
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 '
|
|
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/
|
|
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/
|
|
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 '
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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
|
|
|
@@ -5633,6 +5634,8 @@ test('unpaired: asking is refused, the bridge shows a command', async () => {
|
|
|
5633
5634
|
bunx @abide/bridge <site> <code> -- --tools WebSearch,WebFetch
|
|
5634
5635
|
```
|
|
5635
5636
|
|
|
5637
|
+
Pass the same `tools` to `claudeCode.bridge` or `claudeCode.bridgeCommand` and the command it mints carries that grant, so a visitor who runs it as shown gives the tools. One minted without them grants none.
|
|
5638
|
+
|
|
5636
5639
|
The visitor pairs once per visit by running a command, which dials out to the site. Asking with nothing paired is refused 409.
|
|
5637
5640
|
|
|
5638
5641
|
| Name | What it does for the CRM |
|
|
@@ -5701,7 +5704,7 @@ const summary = reactive(() => getSummary())
|
|
|
5701
5704
|
|
|
5702
5705
|
```ts src/reports/getSummary.ts
|
|
5703
5706
|
import { GET } from '@abide/abide/server'
|
|
5704
|
-
import { DEALS } from './
|
|
5707
|
+
import { DEALS } from './lib/DEALS.server.ts'
|
|
5705
5708
|
|
|
5706
5709
|
/** Answers at /reports/getSummary; the summary tiles and their test read it. */
|
|
5707
5710
|
export default GET(
|
|
@@ -5719,7 +5722,7 @@ export default GET(
|
|
|
5719
5722
|
)
|
|
5720
5723
|
```
|
|
5721
5724
|
|
|
5722
|
-
```ts src/reports/
|
|
5725
|
+
```ts src/reports/lib/PipelineSummary.test.ts
|
|
5723
5726
|
import { expect, test } from 'bun:test'
|
|
5724
5727
|
import { createApp } from '@abide/abide/server'
|
|
5725
5728
|
|
|
@@ -5747,7 +5750,7 @@ test('the summary answers in process as it does on the wire', async () => {
|
|
|
5747
5750
|
|
|
5748
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.
|
|
5749
5752
|
|
|
5750
|
-
A
|
|
5753
|
+
A `.test.ts` has no address, so a test sits beside the file it tests and is never served.
|
|
5751
5754
|
|
|
5752
5755
|
## A test mounts a component and counts what a click did
|
|
5753
5756
|
|
|
@@ -5796,11 +5799,11 @@ export function closedTotals(days: number) {
|
|
|
5796
5799
|
}
|
|
5797
5800
|
```
|
|
5798
5801
|
|
|
5799
|
-
```ts src/reports/
|
|
5802
|
+
```ts src/reports/lib/ClosedTiles.test.ts
|
|
5800
5803
|
import { expect, test } from 'bun:test'
|
|
5801
5804
|
import { flushSync } from '@abide/abide'
|
|
5802
5805
|
import { measure, mount } from '@abide/harness/abide'
|
|
5803
|
-
import ClosedTiles from '
|
|
5806
|
+
import ClosedTiles from './ClosedTiles.abide'
|
|
5804
5807
|
|
|
5805
5808
|
test('a range switch rewrites two numbers and builds nothing', async () => {
|
|
5806
5809
|
using _tiles = await mount(ClosedTiles({}))
|
|
@@ -5835,6 +5838,8 @@ The DOM under `bun test` comes from one line in `bunfig.toml`, which the scaffol
|
|
|
5835
5838
|
```toml bunfig.toml
|
|
5836
5839
|
# A DOM to mount into and measure, and `.abide` files as components.
|
|
5837
5840
|
[test]
|
|
5841
|
+
# Bun's tests are under src; e2e holds the browser's, which playwright runs.
|
|
5842
|
+
root = "src"
|
|
5838
5843
|
preload = ["@abide/harness/abide/preload"]
|
|
5839
5844
|
```
|
|
5840
5845
|
|
|
@@ -5907,12 +5912,12 @@ export function summarizeStage(stage: string) {
|
|
|
5907
5912
|
}
|
|
5908
5913
|
```
|
|
5909
5914
|
|
|
5910
|
-
```ts src/reports/
|
|
5915
|
+
```ts src/reports/lib/StageTiles.test.ts
|
|
5911
5916
|
import { expect, test } from 'bun:test'
|
|
5912
5917
|
import { flushSync } from '@abide/abide'
|
|
5913
5918
|
import { mount } from '@abide/harness/abide'
|
|
5914
5919
|
import { measure, publishedBy } from '@abide/harness/measure'
|
|
5915
|
-
import StageTiles from '
|
|
5920
|
+
import StageTiles from './StageTiles.abide'
|
|
5916
5921
|
|
|
5917
5922
|
test('three tiles read one summary, worked out once per switch', async () => {
|
|
5918
5923
|
using _tiles = await mount(StageTiles({}))
|
|
@@ -5974,13 +5979,13 @@ export const money = {
|
|
|
5974
5979
|
}
|
|
5975
5980
|
```
|
|
5976
5981
|
|
|
5977
|
-
```ts src/reports/
|
|
5982
|
+
```ts src/reports/lib/WonDeals.test.ts
|
|
5978
5983
|
import { expect, spyOn } from 'bun:test'
|
|
5979
5984
|
import { flushSync } from '@abide/abide'
|
|
5980
5985
|
import { mount } from '@abide/harness/abide'
|
|
5981
5986
|
import { gate } from '@abide/harness/gate'
|
|
5982
|
-
import { money } from '
|
|
5983
|
-
import WonDeals from '
|
|
5987
|
+
import { money } from './money.ts'
|
|
5988
|
+
import WonDeals from './WonDeals.abide'
|
|
5984
5989
|
|
|
5985
5990
|
gate(
|
|
5986
5991
|
'a won deal is formatted without building a formatter',
|
|
@@ -6088,11 +6093,11 @@ export function advance(deals: Deal[], index: number): Deal[] {
|
|
|
6088
6093
|
}
|
|
6089
6094
|
```
|
|
6090
6095
|
|
|
6091
|
-
```ts src/reports/
|
|
6096
|
+
```ts src/reports/lib/Forecast.test.ts
|
|
6092
6097
|
import { expect, test } from 'bun:test'
|
|
6093
6098
|
import { flushSync } from '@abide/abide'
|
|
6094
6099
|
import { mount } from '@abide/harness/abide'
|
|
6095
|
-
import Forecast from '
|
|
6100
|
+
import Forecast from './Forecast.abide'
|
|
6096
6101
|
|
|
6097
6102
|
test('advancing a deal moves the forecast by its weighted amount', async () => {
|
|
6098
6103
|
using _forecast = await mount(Forecast({}))
|
|
@@ -6105,10 +6110,10 @@ test('advancing a deal moves the forecast by its weighted amount', async () => {
|
|
|
6105
6110
|
})
|
|
6106
6111
|
```
|
|
6107
6112
|
|
|
6108
|
-
```ts src/reports/
|
|
6113
|
+
```ts src/reports/lib/Forecast.bench.ts
|
|
6109
6114
|
import { reactive } from '@abide/abide'
|
|
6110
6115
|
import { type Batched, batch } from '@abide/harness/report'
|
|
6111
|
-
import { advance, type Deal, forecast } from '
|
|
6116
|
+
import { advance, type Deal, forecast } from './forecast.ts'
|
|
6112
6117
|
|
|
6113
6118
|
// The forecast as the card keeps it, over a pipeline of `count` deals.
|
|
6114
6119
|
function pipeline(count: number): () => void {
|
|
@@ -6207,7 +6212,7 @@ export function topFive(by: 'amount' | 'idle') {
|
|
|
6207
6212
|
}
|
|
6208
6213
|
```
|
|
6209
6214
|
|
|
6210
|
-
```ts
|
|
6215
|
+
```ts e2e/TopDeals.test.ts
|
|
6211
6216
|
import { shares, traceOp } from '@abide/harness/engine'
|
|
6212
6217
|
import { expect, test } from '@playwright/test'
|
|
6213
6218
|
|
|
@@ -6284,12 +6289,12 @@ export function dealsOf(count: number): ExportedDeal[] {
|
|
|
6284
6289
|
}
|
|
6285
6290
|
```
|
|
6286
6291
|
|
|
6287
|
-
```ts src/reports/
|
|
6292
|
+
```ts src/reports/lib/DealsExport.test.ts
|
|
6288
6293
|
import { expect, test } from 'bun:test'
|
|
6289
6294
|
import { render } from '@abide/abide/server'
|
|
6290
6295
|
import { bytes } from '@abide/harness/server'
|
|
6291
|
-
import DealsExport from '
|
|
6292
|
-
import { dealsOf } from '
|
|
6296
|
+
import DealsExport from './DealsExport.abide'
|
|
6297
|
+
import { dealsOf } from './dealsOf.ts'
|
|
6293
6298
|
|
|
6294
6299
|
const exportOf = async (count: number) =>
|
|
6295
6300
|
bytes(await render(DealsExport({ deals: dealsOf(count) })))
|
|
@@ -7837,8 +7842,8 @@ Generated surfaces: what is written in place rather than imported.
|
|
|
7837
7842
|
| Name | Signature | Meaning | Rules |
|
|
7838
7843
|
| --- | --- | --- | --- |
|
|
7839
7844
|
| `claudeCode` | `(config?: ClaudeCodeConfig) => Provider` | The visitor's own Claude Code, reached through the bridge paired with their browser, as a provider. | [44.5](/rulebook#44.5), [44.13](/rulebook#44.13), [44.17](/rulebook#44.17), [44.18](/rulebook#44.18), [44.19](/rulebook#44.19), [44.24](/rulebook#44.24), [44.25](/rulebook#44.25), [44.28](/rulebook#44.28), [44.31](/rulebook#44.31) |
|
|
7840
|
-
| `claudeCode.bridgeCommand` | `() => { command: string; expires: number }` | The command that pairs a bridge with this browser. | [44.14](/rulebook#44.14) |
|
|
7841
|
-
| `claudeCode.bridge` | `() => AsyncGenerator<BridgeStatus>` | This browser's pairing, as it changes. | [44.20](/rulebook#44.20), [44.22](/rulebook#44.22) |
|
|
7845
|
+
| `claudeCode.bridgeCommand` | `(options?: { tools?: string[] }) => { command: string; expires: number }` | The command that pairs a bridge with this browser, granting the built-in tools it is given. | [44.14](/rulebook#44.14), [44.33](/rulebook#44.33) |
|
|
7846
|
+
| `claudeCode.bridge` | `(options?: { tools?: string[] }) => AsyncGenerator<BridgeStatus>` | This browser's pairing, as it changes. | [44.20](/rulebook#44.20), [44.22](/rulebook#44.22), [44.33](/rulebook#44.33) |
|
|
7842
7847
|
| `/__abide/bridge` | `GET`, `POST` | Where a bridge pairs, and is relayed its turns. | [36.7](/rulebook#36.7), [44.14](/rulebook#44.14), [44.15](/rulebook#44.15), [44.19](/rulebook#44.19), [44.23](/rulebook#44.23), [44.26](/rulebook#44.26), [44.27](/rulebook#44.27), [16.78](/rulebook#16.78) |
|
|
7843
7848
|
|
|
7844
7849
|
## Taught in
|
|
@@ -8840,9 +8845,10 @@ Configuration: what is written in place rather than imported.
|
|
|
8840
8845
|
| Path | Meaning | Rules |
|
|
8841
8846
|
| --- | --- | --- |
|
|
8842
8847
|
| `src/**` | The app's source. | [36.10](/rulebook#36.10), [38.25](/rulebook#38.25) |
|
|
8843
|
-
| `**/
|
|
8844
|
-
| `**/lib
|
|
8845
|
-
|
|
|
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) |
|
|
8846
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) |
|
|
8847
8853
|
| `src/public/favicon.ico` | The file answered at `/favicon.ico`. | [36.11](/rulebook#36.11) |
|
|
8848
8854
|
| `src/public/robots.txt` | The file answered at `/robots.txt`. | [36.11](/rulebook#36.11) |
|