@abide/abide 0.103.0 → 0.105.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 +29 -0
- package/bin/abide.ts +80 -173
- package/docs/REGISTRY.md +27 -27
- package/docs/RULEBOOK.md +68 -24
- package/docs/abide.md +31 -46
- package/package.json +1 -1
- package/src/compiler/Lowering.ts +15 -5
- package/src/compiler/Refusals.ts +57 -0
- package/src/compiler/TypeSession.ts +46 -89
- package/src/compiler/appLayout.ts +447 -0
- package/src/compiler/appMap.ts +21 -25
- package/src/compiler/checkApp.ts +61 -97
- package/src/compiler/checkedShadow.ts +12 -10
- package/src/compiler/classifyRead.ts +3 -6
- package/src/compiler/compileApp.ts +183 -478
- package/src/compiler/componentGraph.ts +2 -71
- package/src/compiler/declaredExports.ts +79 -60
- package/src/compiler/emitClient.ts +11 -13
- package/src/compiler/emitComponent.ts +21 -6
- package/src/compiler/emitSource.ts +20 -20
- package/src/compiler/handlersUnder.ts +46 -36
- package/src/compiler/hookCalls.ts +152 -0
- package/src/compiler/mountsOf.ts +31 -54
- package/src/compiler/projectShadow.ts +1 -1
- package/src/compiler/readSources.ts +27 -42
- package/src/compiler/readsIn.ts +5 -16
- package/src/compiler/resolverIn.ts +77 -0
- package/src/server/abideModules.ts +152 -146
- package/src/server/answerRequest.ts +18 -4
- package/src/server/answerSocket.ts +136 -76
- package/src/server/answerValue.ts +28 -15
- package/src/server/appTree.ts +59 -73
- package/src/server/claudeApi.ts +2 -5
- package/src/server/clientBundle.ts +198 -0
- package/src/server/coerceArgs.ts +1 -1
- package/src/server/commands.ts +93 -57
- package/src/server/createApp.ts +90 -16
- package/src/server/deferredBuilds.ts +175 -0
- package/src/server/devWorker.ts +107 -319
- package/src/server/faceOf.ts +120 -0
- package/src/server/fenceServer.ts +10 -18
- package/src/server/generatedSurfaces.ts +12 -53
- package/src/server/index.ts +3 -0
- package/src/server/placement.ts +167 -0
- package/src/server/remote.ts +14 -42
- package/src/server/render.ts +25 -17
- package/src/server/renderComponent.ts +9 -5
- package/src/server/requestScope.ts +10 -4
- package/src/server/responseRelay.ts +389 -0
- package/src/server/runCompiled.ts +9 -12
- package/src/server/runDesktop.ts +6 -9
- package/src/server/serveBuild.ts +26 -73
- package/src/server/serveDev.ts +19 -176
- package/src/server/streamEvent.ts +29 -0
- package/src/server/versionOf.ts +1 -2
- package/src/server/withAbideSources.ts +2 -1
- package/src/shared/HandlerRoute.ts +68 -0
- package/src/shared/REFUSAL_KEY.ts +5 -0
- package/src/shared/RESERVED_DIRECTORIES.ts +6 -0
- package/src/shared/answerOf.ts +105 -78
- package/src/shared/filledPath.ts +1 -9
- package/src/shared/framing.ts +127 -128
- package/src/shared/queryText.ts +15 -7
- package/src/shared/reactive/Cursor.ts +69 -124
- package/src/shared/reactive/Reactive.ts +296 -162
- package/src/shared/reactive/callLoad.ts +4 -18
- package/src/shared/reactive/index.ts +11 -8
- package/src/shared/reactive/pairReactive.ts +1 -1
- package/src/shared/reactive/scoped.ts +4 -3
- package/src/shared/reactive/share.ts +11 -1
- package/src/shared/routePattern.ts +0 -110
- package/src/shared/rpc.ts +17 -24
- package/src/shared/rpcRequest.ts +18 -47
- package/src/shared/takeEarlyCall.ts +12 -37
- package/src/shared/url.ts +1 -1
- package/src/ui/Level.ts +276 -0
- package/src/ui/List.ts +17 -17
- package/src/ui/Router.ts +20 -87
- package/src/ui/applyHead.ts +92 -79
- package/src/ui/index.ts +4 -2
- package/src/ui/reconcile.ts +4 -110
- package/src/ui/rows.ts +123 -2
- package/src/ui/wire.ts +12 -27
- package/src/compiler/hooksCalled.ts +0 -35
- package/src/server/clientBuildOptions.ts +0 -54
- package/src/server/refuseAddressCollision.ts +0 -37
- package/src/shared/reactive/chunksOfStream.ts +0 -57
- package/src/ui/MOUNTING_LEVEL.ts +0 -18
package/docs/RULEBOOK.md
CHANGED
|
@@ -106,11 +106,11 @@ second.
|
|
|
106
106
|
|
|
107
107
|
2.4 A failed revalidation MUST NOT cause a read to throw.
|
|
108
108
|
|
|
109
|
-
2.5 `s[Symbol.asyncIterator]` MUST
|
|
110
|
-
|
|
109
|
+
2.5 On a value that is not a streaming producer, `s[Symbol.asyncIterator]` MUST settle as `s.then`
|
|
110
|
+
does, yielding once what it resolves with.
|
|
111
111
|
|
|
112
|
-
2.6 On a
|
|
113
|
-
|
|
112
|
+
2.6 On a streaming producer, `s[Symbol.asyncIterator]` MUST yield the chunks of the run being served
|
|
113
|
+
from its first, and MUST end when that run closes.
|
|
114
114
|
|
|
115
115
|
2.7 `s.then` MUST resolve with the value where `s.busy` becomes false and 2.2 would not throw.
|
|
116
116
|
|
|
@@ -270,9 +270,7 @@ the production it serves.
|
|
|
270
270
|
|
|
271
271
|
6.9 An expiry MUST NOT report `s.busy` true.
|
|
272
272
|
|
|
273
|
-
6.10 A
|
|
274
|
-
|
|
275
|
-
6.11 A streaming producer's `ttl` MUST run from its run's close, and MUST NOT reach the run in
|
|
273
|
+
6.10 A streaming producer's `ttl` MUST run from its run's close, and MUST NOT reach the run in
|
|
276
274
|
flight.
|
|
277
275
|
|
|
278
276
|
# 7. Triggers
|
|
@@ -396,6 +394,11 @@ made by one (11.61) MUST end with it.
|
|
|
396
394
|
9.14 A subscriber's copy of a wired `Reactive` MUST report `s.busy` true until the first production
|
|
397
395
|
arrives.
|
|
398
396
|
|
|
397
|
+
9.15 A cursor over a streaming producer MUST throw, once it has yielded every chunk, the failure its
|
|
398
|
+
run ended on.
|
|
399
|
+
|
|
400
|
+
9.16 A cursor over a streaming producer MUST end where a new run replaces the one it was reading.
|
|
401
|
+
|
|
399
402
|
# 10. Sharing
|
|
400
403
|
|
|
401
404
|
10.1 `reactive.share` MUST get or create in one call.
|
|
@@ -579,6 +582,9 @@ start again when the load ends.
|
|
|
579
582
|
|
|
580
583
|
11.70 `s.set` MUST take only a `Reactive` whose `Failures` are this value's.
|
|
581
584
|
|
|
585
|
+
11.71 An unkeyed `reactive` with a `store`, built on a server outside every request, MUST NOT run
|
|
586
|
+
the `store`'s `get` before it is reached.
|
|
587
|
+
|
|
582
588
|
# 12. Effects
|
|
583
589
|
|
|
584
590
|
12.1 A `Disposer` MUST run before each rerun of its `Effect`, and once more at teardown.
|
|
@@ -896,7 +902,7 @@ such a call MUST NOT reach it.
|
|
|
896
902
|
the reader asks for that type and `application/jsonl` otherwise.
|
|
897
903
|
|
|
898
904
|
16.45 An sse answer MUST frame each value as `data: <json>` followed by a blank line, with
|
|
899
|
-
`text/event-stream
|
|
905
|
+
`text/event-stream` and `cache-control: private, no-store`.
|
|
900
906
|
|
|
901
907
|
16.46 A stream of value chunks MUST admit jsonl and then sse, and a stream of binary chunks MUST
|
|
902
908
|
admit `application/octet-stream` alone.
|
|
@@ -981,6 +987,15 @@ naming it.
|
|
|
981
987
|
16.74 A module of the client graph importing a handler file MUST receive the wrapper 16.28 states as
|
|
982
988
|
that file's default.
|
|
983
989
|
|
|
990
|
+
16.75 A streamed answer — jsonl, sse, bytes, or a document `page` hands a stream — MUST carry
|
|
991
|
+
`X-Accel-Buffering: no`.
|
|
992
|
+
|
|
993
|
+
16.76 An `Rpc` streaming under 16.32 MUST end its answer where the run it sends ends: at that run's
|
|
994
|
+
close, where a new run replaces it, and with the failure that run ended on.
|
|
995
|
+
|
|
996
|
+
16.77 A `timeout` MUST NOT end a `throttle` or `debounce` window holding a production back, and the
|
|
997
|
+
call MUST wait on that window as 5.15 has it.
|
|
998
|
+
|
|
984
999
|
# 17. Response helpers
|
|
985
1000
|
|
|
986
1001
|
17.1 Every body helper MUST take `Values<T>`.
|
|
@@ -1083,6 +1098,10 @@ value again rather than apply what a patch wrote, as 18.12 has it for a version
|
|
|
1083
1098
|
18.26 A subscription to a `GET` MUST pass the `middleware` 37.2 runs at the route of the file that
|
|
1084
1099
|
declared it, as it opens, and MUST NOT be fed where a rung answered in the handler's place.
|
|
1085
1100
|
|
|
1101
|
+
18.27 A reader of a `text/event-stream` answer — an `Rpc`'s copy and `abide watch` alike — MUST read
|
|
1102
|
+
it by the format's fields: a comment skipped, the space after a field's colon optional, `data` lines
|
|
1103
|
+
joined, and an event ended by a blank line.
|
|
1104
|
+
|
|
1086
1105
|
# 19. Mount paths and schemas
|
|
1087
1106
|
|
|
1088
1107
|
19.1 A route MUST be answered for each method by one file at most, a page answering `GET` and `HEAD`
|
|
@@ -1164,8 +1183,8 @@ lists a handler for it. What a closed one answers is 36.7.
|
|
|
1164
1183
|
|
|
1165
1184
|
20.8 Every `/__abide/**` address MUST run inside the app's own middleware.
|
|
1166
1185
|
|
|
1167
|
-
20.9 `/__abide/mcp` MUST publish
|
|
1168
|
-
|
|
1186
|
+
20.9 `/__abide/mcp` MUST publish every handler it lists as a tool, a `GET` taking no args included,
|
|
1187
|
+
and MUST NOT publish one as a resource.
|
|
1169
1188
|
|
|
1170
1189
|
20.10 A handler whose own `Middleware` rung would refuse a caller MUST still be listed, and MUST
|
|
1171
1190
|
still be refused on the call.
|
|
@@ -1294,7 +1313,8 @@ segment a page below it adds.
|
|
|
1294
1313
|
|
|
1295
1314
|
23.15 A file under a fence MUST NOT have a route.
|
|
1296
1315
|
|
|
1297
|
-
23.16 A fence under a fence MUST be a build error naming both
|
|
1316
|
+
23.16 A fence under a fence MUST be a build error naming both, save a `server` directory under a
|
|
1317
|
+
`lib` or a `tests` directory.
|
|
1298
1318
|
|
|
1299
1319
|
23.17 A route whose first segment is `__abide` MUST be a build error.
|
|
1300
1320
|
|
|
@@ -1432,8 +1452,7 @@ module ran is not the one served after it.
|
|
|
1432
1452
|
27.12 `Config` MUST admit a field an app declares on it, of any type.
|
|
1433
1453
|
|
|
1434
1454
|
27.13 `abide check` and `abide lsp` MUST type `Config` with what the defaults of every `onConfig`
|
|
1435
|
-
called as a statement at the top of a `<script server
|
|
1436
|
-
directory return, and a union return with the keys
|
|
1455
|
+
called as a statement at the top of a `<script server>` or a handler file return, and a union return with the keys
|
|
1437
1456
|
every member of it has.
|
|
1438
1457
|
|
|
1439
1458
|
# 28. Logging
|
|
@@ -1563,12 +1582,16 @@ the page shown, and MUST NOT where only its query or fragment does.
|
|
|
1563
1582
|
30.25 A `navigate` that does not remount the page MUST NOT move the scroll, except to the element a
|
|
1564
1583
|
fragment it carries names.
|
|
1565
1584
|
|
|
1566
|
-
30.26 `abide check` MUST refuse a `url` literal that no page of the app is filed under
|
|
1567
|
-
file of the app is served at.
|
|
1585
|
+
30.26 `abide check` MUST refuse a `url` literal that no page route of the app (23.3) is filed under
|
|
1586
|
+
and no public file of the app is served at.
|
|
1568
1587
|
|
|
1569
1588
|
30.27 A `navigate` whose request fails, or whose route's chunks fail to load, MUST leave the address
|
|
1570
1589
|
to the document.
|
|
1571
1590
|
|
|
1591
|
+
30.28 A `Query` record and the args an `Rpc` carries in its URL MUST be written in one spelling: a
|
|
1592
|
+
member left `undefined` omitted, a list its member repeated, an empty list its name with no `=`, and
|
|
1593
|
+
anything else its text.
|
|
1594
|
+
|
|
1572
1595
|
# 31. Reading and writing by name
|
|
1573
1596
|
|
|
1574
1597
|
31.1 Inside a `.abide` file a name bound to a `Reactive` MUST be read wherever 31.3 does not hold it.
|
|
@@ -2042,7 +2065,8 @@ answered per 41.12.
|
|
|
2042
2065
|
|
|
2043
2066
|
37.1 A lifecycle hook MUST be filed at the route of the `.abide` or the handler file that called it,
|
|
2044
2067
|
one under a `lib` or a `tests` directory at the route of the directory holding that one, and at the
|
|
2045
|
-
root route where neither did
|
|
2068
|
+
root route where neither did; a call MUST be read as a hook only where it names a hook the module
|
|
2069
|
+
imports from abide, under any name, or a member of a namespace it imports from abide.
|
|
2046
2070
|
|
|
2047
2071
|
37.2 `middleware` filed below the root route MUST run after routing, over a request its route or a
|
|
2048
2072
|
route below it answers, outermost route first.
|
|
@@ -2079,6 +2103,9 @@ through `App.stop`, and the process MUST then exit with 0.
|
|
|
2079
2103
|
|
|
2080
2104
|
37.12 A second `SIGTERM` or `SIGINT` while the app is stopping MUST end the process at once.
|
|
2081
2105
|
|
|
2106
|
+
37.13 A lifecycle hook called in a module of the app's own that is neither a `.abide` nor a handler
|
|
2107
|
+
file MUST be a build error naming the module and the hook.
|
|
2108
|
+
|
|
2082
2109
|
# 38. Commands
|
|
2083
2110
|
|
|
2084
2111
|
38.1 `abide scaffold` MUST write a starter project, and MUST skip a step already done.
|
|
@@ -2139,7 +2166,8 @@ link none where the build bundled none.
|
|
|
2139
2166
|
by the replacement.
|
|
2140
2167
|
|
|
2141
2168
|
38.25 `--root` MUST default to `src` where the working directory holds one, and to the working
|
|
2142
|
-
directory otherwise
|
|
2169
|
+
directory otherwise; and the app it finds MUST be the one the whole command means — what it builds,
|
|
2170
|
+
boots, serves and announces.
|
|
2143
2171
|
|
|
2144
2172
|
38.26 A command that finds no app at its `--root` MUST fail, naming where it looked.
|
|
2145
2173
|
|
|
@@ -2163,7 +2191,7 @@ warnings alone.
|
|
|
2163
2191
|
every file that refusal does not stop.
|
|
2164
2192
|
|
|
2165
2193
|
38.34 `abide lsp` MUST report what 38.3 reports, over the text the editor holds rather than the
|
|
2166
|
-
text on disk.
|
|
2194
|
+
text on disk, and every refusal the app's tree makes of the file, as the tree was last saved.
|
|
2167
2195
|
|
|
2168
2196
|
38.35 `abide dev` MUST send a response body as the app produces it.
|
|
2169
2197
|
|
|
@@ -2385,6 +2413,11 @@ with `--json` it MUST print the same as one JSON document.
|
|
|
2385
2413
|
38.109 Under `bun test` with `@abide/abide/test` preloaded, a test MUST import a `.abide` of the app as
|
|
2386
2414
|
the module it compiles to, whether or not `createApp` has run.
|
|
2387
2415
|
|
|
2416
|
+
38.110 A build `abide start`, `abide call`, `abide openapi` or `abide mcp` makes first (38.100) MUST
|
|
2417
|
+
be reported on stderr, leaving the command's stdout to its own output.
|
|
2418
|
+
|
|
2419
|
+
38.111 `abide openapi` MUST print the OpenAPI document on stdout.
|
|
2420
|
+
|
|
2388
2421
|
# 39. Head and view transitions
|
|
2389
2422
|
|
|
2390
2423
|
39.1 A `<head>` element in a `.abide` file MUST contribute its children to the document head.
|
|
@@ -2396,7 +2429,8 @@ the module it compiles to, whether or not `createApp` has run.
|
|
|
2396
2429
|
39.4 `<head>` contributions MUST be merged by key, and the deepest contributor MUST win. The shell's
|
|
2397
2430
|
`<head>` MUST be the outermost contributor.
|
|
2398
2431
|
|
|
2399
|
-
39.5 A `<head>` contribution with no merge key MUST append in tree order
|
|
2432
|
+
39.5 A `<head>` contribution with no merge key MUST append — in tree order among the contributions
|
|
2433
|
+
its level mounts with, and when it mounts for a component mounted after its level.
|
|
2400
2434
|
|
|
2401
2435
|
39.6 On a client navigation the incoming page's keyed `<head>` contributions MUST replace the
|
|
2402
2436
|
outgoing page's by key, and a layout's MUST NOT move.
|
|
@@ -2418,12 +2452,19 @@ app MUST NOT be required to feature-detect.
|
|
|
2418
2452
|
|
|
2419
2453
|
39.12 The last `<head>` contribution for a key within one file MUST win.
|
|
2420
2454
|
|
|
2421
|
-
39.13 In a browser, a `<head>` contribution that reads a value MUST follow it for as long as
|
|
2422
|
-
|
|
2455
|
+
39.13 In a browser, a `<head>` contribution that reads a value MUST follow it for as long as the
|
|
2456
|
+
page, layout or component that made it is mounted.
|
|
2423
2457
|
|
|
2424
|
-
39.14
|
|
2425
|
-
no
|
|
2426
|
-
|
|
2458
|
+
39.14 When a page, layout or component leaves — on a client navigation, or with its region — a keyed
|
|
2459
|
+
`<head>` contribution it made and no deeper contributor still makes MUST return to the nearest
|
|
2460
|
+
remaining outer contributor's value for its key, and MUST be removed where no remaining outer
|
|
2461
|
+
contributor has that key.
|
|
2462
|
+
|
|
2463
|
+
39.15 A `<head>` contribution with no merge key that its page, layout or component writes again
|
|
2464
|
+
MUST replace, in place, the one that same contributor wrote before.
|
|
2465
|
+
|
|
2466
|
+
39.16 A `<head>` contribution with no merge key MUST be removed when the page, layout or component
|
|
2467
|
+
that made it leaves.
|
|
2427
2468
|
|
|
2428
2469
|
# 40. Documentation
|
|
2429
2470
|
|
|
@@ -2595,6 +2636,9 @@ has answered or failed.
|
|
|
2595
2636
|
41.27 An `Rpc` call `render` built the page's markup from in flight MUST be in flight as hydration
|
|
2596
2637
|
adopts the page, however early its answer arrived.
|
|
2597
2638
|
|
|
2639
|
+
41.28 A `render` MUST let the turn's queued microtasks run between setting up a `<Name/>` whose setup
|
|
2640
|
+
made an `Rpc` call and building that component's markup.
|
|
2641
|
+
|
|
2598
2642
|
# 42. The app object
|
|
2599
2643
|
|
|
2600
2644
|
42.1 `createApp` MUST build the route table and compose every `Middleware` rung, per 16.14.
|
package/docs/abide.md
CHANGED
|
@@ -573,7 +573,7 @@ Read on: [Head & metadata](/pages/set-the-title-and-social-preview.md) · [Layou
|
|
|
573
573
|
|
|
574
574
|
Rendering a page runs its setup: the `<script>` blocks, the `reactive` calls, the bodies the markup reads. Each read that needs the server starts its work immediately, so the loads on a page are in flight together rather than in the order the markup happens to mention them.
|
|
575
575
|
|
|
576
|
-
The total time is the slowest one, not the sum — and the first byte does not wait for any of them. The markup waits one turn, so a load that answers without waiting on anything is painted rather than left as a hole.
|
|
576
|
+
The total time is the slowest one, not the sum — and the first byte does not wait for any of them. The markup waits one turn, so a load that answers without waiting on anything is painted rather than left as a hole. A component whose setup starts a load gets the same turn before its own markup is built, however deep it sits.
|
|
577
577
|
|
|
578
578
|
## 4. The first bytes leave
|
|
579
579
|
|
|
@@ -1597,11 +1597,11 @@ Read on: [Throttle & debounce](/values/slow-down-a-value-that-changes-too-fast.m
|
|
|
1597
1597
|
|
|
1598
1598
|
## Following a channel sees turns, not every message
|
|
1599
1599
|
|
|
1600
|
-
`
|
|
1600
|
+
`onboarding.watch((turns) => …)` follows the channel. The effect runs once at the end of a turn, however many writes landed in it, and a patch in place is one of them (12.10). A burst of three messages in one turn is one run, holding all three.
|
|
1601
1601
|
|
|
1602
|
-
So there is no exactly-once tail of a channel. Code that has to act on every message acts where it writes it — in `say`, beside the push.
|
|
1602
|
+
So there is no exactly-once tail of a channel. Code that has to act on every message acts where it writes it — in `say`, beside the push.
|
|
1603
1603
|
|
|
1604
|
-
|
|
1604
|
+
`for await` is not a follow. Over a value it yields the settled value once and ends, as `await` does (2.5). Over a stream it yields each chunk of the run and ends at the close (2.6), and a capped window still delivers every chunk (5.10).
|
|
1605
1605
|
|
|
1606
1606
|
Read on: [Watching values](/values/do-something-when-a-value-changes.md) · [Streaming data](/data/send-data-as-it-arrives.md)
|
|
1607
1607
|
|
|
@@ -2506,7 +2506,7 @@ Two costs, and both are yours rather than abide's.
|
|
|
2506
2506
|
|
|
2507
2507
|
And a global value is bounded by its own `ttl` and the app's `invalidate` and by nothing else. There is no byte ceiling and no eviction count: measuring an arbitrary value costs an O(size) walk, and an LRU would evict an entry a reader is mid-flight on. `global: true` with `ttl: Infinity` is an unbounded cache — unbounded because the app asked for it, the bound being a number the app knows and abide does not.
|
|
2508
2508
|
|
|
2509
|
-
A global value over a stream fans one producer out to many readers, and it needs no retention to do it. A reader arriving mid-stream gets the run in flight from its
|
|
2509
|
+
A global value over a stream fans one producer out to many readers, and it needs no retention to do it. A reader arriving mid-stream gets the run in flight from its first chunk to its close (2.6), and a reader leaving does not cancel the producer for the ones that remain (11.39).
|
|
2510
2510
|
|
|
2511
2511
|
Read on: [Recent values](/values/keep-the-last-few-values.md) · [Auth & principal](/app/know-who-is-calling.md) · [Authorization](/data/decide-who-may-call-what.md) · [Response types](/data/answer-with-something-other-than-json.md)
|
|
2512
2512
|
|
|
@@ -3652,7 +3652,7 @@ Read on: [Patching a list](/values/patch-a-list-from-a-live-feed.md) · [Lists](
|
|
|
3652
3652
|
|
|
3653
3653
|
Every `Reactive` holds its standing value and nothing before it. Where you want the ones before it, hold them: make the value the array, and append where you write.
|
|
3654
3654
|
|
|
3655
|
-
A reader cannot build the window for you.
|
|
3655
|
+
A reader cannot build the window for you. A `watch` runs once per turn, and a burst of writes in one turn is one run (12.10), so a window a reader assembles misses whatever landed in the same turn. The writer sees every value, which is why the window is built where the write is.
|
|
3656
3656
|
|
|
3657
3657
|
A channel is this same shape with many writers — a `reactive` over an array that everyone connected reads, trimmed the same way.
|
|
3658
3658
|
|
|
@@ -4930,7 +4930,7 @@ Each takes `Values<T>`, and sse is reachable from jsonl without declaring it: se
|
|
|
4930
4930
|
|
|
4931
4931
|
## An async generator streams as JSON Lines
|
|
4932
4932
|
|
|
4933
|
-
A handler returning an `AsyncGenerator` is framed as **jsonl** — one JSON value per line, `application/jsonl`, or `application/x-ndjson` for a reader that asks for NDJSON by name (16.44). The body is written per `pull`, so back-pressure reaches your database cursor rather than piling rows up in memory.
|
|
4933
|
+
A handler returning an `AsyncGenerator` is framed as **jsonl** — one JSON value per line, `application/jsonl`, or `application/x-ndjson` for a reader that asks for NDJSON by name (16.44). The body is written per `pull`, so back-pressure reaches your database cursor rather than piling rows up in memory, and it carries `X-Accel-Buffering: no`, as every streamed answer does, so a proxy in front passes each line on as it arrives (16.75).
|
|
4934
4934
|
|
|
4935
4935
|
That is the default framing and not the only one.
|
|
4936
4936
|
|
|
@@ -4986,7 +4986,7 @@ const feed = reactive(async () => {
|
|
|
4986
4986
|
*1 helper, 0 lines of framing* — the arm writes `data: `, the blank line after it and the two headers a proxy needs to leave the stream alone, by hand, per event.
|
|
4987
4987
|
|
|
4988
4988
|
The card's request carries no `Accept`, which is what a wallboard script or a `curl` sends, and without `sse` that caller is answered jsonl. A caller that does ask — an `EventSource` sends `Accept:
|
|
4989
|
-
text/event-stream` — gets the same events from a handler that never declared them: the jsonl transcript reframed as `data: <json>` per line with a blank line after it, `no-
|
|
4989
|
+
text/event-stream` — gets the same events from a handler that never declared them: the jsonl transcript reframed as `data: <json>` per line with a blank line after it, under `private, no-store` like any rpc answer. That is one URL with two bodies, so it carries `vary: accept`. Declaring `sse` is for the caller who cannot say what it wants. The `:` the card's answer opens on is a comment, which sends the head before the first event exists.
|
|
4990
4990
|
|
|
4991
4991
|
Read on: [Response types](/data/answer-with-something-other-than-json.md)
|
|
4992
4992
|
|
|
@@ -5176,7 +5176,7 @@ Read on: [Loading states](/values/show-a-value-that-isnt-there-yet.md) · [Lists
|
|
|
5176
5176
|
|
|
5177
5177
|
## A reader arriving mid-run replays the run
|
|
5178
5178
|
|
|
5179
|
-
There is no retention option on the transport and none is owed. What a late reader needs is the answer being built, and a
|
|
5179
|
+
There is no retention option on the transport and none is owed. What a late reader needs is the answer being built, and a `for await` over the stream hands it the run in flight from its first chunk to its close (2.6). So a reader arriving halfway through still paints every row.
|
|
5180
5180
|
|
|
5181
5181
|
## A stream is not a channel
|
|
5182
5182
|
|
|
@@ -5386,7 +5386,7 @@ The call hands back the same `Reactive` the handler had, `Produced` included (16
|
|
|
5386
5386
|
|
|
5387
5387
|
Writing to that copy is not how to send. A `set` on it stands only until the next production replaces it (9.4), so a turn goes through `say`.
|
|
5388
5388
|
|
|
5389
|
-
`room.invalidate({ id })` and `room.refresh({ id })` inside a handler reach every browser's copy of that room (7.10, 7.18). A `
|
|
5389
|
+
`room.invalidate({ id })` and `room.refresh({ id })` inside a handler reach every browser's copy of that room (7.10, 7.18). A `watch` on the room runs once per turn, a burst collapsing to the latest (12.10), so code that has to act on every message acts in `say`.
|
|
5390
5390
|
|
|
5391
5391
|
## Each instance holds its own room
|
|
5392
5392
|
|
|
@@ -7220,7 +7220,7 @@ Read on: [Streaming HTML](/pages/send-the-page-before-the-data-lands.md) · [Req
|
|
|
7220
7220
|
|
|
7221
7221
|
The incoming page's keyed contributions replace the outgoing page's **by key**. A layout's contributions do not move at all, the layout not having been rebuilt — so the app's icon and its font preloads are declared once and never touched again.
|
|
7222
7222
|
|
|
7223
|
-
A key only the outgoing page set goes back to the layout's value, and is removed where no layout has one. The tab ends on the title a fresh load of that address shows.
|
|
7223
|
+
A key only the outgoing page set goes back to the layout's value, and is removed where no layout has one, and what it appended with no key leaves with it. The tab ends on the title a fresh load of that address shows.
|
|
7224
7224
|
|
|
7225
7225
|
Read on: [Links & navigation](/pages/link-to-another-page.md)
|
|
7226
7226
|
|
|
@@ -9229,27 +9229,10 @@ Read on: [Config](/app/configure-the-app.md) · [Authorization](/data/decide-who
|
|
|
9229
9229
|
|
|
9230
9230
|
## A root rung answers what no route does
|
|
9231
9231
|
|
|
9232
|
-
`middleware` filed at the root route runs before routing, so a rung there can answer an address no page or handler claims, and hand everything else on with `next()`. These docs serve every page as markdown that way:
|
|
9233
|
-
|
|
9234
|
-
```ts src/lib/
|
|
9235
|
-
|
|
9236
|
-
const { pathname, origin } = new URL(request.url)
|
|
9237
|
-
if (!pathname.endsWith('.md') && !pathname.endsWith('/llms.txt'))
|
|
9238
|
-
return next()
|
|
9239
|
-
if (pathname === '/llms.txt')
|
|
9240
|
-
return new Response('# abide\n\nEvery page, as markdown: abide.md\n')
|
|
9241
|
-
if (pathname === '/abide.md') {
|
|
9242
|
-
bundled ??= bundle(origin)
|
|
9243
|
-
return new Response(await bundled, { headers: MARKDOWN })
|
|
9244
|
-
}
|
|
9245
|
-
for (const group of SITE)
|
|
9246
|
-
for (const page of group.pages)
|
|
9247
|
-
if (pathname === markdownAddressOf(page.route))
|
|
9248
|
-
return new Response(await pageMarkdown(page.route, origin), {
|
|
9249
|
-
headers: MARKDOWN,
|
|
9250
|
-
})
|
|
9251
|
-
return next()
|
|
9252
|
-
})
|
|
9232
|
+
`middleware` filed at the root route runs before routing, so a rung there can answer an address no page or handler claims, and hand everything else on with `next()`. These docs serve every page as markdown that way — the rung is a module's export, and the root layout files it with `middleware(serveDownloads)`, since a hook is only called in a page, a layout or a handler file (37.13):
|
|
9233
|
+
|
|
9234
|
+
```ts src/lib/serveDownloads.ts — excerpt
|
|
9235
|
+
export const serveDownloads: Middleware<
|
|
9253
9236
|
```
|
|
9254
9237
|
|
|
9255
9238
|
It uses the same response helpers everything else does, there being no second set.
|
|
@@ -10006,13 +9989,15 @@ let refused = reactive('—')
|
|
|
10006
9989
|
<script server>
|
|
10007
9990
|
// The docs app's own hooks; each example files its own in its own file.
|
|
10008
9991
|
import { csp, middleware, onConfig } from '@abide/abide/server'
|
|
10009
|
-
import '#src/lib/
|
|
9992
|
+
import { serveDownloads } from '#src/lib/serveDownloads.ts'
|
|
10010
9993
|
|
|
10011
9994
|
// The sign-in examples need a secret: one per process, or the deployment's.
|
|
10012
9995
|
const PRINCIPAL_SECRET = crypto.randomUUID()
|
|
10013
9996
|
|
|
10014
9997
|
onConfig(() => ({ ABIDE_PRINCIPAL_SECRET: PRINCIPAL_SECRET }))
|
|
10015
9998
|
|
|
9999
|
+
middleware(serveDownloads)
|
|
10000
|
+
|
|
10016
10001
|
// Every docs page's policy; 'self' so the docs can frame invoice previews.
|
|
10017
10002
|
middleware(csp({ 'frame-ancestors': ["'self'"] }))
|
|
10018
10003
|
</script>
|
|
@@ -10276,10 +10261,10 @@ The address runs inside the app's own middleware like every `/__abide/**` addres
|
|
|
10276
10261
|
## Written rather than served
|
|
10277
10262
|
|
|
10278
10263
|
```
|
|
10279
|
-
abide openapi
|
|
10264
|
+
abide openapi > dist/openapi.json
|
|
10280
10265
|
```
|
|
10281
10266
|
|
|
10282
|
-
The same document,
|
|
10267
|
+
The same document, printed from the source rather than asked of a server. That is what a build step wants — a spec committed beside the code, or handed to a client generator — and it does not require the app to be running or the address to be on.
|
|
10283
10268
|
|
|
10284
10269
|
`--url` points it at a running app instead, which is what you want when the question is what *that deployment* is serving.
|
|
10285
10270
|
|
|
@@ -10307,11 +10292,11 @@ Read on: [Withholding a surface](/machines/keep-a-handler-off-a-surface.md)
|
|
|
10307
10292
|
|
|
10308
10293
|
An MCP server is a list of tools with typed arguments and descriptions. A declared handler is a typed argument shape with a description. `/__abide/mcp` is the second read as the first, over streamable http.
|
|
10309
10294
|
|
|
10310
|
-
##
|
|
10295
|
+
## Every handler is a tool
|
|
10311
10296
|
|
|
10312
|
-
|
|
10297
|
+
Every handler the route table publishes is a **tool** — a read that takes no arguments as much as a mutation. None is published as an MCP resource, because a client that only calls tools, Claude Code under `abide-bridge` among them, would never reach a handler listed as one.
|
|
10313
10298
|
|
|
10314
|
-
That is the whole of the mapping
|
|
10299
|
+
That is the whole of the mapping: the model sees every handler your app publishes, and whether one changes something is the handler's own method, not a second listing.
|
|
10315
10300
|
|
|
10316
10301
|
## The description is the handler's
|
|
10317
10302
|
|
|
@@ -11810,8 +11795,8 @@ Every `Reactive` takes these, whichever overload made it.
|
|
|
11810
11795
|
| `s.then` | `<Result>(onSettled?: (value: Stored) => Result \| PromiseLike<Result>, onFailed?: (error: unknown) => Result \| PromiseLike<Result>) => Promise<Result>` | The settling, as a promise. Resolves when `busy()` goes false, and rejects with what a read would throw. |
|
|
11811
11796
|
| `s.catch` | `<Result>(onFailed: (error: unknown) => Result \| PromiseLike<Result>) => Promise<Stored \| Result>` | The settling's refusal path, derived from `then`. Fires for a producer that failed with nothing landed, and for nothing else. |
|
|
11812
11797
|
| `s.finally` | `(onSettled: () => void) => Promise<Stored>` | The settling's end, whichever way it went. What ends is the settling, not the value — a subscriber's copy of a channel settles at its first production and goes on. |
|
|
11813
|
-
| `for await (… of s)` | `AsyncIterable<Produced>` |
|
|
11814
|
-
| `s[Symbol.asyncIterator]` | `() => AsyncIterator<Produced>` | The
|
|
11798
|
+
| `for await (… of s)` | `AsyncIterable<Produced>` | What one run produced. On a stream, one turn per chunk, ending at the close and throwing what failed the run. On any other value, the settled value once, as `await` gives it. To follow changes, `watch`. |
|
|
11799
|
+
| `s[Symbol.asyncIterator]` | `() => AsyncIterator<Produced>` | The run being served, from its start to its end. A chunk is a piece of one value, so a stream's cursor sees every chunk; any other value's cursor sees the value it settles on. |
|
|
11815
11800
|
|
|
11816
11801
|
## Writes
|
|
11817
11802
|
|
|
@@ -12843,7 +12828,7 @@ src/
|
|
|
12843
12828
|
|
|
12844
12829
|
A route is its file's path without the extension, and an `index.abide`, a `layout.abide` and an `error.abide` take their directory's (23.14). Every other `.abide` directly in a directory is a page (23.21), so a component lives under a `lib`. A `.ts` there is a handler file.
|
|
12845
12830
|
|
|
12846
|
-
`lib`, `tests`, `server` and `public` are the fences, and nothing under one has a route (23.15). One inside another is a build error, and so is an `index.abide`, a `layout.abide` or an `error.abide` under one (23.16, 23.18). A directory's top level holds pages, layouts, error pages, handler files, directories and fences alone — any other file there is a build error naming the `lib` to move it into (23.22). So listing a directory lists its routes, and `abide routes` lists the app's.
|
|
12831
|
+
`lib`, `tests`, `server` and `public` are the fences, and nothing under one has a route (23.15). One inside another is a build error, save a `server` inside a `lib` or a `tests`, which keeps code only the server loads beside the components that call it — and so is an `index.abide`, a `layout.abide` or an `error.abide` under one (23.16, 23.18). A directory's top level holds pages, layouts, error pages, handler files, directories and fences alone — any other file there is a build error naming the `lib` to move it into (23.22). So listing a directory lists its routes, and `abide routes` lists the app's.
|
|
12847
12832
|
|
|
12848
12833
|
### Resolution order
|
|
12849
12834
|
|
|
@@ -12967,7 +12952,7 @@ A page and a handler file are **addresses rather than names**, and so are `index
|
|
|
12967
12952
|
| `onPrincipal` | `(resolve: (claims: unknown) => unknown \| Promise<unknown>) => () => void` | Turns claims into the app's half of a principal. |
|
|
12968
12953
|
| `onLauncher` | `(options: () => LauncherOptions) => () => void` | The window the desktop app opens and the forms its launcher offers. |
|
|
12969
12954
|
|
|
12970
|
-
Each is a hook you **call** rather than an export the framework looks for, a `.abide` exporting nothing. It is filed at the route of the `.abide` or the handler file that called it, and
|
|
12955
|
+
Each is a hook you **call** rather than an export the framework looks for, a `.abide` exporting nothing. It is filed at the route of the `.abide` or the handler file that called it (37.1), and any other module calling one is a build error (37.13) — export the function and call the hook where it serves. A `.abide` under a `lib` or a `tests` directory files its hooks at the route of the directory holding that one. `onLauncher` is called in `src/launcher.abide` alone.
|
|
12971
12956
|
|
|
12972
12957
|
`middleware` runs over that route and the ones below it — before routing at the root, after it anywhere else — and `onError` hears an error there, nearest route first. The rest are the process's wherever they are written, and every one of a kind composes: `onConfig` and `onPrincipal` merge what each answers, the deeper route's over the root's. `abide routes` lists every call with the route it is filed at.
|
|
12973
12958
|
|
|
@@ -12985,7 +12970,7 @@ Every one hands back the way off again, so a module that is torn down can remove
|
|
|
12985
12970
|
|
|
12986
12971
|
### `Config` is typed from the `onConfig` calls
|
|
12987
12972
|
|
|
12988
|
-
`abide check` and `abide lsp` type `Config` from the defaults every `onConfig` returns, read where it is called as a statement at the top of a `<script server
|
|
12973
|
+
`abide check` and `abide lsp` type `Config` from the defaults every `onConfig` returns, read where it is called as a statement at the top of a `<script server>` or of a handler file (27.13). A default that returns a union types the keys every member of it has. So a config read is typed without a declaration of its own, wherever in the app the default is written.
|
|
12989
12974
|
|
|
12990
12975
|
### `NODE_ENV` is verbatim
|
|
12991
12976
|
|
|
@@ -13047,7 +13032,7 @@ abide --help
|
|
|
13047
13032
|
- `--port <n>` — the listen port for `dev` and `start`. Defaults to `PORT`, then to 3000 — where `dev` moves on to the first free port through 3099.
|
|
13048
13033
|
- `--root <dir>` — the app's source directory. Defaults to `src` where you are standing, and to where you are standing when there is none — so `cd ledger && abide dev` and `abide dev --root
|
|
13049
13034
|
./src` are the same command. A directory holding no `.abide` under a `package.json` is not an app, and the command says so rather than building nothing.
|
|
13050
|
-
- `--out <dir>` — where `build` writes, beside the app rather than beside you: building `packages/ledger/src` from anywhere leaves `packages/ledger/.abide`, which is where `start` looks. It is also the file `
|
|
13035
|
+
- `--out <dir>` — where `build` writes, beside the app rather than beside you: building `packages/ledger/src` from anywhere leaves `packages/ledger/.abide`, which is where `start` looks. It is also the file `compile` and `bundle` write.
|
|
13051
13036
|
- `--url <origin>` — the running app the shell, `openapi`, `mcp`, `call` and `logs` talk to. Defaults to `ABIDE_APP_URL`, then to the local app.
|
|
13052
13037
|
- `--target`, `--platforms` — what `compile` builds for.
|
|
13053
13038
|
|
|
@@ -13071,7 +13056,7 @@ abide --help
|
|
|
13071
13056
|
| `abide start [--port <n>]` | Boots the built app. |
|
|
13072
13057
|
| `abide [--url <origin>] [--port <n>]` | The interactive shell — on the built app it boots, or on the running app `--url` names. |
|
|
13073
13058
|
| `abide call <address> [args]` | One call to one handler. |
|
|
13074
|
-
| `abide openapi [--
|
|
13059
|
+
| `abide openapi [--url <origin>]` | The OpenAPI document printed rather than served. |
|
|
13075
13060
|
| `abide mcp [--url <origin>]` | The app's MCP server over stdio. |
|
|
13076
13061
|
| `abide watch <address> [args]` | A live subscription to one of the app's followed `GET`s. |
|
|
13077
13062
|
| `abide compile [--target] [--out] [--platforms]` | One standalone executable. |
|
|
@@ -13115,7 +13100,7 @@ abide call invoices/getInvoice id=4310
|
|
|
13115
13100
|
### Write the OpenAPI document at build time
|
|
13116
13101
|
|
|
13117
13102
|
```
|
|
13118
|
-
abide openapi
|
|
13103
|
+
abide openapi > dist/openapi.json
|
|
13119
13104
|
```
|
|
13120
13105
|
|
|
13121
13106
|
## See also
|
package/package.json
CHANGED
package/src/compiler/Lowering.ts
CHANGED
|
@@ -33,6 +33,12 @@ import { nesting, type Token, tokenize } from './tokenize.ts'
|
|
|
33
33
|
// no parentheses to survive one.
|
|
34
34
|
const SHORT_CIRCUIT: ReadonlySet<string> = new Set(['||', '&&', '??'])
|
|
35
35
|
|
|
36
|
+
/** 23.9 — a page's or a layout's route, which its `route.params` promises. */
|
|
37
|
+
export type Routed = { route: string; kind: 'page' | 'layout' }
|
|
38
|
+
|
|
39
|
+
/** A run of emitted code a `Lowering` wrote, and where each of its runs stood in the projection. */
|
|
40
|
+
export type LoweredChunk = { text: string; origins: Origin[] }
|
|
41
|
+
|
|
36
42
|
/**
|
|
37
43
|
* One file's lowering: each piece the emitters ask for, with its reactive reads rewritten.
|
|
38
44
|
*
|
|
@@ -42,9 +48,6 @@ const SHORT_CIRCUIT: ReadonlySet<string> = new Set(['||', '&&', '??'])
|
|
|
42
48
|
* so the import goes with the call; left to the callers it is one `used.add` per call site and a
|
|
43
49
|
* missed one emits a module calling a name it never imported.
|
|
44
50
|
*/
|
|
45
|
-
/** A run of emitted code a `Lowering` wrote, and where each of its runs stood in the projection. */
|
|
46
|
-
export type LoweredChunk = { text: string; origins: Origin[] }
|
|
47
|
-
|
|
48
51
|
export class Lowering {
|
|
49
52
|
readonly projection: Projection
|
|
50
53
|
readonly used = new Set<string>()
|
|
@@ -85,18 +88,25 @@ export class Lowering {
|
|
|
85
88
|
* the projection: what `sourceMapOf` finds in the module the emitter writes.
|
|
86
89
|
*/
|
|
87
90
|
private readonly chunks: LoweredChunk[] | undefined
|
|
91
|
+
/**
|
|
92
|
+
* 23.9 — for a checked lowering of a page or a layout, its route and which of the two it is:
|
|
93
|
+
* what its `route.params` is typed by.
|
|
94
|
+
*/
|
|
95
|
+
private readonly routed: Routed | undefined
|
|
88
96
|
|
|
89
97
|
constructor(
|
|
90
98
|
file: AbideFile,
|
|
91
99
|
answer: CheckerAnswer,
|
|
92
100
|
checking = false,
|
|
93
101
|
chunks?: LoweredChunk[],
|
|
102
|
+
routed?: Routed,
|
|
94
103
|
) {
|
|
95
104
|
this.projection = projectShadow(file)
|
|
96
105
|
this.answer = answer
|
|
97
106
|
this.holdsSlot = holdsSlot(file.template)
|
|
98
107
|
this.checking = checking
|
|
99
108
|
this.chunks = chunks
|
|
109
|
+
this.routed = routed
|
|
100
110
|
}
|
|
101
111
|
|
|
102
112
|
/**
|
|
@@ -441,8 +451,8 @@ export class Lowering {
|
|
|
441
451
|
const spelled = this.projection.text.slice(read.start, read.end)
|
|
442
452
|
// 23.9 — WHAT THIS PAGE'S OWN ROUTE PROMISES, for the checker alone: the running module
|
|
443
453
|
// reads the one `route` every page shares.
|
|
444
|
-
if (this.
|
|
445
|
-
return `(${spelled} as unknown as () => __RouteParams<${JSON.stringify(
|
|
454
|
+
if (this.routed !== undefined && read.routeParams)
|
|
455
|
+
return `(${spelled} as unknown as () => __RouteParams<${JSON.stringify(this.routed.route)}, ${JSON.stringify(this.routed.kind)}>)`
|
|
446
456
|
return read.through ? `${spelled}()` : spelled
|
|
447
457
|
}
|
|
448
458
|
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { resolve } from 'node:path' // bun exposes no path helpers of its own
|
|
2
|
+
import { formatDiagnosis } from './formatDiagnosis.ts'
|
|
3
|
+
import { diagnosisAt } from './lineOf.ts'
|
|
4
|
+
import type { Diagnosis } from './TypeSession.ts'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Where everything the compile refuses goes, each at the file it was refused in.
|
|
8
|
+
*
|
|
9
|
+
* TWO MODES AND ONE CALL: a build stops at a refusal, so a throwing sink throws it; `abide
|
|
10
|
+
* check` reports every one at its file and goes on (38.33), so a collecting sink keeps each as a
|
|
11
|
+
* finding at the file's first line. Every stage takes this and nothing else, so no stage decides for
|
|
12
|
+
* itself what a refusal does, and none spells a path its own way: a file is named relative to `root`
|
|
13
|
+
* or absolutely, and kept absolute.
|
|
14
|
+
*/
|
|
15
|
+
export class Refusals {
|
|
16
|
+
/** Every refusal kept, in the order met; empty in a throwing sink. */
|
|
17
|
+
readonly found: Diagnosis[] = []
|
|
18
|
+
/** What a file below the root is named relative to. */
|
|
19
|
+
readonly root: string
|
|
20
|
+
/** Whether a refusal is kept and the compile goes on, rather than thrown. */
|
|
21
|
+
readonly collecting: boolean
|
|
22
|
+
|
|
23
|
+
constructor(root: string, collecting: boolean) {
|
|
24
|
+
this.root = root
|
|
25
|
+
this.collecting = collecting
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Refuses at `file`, below the root or absolute: `thrown` is thrown, or kept. */
|
|
29
|
+
at(file: string, thrown: unknown): void {
|
|
30
|
+
if (!this.collecting) throw thrown
|
|
31
|
+
this.found.push(
|
|
32
|
+
diagnosisAt(
|
|
33
|
+
resolve(this.root, file),
|
|
34
|
+
undefined,
|
|
35
|
+
0,
|
|
36
|
+
'error',
|
|
37
|
+
thrown instanceof Error ? thrown.message : String(thrown),
|
|
38
|
+
),
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* What another stage kept, as this one takes it: every one kept, or every one thrown as one
|
|
44
|
+
* error, each at its file — a build that stops names all it found rather than a first one and a
|
|
45
|
+
* second run (11.37).
|
|
46
|
+
*/
|
|
47
|
+
adopt(found: readonly Diagnosis[]): void {
|
|
48
|
+
if (found.length === 0) return
|
|
49
|
+
if (this.collecting) {
|
|
50
|
+
this.found.push(...found)
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
const lines: string[] = []
|
|
54
|
+
for (const one of found) lines.push(formatDiagnosis(one, process.cwd()))
|
|
55
|
+
throw new Error(lines.join('\n'))
|
|
56
|
+
}
|
|
57
|
+
}
|