@abide/abide 0.102.0 → 0.104.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 +23 -0
- package/bin/abide.ts +31 -28
- package/docs/REGISTRY.md +40 -36
- package/docs/RULEBOOK.md +113 -92
- package/docs/abide.md +788 -554
- package/package.json +2 -1
- package/scaffold/AGENTS.md +13 -6
- package/src/compiler/TypeSession.ts +108 -35
- package/src/compiler/appMap.ts +91 -0
- package/src/compiler/argsSchemaOf.ts +1 -1
- package/src/compiler/checkApp.ts +35 -18
- package/src/compiler/checkedShadow.ts +40 -7
- package/src/compiler/compileApp.ts +271 -76
- package/src/compiler/compiledEntry.ts +10 -6
- package/src/compiler/componentGraph.ts +6 -7
- package/src/compiler/declaredExports.ts +3 -3
- package/src/compiler/emitClient.ts +3 -3
- package/src/compiler/emitComponent.ts +9 -9
- package/src/compiler/emitSource.ts +5 -9
- package/src/compiler/handlersUnder.ts +114 -73
- package/src/compiler/hooksCalled.ts +35 -0
- package/src/compiler/mountsOf.ts +30 -18
- package/src/compiler/parseAbide.ts +12 -12
- package/src/compiler/projectShadow.ts +71 -46
- package/src/compiler/readsIn.ts +2 -2
- package/src/compiler/scaffold.ts +24 -21
- package/src/compiler/serveLanguage.ts +0 -24
- package/src/compiler/sharedModuleWrites.ts +2 -2
- package/src/compiler/splitImports.ts +4 -4
- package/src/compiler/writeDefaultProject.ts +28 -1
- package/src/server/MODULE_QUERIES.ts +12 -3
- package/src/server/abideModules.ts +156 -53
- package/src/server/answerRequest.ts +9 -4
- package/src/server/answerRpc.ts +18 -16
- package/src/server/appTree.ts +92 -25
- package/src/server/commands.ts +5 -4
- package/src/server/componentModules.d.ts +1 -2
- package/src/server/createApp.ts +23 -11
- package/src/server/devWorker.ts +5 -1
- package/src/server/fenceServer.ts +36 -13
- package/src/server/generatedSurfaces.ts +12 -53
- package/src/server/hooks.ts +17 -4
- package/src/server/index.ts +1 -0
- package/src/server/installSourceMaps.ts +6 -1
- package/src/server/launcherOptions.ts +12 -13
- package/src/server/launcherPage.ts +2 -2
- package/src/server/nativeMenu.ts +3 -3
- package/src/server/preload.ts +13 -0
- package/src/server/runDesktop.ts +11 -16
- package/src/server/serveDev.ts +8 -2
- package/src/server/surfaceOf.ts +2 -0
- package/src/server/versionOf.ts +1 -2
- package/src/server/withAbideSources.ts +1 -1
- package/src/shared/ADDRESSES.ts +3 -0
- package/src/shared/LAUNCHER_FILES.ts +9 -0
- package/src/shared/RESERVED_DIRECTORIES.ts +19 -4
- package/src/shared/index.ts +2 -1
- package/src/{ui → shared}/menuBridge.ts +1 -1
- package/src/shared/menuItem.ts +2 -2
- package/src/shared/onMenu.ts +58 -0
- package/src/shared/reactive/Cursor.ts +66 -123
- package/src/shared/reactive/Reactive.ts +74 -28
- package/src/shared/reactive/callLoad.ts +4 -18
- package/src/shared/reactive/chunksOfStream.ts +10 -34
- package/src/{ui → shared}/reportMenu.ts +7 -9
- package/src/shared/routePattern.ts +74 -23
- package/src/shared/rpc.ts +1 -1
- package/src/ui/Router.ts +0 -6
- package/src/compiler/syncTypes.ts +0 -32
- package/src/compiler/writeTypes.ts +0 -82
- package/src/server/LAUNCHER_FILES.ts +0 -9
- /package/scaffold/src/{page.abide → index.abide} +0 -0
- /package/src/{compiler → shared}/SPECIFIERS.ts +0 -0
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
// WHICH FACE AN IMPORT GETS IS SPELLED ON ITS SPECIFIER (`MODULE_QUERIES`), because bun hands a plugin
|
|
8
8
|
// no import attributes: a compiled module asks for `?component`, a browser module for its own
|
|
9
9
|
// `?style`, a text import for `?text`. A `.abide` named bare is a module abide did not compile
|
|
10
|
-
// asking — on a server that is the bound face 34.16 states,
|
|
11
|
-
//
|
|
10
|
+
// asking — on a server that is the bound face 34.16 states, and in a browser the component module
|
|
11
|
+
// itself.
|
|
12
12
|
//
|
|
13
13
|
// THE TABLES — the route, error and handler tables, and in a browser the router's table and the
|
|
14
14
|
// entry — are the build's own modules, served from `__abide` below the root, where no app writes.
|
|
@@ -17,13 +17,16 @@
|
|
|
17
17
|
// that registered one, so a second app booted in the same process — a test's — adds its compilation
|
|
18
18
|
// to this one rather than registering a plugin nobody asks.
|
|
19
19
|
|
|
20
|
-
import { dirname, resolve } from 'node:path' // bun exposes no path helpers of its own
|
|
20
|
+
import { basename, dirname, resolve } from 'node:path' // bun exposes no path helpers of its own
|
|
21
|
+
import { SERVER_SPECIFIER } from '#shared/SPECIFIERS.ts'
|
|
21
22
|
import type { ServerTable } from './answerRequest.ts'
|
|
22
23
|
import type { MountRow } from './answerRpc.ts'
|
|
23
24
|
import { installSourceMaps, type SourceMaps } from './installSourceMaps.ts'
|
|
24
25
|
import {
|
|
25
26
|
CLIENT_QUERY,
|
|
26
27
|
COMPONENT_QUERY,
|
|
28
|
+
HANDLER_FACE,
|
|
29
|
+
HANDLER_SUFFIX,
|
|
27
30
|
STYLE_QUERY,
|
|
28
31
|
TEXT_QUERY,
|
|
29
32
|
} from './MODULE_QUERIES.ts'
|
|
@@ -51,8 +54,18 @@ export type CompiledFile = {
|
|
|
51
54
|
export type Compilation = {
|
|
52
55
|
/** Every `.abide` compiled, by its path below the root. */
|
|
53
56
|
files: Record<string, CompiledFile>
|
|
57
|
+
/**
|
|
58
|
+
* Every handler file, by its path below the root: the route its hooks are filed at (37.1), and
|
|
59
|
+
* the module a browser is handed in its place (16.74), `''` where the browser is handed none.
|
|
60
|
+
*/
|
|
61
|
+
handlers: Record<string, HandlerFace>
|
|
54
62
|
/** The build's own modules, by name below `__abide`. */
|
|
55
63
|
tables: Record<string, string>
|
|
64
|
+
/**
|
|
65
|
+
* 23.19 — every directory a file mounts, by its path below the root, to the position its files
|
|
66
|
+
* take: what the browser's fence reads a mounted file's place by (16.29).
|
|
67
|
+
*/
|
|
68
|
+
mounted: Record<string, string>
|
|
56
69
|
}
|
|
57
70
|
|
|
58
71
|
// A VIRTUAL FACE'S PATH: its file's, with a suffix that is not a query. Both registries key a module
|
|
@@ -68,29 +81,40 @@ export const COMPONENT_NAMESPACE = 'abide-component'
|
|
|
68
81
|
/** The namespaces a file's text and a component's stylesheet are loaded in. */
|
|
69
82
|
export const TEXT_NAMESPACE = 'abide-text'
|
|
70
83
|
export const STYLE_NAMESPACE = 'abide-style'
|
|
84
|
+
const RELATIVE_OR_SUBPATH = /^(?:\.\.?\/|#)/
|
|
71
85
|
|
|
72
|
-
/**
|
|
73
|
-
type
|
|
74
|
-
file: (path: string) => CompiledFile | undefined
|
|
75
|
-
table: (path: string) => string | undefined
|
|
76
|
-
}
|
|
86
|
+
/** One handler file, as `Compilation.handlers` holds it. */
|
|
87
|
+
type HandlerFace = { scope: string; client: string }
|
|
77
88
|
|
|
78
89
|
/** The plugin serving `compilation`'s files and tables, for one bundle. */
|
|
79
90
|
export function abideModules(
|
|
80
91
|
root: string,
|
|
81
92
|
compilation: Compilation,
|
|
82
93
|
): Bun.BunPlugin {
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
94
|
+
const held: Held = {
|
|
95
|
+
files: new Map(),
|
|
96
|
+
handlers: new Map(),
|
|
97
|
+
tables: new Map(),
|
|
98
|
+
}
|
|
99
|
+
addCompilation(realOrAsGiven(root), compilation, held)
|
|
100
|
+
return modulesPlugin(held)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* A compilation as the plugin keys it, and answers from: a compiled file, a handler file or a table
|
|
105
|
+
* by absolute path.
|
|
106
|
+
*/
|
|
107
|
+
type Held = {
|
|
108
|
+
files: Map<string, CompiledFile>
|
|
109
|
+
handlers: Map<string, HandlerFace>
|
|
110
|
+
tables: Map<string, string>
|
|
90
111
|
}
|
|
91
112
|
|
|
92
|
-
const
|
|
93
|
-
|
|
113
|
+
const RUNTIME: Held = {
|
|
114
|
+
files: new Map(),
|
|
115
|
+
handlers: new Map(),
|
|
116
|
+
tables: new Map(),
|
|
117
|
+
}
|
|
94
118
|
let registered = false
|
|
95
119
|
|
|
96
120
|
/**
|
|
@@ -99,16 +123,37 @@ let registered = false
|
|
|
99
123
|
*/
|
|
100
124
|
export function serveCompilation(root: string, compilation: Compilation): void {
|
|
101
125
|
const real = realOrAsGiven(root)
|
|
102
|
-
addCompilation(real, compilation,
|
|
126
|
+
const handlerFiles = addCompilation(real, compilation, RUNTIME)
|
|
103
127
|
installSourceMaps(serverMapsOf(compilation, real), real)
|
|
128
|
+
if (handlerFiles.length !== 0) Bun.plugin(handlerFilesPlugin(handlerFiles))
|
|
104
129
|
if (registered) return
|
|
105
130
|
registered = true
|
|
106
|
-
Bun.plugin(
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
131
|
+
Bun.plugin(modulesPlugin(RUNTIME))
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// 16.2 — A HANDLER FILE LOADED AT ITS OWN PATH RE-EXPORTS ITS FACE, so every spelling of its import
|
|
135
|
+
// is the module the server mounted. The runtime asks `onResolve` only for a specifier with an
|
|
136
|
+
// extension, while `onLoad` is asked by the path the file resolved to, however it was spelled. One
|
|
137
|
+
// plugin per compilation because the filter is fixed at registration, and the runtime's `onLoad`
|
|
138
|
+
// cannot fall through, so the filter names the handler files and nothing else. The face it names
|
|
139
|
+
// is resolved by `modulesPlugin`, registered once.
|
|
140
|
+
function handlerFilesPlugin(paths: string[]): Bun.BunPlugin {
|
|
141
|
+
return {
|
|
142
|
+
name: 'abide-handler-files',
|
|
143
|
+
setup(build) {
|
|
144
|
+
build.onLoad(
|
|
145
|
+
{
|
|
146
|
+
filter: new RegExp(
|
|
147
|
+
`^(?:${paths.map(RegExp.escape).join('|')})$`,
|
|
148
|
+
),
|
|
149
|
+
},
|
|
150
|
+
(args) => ({
|
|
151
|
+
contents: `export { default } from ${JSON.stringify(`${args.path}${HANDLER_SUFFIX}`)}\n`,
|
|
152
|
+
loader: 'js',
|
|
153
|
+
}),
|
|
154
|
+
)
|
|
155
|
+
},
|
|
156
|
+
}
|
|
112
157
|
}
|
|
113
158
|
|
|
114
159
|
/** What the server's table exports: its routes and error pages, server blocks and handlers. */
|
|
@@ -143,17 +188,24 @@ export function serverMapsOf(
|
|
|
143
188
|
return maps
|
|
144
189
|
}
|
|
145
190
|
|
|
191
|
+
/** Adds `compilation` to `held`, and answers its handler files' absolute paths. */
|
|
146
192
|
function addCompilation(
|
|
147
193
|
root: string,
|
|
148
194
|
compilation: Compilation,
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
): void {
|
|
195
|
+
held: Held,
|
|
196
|
+
): string[] {
|
|
152
197
|
// RESOLVED, not joined: a dependency's file is keyed above the root (`../node_modules/…`).
|
|
153
198
|
for (const [relative, compiled] of Object.entries(compilation.files))
|
|
154
|
-
files.set(resolve(root, relative), compiled)
|
|
199
|
+
held.files.set(resolve(root, relative), compiled)
|
|
200
|
+
const handlerFiles: string[] = []
|
|
201
|
+
for (const [relative, face] of Object.entries(compilation.handlers)) {
|
|
202
|
+
const path = resolve(root, relative)
|
|
203
|
+
held.handlers.set(path, face)
|
|
204
|
+
handlerFiles.push(path)
|
|
205
|
+
}
|
|
155
206
|
for (const [name, text] of Object.entries(compilation.tables))
|
|
156
|
-
tables.set(`${root}/${VIRTUAL_DIRECTORY}/${name}`, text)
|
|
207
|
+
held.tables.set(`${root}/${VIRTUAL_DIRECTORY}/${name}`, text)
|
|
208
|
+
return handlerFiles
|
|
157
209
|
}
|
|
158
210
|
|
|
159
211
|
// `Bun.resolveSync` asks EVERY abide plugin's `onResolve` again — this one's, and the runtime's, which
|
|
@@ -178,7 +230,7 @@ export function resolvedFile(specifier: string, from: string): string {
|
|
|
178
230
|
}
|
|
179
231
|
}
|
|
180
232
|
|
|
181
|
-
function modulesPlugin(served:
|
|
233
|
+
function modulesPlugin(served: Held): Bun.BunPlugin {
|
|
182
234
|
return {
|
|
183
235
|
name: 'abide-modules',
|
|
184
236
|
setup(build) {
|
|
@@ -264,12 +316,54 @@ function modulesPlugin(served: Served): Bun.BunPlugin {
|
|
|
264
316
|
dirname(pathOf(args.importer)),
|
|
265
317
|
args.path,
|
|
266
318
|
)
|
|
267
|
-
return served.
|
|
319
|
+
return served.tables.get(path) === undefined
|
|
268
320
|
? undefined
|
|
269
321
|
: { path, namespace: 'abide-table' }
|
|
270
322
|
},
|
|
271
323
|
)
|
|
272
324
|
|
|
325
|
+
// A HANDLER FILE IS FOUND BY WHERE ITS IMPORT RESOLVES, however the import spells it, and
|
|
326
|
+
// only from the app's own modules: a package's never names one, and asking bun again for
|
|
327
|
+
// each of a package's relative imports is a resolve per module of the bundle. THE FILTER
|
|
328
|
+
// IS THE HANDLER FILES' NAMES: a hook matching every relative import costs the bundle
|
|
329
|
+
// abide's own `sideEffects`, and the barrel's rest — the validator, the csp — moved into
|
|
330
|
+
// the chunk every page loads (38.94). ONLY IN A BUNDLE: the runtime finds a handler file
|
|
331
|
+
// by the path it loads at (`handlerFilesPlugin`), which costs no resolve per import.
|
|
332
|
+
// Each handler file's name without `.ts` — what an import of one ends in.
|
|
333
|
+
const stems = new Set<string>()
|
|
334
|
+
if (bundling)
|
|
335
|
+
for (const path of served.handlers.keys())
|
|
336
|
+
stems.add(basename(path, '.ts'))
|
|
337
|
+
if (stems.size !== 0)
|
|
338
|
+
build.onResolve(
|
|
339
|
+
{
|
|
340
|
+
filter: new RegExp(
|
|
341
|
+
`(?:^|/)(?:${[...stems].map(RegExp.escape).join('|')})(?:\\.ts)?$`,
|
|
342
|
+
),
|
|
343
|
+
},
|
|
344
|
+
(args) => {
|
|
345
|
+
if (resolving || !RELATIVE_OR_SUBPATH.test(args.path))
|
|
346
|
+
return undefined
|
|
347
|
+
const importer = pathOf(args.importer)
|
|
348
|
+
if (importer.includes('/node_modules/'))
|
|
349
|
+
return undefined
|
|
350
|
+
let path: string
|
|
351
|
+
try {
|
|
352
|
+
path = resolved(args.path, args.importer)
|
|
353
|
+
} catch {
|
|
354
|
+
return undefined
|
|
355
|
+
}
|
|
356
|
+
const face = served.handlers.get(path)
|
|
357
|
+
if (face === undefined) return undefined
|
|
358
|
+
// 20.4 — a handler the browser is handed no wrapper for is no import of its.
|
|
359
|
+
if (inBrowser(args.importer) && face.client === '')
|
|
360
|
+
throw new Error(
|
|
361
|
+
`abide: ${pathOf(args.importer)} imports ${args.path}, whose handler withholds itself from the browser (\`clients: { ui: false }\`). Call it from a <script server>, or let the browser have it. RULEBOOK 20.4.`,
|
|
362
|
+
)
|
|
363
|
+
return { path: `${path}${HANDLER_SUFFIX}` }
|
|
364
|
+
},
|
|
365
|
+
)
|
|
366
|
+
|
|
273
367
|
// A RELATIVE SPECIFIER A FACE IMPORTS AT RUN TIME is resolved from the file the face
|
|
274
368
|
// stands for: bun's runtime reads one from an importer in a namespace as no path at
|
|
275
369
|
// all. A bare one it resolves itself. Registered after every face, so a `.abide` is
|
|
@@ -277,12 +371,18 @@ function modulesPlugin(served: Served): Bun.BunPlugin {
|
|
|
277
371
|
// and where a hook matching every relative import in a package costs the bundle that
|
|
278
372
|
// package's `sideEffects`: a two-page app grew by 12 KB, the validator one page never
|
|
279
373
|
// calls moved into the chunk both pages load.
|
|
280
|
-
if (!bundling)
|
|
374
|
+
if (!bundling) {
|
|
375
|
+
// A face `handlerFilesPlugin` re-exports, named by its absolute path, which no file
|
|
376
|
+
// is at.
|
|
377
|
+
build.onResolve({ filter: HANDLER_FACE }, (args) => ({
|
|
378
|
+
path: args.path,
|
|
379
|
+
}))
|
|
281
380
|
build.onResolve({ filter: /^\.\.?\// }, (args) =>
|
|
282
381
|
resolving || pathOf(args.importer) === args.importer
|
|
283
382
|
? undefined
|
|
284
383
|
: { path: resolved(args.path, args.importer) },
|
|
285
384
|
)
|
|
385
|
+
}
|
|
286
386
|
|
|
287
387
|
build.onLoad(
|
|
288
388
|
{ filter: /\.abide$/, namespace: componentNamespace },
|
|
@@ -294,6 +394,22 @@ function modulesPlugin(served: Served): Bun.BunPlugin {
|
|
|
294
394
|
}
|
|
295
395
|
},
|
|
296
396
|
)
|
|
397
|
+
// 16.74, 37.1 — A HANDLER FILE: the browser's is its wrapper, and the server's is the file
|
|
398
|
+
// as written, between the two calls that file its hooks at its route — on its first
|
|
399
|
+
// line, so every line of the file stays where its frames name it. ITS FACE is its path
|
|
400
|
+
// with a suffix, in bun's own namespace — so what it imports, relative, `#` or bare,
|
|
401
|
+
// resolves from its directory as the file's own imports do. A namespace of abide's own
|
|
402
|
+
// had bun resolve a bare one from the working directory: a second abide, run from `/`.
|
|
403
|
+
build.onLoad({ filter: HANDLER_FACE }, async (args) => {
|
|
404
|
+
const path = args.path.slice(0, -HANDLER_SUFFIX.length)
|
|
405
|
+
const face = served.handlers.get(path) as HandlerFace
|
|
406
|
+
if (browser) return { contents: face.client, loader: 'js' }
|
|
407
|
+
const text = await Bun.file(path).text()
|
|
408
|
+
return {
|
|
409
|
+
contents: `import { fileRoute as __fileRoute } from '${SERVER_SPECIFIER}';__fileRoute(${JSON.stringify(face.scope)});${text}\n;__fileRoute(undefined)\n`,
|
|
410
|
+
loader: 'ts',
|
|
411
|
+
}
|
|
412
|
+
})
|
|
297
413
|
build.onLoad(
|
|
298
414
|
{ filter: /.*/, namespace: 'abide-client' },
|
|
299
415
|
(args) => ({
|
|
@@ -307,10 +423,7 @@ function modulesPlugin(served: Served): Bun.BunPlugin {
|
|
|
307
423
|
build.onLoad({ filter: /.*/, namespace: 'abide-bound' }, (args) => {
|
|
308
424
|
const path = args.path.slice(0, -BOUND.length)
|
|
309
425
|
return {
|
|
310
|
-
contents: boundModule(
|
|
311
|
-
path,
|
|
312
|
-
compiledAt(served, path).server,
|
|
313
|
-
),
|
|
426
|
+
contents: boundModule(path),
|
|
314
427
|
loader: 'js',
|
|
315
428
|
}
|
|
316
429
|
})
|
|
@@ -339,7 +452,7 @@ function modulesPlugin(served: Served): Bun.BunPlugin {
|
|
|
339
452
|
build.onLoad(
|
|
340
453
|
{ filter: /.*/, namespace: 'abide-table' },
|
|
341
454
|
(args) => ({
|
|
342
|
-
contents: served.
|
|
455
|
+
contents: served.tables.get(args.path) ?? '',
|
|
343
456
|
loader: 'ts',
|
|
344
457
|
}),
|
|
345
458
|
)
|
|
@@ -355,8 +468,8 @@ function pathOf(importer: string): string {
|
|
|
355
468
|
|
|
356
469
|
// THE PATH BUN NAMES A FILE BY HAS ITS LINKS RESOLVED, and so has the root a compilation is served
|
|
357
470
|
// under (`serveCompilation`).
|
|
358
|
-
function compiledAt(served:
|
|
359
|
-
const compiled = served.
|
|
471
|
+
function compiledAt(served: Held, path: string): CompiledFile {
|
|
472
|
+
const compiled = served.files.get(path)
|
|
360
473
|
if (compiled === undefined)
|
|
361
474
|
throw new Error(
|
|
362
475
|
`abide: ${path} is imported, and no compile of this app reached it.`,
|
|
@@ -364,22 +477,12 @@ function compiledAt(served: Served, path: string): CompiledFile {
|
|
|
364
477
|
return compiled
|
|
365
478
|
}
|
|
366
479
|
|
|
367
|
-
const SCANNER = new Bun.Transpiler({ loader: 'ts' })
|
|
368
|
-
|
|
369
480
|
// 34.16 — BOUND, NOT RAW. The compiled module is a function of accessors answering its markup (33.4);
|
|
370
481
|
// what an app's own module calls is a function of plain props answering a `Component` (35.4), which
|
|
371
|
-
// `render` invokes with nothing.
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
// through a module still loading found none of its names.
|
|
376
|
-
function boundModule(path: string, server: string): string {
|
|
377
|
-
const specifier = JSON.stringify(path)
|
|
378
|
-
const names: string[] = []
|
|
379
|
-
for (const name of SCANNER.scan(server).exports)
|
|
380
|
-
if (name !== 'default') names.push(name)
|
|
381
|
-
return `import component from ${specifier}
|
|
382
|
-
${names.length === 0 ? '' : `export { ${names.join(', ')} } from ${specifier}\n`}export default (props = {}) => {
|
|
482
|
+
// `render` invokes with nothing. The component is the module's one export (34.17).
|
|
483
|
+
function boundModule(path: string): string {
|
|
484
|
+
return `import component from ${JSON.stringify(path)}
|
|
485
|
+
export default (props = {}) => {
|
|
383
486
|
const accessors = {}
|
|
384
487
|
for (const key in props) {
|
|
385
488
|
const value = props[key]
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
HEALTH_ADDRESS,
|
|
14
14
|
MCP_ADDRESS,
|
|
15
15
|
OPENAPI_ADDRESS,
|
|
16
|
+
PRIVATE_HANDLERS_ADDRESS,
|
|
16
17
|
} from '#shared/ADDRESSES.ts'
|
|
17
18
|
import { APP_META } from '#shared/APP_META.ts'
|
|
18
19
|
import { DEBUG_META } from '#shared/DEBUG_META.ts'
|
|
@@ -102,7 +103,7 @@ type Ranked = RankedError & {
|
|
|
102
103
|
/**
|
|
103
104
|
* One route of the app: the page at it, and the handler answering each method there.
|
|
104
105
|
*
|
|
105
|
-
* 19.1 — one file per method per route, so a route is a row and a method is a column, and 19.
|
|
106
|
+
* 19.1 — one file per method per route, so a route is a row and a method is a column, and 19.20's
|
|
106
107
|
* "the most specific route answering the method" is a walk down the rows asking each column.
|
|
107
108
|
*/
|
|
108
109
|
type RouteRow = {
|
|
@@ -196,7 +197,11 @@ export async function answerRequest(
|
|
|
196
197
|
// 37.3 — THE MUX'S UPGRADE PASSES THE APP LANE as every request does (`muxUpgrade`).
|
|
197
198
|
const admitted = muxAdmission(asked, request, app)
|
|
198
199
|
if (admitted !== undefined) return admitted
|
|
199
|
-
|
|
200
|
+
// 16.70 — a private handler is answered by the route table, below abide's own namespace.
|
|
201
|
+
if (
|
|
202
|
+
asked.startsWith(`${ABIDE_ADDRESS}/`) &&
|
|
203
|
+
!asked.startsWith(`${PRIVATE_HANDLERS_ADDRESS}/`)
|
|
204
|
+
) {
|
|
200
205
|
const answer = app.framework(asked, request)
|
|
201
206
|
if (answer === undefined) return notFound()
|
|
202
207
|
return readsOnly(request) ? answer : methodRefused(app, asked, READS)
|
|
@@ -222,7 +227,7 @@ export async function answerRequest(
|
|
|
222
227
|
refuse(400, 'The address is not a valid path.'),
|
|
223
228
|
)
|
|
224
229
|
|
|
225
|
-
// 19.
|
|
230
|
+
// 19.20 — THE MOST SPECIFIC ROUTE ANSWERING THE METHOD, an `OPTIONS` asking about the method
|
|
226
231
|
// its preflight names (16.24). A route that matches and answers some other method is held, so
|
|
227
232
|
// an address nothing answers the method at is a 405 naming what is answered there (15.17).
|
|
228
233
|
const preflight = request.method === 'OPTIONS'
|
|
@@ -271,7 +276,7 @@ export async function answerRequest(
|
|
|
271
276
|
// above it, the root's having run already, and an error in it reaches that route's `onError`.
|
|
272
277
|
const scope = currentRequestScope() as RequestScope
|
|
273
278
|
scope.route =
|
|
274
|
-
found.handler === undefined ? found.route.pattern : found.handler.
|
|
279
|
+
found.handler === undefined ? found.route.pattern : found.handler.scope
|
|
275
280
|
scope.params = found.params
|
|
276
281
|
const answer = found.route.answers.get(
|
|
277
282
|
found.handler ?? (found.route.page as Ranked),
|
package/src/server/answerRpc.ts
CHANGED
|
@@ -16,7 +16,6 @@ import type { Gated } from '#shared/reactive/argsContract.ts'
|
|
|
16
16
|
import { deadlineOf, heldGenerationOf } from '#shared/reactive/index.ts'
|
|
17
17
|
import type { Defaults } from '#shared/reactive/parameterDefaults.ts'
|
|
18
18
|
import { notFound, refuse } from '#shared/refuse.ts'
|
|
19
|
-
import { routeOf } from '#shared/routePattern.ts'
|
|
20
19
|
import type { Method, Rpc } from '#shared/rpc.ts'
|
|
21
20
|
import { argsInURL } from '#shared/rpcRequest.ts'
|
|
22
21
|
import { VERSION_HEADER } from '#shared/VERSION_HEADER.ts'
|
|
@@ -66,7 +65,14 @@ export type Mount = {
|
|
|
66
65
|
address: string
|
|
67
66
|
method: Method
|
|
68
67
|
source: string
|
|
68
|
+
/** The handler file's name, or the private handler's binding. */
|
|
69
69
|
name: string
|
|
70
|
+
/** What the source's module exports it as: `default` for a handler file. */
|
|
71
|
+
export: string
|
|
72
|
+
/** The route its hooks are filed at, which its rungs are read over (37.1, 16.72). */
|
|
73
|
+
scope: string
|
|
74
|
+
/** Whether it is a private handler, listed nowhere (16.71). */
|
|
75
|
+
private: boolean
|
|
70
76
|
/** Whether a browser is handed a wrapper for it. 20.4. */
|
|
71
77
|
ui: boolean
|
|
72
78
|
/** Whether the command line lists it — the shell's listing and completion, never whether `call` reaches it. 20.3, 38.62. */
|
|
@@ -97,18 +103,15 @@ export type Mount = {
|
|
|
97
103
|
|
|
98
104
|
/**
|
|
99
105
|
* A row of the table the build wrote, with the import of the module its handler is declared in —
|
|
100
|
-
* the file's server module, the one the route table
|
|
101
|
-
* handler.
|
|
106
|
+
* the handler file itself, or a private handler's `.abide` server module, the one the route table
|
|
107
|
+
* loads too, so a page and a call reach one handler.
|
|
102
108
|
*/
|
|
103
109
|
export type MountRow = Mount & { load: () => Promise<Record<string, unknown>> }
|
|
104
110
|
|
|
105
111
|
/** A handler's row, mounted: the handler imported and its args schema probed. */
|
|
106
112
|
export type Mounted = Mount & {
|
|
107
|
-
method: Method
|
|
108
|
-
/** The route of the file declaring it, which its hooks are filed at (37.1, 37.2). */
|
|
109
|
-
route: string
|
|
110
113
|
/**
|
|
111
|
-
* The rungs filed over `
|
|
114
|
+
* The rungs filed over `scope`, read once at mount (16.14) — what an http call and a socket
|
|
112
115
|
* subscription to it both pass through (37.2, 18.26).
|
|
113
116
|
*/
|
|
114
117
|
rungs: AppRung[]
|
|
@@ -148,12 +151,14 @@ export function mountedOwn(
|
|
|
148
151
|
{
|
|
149
152
|
address,
|
|
150
153
|
method: 'GET',
|
|
151
|
-
route: '/',
|
|
152
154
|
// The root's rungs run before routing (37.3), and nothing is filed below a route
|
|
153
155
|
// of abide's own.
|
|
154
156
|
rungs: [],
|
|
155
157
|
source: '',
|
|
156
158
|
name,
|
|
159
|
+
export: name,
|
|
160
|
+
scope: '/',
|
|
161
|
+
private: false,
|
|
157
162
|
ui: false,
|
|
158
163
|
cli: false,
|
|
159
164
|
mcp: false,
|
|
@@ -188,20 +193,17 @@ export async function mountHandlers(
|
|
|
188
193
|
const handlers = new Map<string, Mounted>()
|
|
189
194
|
for (let at = 0; at < table.length; at += 1) {
|
|
190
195
|
const mount = table[at] as Mount
|
|
191
|
-
const exported = (modules[at] as Record<string, unknown>)[mount.
|
|
196
|
+
const exported = (modules[at] as Record<string, unknown>)[mount.export]
|
|
192
197
|
const handler = exported as Rpc<unknown, unknown> & Gated<unknown>
|
|
193
198
|
const declared = argsDocumentOf(handler)
|
|
194
199
|
// 16.9, 16.10 — the address `rpc.url` and `rpc.raw` build on, under the mount — and what a
|
|
195
200
|
// page's render turns a call it made into the browser's request by (41.20).
|
|
196
201
|
handler.address = underMount(mount.address)
|
|
197
|
-
// 23.19 — the route its address answers below, which is its mount's for a package's file,
|
|
198
|
-
// never the file's place on disk.
|
|
199
|
-
const route = routeOf(mount.address)
|
|
200
202
|
handlers.set(handlerKey(mount.method, mount.address), {
|
|
201
203
|
...mount,
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
rungs: rungsOver(
|
|
204
|
+
// 37.1, 16.72 — the route its file's hooks are filed at, which the build read off
|
|
205
|
+
// where the file answers (23.19's mount for a package's), never its place on disk.
|
|
206
|
+
rungs: rungsOver(mount.scope),
|
|
205
207
|
handler,
|
|
206
208
|
argsDocument: declared ?? (mount.args as Document | undefined),
|
|
207
209
|
wireArgs: declared === undefined ? mount.args : undefined,
|
|
@@ -219,7 +221,7 @@ export function handlerKey(method: string, address: string): string {
|
|
|
219
221
|
* The `Response` to a call of `found` made over http, `params` being what its route matched.
|
|
220
222
|
*
|
|
221
223
|
* `found` answers the request's method — or, for an `OPTIONS`, the method its preflight asks
|
|
222
|
-
* about: which handler a request reaches is the route table's to say (19.
|
|
224
|
+
* about: which handler a request reaches is the route table's to say (19.20).
|
|
223
225
|
*/
|
|
224
226
|
export async function answerRpc(
|
|
225
227
|
found: Mounted,
|