@abide/abide 0.123.2 → 0.124.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/ABIDE.md +8 -5
- package/CHANGELOG.md +58 -0
- package/bin/abide.ts +36 -65
- package/docs/REGISTRY.md +45 -12
- package/docs/RULEBOOK.md +24 -14
- package/docs/abide.md +999 -459
- package/package.json +1 -1
- package/src/compiler/ARRAY_MUTATORS.ts +20 -0
- package/src/compiler/Diagnostics.ts +198 -0
- package/src/compiler/KeptChecker.ts +97 -0
- package/src/compiler/LoweredFile.ts +609 -0
- package/src/compiler/Lowering.ts +18 -1
- package/src/compiler/PositionedRefusal.ts +1 -1
- package/src/compiler/SHARED_NAMES.ts +2 -0
- package/src/compiler/ShadowHost.ts +670 -0
- package/src/compiler/ShadowMap.ts +1 -1
- package/src/compiler/TypeSession.ts +76 -1743
- package/src/compiler/appLayout.ts +28 -20
- package/src/compiler/appLints.ts +109 -0
- package/src/compiler/argsDefaultsIn.ts +1 -1
- package/src/compiler/checkApp.ts +64 -68
- package/src/compiler/checkedShadow.ts +2 -2
- package/src/compiler/classifyRead.ts +16 -1
- package/src/compiler/compileApp.ts +102 -71
- package/src/compiler/declaredExports.ts +46 -21
- package/src/compiler/diagnoseSources.ts +232 -0
- package/src/compiler/emitClient.ts +1 -1
- package/src/compiler/formatAbide.ts +7 -8
- package/src/compiler/handlersUnder.ts +32 -63
- package/src/compiler/lineOf.ts +0 -19
- package/src/compiler/matchersOf.ts +3 -3
- package/src/compiler/mountsOf.ts +3 -3
- package/src/compiler/reactiveReads.ts +235 -0
- package/src/compiler/reactiveTypes.ts +13 -0
- package/src/compiler/readSources.ts +16 -18
- package/src/compiler/readsIn.ts +181 -82
- package/src/compiler/routesDeclaration.ts +46 -0
- package/src/compiler/scaffold.ts +7 -2
- package/src/compiler/serveLanguage.ts +46 -74
- package/src/compiler/sharedModuleWrites.ts +1 -1
- package/src/compiler/splitImports.ts +2 -2
- package/src/compiler/templateWarnings.ts +1 -2
- package/src/compiler/trackingWarnings.ts +1 -1
- package/src/server/CLIENT_BUILD.ts +4 -32
- package/src/server/DesktopSession.ts +213 -0
- package/src/server/Followers.ts +399 -0
- package/src/server/MODULE_QUERIES.ts +6 -2
- package/src/server/PageRender.ts +201 -0
- package/src/server/RESUME_MILLISECONDS.ts +2 -0
- package/src/server/abideModules.ts +7 -7
- package/src/server/answerFailure.ts +3 -2
- package/src/server/answerRequest.ts +16 -369
- package/src/server/answerRpc.ts +48 -302
- package/src/server/answerSocket.ts +41 -490
- package/src/server/appBuild.ts +377 -0
- package/src/server/appKit.ts +1 -1
- package/src/server/appPlugins.ts +15 -17
- package/src/server/appTree.ts +6 -5
- package/src/server/argsContract.ts +209 -0
- package/src/server/bridgeRelay.ts +153 -65
- package/src/server/claudeCode.ts +30 -127
- package/src/server/config.ts +6 -7
- package/src/server/createApp.ts +38 -66
- package/src/server/csp.ts +1 -1
- package/src/server/deferredBuilds.ts +3 -16
- package/src/server/devWorker.ts +11 -15
- package/src/server/faceOf.ts +1 -1
- package/src/server/generatedSurfaces.ts +2 -1
- package/src/server/handlerFileMount.ts +40 -0
- package/src/server/index.ts +3 -3
- package/src/server/installServer.ts +5 -9
- package/src/server/invokeHandler.ts +230 -0
- package/src/server/mountHandlers.ts +184 -0
- package/src/server/openDesktopWindow.ts +308 -0
- package/src/server/packageDirectory.ts +14 -0
- package/src/server/preload.ts +21 -2
- package/src/server/principal.ts +5 -4
- package/src/server/remote.ts +2 -2
- package/src/server/render.ts +31 -59
- package/src/server/requestScope.ts +5 -36
- package/src/server/runCompiled.ts +1 -1
- package/src/server/runDesktop.ts +20 -448
- package/src/server/seedBuffer.ts +12 -12
- package/src/server/serveDev.ts +24 -58
- package/src/server/servedApp.ts +390 -0
- package/src/server/shell.ts +1 -1
- package/src/server/signInChanged.ts +20 -0
- package/src/server/surfaceOf.ts +3 -2
- package/src/server/versionOf.ts +7 -16
- package/src/shared/RPC_SERVER.ts +27 -31
- package/src/shared/Schema.ts +1 -2
- package/src/shared/applyPatch.ts +1 -1
- package/src/shared/index.ts +1 -1
- package/src/shared/{note.ts → issueReport.ts} +22 -0
- package/src/shared/loadCall.ts +66 -0
- package/src/shared/muxFrames.ts +1 -1
- package/src/shared/reactive/Keyed.ts +24 -16
- package/src/shared/reactive/Reactive.ts +37 -991
- package/src/shared/reactive/argsContract.ts +131 -4
- package/src/shared/reactive/byOf.ts +2 -5
- package/src/shared/reactive/callLoad.ts +8 -14
- package/src/shared/reactive/cellShape.ts +734 -0
- package/src/shared/reactive/index.ts +12 -23
- package/src/shared/reactive/pairReactive.ts +5 -3
- package/src/shared/reactive/patchLog.ts +79 -0
- package/src/shared/reactive/patchMarks.ts +34 -0
- package/src/shared/reactive/producer.ts +121 -0
- package/src/shared/reactive/recordWrites.ts +11 -14
- package/src/shared/reactive/scoped.ts +3 -2
- package/src/shared/reactive/selections.ts +209 -0
- package/src/shared/reactive/share.ts +4 -3
- package/src/shared/reactive/watch.ts +3 -4
- package/src/shared/rpc.ts +20 -24
- package/src/shared/validateJson.ts +1 -2
- package/src/shared/writePath.ts +29 -0
- package/src/ui/ArmRegion.ts +10 -4
- package/src/ui/Level.ts +11 -20
- package/src/ui/LevelChain.ts +272 -0
- package/src/ui/Router.ts +106 -313
- package/src/ui/Scope.ts +17 -3
- package/src/ui/TryRegion.ts +22 -23
- package/src/ui/bindSpread.ts +2 -4
- package/src/ui/listenForRoutes.ts +3 -10
- package/src/ui/outboxQueue.ts +23 -11
- package/src/ui/outcomeOf.ts +120 -0
- package/src/ui/reconcile.ts +4 -6
- package/src/ui/routing.ts +2 -1
- package/src/ui/rows.ts +2 -2
- package/src/ui/setAttribute.ts +12 -3
- package/src/ui/socketWire.ts +28 -13
- package/src/ui/wire.ts +9 -10
- package/src/compiler/Finding.ts +0 -2
- package/src/compiler/Refusals.ts +0 -65
- package/src/compiler/formatDiagnosis.ts +0 -20
- package/src/server/appDirectories.ts +0 -9
- package/src/server/argsFor.ts +0 -39
- package/src/server/buildDirectory.ts +0 -10
- package/src/server/coerceArgs.ts +0 -109
- package/src/server/derivedArgs.ts +0 -26
- package/src/server/runBuild.ts +0 -24
- package/src/server/serveBuild.ts +0 -144
- package/src/server/testBuild.ts +0 -220
- package/src/shared/LOAD_OFFLINE.ts +0 -15
- package/src/shared/LOAD_SIGNAL.ts +0 -10
- package/src/shared/issuesOf.ts +0 -18
- package/src/shared/reactive/ENTRY_LIVES.ts +0 -7
- package/src/shared/reactive/FOLLOWED.ts +0 -8
- package/src/shared/reactive/PATCHES.ts +0 -11
- package/src/shared/reactive/PATCH_SINKS.ts +0 -26
- package/src/shared/reactive/canonicalKey.ts +0 -95
package/src/server/answerRpc.ts
CHANGED
|
@@ -1,21 +1,15 @@
|
|
|
1
|
-
// 16.2 — A HANDLER, ANSWERED OVER HTTP. The call is
|
|
2
|
-
//
|
|
3
|
-
//
|
|
1
|
+
// 16.2 — A HANDLER, ANSWERED OVER HTTP. The call is `invokeHandler`'s, made in a request's own scope
|
|
2
|
+
// (11.32), and what this module adds is the transport either side of it: the args off the request,
|
|
3
|
+
// and the answer onto a response.
|
|
4
4
|
//
|
|
5
|
-
// WIRE-ONLY, per 16.13: the `
|
|
6
|
-
//
|
|
5
|
+
// WIRE-ONLY, per 16.13: `maxBodySize`, the `crossOrigin` preflight, and the text a URL or a form
|
|
6
|
+
// carried the args as.
|
|
7
7
|
|
|
8
|
-
import type { CopyOptions } from '#shared/CopyOptions.ts'
|
|
9
8
|
import { type Failed, isFailed } from '#shared/Failed.ts'
|
|
10
|
-
import { GLOBAL_GETS } from '#shared/GLOBAL_GETS.ts'
|
|
11
9
|
import { isRecord } from '#shared/isRecord.ts'
|
|
12
|
-
import type { JsonValue } from '#shared/JsonValue.ts'
|
|
13
10
|
import { mounted as underMount } from '#shared/mount.ts'
|
|
14
|
-
import
|
|
15
|
-
import { deadlineOf, heldGenerationOf } from '#shared/reactive/index.ts'
|
|
16
|
-
import type { Defaults } from '#shared/reactive/parameterDefaults.ts'
|
|
11
|
+
import { deadlineOf } from '#shared/reactive/producer.ts'
|
|
17
12
|
import { notFound, refuse } from '#shared/refuse.ts'
|
|
18
|
-
import type { HeldToAnnotation, Method, Rpc } from '#shared/rpc.ts'
|
|
19
13
|
import { RPC_SERVER } from '#shared/RPC_SERVER.ts'
|
|
20
14
|
import { argsInURL } from '#shared/rpcRequest.ts'
|
|
21
15
|
import { VERSION_HEADER } from '#shared/VERSION_HEADER.ts'
|
|
@@ -27,196 +21,24 @@ import {
|
|
|
27
21
|
type ErrorPages,
|
|
28
22
|
handlerHeaders,
|
|
29
23
|
} from './answerFailure.ts'
|
|
30
|
-
import { followAsEvents } from './answerSocket.ts'
|
|
31
24
|
import { answerValue, namesEtag, sseHeaders } from './answerValue.ts'
|
|
32
|
-
import {
|
|
33
|
-
import { callerOf } from './callerOf.ts'
|
|
34
|
-
import {
|
|
35
|
-
argsDocumentOf,
|
|
36
|
-
coerceArgs,
|
|
37
|
-
collapsed,
|
|
38
|
-
type Document,
|
|
39
|
-
} from './coerceArgs.ts'
|
|
25
|
+
import { collapsed } from './argsContract.ts'
|
|
40
26
|
import { positiveSetting } from './config.ts'
|
|
41
|
-
import {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
crossingRefusal,
|
|
46
|
-
} from './crossingOrigin.ts'
|
|
47
|
-
import { failureOf, type LaneRung, rungsOver } from './hooks.ts'
|
|
27
|
+
import { allows, crossingOrigin } from './crossingOrigin.ts'
|
|
28
|
+
import { failureOf } from './hooks.ts'
|
|
29
|
+
import { type Invocation, invokeHandler, originGate } from './invokeHandler.ts'
|
|
30
|
+
import type { Mounted } from './mountHandlers.ts'
|
|
48
31
|
import { OFFLINE_STALE } from './OFFLINE_STALE.ts'
|
|
49
32
|
import { prefers } from './prefers.ts'
|
|
50
|
-
import {
|
|
51
|
-
|
|
52
|
-
rungsTook,
|
|
53
|
-
timed,
|
|
54
|
-
unreap,
|
|
55
|
-
writtenStatus,
|
|
56
|
-
} from './requestScope.ts'
|
|
57
|
-
import { type Outcome, outcomeOf, takeSeed } from './seedBuffer.ts'
|
|
33
|
+
import { currentRequestScope, unreap, writtenStatus } from './requestScope.ts'
|
|
34
|
+
import type { Outcome } from './seedBuffer.ts'
|
|
58
35
|
import { versionOf } from './versionOf.ts'
|
|
59
36
|
|
|
60
|
-
/**
|
|
61
|
-
* One row of the handler table the build writes into the server's table (`SERVER_TABLE`): where a
|
|
62
|
-
* handler answers, and its module.
|
|
63
|
-
*
|
|
64
|
-
* The module is relative to the app's root, which both commands that serve already know.
|
|
65
|
-
*/
|
|
66
|
-
export type Mount = {
|
|
67
|
-
address: string
|
|
68
|
-
method: Method
|
|
69
|
-
source: string
|
|
70
|
-
/** The handler file's name, or the private handler's binding. */
|
|
71
|
-
name: string
|
|
72
|
-
/** What the source's module exports it as: `default` for a handler file. */
|
|
73
|
-
export: string
|
|
74
|
-
/** The route its hooks are filed at, which its rungs are read over (37.1, 16.72). */
|
|
75
|
-
scope: string
|
|
76
|
-
/** 23.26 — each matcher its address names, to the anchored source its row is ranked with. */
|
|
77
|
-
matchers: Record<string, string>
|
|
78
|
-
/** Whether it is a private handler, listed nowhere (16.71). */
|
|
79
|
-
private: boolean
|
|
80
|
-
/** Whether a browser is handed a wrapper for it. 20.4. */
|
|
81
|
-
ui: boolean
|
|
82
|
-
/** Whether the command line lists it — the shell's listing and completion, never whether `call` reaches it. 20.3, 38.62. */
|
|
83
|
-
cli: boolean
|
|
84
|
-
/**
|
|
85
|
-
* Whether MCP lists it where its runtime `clients` does not say.
|
|
86
|
-
*
|
|
87
|
-
* 16.37 — false for a handler typed as returning a `Response`, which only the build can tell.
|
|
88
|
-
*/
|
|
89
|
-
mcp: boolean
|
|
90
|
-
/** Its `description`, where the declaration writes one as a literal the build can read. */
|
|
91
|
-
description: string | undefined
|
|
92
|
-
/**
|
|
93
|
-
* What a `GET`'s value declares that the browser's copy behaves by, or `undefined`.
|
|
94
|
-
*
|
|
95
|
-
* 18.4 — set where its declaration names any of it.
|
|
96
|
-
*/
|
|
97
|
-
copy: CopyOptions | undefined
|
|
98
|
-
/**
|
|
99
|
-
* The defaults the handler's parameter writes, for the browser's copy to key by.
|
|
100
|
-
*
|
|
101
|
-
* 11.29 — the server reads its own off the handler.
|
|
102
|
-
*/
|
|
103
|
-
defaults: Defaults | undefined
|
|
104
|
-
/** The JSON Schema the handler's args annotation derives, where it takes a record. 19.9. */
|
|
105
|
-
args: JsonValue | undefined
|
|
106
|
-
/** The JSON Schema of what it answers, where its type says one. 19.21. */
|
|
107
|
-
result: JsonValue | undefined
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* A row of the table the build wrote, with the import of the module its handler is declared in —
|
|
112
|
-
* the handler file itself, or a private handler's `.abide` server module, the one the route table
|
|
113
|
-
* loads too, so a page and a call reach one handler.
|
|
114
|
-
*/
|
|
115
|
-
export type MountRow = Mount & { load: () => Promise<Record<string, unknown>> }
|
|
116
|
-
|
|
117
|
-
/** A handler's row, mounted: the handler imported and its args schema probed. */
|
|
118
|
-
export type Mounted = Mount & {
|
|
119
|
-
/**
|
|
120
|
-
* The rungs filed over `scope`, read once at mount (16.14) — what an http call and a socket
|
|
121
|
-
* subscription to it both pass through (37.2, 18.26).
|
|
122
|
-
*/
|
|
123
|
-
rungs: LaneRung[]
|
|
124
|
-
handler: Rpc<unknown, unknown> & Gated<unknown> & HeldToAnnotation
|
|
125
|
-
/**
|
|
126
|
-
* The args' JSON Schema, probed once at mount.
|
|
127
|
-
*
|
|
128
|
-
* 19.7, 20.11 — a third-party schema converts itself on every probe.
|
|
129
|
-
*/
|
|
130
|
-
argsDocument: Document | undefined
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* The row of a handler abide mounts itself, keyed as `answerRpc` looks one up.
|
|
135
|
-
*
|
|
136
|
-
* Built on the core's `declare` with no generated surface (20.1) — the app's tree never names it.
|
|
137
|
-
*/
|
|
138
|
-
export function mountedOwn(
|
|
139
|
-
address: string,
|
|
140
|
-
name: string,
|
|
141
|
-
handler: object,
|
|
142
|
-
): [string, Mounted] {
|
|
143
|
-
// 16.10 — READ WHEN IT IS REPORTED, where a mounted handler's is stamped at boot: the mount is
|
|
144
|
-
// the configuration's, and nothing boots this handler.
|
|
145
|
-
Object.defineProperty(handler, 'address', {
|
|
146
|
-
get: () => underMount(address),
|
|
147
|
-
})
|
|
148
|
-
return [
|
|
149
|
-
address,
|
|
150
|
-
{
|
|
151
|
-
address,
|
|
152
|
-
method: 'GET',
|
|
153
|
-
// The root's rungs run before routing (37.3), and nothing is filed below a route
|
|
154
|
-
// of abide's own.
|
|
155
|
-
rungs: [],
|
|
156
|
-
source: '',
|
|
157
|
-
name,
|
|
158
|
-
export: name,
|
|
159
|
-
scope: '/',
|
|
160
|
-
matchers: {},
|
|
161
|
-
private: false,
|
|
162
|
-
ui: false,
|
|
163
|
-
cli: false,
|
|
164
|
-
mcp: false,
|
|
165
|
-
description: undefined,
|
|
166
|
-
copy: undefined,
|
|
167
|
-
defaults: undefined,
|
|
168
|
-
args: undefined,
|
|
169
|
-
result: undefined,
|
|
170
|
-
handler: handler as Mounted['handler'],
|
|
171
|
-
argsDocument: undefined,
|
|
172
|
-
},
|
|
173
|
-
]
|
|
174
|
-
}
|
|
175
|
-
|
|
176
37
|
// 16.58 — the environment's `timeout`, for every call a handler that names none makes.
|
|
177
38
|
export function environmentTimeout(): number | undefined {
|
|
178
39
|
return positiveSetting('ABIDE_RPC_TIMEOUT', undefined)
|
|
179
40
|
}
|
|
180
41
|
|
|
181
|
-
/**
|
|
182
|
-
* The table's handlers, imported and keyed by method and address.
|
|
183
|
-
*
|
|
184
|
-
* Imported through each row's own loader, which the route table shares, so a page rendering on the
|
|
185
|
-
* server and a call over the wire reach one module and so one handler. AT BOOT rather than on the
|
|
186
|
-
* first call, because a page's render reaches a handler through the page's own import and has to
|
|
187
|
-
* know its address before anything has called it over the wire.
|
|
188
|
-
*/
|
|
189
|
-
export async function mountHandlers(
|
|
190
|
-
table: MountRow[],
|
|
191
|
-
): Promise<{ handlers: Map<string, Mounted> }> {
|
|
192
|
-
const modules = await Promise.all(table.map((mount) => mount.load()))
|
|
193
|
-
const handlers = new Map<string, Mounted>()
|
|
194
|
-
for (let at = 0; at < table.length; at += 1) {
|
|
195
|
-
const mount = table[at] as Mount
|
|
196
|
-
const exported = (modules[at] as Record<string, unknown>)[mount.export]
|
|
197
|
-
const handler = exported as Mounted['handler']
|
|
198
|
-
const declared = argsDocumentOf(handler)
|
|
199
|
-
// 16.9, 16.10 — the address `rpc.url` and `rpc.raw` build on, under the mount — and what a
|
|
200
|
-
// page's render turns a call it made into the browser's request by (41.20).
|
|
201
|
-
handler.address = underMount(mount.address)
|
|
202
|
-
handlers.set(handlerKey(mount.method, mount.address), {
|
|
203
|
-
...mount,
|
|
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
|
-
// 37.14 — a handler's lane answers a refusal as Problem Details (15.1).
|
|
207
|
-
rungs: rungsOver(mount.scope, answered),
|
|
208
|
-
handler,
|
|
209
|
-
argsDocument: declared ?? (mount.args as Document | undefined),
|
|
210
|
-
})
|
|
211
|
-
}
|
|
212
|
-
return { handlers }
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/** What a handler is keyed by among the mounted: its method and its route. 19.1. */
|
|
216
|
-
export function handlerKey(method: string, address: string): string {
|
|
217
|
-
return `${method} ${address}`
|
|
218
|
-
}
|
|
219
|
-
|
|
220
42
|
/**
|
|
221
43
|
* The `Response` to a call of `found` made over http, `params` being what its route matched.
|
|
222
44
|
*
|
|
@@ -240,89 +62,46 @@ export async function answerRpc(
|
|
|
240
62
|
return origin !== null && allows(found.handler.crossOrigin, origin)
|
|
241
63
|
? preflight(found, origin, request)
|
|
242
64
|
: answered(notFound({ path: pathname, method: request.method }))
|
|
243
|
-
// 16.50 — a `HEAD` is the `GET`'s answer, which the server sends without its body.
|
|
244
|
-
const head = request.method === 'HEAD'
|
|
245
|
-
const handler = found.handler
|
|
246
65
|
const inUrl = argsInURL(found.method)
|
|
247
66
|
const form = isForm(request)
|
|
248
|
-
const crossing = crossingOrigin(origin, url)
|
|
249
|
-
const closed = closedTo(handler.crossOrigin, crossing)
|
|
250
67
|
// 21.19 — the origin the answer admits, where the handler declared it may read.
|
|
251
|
-
const admitted =
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
68
|
+
const admitted = originGate(
|
|
69
|
+
found.handler,
|
|
70
|
+
crossingOrigin(origin, url),
|
|
71
|
+
false,
|
|
72
|
+
)
|
|
73
|
+
if (isFailed(admitted)) return answered(admitted)
|
|
74
|
+
const raw = await argsOf(
|
|
75
|
+
request,
|
|
76
|
+
url,
|
|
77
|
+
inUrl,
|
|
78
|
+
form,
|
|
79
|
+
found.handler.maxBodySize,
|
|
80
|
+
)
|
|
256
81
|
if (isFailed(raw)) return answered(raw)
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
// 16.51 — a `HEAD` leaves the seed for the `GET` it stands in front of.
|
|
270
|
-
const caller = head ? undefined : callerOf()
|
|
271
|
-
const seeded =
|
|
272
|
-
caller === undefined ? undefined : takeSeed(caller, handler, args)
|
|
273
|
-
// The entry key of a `global` value, taken once for every read of it below.
|
|
274
|
-
const global = GLOBAL_GETS.get(handler)
|
|
275
|
-
const key = global?.key(args)
|
|
276
|
-
// 21.15 — A `global` PRODUCTION ALREADY HELD, and not loading, answers without a run: a run
|
|
277
|
-
// this call started moves the generation, and one it joined was loading when it came.
|
|
278
|
-
const held =
|
|
279
|
-
seeded === undefined && key !== undefined ? global?.at(key) : undefined
|
|
280
|
-
const heldAt = held === undefined ? undefined : heldGenerationOf(held)
|
|
281
|
-
const timing = currentRequestScope()?.timing
|
|
282
|
-
if (timing !== undefined) timing.answering = handler
|
|
283
|
-
const started = performance.now()
|
|
284
|
-
const outcome =
|
|
285
|
-
seeded === undefined ? await outcomeOf(handler(args)) : await seeded
|
|
286
|
-
// The entry as the run left it, which the answer reads too.
|
|
287
|
-
const entry = key === undefined ? undefined : global?.at(key)
|
|
288
|
-
const cached =
|
|
289
|
-
heldAt !== undefined &&
|
|
290
|
-
entry === held &&
|
|
291
|
-
heldGenerationOf(held) === heldAt
|
|
82
|
+
// A URL's and a form's args are text, and the query tells a bare name from `name=` (19.7).
|
|
83
|
+
// 16.50 — a `HEAD` is the `GET`'s answer, which the server sends without its body; 16.51 — and
|
|
84
|
+
// it leaves the seed for the `GET` it stands in front of.
|
|
85
|
+
const invoked = await invokeHandler(
|
|
86
|
+
found,
|
|
87
|
+
raw,
|
|
88
|
+
inUrl ? url.search : form ? '' : undefined,
|
|
89
|
+
params,
|
|
90
|
+
request.method !== 'HEAD',
|
|
91
|
+
)
|
|
92
|
+
if (isFailed(invoked)) return answered(invoked)
|
|
93
|
+
const outcome = invoked.outcome
|
|
292
94
|
// 16.38 — A MUTATION A DOCUMENT MADE — a form posted with no script — is answered as a navigation.
|
|
293
95
|
if (!inUrl && prefers(request, 'text/html', 'application/json'))
|
|
294
96
|
return navigated(outcome, request, pathname, pages)
|
|
295
|
-
const answer = await answerOutcome(
|
|
296
|
-
outcome,
|
|
297
|
-
request,
|
|
298
|
-
found,
|
|
299
|
-
args,
|
|
300
|
-
key,
|
|
301
|
-
entry,
|
|
302
|
-
offline,
|
|
303
|
-
)
|
|
97
|
+
const answer = await answerOutcome(invoked, request, found, offline)
|
|
304
98
|
// A `Response` the handler built is its own — but the origin answer below is laid over it, so it
|
|
305
99
|
// varies on `Origin` too (21.7), which `handlerHeaders` never reached.
|
|
306
100
|
if (outcome.settled && outcome.value instanceof Response) {
|
|
307
101
|
answer.headers.append('vary', 'origin')
|
|
308
102
|
return admitting(answer, admitted)
|
|
309
103
|
}
|
|
310
|
-
|
|
311
|
-
// already names the handler, so the metric names the run. A stream's time is to its first
|
|
312
|
-
// chunk, which the answer's head waited for.
|
|
313
|
-
const took = outcome.duration ?? performance.now() - started
|
|
314
|
-
const run =
|
|
315
|
-
seeded !== undefined
|
|
316
|
-
? 'Seeded'
|
|
317
|
-
: cached
|
|
318
|
-
? 'Cached'
|
|
319
|
-
: outcome.duration === undefined
|
|
320
|
-
? 'Started'
|
|
321
|
-
: 'Direct'
|
|
322
|
-
// 21.20 — what the rungs took is theirs, and the handler's own body is what `Direct` reports.
|
|
323
|
-
const handled = run === 'Direct' ? timing?.handled : undefined
|
|
324
|
-
if (handled !== undefined) rungsTook(took - handled)
|
|
325
|
-
timed(timing, run, handled ?? took)
|
|
104
|
+
invoked.timed()
|
|
326
105
|
return admitting(answer, admitted)
|
|
327
106
|
}
|
|
328
107
|
|
|
@@ -344,15 +123,12 @@ function admitting(answer: Response, admitted: string | null): Response {
|
|
|
344
123
|
}
|
|
345
124
|
|
|
346
125
|
async function answerOutcome(
|
|
347
|
-
|
|
126
|
+
invoked: Invocation,
|
|
348
127
|
request: Request,
|
|
349
128
|
found: Mounted,
|
|
350
|
-
args: unknown,
|
|
351
|
-
key: string | undefined,
|
|
352
|
-
entry: unknown,
|
|
353
129
|
offline: boolean,
|
|
354
130
|
): Promise<Response> {
|
|
355
|
-
const
|
|
131
|
+
const { outcome, entry } = invoked
|
|
356
132
|
if (!outcome.settled) return answered(failureOf(outcome.error))
|
|
357
133
|
const value = outcome.value
|
|
358
134
|
// The `global` value the answer came from, or `undefined` for one built per request. 18.22 — or
|
|
@@ -379,7 +155,8 @@ async function answerOutcome(
|
|
|
379
155
|
const etag = serving !== undefined ? `"${versionOf(serving)}"` : undefined
|
|
380
156
|
if (etag !== undefined) {
|
|
381
157
|
headers.set(VERSION_HEADER, etag)
|
|
382
|
-
|
|
158
|
+
for (const followers of RPC_SERVER.following)
|
|
159
|
+
followers.served(found.handler, serving, invoked.key as string)
|
|
383
160
|
}
|
|
384
161
|
// 21.22 — A `GET` THE DEVICE KEEPS, where the worker is there to keep it: a value, not a
|
|
385
162
|
// refusal (16.40) nor a stream with no end to keep (16.32) — a generator's, or the lines a
|
|
@@ -413,17 +190,19 @@ async function answerOutcome(
|
|
|
413
190
|
// 18.20 — A READER ASKING FOR AN EVENT STREAM IS FOLLOWED, rather than answered once: the value,
|
|
414
191
|
// then each change, from the version its `Last-Event-ID` names. A handler that streams answers
|
|
415
192
|
// its own chunks as it always has (16.45).
|
|
193
|
+
const followers = currentRequestScope()?.app?.followers
|
|
416
194
|
if (
|
|
417
195
|
etag !== undefined &&
|
|
196
|
+
followers !== undefined &&
|
|
418
197
|
!streams(outcome.value) &&
|
|
419
198
|
prefers(request, 'text/event-stream', 'application/json')
|
|
420
199
|
) {
|
|
421
200
|
const events = handlerHeaders()
|
|
422
201
|
sseHeaders(events)
|
|
423
202
|
return new Response(
|
|
424
|
-
|
|
203
|
+
followers.events(
|
|
425
204
|
found,
|
|
426
|
-
args,
|
|
205
|
+
invoked.args,
|
|
427
206
|
versionInEtag(request.headers.get('last-event-id')),
|
|
428
207
|
),
|
|
429
208
|
{ status: 200, headers: events },
|
|
@@ -478,39 +257,6 @@ async function argsOf(
|
|
|
478
257
|
return parsed
|
|
479
258
|
}
|
|
480
259
|
|
|
481
|
-
/**
|
|
482
|
-
* The args a request sent, with the parameters its route matched laid in.
|
|
483
|
-
*
|
|
484
|
-
* 16.65 — the path's, whatever the method. 16.66 — a parameter the query or the body names as
|
|
485
|
-
* well is refused rather than one of the two chosen. A JSON body's members are typed already, so the
|
|
486
|
-
* path's text is coerced to the declared types before it joins them (19.7); text args are coerced
|
|
487
|
-
* with the rest of the text.
|
|
488
|
-
*/
|
|
489
|
-
function withParameters(
|
|
490
|
-
sent: unknown,
|
|
491
|
-
params: Record<string, string | string[]>,
|
|
492
|
-
text: boolean,
|
|
493
|
-
found: Mounted,
|
|
494
|
-
): unknown | Failed<'HttpError', undefined> {
|
|
495
|
-
let none = true
|
|
496
|
-
for (const name in params) {
|
|
497
|
-
none = false
|
|
498
|
-
if (isRecord(sent) && name in sent)
|
|
499
|
-
return refuse(
|
|
500
|
-
400,
|
|
501
|
-
`The parameter '${name}' is in the path and in the request as well.`,
|
|
502
|
-
)
|
|
503
|
-
}
|
|
504
|
-
if (none) return sent
|
|
505
|
-
const fromPath = text
|
|
506
|
-
? params
|
|
507
|
-
: (coerceArgs(params, found.argsDocument, '') as Record<
|
|
508
|
-
string,
|
|
509
|
-
unknown
|
|
510
|
-
>)
|
|
511
|
-
return isRecord(sent) ? { ...sent, ...fromPath } : { ...fromPath }
|
|
512
|
-
}
|
|
513
|
-
|
|
514
260
|
/**
|
|
515
261
|
* The request's body, received whole, under the ceiling `maxBodySize` sets, or
|
|
516
262
|
* `ABIDE_MAX_REQUEST_BODY_SIZE` where it is unset, or a 413 refusal past it.
|