@abide/abide 0.104.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 +16 -0
- package/bin/abide.ts +80 -173
- package/docs/REGISTRY.md +17 -17
- package/docs/RULEBOOK.md +50 -12
- package/docs/abide.md +9 -9
- package/package.json +1 -1
- package/src/compiler/Lowering.ts +15 -5
- package/src/compiler/Refusals.ts +57 -0
- package/src/compiler/TypeSession.ts +45 -86
- package/src/compiler/appLayout.ts +447 -0
- package/src/compiler/appMap.ts +21 -20
- package/src/compiler/checkApp.ts +61 -97
- package/src/compiler/checkedShadow.ts +11 -9
- package/src/compiler/classifyRead.ts +3 -6
- package/src/compiler/compileApp.ts +180 -512
- 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/readSources.ts +27 -42
- package/src/compiler/readsIn.ts +5 -16
- package/src/compiler/resolverIn.ts +77 -0
- package/src/server/abideModules.ts +92 -123
- 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 +49 -71
- 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/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/withAbideSources.ts +2 -1
- package/src/shared/HandlerRoute.ts +68 -0
- package/src/shared/REFUSAL_KEY.ts +5 -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 +4 -2
- package/src/shared/reactive/Reactive.ts +285 -197
- 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 -123
- 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 -33
- package/src/ui/MOUNTING_LEVEL.ts +0 -18
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @abide/abide
|
|
2
2
|
|
|
3
|
+
## 0.105.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 98e55a3: the app is laid out once per compile, hooks are read by what a module imports, and abide lsp reports the tree's refusals (37.1, 38.34) ([`3b3c6c2`](https://github.com/briancray/abide/commit/3b3c6c277f9cc65a9b08a0d3b4115e7ff8e7c3c5))
|
|
8
|
+
- 98e55a3: a child whose setup made a call is built after the turn, as a page is (41.28) ([`6ae9acd`](https://github.com/briancray/abide/commit/6ae9acd75855b446de1680e4413deb47e0a223d9))
|
|
9
|
+
- 98e55a3: a component's <head> follows the component, and an unkeyed contribution replaces in place and leaves with it (39.13-39.16) ([`8dc5e9e`](https://github.com/briancray/abide/commit/8dc5e9e22054a13bb7fa648ef7df11a243f19639))
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 98e55a3: streamed answers end with their run, carry X-Accel-Buffering, and sse is read by its fields (16.75, 16.76, 16.77, 18.27) ([`696c5cb`](https://github.com/briancray/abide/commit/696c5cb61cbeb0d83382d742924ff8a35cd14339))
|
|
14
|
+
- 98e55a3: a command means one app, reports its first build on stderr, and abide openapi prints to stdout (38.25, 38.110, 38.111) ([`7ca9f4b`](https://github.com/briancray/abide/commit/7ca9f4b90294ca34f55c0ce3a725a336f8c1f11b))
|
|
15
|
+
- 98e55a3: an unkeyed reactive with a store built outside a request reads it lazily (11.71) ([`acf9440`](https://github.com/briancray/abide/commit/acf94407f779617d63cdc7ed5f371870d58e151e))
|
|
16
|
+
- 98e55a3: a query record and an rpc's URL args share one spelling (30.28) ([`b8c3c48`](https://github.com/briancray/abide/commit/b8c3c48b1c3b056d7172f7aad4b2f1a184b39689))
|
|
17
|
+
- 98e55a3: a keyed list's row record lives with its nodes in rows.ts ([`e37bbb1`](https://github.com/briancray/abide/commit/e37bbb170685cb2714584287d22946c229050781))
|
|
18
|
+
|
|
3
19
|
## 0.104.0
|
|
4
20
|
|
|
5
21
|
### Minor Changes
|
package/bin/abide.ts
CHANGED
|
@@ -6,13 +6,13 @@ import { existsSync, realpathSync } from 'node:fs' // bun exposes no sync existe
|
|
|
6
6
|
import { mkdir, rename, rm } from 'node:fs/promises' // bun exposes no directory creation, rename or recursive directory removal of its own
|
|
7
7
|
import { dirname, resolve } from 'node:path' // bun exposes no path helpers of its own
|
|
8
8
|
import { brotliCompress, constants } from 'node:zlib' // bun's brotli is `CompressionStream`, which compresses on the main thread — 1390 ms against this pool's 300 ms over the docs bundle
|
|
9
|
+
import { paints } from '#compiler/appLayout.ts'
|
|
9
10
|
import { appMap, mapText } from '#compiler/appMap.ts'
|
|
10
11
|
import { checkApp } from '#compiler/checkApp.ts'
|
|
11
12
|
import {
|
|
12
13
|
type CompiledApp,
|
|
13
14
|
compilationOf,
|
|
14
15
|
compileApp,
|
|
15
|
-
paints,
|
|
16
16
|
} from '#compiler/compileApp.ts'
|
|
17
17
|
import { compiledEntry } from '#compiler/compiledEntry.ts'
|
|
18
18
|
import { formatDiagnosis } from '#compiler/formatDiagnosis.ts'
|
|
@@ -22,10 +22,8 @@ import { serveLanguage } from '#compiler/serveLanguage.ts'
|
|
|
22
22
|
import {
|
|
23
23
|
abideModules,
|
|
24
24
|
type Compilation,
|
|
25
|
-
servedTable,
|
|
26
25
|
serverMapsOf,
|
|
27
26
|
} from '#server/abideModules.ts'
|
|
28
|
-
import { appPlugins } from '#server/appPlugins.ts'
|
|
29
27
|
import { banner } from '#server/banner.ts'
|
|
30
28
|
import { buildDirectory } from '#server/buildDirectory.ts'
|
|
31
29
|
import {
|
|
@@ -35,26 +33,15 @@ import {
|
|
|
35
33
|
MANIFEST,
|
|
36
34
|
PUBLIC_DIRECTORY,
|
|
37
35
|
} from '#server/CLIENT_BUILD.ts'
|
|
38
|
-
import {
|
|
39
|
-
clientBuildOptions,
|
|
40
|
-
stylesheetUnderMount,
|
|
41
|
-
} from '#server/clientBuildOptions.ts'
|
|
36
|
+
import { type ClientBundle, clientBundle } from '#server/clientBundle.ts'
|
|
42
37
|
import { type Given, type Host, runCommands } from '#server/commands.ts'
|
|
43
38
|
import { type AppPackage, packageUnder } from '#server/config.ts'
|
|
44
|
-
import {
|
|
39
|
+
import { appOnDisk, appRoot, noAppHere } from '#server/createApp.ts'
|
|
45
40
|
import { executableName } from '#server/executableName.ts'
|
|
46
|
-
import { fenceServer } from '#server/fenceServer.ts'
|
|
47
41
|
import { iconAccent } from '#server/iconAccent.ts'
|
|
48
|
-
import { manifestOf, NO_MANIFEST } from '#server/manifestOf.ts'
|
|
49
42
|
import { namesUnder } from '#server/namesUnder.ts'
|
|
50
|
-
import { refuseAddressCollision } from '#server/refuseAddressCollision.ts'
|
|
51
43
|
import { DEFAULT_DOCUMENT, intoHead } from '#server/render.ts'
|
|
52
|
-
import {
|
|
53
|
-
loadBuild,
|
|
54
|
-
pathsUnder,
|
|
55
|
-
serveApp,
|
|
56
|
-
serveBuild,
|
|
57
|
-
} from '#server/serveBuild.ts'
|
|
44
|
+
import { pathsUnder, serveBuild } from '#server/serveBuild.ts'
|
|
58
45
|
import { serveDev } from '#server/serveDev.ts'
|
|
59
46
|
import { escapeText } from '#shared/escapeText.ts'
|
|
60
47
|
import { ICON_ADDRESSES, LAUNCHER_PAGE } from '#shared/LAUNCHER_FILES.ts'
|
|
@@ -68,30 +55,16 @@ type AppDirectories = { root: string; out: string }
|
|
|
68
55
|
*/
|
|
69
56
|
const DISK: Host = {
|
|
70
57
|
name: 'abide',
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
},
|
|
82
|
-
appPackage: () => packageUnder(appRoot(undefined) ?? process.cwd()),
|
|
83
|
-
boot: createApp,
|
|
84
|
-
serve: inApp(async ({ root, out }, { values: { port } }) => {
|
|
85
|
-
if (!(await serveBuild(root, out, buildInProcess))) {
|
|
86
|
-
console.error(`abide start: nothing under ${root} to serve.`)
|
|
87
|
-
return 1
|
|
88
|
-
}
|
|
89
|
-
return serveApp(
|
|
90
|
-
originUnder(root),
|
|
91
|
-
() => loadBuild({ root, out }),
|
|
92
|
-
port === undefined ? undefined : Number(port),
|
|
93
|
-
)
|
|
94
|
-
}),
|
|
58
|
+
// 38.100 — built in this process rather than a child, where nobody built it, and said on
|
|
59
|
+
// stderr: the command's stdout is its own — `openapi`'s document, `mcp`'s messages.
|
|
60
|
+
app: ({ values }) =>
|
|
61
|
+
appOnDisk(directoriesOf(values), async (root, out) =>
|
|
62
|
+
(await serveBuild(root, out, (root, out) =>
|
|
63
|
+
writeBuild({ root, out }, console.error),
|
|
64
|
+
))
|
|
65
|
+
? out
|
|
66
|
+
: undefined,
|
|
67
|
+
),
|
|
95
68
|
source: {
|
|
96
69
|
scaffold: scaffoldCommand,
|
|
97
70
|
dev: inApp(dev),
|
|
@@ -104,22 +77,20 @@ const DISK: Host = {
|
|
|
104
77
|
},
|
|
105
78
|
}
|
|
106
79
|
|
|
107
|
-
// 38.100 — what `serveBuild` builds an app nobody built with, in this process rather than a child.
|
|
108
|
-
function buildInProcess(root: string, out: string): Promise<unknown> {
|
|
109
|
-
return writeBuild({ root, out })
|
|
110
|
-
}
|
|
111
|
-
|
|
112
80
|
/** `abide build`: compiles the app, bundles its client and copies its public files into `out`. */
|
|
113
81
|
async function build(directories: AppDirectories): Promise<number> {
|
|
114
82
|
await writeBuild(directories)
|
|
115
83
|
return 0
|
|
116
84
|
}
|
|
117
85
|
|
|
118
|
-
/**
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
86
|
+
/**
|
|
87
|
+
* `build`'s work; answers the app it built, or `undefined` where there was nothing under `root`.
|
|
88
|
+
* `say` is where its summary goes.
|
|
89
|
+
*/
|
|
90
|
+
async function writeBuild(
|
|
91
|
+
{ root, out }: AppDirectories,
|
|
92
|
+
say: (line: string) => void = console.log,
|
|
93
|
+
): Promise<CompiledApp | undefined> {
|
|
123
94
|
process.env.NODE_ENV ??= 'production'
|
|
124
95
|
// 38.66 — what this build may remove is only what was written before it began.
|
|
125
96
|
const began = Date.now()
|
|
@@ -136,7 +107,7 @@ async function writeBuild({
|
|
|
136
107
|
Object.keys(app.files).length === 0 &&
|
|
137
108
|
Object.keys(app.handlers).length === 0
|
|
138
109
|
) {
|
|
139
|
-
|
|
110
|
+
say(`abide build: no .abide sources or handlers under ${root}`)
|
|
140
111
|
return undefined
|
|
141
112
|
}
|
|
142
113
|
// 38.6 — THE COMPILATION, KEPT WHOLE: `abide start` serves every `.abide` out of it at the
|
|
@@ -144,28 +115,16 @@ async function writeBuild({
|
|
|
144
115
|
const compilation = compilationOf(app)
|
|
145
116
|
// 36.11 — the directories above `public` and the path below it ARE the address.
|
|
146
117
|
// `src/public/favicon.ico` answers at `/favicon.ico`, and nothing declares that anywhere.
|
|
147
|
-
|
|
148
|
-
// 36.13 IS CHECKED BEFORE A BYTE IS BUNDLED OR COPIED, through `refuseAddressCollision` — which
|
|
149
|
-
// `abide dev` runs too. Checking it mid-copy left a partial output behind on the throw, and it
|
|
150
|
-
// left the dev lane unchecked entirely.
|
|
151
|
-
refuseAddressCollision(app.routes, app.mounts, app.published, 'abide build')
|
|
152
|
-
await bundleClient(
|
|
153
|
-
root,
|
|
118
|
+
await writeClient(
|
|
154
119
|
out,
|
|
155
|
-
|
|
156
|
-
fenceServer(root, compilation.mounted),
|
|
157
|
-
abideModules(root, compilation),
|
|
158
|
-
...(await appPlugins(root)),
|
|
159
|
-
],
|
|
160
|
-
publicAddresses,
|
|
161
|
-
app.routes.length > 0,
|
|
120
|
+
await clientBundle(root, compilation, app.published, 'build'),
|
|
162
121
|
began,
|
|
163
122
|
)
|
|
164
123
|
// An app's static files are its own whether or not it has a page yet.
|
|
165
124
|
const copied = await copyPublic(root, out, app.published, began)
|
|
166
125
|
// LAST, because its presence is what `serveBuild` reads as a finished build (38.100).
|
|
167
126
|
await writeWhole(marker, JSON.stringify(compilation))
|
|
168
|
-
|
|
127
|
+
say(
|
|
169
128
|
`abide build: ${app.sources} source(s), ${copied} public file(s) -> ${out}`,
|
|
170
129
|
)
|
|
171
130
|
return app
|
|
@@ -559,69 +518,28 @@ async function copyPublic(
|
|
|
559
518
|
}
|
|
560
519
|
|
|
561
520
|
/**
|
|
562
|
-
*
|
|
521
|
+
* Writes the client bundle (`clientBundle`) into `out`, with its manifest and precompressed copies.
|
|
563
522
|
*
|
|
564
|
-
*
|
|
565
|
-
*
|
|
566
|
-
*
|
|
567
|
-
*
|
|
568
|
-
* `--splitting`, so a destination's markup and bindings stay in the chunk its own route loads
|
|
569
|
-
* (30.11) rather than landing in the entry every page pays for. 38.5 IS THIS FUNCTION: hashed and
|
|
570
|
-
* minified by the bundler, the manifest off its metafile (the entry's name, and the chunks each
|
|
571
|
-
* route module needs for 38.28), and the precompressed copies (`precompressOne`). What the manifest
|
|
572
|
-
* does NOT carry yet is 38.18's list of public files.
|
|
523
|
+
* 38.5 — the manifest beside the bundle, and every file in each encoding (`precompressed`). AN APP
|
|
524
|
+
* OF HANDLERS ALONE is written no client, and its manifest anyway, so `abide start` reads one shape
|
|
525
|
+
* whatever the app holds.
|
|
573
526
|
*/
|
|
574
|
-
async function
|
|
575
|
-
root: string,
|
|
527
|
+
async function writeClient(
|
|
576
528
|
out: string,
|
|
577
|
-
|
|
578
|
-
publicAddresses: string[],
|
|
579
|
-
paged: boolean,
|
|
529
|
+
bundle: ClientBundle,
|
|
580
530
|
began: number,
|
|
581
531
|
): Promise<void> {
|
|
582
532
|
const client = `${out}/${CLIENT_DIRECTORY}`
|
|
583
|
-
//
|
|
584
|
-
// written
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
await writeWhole(`${out}/${MANIFEST}`, JSON.stringify(NO_MANIFEST))
|
|
588
|
-
return
|
|
589
|
-
}
|
|
590
|
-
// 38.66 — STAGED, THEN MOVED IN FILE BY FILE. `Bun.build` writes in place, so a second build
|
|
591
|
-
// into the same `out` — or `abide start` serving it — read a chunk half written, and emptying
|
|
592
|
-
// `client/` first took the one it was serving away. Every name but the manifest's is a hash of
|
|
593
|
-
// what it holds, so two builds of one tree move in the same bytes under the same names.
|
|
533
|
+
// 38.66 — STAGED, THEN MOVED IN FILE BY FILE. A second build into the same `out` — or `abide
|
|
534
|
+
// start` serving it — read a chunk half written, and emptying `client/` first took the one it
|
|
535
|
+
// was serving away. Every name but the manifest's is a hash of what it holds, so two builds of
|
|
536
|
+
// one tree move in the same bytes under the same names.
|
|
594
537
|
const staging = `${out}/.staging-${crypto.randomUUID()}`
|
|
595
538
|
try {
|
|
596
|
-
const
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
// already `[name]-[hash]` by default and the entry is not, which left the one file every
|
|
601
|
-
// page loads served for a year under a name the next deploy writes different bytes to.
|
|
602
|
-
// The manifest is how the document learns what the entry was called this time.
|
|
603
|
-
//
|
|
604
|
-
// NAMES KEPT, because a class's name is what an error reads to say what it was handed
|
|
605
|
-
// (`canonicalKey`), and a production error naming `a e` is one nobody can act on. It is
|
|
606
|
-
// free where it is paid: the JS the docs app's `/values` sends is 25.6 KB brotli with
|
|
607
|
-
// the names and without, against 31.9 unminified.
|
|
608
|
-
minify: {
|
|
609
|
-
whitespace: true,
|
|
610
|
-
syntax: true,
|
|
611
|
-
identifiers: true,
|
|
612
|
-
keepNames: true,
|
|
613
|
-
},
|
|
614
|
-
naming: { entry: '[name]-[hash].[ext]' },
|
|
615
|
-
})
|
|
616
|
-
// THROWN RATHER THAN LOGGED. A build that emitted server modules and no bundle is an app that
|
|
617
|
-
// serves every page dead — the markup is right and nothing is wired — which is the one failure
|
|
618
|
-
// a reader cannot see, so it stops the command instead.
|
|
619
|
-
if (!built.success)
|
|
620
|
-
throw new AggregateError(
|
|
621
|
-
built.logs,
|
|
622
|
-
'abide build: the client bundle failed',
|
|
623
|
-
)
|
|
624
|
-
await Promise.all(built.outputs.map(precompressOne))
|
|
539
|
+
const written: Promise<void>[] = []
|
|
540
|
+
for (const [name, { bytes }] of bundle.files)
|
|
541
|
+
written.push(precompressed(`${staging}/${name}`, bytes))
|
|
542
|
+
await Promise.all(written)
|
|
625
543
|
const produced = new Set<string>()
|
|
626
544
|
const moves: Promise<void>[] = []
|
|
627
545
|
for (const name of namesUnder(staging)) {
|
|
@@ -634,17 +552,50 @@ async function bundleClient(
|
|
|
634
552
|
}
|
|
635
553
|
await Promise.all(moves)
|
|
636
554
|
await pruneStale(client, produced, began)
|
|
637
|
-
await writeWhole(
|
|
638
|
-
`${out}/${MANIFEST}`,
|
|
639
|
-
JSON.stringify(
|
|
640
|
-
manifestOf(built.metafile as Bun.BuildMetafile, root),
|
|
641
|
-
),
|
|
642
|
-
)
|
|
555
|
+
await writeWhole(`${out}/${MANIFEST}`, JSON.stringify(bundle.manifest))
|
|
643
556
|
} finally {
|
|
644
557
|
await rm(staging, { recursive: true, force: true })
|
|
645
558
|
}
|
|
646
559
|
}
|
|
647
560
|
|
|
561
|
+
/**
|
|
562
|
+
* Writes `bytes` to `path`, and brotli and gzip copies of them beside it.
|
|
563
|
+
*
|
|
564
|
+
* 38.5 — EVERY FILE THE BUNDLE WROTE, BESIDE ITSELF IN EACH OF `ENCODING_SUFFIX`'s ENCODINGS, at
|
|
565
|
+
* the highest level each has. The price is paid here once, where a server compressing per request
|
|
566
|
+
* would pay it on every response or settle for a worse level to keep the response fast.
|
|
567
|
+
*
|
|
568
|
+
* ALL AT ONCE. The async brotli runs off the main thread, so the files compress side by side
|
|
569
|
+
* rather than one after another — brotli at 11 is most of what this step costs.
|
|
570
|
+
*/
|
|
571
|
+
async function precompressed(
|
|
572
|
+
path: string,
|
|
573
|
+
bytes: Uint8Array<ArrayBuffer>,
|
|
574
|
+
): Promise<void> {
|
|
575
|
+
const brotli = await new Promise<Uint8Array>((compressed, failed) =>
|
|
576
|
+
brotliCompress(
|
|
577
|
+
bytes,
|
|
578
|
+
{
|
|
579
|
+
params: {
|
|
580
|
+
[constants.BROTLI_PARAM_QUALITY]:
|
|
581
|
+
constants.BROTLI_MAX_QUALITY,
|
|
582
|
+
[constants.BROTLI_PARAM_SIZE_HINT]: bytes.length,
|
|
583
|
+
},
|
|
584
|
+
},
|
|
585
|
+
(thrown, result) =>
|
|
586
|
+
thrown === null ? compressed(result) : failed(thrown),
|
|
587
|
+
),
|
|
588
|
+
)
|
|
589
|
+
await Promise.all([
|
|
590
|
+
Bun.write(path, bytes),
|
|
591
|
+
Bun.write(path + ENCODING_SUFFIX.br, brotli),
|
|
592
|
+
Bun.write(
|
|
593
|
+
path + ENCODING_SUFFIX.gzip,
|
|
594
|
+
Bun.gzipSync(bytes, { level: 9 }),
|
|
595
|
+
),
|
|
596
|
+
])
|
|
597
|
+
}
|
|
598
|
+
|
|
648
599
|
/**
|
|
649
600
|
* Removes from `directory` what this build did not produce.
|
|
650
601
|
*
|
|
@@ -680,50 +631,6 @@ async function writeWhole(path: string, content: string | Blob): Promise<void> {
|
|
|
680
631
|
await rename(partial, path)
|
|
681
632
|
}
|
|
682
633
|
|
|
683
|
-
/**
|
|
684
|
-
* Writes brotli and gzip copies of one bundle output beside it — a stylesheet's addresses written
|
|
685
|
-
* relative to the bundle first (`stylesheetUnderMount`).
|
|
686
|
-
*
|
|
687
|
-
* 38.5 — EVERY FILE THE BUNDLE WROTE, BESIDE ITSELF IN EACH OF `ENCODING_SUFFIX`'s ENCODINGS, at
|
|
688
|
-
* the highest level each has. The price is paid here once, where a server compressing per request
|
|
689
|
-
* would pay it on every response or settle for a worse level to keep the response fast.
|
|
690
|
-
*
|
|
691
|
-
* ALL AT ONCE. The async brotli runs off the main thread, so the files compress side by side
|
|
692
|
-
* rather than one after another — brotli at 11 is most of what this step costs.
|
|
693
|
-
*/
|
|
694
|
-
async function precompressOne(output: Bun.BuildArtifact): Promise<void> {
|
|
695
|
-
// `arrayBuffer` AND NOT `bytes`: the artifact's type declares both and it answers only the one.
|
|
696
|
-
let bytes = new Uint8Array(await output.arrayBuffer())
|
|
697
|
-
// A stylesheet is rewritten on disk before it is compressed, so all three copies carry it.
|
|
698
|
-
if (output.path.endsWith('.css')) {
|
|
699
|
-
bytes = new TextEncoder().encode(
|
|
700
|
-
stylesheetUnderMount(new TextDecoder().decode(bytes)),
|
|
701
|
-
)
|
|
702
|
-
await Bun.write(output.path, bytes)
|
|
703
|
-
}
|
|
704
|
-
const brotli = await new Promise<Uint8Array>((compressed, failed) =>
|
|
705
|
-
brotliCompress(
|
|
706
|
-
bytes,
|
|
707
|
-
{
|
|
708
|
-
params: {
|
|
709
|
-
[constants.BROTLI_PARAM_QUALITY]:
|
|
710
|
-
constants.BROTLI_MAX_QUALITY,
|
|
711
|
-
[constants.BROTLI_PARAM_SIZE_HINT]: bytes.length,
|
|
712
|
-
},
|
|
713
|
-
},
|
|
714
|
-
(thrown, result) =>
|
|
715
|
-
thrown === null ? compressed(result) : failed(thrown),
|
|
716
|
-
),
|
|
717
|
-
)
|
|
718
|
-
await Promise.all([
|
|
719
|
-
Bun.write(output.path + ENCODING_SUFFIX.br, brotli),
|
|
720
|
-
Bun.write(
|
|
721
|
-
output.path + ENCODING_SUFFIX.gzip,
|
|
722
|
-
Bun.gzipSync(bytes, { level: 9 }),
|
|
723
|
-
),
|
|
724
|
-
])
|
|
725
|
-
}
|
|
726
|
-
|
|
727
634
|
/**
|
|
728
635
|
* Wraps a command body with the app's absolute root and build directory, or fails with no app.
|
|
729
636
|
*
|
|
@@ -739,7 +646,7 @@ function inApp<Answer>(
|
|
|
739
646
|
const app = directoriesOf(given.values)
|
|
740
647
|
if (app === undefined) {
|
|
741
648
|
console.error(
|
|
742
|
-
`abide ${given.positionals[0]}:
|
|
649
|
+
`abide ${given.positionals[0]}: ${noAppHere()} Point --root at the directory your pages sit under.`,
|
|
743
650
|
)
|
|
744
651
|
return 1
|
|
745
652
|
}
|
package/docs/REGISTRY.md
CHANGED
|
@@ -89,7 +89,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
|
|
|
89
89
|
| --- | --- | --- | --- |
|
|
90
90
|
| `schema` | `Schema<Accepted>` | The gate on the way in. | 1.3, 4.3, 4.4, 4.5, 11.46, 11.47 |
|
|
91
91
|
| `transform` | `(value: Accepted) => Shaped` | The shaping on the way to storage. | 4.6, 4.7, 4.8, 4.9, 11.14 |
|
|
92
|
-
| `store` | `Store<Stored> \| ((args: Args) => Store<Stored>)` | Where the value lives when the process does not. | 8.1, 8.2, 8.3, 8.14, 9.5, 11.42 |
|
|
92
|
+
| `store` | `Store<Stored> \| ((args: Args) => Store<Stored>)` | Where the value lives when the process does not. | 8.1, 8.2, 8.3, 8.14, 9.5, 11.42, 11.71 |
|
|
93
93
|
| `identity` | `(next: Stored, previous: Stored) => unknown` | What makes it the same value. | 5.1, 5.2, 5.3, 5.4, 5.5, 9.3, 11.17, 18.4 |
|
|
94
94
|
| `structural` | `(next: unknown, previous: unknown) => boolean` | The by-value comparator an app declares on `identity`. | 5.4, 5.5, 18.4 |
|
|
95
95
|
| `by` | `(keyof Element & string) \| ((element: Element) => unknown)`, over the `Element` a `Stored` array holds | What tells one element of the value from another. | 5.22, 18.4, 18.15, 32.32 |
|
|
@@ -214,10 +214,10 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
|
|
|
214
214
|
| `Produced` | `unknown` | What it yields, per the unit rule. | 1.2, 16.3 |
|
|
215
215
|
| `Args` | `Record<string, JsonValue> \| undefined` | The arguments, in the body on a mutation and in the URL on a `GET` or `DELETE`. | 11.24, 16.18, 19.15 |
|
|
216
216
|
| `Middleware` | `<Ctx, Result>(next: (ctx?: Ctx) => Promise<Result>, ctx: Ctx) => Result \| Promise<Result>` | One rung of `middleware`. | 16.14, 16.15, 16.16, 16.21 |
|
|
217
|
-
| `GET` | `<Value, Args, Failures, RungFailures, Produced = Value>(handler: Reactive<Value, Value, Failures, Produced> \| Keyed<Value, Args, Value, Failures, Produced>, options?: RpcOptions<Args, RungFailures>) => Rpc<Value, Args, Failures \| RungFailures, Produced>` | Declares a read any surface may call. | 16.1, 16.2, 16.5, 16.6, 16.30, 16.31, 16.32, 16.68, 16.34, 16.35, 16.36, 16.37, 16.39, 16.44, 16.45, 16.46, 16.47, 16.50, 16.51, 16.52, 16.64, 18.1, 18.3, 18.6, 18.8, 18.10, 18.12, 18.21, 18.24 |
|
|
217
|
+
| `GET` | `<Value, Args, Failures, RungFailures, Produced = Value>(handler: Reactive<Value, Value, Failures, Produced> \| Keyed<Value, Args, Value, Failures, Produced>, options?: RpcOptions<Args, RungFailures>) => Rpc<Value, Args, Failures \| RungFailures, Produced>` | Declares a read any surface may call. | 16.1, 16.2, 16.5, 16.6, 16.30, 16.31, 16.32, 16.68, 16.76, 16.34, 16.35, 16.36, 16.37, 16.39, 16.44, 16.45, 16.46, 16.47, 16.75, 16.50, 16.51, 16.52, 16.64, 18.1, 18.3, 18.6, 18.8, 18.10, 18.12, 18.21, 18.24, 18.27 |
|
|
218
218
|
| `GET` | `<Value, Args, Failures, RungFailures, Produced = Value>(handler: (args?: Args) => Value, options?: RpcOptions<Args, RungFailures> & ReactiveOptions<Value, Value, Failures, Args>) => Rpc<Value, Args, Failures \| RungFailures, Produced>` | The plain-function overload, whose options build the value it becomes beside its address. | 16.1, 16.6, 16.7 |
|
|
219
219
|
| `GET` | `<Value, Failures, RungFailures>(handler: Value \| PromiseLike<Value>, options?: RpcOptions<undefined, RungFailures> & ReactiveOptions<Value, Value, Failures>) => Rpc<Value, undefined, Failures \| RungFailures>` | The value overload, for what the handler owns outright. | 16.1, 16.6, 16.7 |
|
|
220
|
-
| `POST` / `PUT` / `PATCH` / `DELETE` | same as `GET` | Declares a mutation. | 16.2, 16.7, 16.32, 16.68, 16.38, 16.53 |
|
|
220
|
+
| `POST` / `PUT` / `PATCH` / `DELETE` | same as `GET` | Declares a mutation. | 16.2, 16.7, 16.32, 16.68, 16.76, 16.38, 16.53 |
|
|
221
221
|
| `rpc.busy` | `(pattern?: Partial<Args>) => boolean` | `busy` narrowed to the handler's matching entries. | 13.2, 13.3, 13.12 |
|
|
222
222
|
| `rpc.ready` | `(pattern?: Partial<Args>) => boolean` | `ready` narrowed to the handler's matching entries. | 13.11, 13.12 |
|
|
223
223
|
| `rpc.invalidate` | `(pattern?: Partial<Args>) => void` | `invalidate` narrowed to the handler's matching entries. | 7.2, 7.3, 13.4, 13.12 |
|
|
@@ -225,7 +225,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
|
|
|
225
225
|
| `rpc.isError` | `<Name extends Failures['name']>(error: unknown, name: Name) => error is Extract<Failures, { name: Name }>` | Whether a caught failure is the one named. | 16.8 |
|
|
226
226
|
| `rpc.raw` | `(args: Args, init?: RequestInit) => Promise<Response>` | The same call handed back as the raw response. | 16.11, 16.42 |
|
|
227
227
|
| `rpc.method` | `string` | The HTTP method. | 16.2 |
|
|
228
|
-
| `rpc.url` | `(args?: Args) => string` | The address, resolved against the mount. | 16.9, 16.10 |
|
|
228
|
+
| `rpc.url` | `(args?: Args) => string` | The address, resolved against the mount. | 16.9, 16.10, 30.28 |
|
|
229
229
|
| `rpc.description` | `string \| undefined` | The human description. | 16.28 |
|
|
230
230
|
| `PatchOp` | `{ op: 'add' \| 'replace' \| 'test'; path: string; value: unknown } \| { op: 'remove'; path: string } \| { op: 'move' \| 'copy'; from: string; path: string }` | One JSON Patch operation (RFC 6902), its paths JSON Pointers, as a followed `GET` is sent what an `s.patch` wrote. | 18.11, 18.13 |
|
|
231
231
|
|
|
@@ -235,7 +235,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
|
|
|
235
235
|
| --- | --- | --- | --- |
|
|
236
236
|
| `description` | `string` | The human description carried onto every generated surface. | 16.28 |
|
|
237
237
|
| `middleware` | `Middleware<{ request: Request; args: () => Args }, Value \| Failures>[]` | The rungs run per call. | 16.11, 16.12, 16.16, 16.17, 16.18, 16.19, 16.20 |
|
|
238
|
-
| `timeout` | `number` | How long the call may stay open, and the floor under the request's idle timeout. | 16.22, 16.23, 16.56, 16.57, 16.58, 16.59, 16.60, 16.61 |
|
|
238
|
+
| `timeout` | `number` | How long the call may stay open, and the floor under the request's idle timeout. | 16.22, 16.23, 16.56, 16.57, 16.58, 16.59, 16.60, 16.61, 16.77 |
|
|
239
239
|
| `clients` | `Clients` | Which surfaces carry this handler. | 16.37, 20.3 |
|
|
240
240
|
| `crossOrigin` | `boolean \| string[]` | Whether other origins may call it, or follow it over the mux. | 16.24, 18.2, 21.19 |
|
|
241
241
|
| `maxBodySize` | `number` | The largest request body a mutation will accept, in bytes. | 16.13, 16.54, 16.55 |
|
|
@@ -245,12 +245,12 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
|
|
|
245
245
|
| Name | Signature | Meaning | Rules |
|
|
246
246
|
| --- | --- | --- | --- |
|
|
247
247
|
| `Values<T>` | `Iterable<T> \| AsyncIterable<T> \| ReadableStream<T>` | What every body helper takes. | 17.1 |
|
|
248
|
-
| `page` | `<Body extends string \| Uint8Array \| Values<Uint8Array \| string>>(body: Body) => Body` | A rendered document as `text/html`. | 17.2 |
|
|
248
|
+
| `page` | `<Body extends string \| Uint8Array \| Values<Uint8Array \| string>>(body: Body) => Body` | A rendered document as `text/html`. | 16.75, 17.2 |
|
|
249
249
|
| `json` | `<T>(value: T) => T` | A value as JSON, fixed, with `undefined` sent as `null`. | 17.7, 17.9, 17.10 |
|
|
250
250
|
| `redirect` | `(to: string, status?: RedirectStatus) => undefined` | A navigation. | 16.52, 17.3 |
|
|
251
|
-
| `jsonl` | `<T>(values: Values<T>) => Values<T>` | One JSON value per line, fixed. | 16.44, 17.6, 17.7 |
|
|
252
|
-
| `sse` | `<T>(values: Values<T>) => Values<T>` | The same machine framed as server-sent events, fixed. | 16.45, 17.6, 17.7 |
|
|
253
|
-
| `bytes` | `(values: Values<Uint8Array>) => Values<Uint8Array>` | A stream of binary chunks, fixed. | 16.34, 17.6, 17.7 |
|
|
251
|
+
| `jsonl` | `<T>(values: Values<T>) => Values<T>` | One JSON value per line, fixed. | 16.44, 16.75, 17.6, 17.7 |
|
|
252
|
+
| `sse` | `<T>(values: Values<T>) => Values<T>` | The same machine framed as server-sent events, fixed. | 16.45, 16.75, 17.6, 17.7 |
|
|
253
|
+
| `bytes` | `(values: Values<Uint8Array>) => Values<Uint8Array>` | A stream of binary chunks, fixed. | 16.34, 16.75, 17.6, 17.7 |
|
|
254
254
|
| `RedirectStatus` | `301 \| 302 \| 303 \| 307 \| 308` | The statuses a `redirect` may carry. | 17.3 |
|
|
255
255
|
| `refuse` | `(status: number, message?: string) => Failed<'HttpError', undefined>` | An undeclared refusal at a status. | 15.5, 15.9, 15.10, 15.16, 15.19, 15.20, 16.40, 17.4, 17.5, 17.8 |
|
|
256
256
|
|
|
@@ -490,7 +490,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
|
|
|
490
490
|
| `RequiredNames<P>` | `string` | The required segment names. | 30.3 |
|
|
491
491
|
| `OptionalNames<P>` | `string` | The optional segment names. | 30.4 |
|
|
492
492
|
| `RestNames<P>` | `string` | The rest segment names. | 30.5 |
|
|
493
|
-
| `Query` | `Record<string, unknown> \| URLSearchParams` | The query half. | 30.6 |
|
|
493
|
+
| `Query` | `Record<string, unknown> \| URLSearchParams` | The query half. | 30.6, 30.28 |
|
|
494
494
|
| `ParamsOf<P>` | `& { [K in RequiredNames<P>]: string \| number } & { [K in OptionalNames<P>]?: string \| number } & { [K in RestNames<P>]?: string \| number \| (string \| number)[] }` | What a route literal's segments accept. | 30.3, 30.4, 30.5 |
|
|
495
495
|
| `RouteParams<P>` | `{ [K in RequiredNames<P>]: string } & { [K in OptionalNames<P>]?: string } & { [K in RestNames<P>]: string[] }` | What a page filed under `P` reads its params as. | 23.9 |
|
|
496
496
|
| `Routes` | `interface Routes {}` | The app's route literals and public file addresses, which `abide check` declares. | 30.26 |
|
|
@@ -564,7 +564,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
|
|
|
564
564
|
| Spelling | Meaning | Rules |
|
|
565
565
|
| --- | --- | --- |
|
|
566
566
|
| `{#component Name(pattern)}` | An inline component. | 32.39, 32.40, 32.41, 32.42, 32.43, 32.44, 32.45, 32.46, 32.47, 32.48, 32.49, 32.50, 32.51, 32.52, 32.53, 32.54, 32.55 |
|
|
567
|
-
| `<Name/>` | A component invocation. | 32.17, 32.34, 32.35, 32.36, 33.19, 33.21, 41.24 |
|
|
567
|
+
| `<Name/>` | A component invocation. | 32.17, 32.34, 32.35, 32.36, 33.19, 33.21, 41.24, 41.28 |
|
|
568
568
|
| `<slot/>` | The position children render into. | 32.18 |
|
|
569
569
|
| `<slot>fallback</slot>` | Renders children, or a fallback. | 32.18 |
|
|
570
570
|
| `<Tag>…</Tag>` | Children passed to a component's slot. | 32.18 |
|
|
@@ -594,7 +594,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
|
|
|
594
594
|
|
|
595
595
|
| Path | Meaning | Rules |
|
|
596
596
|
| --- | --- | --- |
|
|
597
|
-
| `<head>` | A page's contribution to the document head. | 39.1, 39.2, 39.3, 39.4, 39.5, 39.6, 39.11, 39.12, 39.13, 39.14 |
|
|
597
|
+
| `<head>` | A page's, layout's or component's contribution to the document head. | 39.1, 39.2, 39.3, 39.4, 39.5, 39.6, 39.11, 39.12, 39.13, 39.14, 39.15, 39.16 |
|
|
598
598
|
| `view-transition-name` | What turns view transitions on, in the app's own stylesheet. | 39.7, 39.8, 39.9, 39.10 |
|
|
599
599
|
| `[name]` | A required dynamic segment. | 16.65, 23.5, 30.3 |
|
|
600
600
|
| `[[name]]` | An optional segment. | 23.5, 30.4 |
|
|
@@ -606,7 +606,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
|
|
|
606
606
|
| Name | Signature | Meaning | Rules |
|
|
607
607
|
| --- | --- | --- | --- |
|
|
608
608
|
| `Component` | `interface Component {}` | A component bound to its props. | 32.17, 34.16, 35.4 |
|
|
609
|
-
| `render` | `(component: Component) => Promise<Uint8Array>` | What produces a document. | 29.10, 35.4, 35.5, 35.6, 35.15, 35.16, 41.1, 41.2, 41.3, 41.4, 41.9, 41.13, 41.16, 41.17, 41.18, 41.19, 41.20, 35.11 |
|
|
609
|
+
| `render` | `(component: Component) => Promise<Uint8Array>` | What produces a document. | 29.10, 35.4, 35.5, 35.6, 35.15, 35.16, 41.1, 41.2, 41.3, 41.4, 41.9, 41.13, 41.16, 41.17, 41.18, 41.19, 41.20, 41.28, 35.11 |
|
|
610
610
|
|
|
611
611
|
## Sinks
|
|
612
612
|
|
|
@@ -718,11 +718,11 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
|
|
|
718
718
|
| `abide routes [--json]` | Every address the app answers, the file answering it, and where each hook is filed. | 38.108 |
|
|
719
719
|
| `abide dev [--port <n>]` | Watches the project and keeps the app up. | 37.8, 37.9, 38.4, 38.14, 38.15, 38.16, 38.17, 38.19, 38.24, 38.25, 38.26, 38.28, 38.30, 38.35, 38.36, 38.37, 38.38, 38.41, 38.43, 38.58, 38.63, 38.64, 38.65, 38.94, 38.96, 38.97 |
|
|
720
720
|
| `abide build` | Builds the client into content-hashed chunks and a manifest. | 38.43, 38.5, 38.18, 38.25, 38.26, 38.27, 38.30, 38.66, 38.94 |
|
|
721
|
-
| `abide start [--port <n>]` | Boots the built app. | 37.8, 37.10, 37.11, 37.12, 38.6, 38.100, 38.20, 38.21, 38.22, 38.23, 38.25, 38.26, 38.27, 38.28, 38.29, 38.58, 38.67, 38.68, 38.95, 38.97 |
|
|
721
|
+
| `abide start [--port <n>]` | Boots the built app. | 37.8, 37.10, 37.11, 37.12, 38.6, 38.100, 38.20, 38.21, 38.22, 38.23, 38.25, 38.26, 38.27, 38.28, 38.29, 38.58, 38.67, 38.68, 38.95, 38.97, 38.110 |
|
|
722
722
|
| `abide [--url <origin>] [--port <n>]` | The interactive shell — on the built app it boots, or on the running app `--url` names. | 37.8, 37.10, 37.11, 37.12, 38.6, 38.7, 38.53, 38.54, 38.55, 38.58, 38.59, 38.60, 38.62 |
|
|
723
|
-
| `abide call <address> [args]` | One call to one handler. | 38.8, 38.61 |
|
|
724
|
-
| `abide openapi [--
|
|
725
|
-
| `abide mcp [--url <origin>]` | The app's MCP server over stdio. | 38.9, 38.13 |
|
|
723
|
+
| `abide call <address> [args]` | One call to one handler. | 38.8, 38.61, 38.110 |
|
|
724
|
+
| `abide openapi [--url <origin>]` | The OpenAPI document printed rather than served. | 38.9, 38.25, 38.110, 38.111 |
|
|
725
|
+
| `abide mcp [--url <origin>]` | The app's MCP server over stdio. | 38.9, 38.13, 38.25, 38.110 |
|
|
726
726
|
| `abide watch <address> [args]` | A live subscription to one of the app's followed `GET`s. | 38.69 |
|
|
727
727
|
| `abide compile [--target] [--out] [--platforms]` | One standalone executable. | 37.8, 37.10, 37.11, 37.12, 38.10, 38.25, 38.26, 38.44, 38.45, 38.46, 38.47, 38.48, 38.49, 38.50, 38.51, 38.52, 38.56, 38.93, 38.97 |
|
|
728
728
|
| `abide bundle [--out]` | A desktop app for the host platform, with a window on the app. | 38.10, 38.25, 38.26, 38.70, 38.71, 38.72, 38.73, 38.74, 38.75, 38.76, 38.77, 38.78, 38.79, 38.80, 38.81, 38.82, 38.83, 38.84, 38.85, 38.86, 38.87, 38.88, 38.89, 38.90, 38.91, 38.92, 38.101, 38.102 |
|