@abide/abide 0.125.0 → 0.125.2

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 CHANGED
@@ -1,5 +1,25 @@
1
1
  # @abide/abide
2
2
 
3
+ ## 0.125.2
4
+
5
+ ### Patch Changes
6
+
7
+ - d1029ab: A `Middleware` whose `Extra` asks for `args` fits a keyed handler again — 16.86. 0.125.1's plain rung array inferred the handler's `Args` off the rung's `args: () => unknown` as well as off the `Keyed`, and refused the handler.
8
+
9
+ ## 0.125.1
10
+
11
+ ### Patch Changes
12
+
13
+ - one late-read writer for a handler's address, a named handler rung, resultsOf reads Rpc's own members — 16.88 ([`b74dc3a`](https://github.com/briancray/abide/commit/b74dc3a99385125ad76d5180e8ad9479cb4798b5))
14
+ - b9e4187: Rung helpers, `resultsOf` and a handler's address, from updating an app to 0.125 — 16.86, 16.88, 44.51
15
+
16
+ - `resultsOf` takes any handler, whatever its args (it refused every handler with a parameter).
17
+ - A rung list a helper builds (`middleware: rungs()`, `[...rungs()]`) carries its `Middleware`s' named refusals onto the `Rpc`, as an inline list does.
18
+ - A generic rung helper takes its `Args` from the handler it is listed on again.
19
+ - A handler file's `rpc.address` is its route as soon as the file loads, so a `bun test` importing it matches it with no app booted.
20
+
21
+ MIGRATION from 0.124: a rung's `next` is typed `(ctx?: RpcContext<Args>) => Promise<…>`, so a rung written for any handler whose `next` takes a ctx with `args: () => unknown` no longer fits a handler with typed args — it could hand that handler args of the wrong shape. Type the helper over the handler's `Args` (`<Args>(…) => ((next: (ctx?: { request: Request; args: () => Args }) => Promise<unknown>, ctx: { request: Request; args: () => Args }) => unknown)[]`), or write the rung as a `Middleware`. A mismatch is now reported against `middleware` rather than as the handler not being assignable to `never`.
22
+
3
23
  ## 0.125.0
4
24
 
5
25
  ### Minor Changes
package/docs/REGISTRY.md CHANGED
@@ -225,7 +225,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
225
225
  | `Value` | `unknown` | What the addressed `Reactive` holds. | 16.3, 16.30 |
226
226
  | `Produced` | `unknown` | What it yields, per the unit rule. | 1.2, 16.3 |
227
227
  | `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 |
228
- | `Middleware` | `Middleware<Answer = never, Extra extends object = object, Refused extends Failed<string, unknown> = Failed<string, unknown>> = <Given extends { request: Request } & Extra, Result>(next: (ctx?: Given) => Promise<Result>, ctx: Given) => Result \| Answer \| Refused \| Promise<Result \| Answer \| Refused>` | One rung, written once for every lane whose context carries `Extra`. | 16.8, 16.14, 16.15, 16.16, 16.21, 16.86, 37.14, 37.15 |
228
+ | `Middleware` | `Middleware<Answer = never, Extra extends object = object, Refused extends Failed<string, unknown> = Failed<string, unknown>> = <Given extends { request: Request } & Extra, Result extends {} \| null \| undefined>(next: (ctx?: Given) => Promise<Result>, ctx: Given) => Result \| Answer \| Refused \| Promise<Result \| Answer \| Refused>` | One rung, written once for every lane whose context carries `Extra`. | 16.8, 16.14, 16.15, 16.16, 16.21, 16.86, 37.14, 37.15 |
229
229
  | `GET` | `<Stored, Accepted, Failures, Produced, Answers extends readonly unknown[] = unknown[]>(handler: Reactive<Stored, Accepted, Failures, Produced>, options?: RpcOptions<undefined, Answers>) => Rpc<Stored, undefined, Failures \| RungRefusals<Answers>, 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, 12.17, 18.1, 18.3, 18.6, 18.8, 18.10, 18.12, 18.21, 18.24, 18.27 |
230
230
  | `GET` | `<Stored, Args, Accepted, Failures, Produced, Answers extends readonly unknown[] = unknown[]>(handler: Keyed<Stored, Args, Accepted, Failures, Produced>, options?: RpcOptions<Args, Answers>) => Rpc<Stored, Args, Failures \| RungRefusals<Answers>, Produced>` | The keyed overload, for a `Keyed` given an address. | 11.2, 16.1, 16.3, 16.6 |
231
231
  | `GET` | `<Chunk, Shaped = Chunk[], Gate = Schema<Chunk[]>, Answers extends readonly unknown[] = unknown[]>(handler: () => AsyncGenerator<Chunk> \| Promise<AsyncGenerator<Chunk>>, options?: Shaping<Chunk[], Shaped, never, Gate> & RpcOptions<undefined, Answers>) => Rpc<Kept<Shaped>, undefined, Refusals<Shaped, Gate, Chunk[]> \| RungRefusals<Answers>, Chunk>` | The streaming overload, whose options build the value it becomes beside its address. | 16.1, 16.6, 16.7, 16.44, 16.45, 16.46, 16.47 |
@@ -245,6 +245,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
245
245
  | `rpc.raw` | `(args: Args, init?: RequestInit) => Promise<Response>` | The same call handed back as the raw response. | 16.11, 16.42 |
246
246
  | `rpc.method` | `string` | The HTTP method. | 16.2 |
247
247
  | `rpc.url` | `(args?: Args) => string` | The address, resolved against the mount. | 16.9, 16.10, 30.28 |
248
+ | `rpc.address` | `string \| undefined` | Where the handler answers. | 16.88 |
248
249
  | `rpc.description` | `string \| undefined` | The human description. | 16.28 |
249
250
  | `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 |
250
251
 
@@ -267,7 +268,7 @@ type Reached = { readonly [REACHED]: true }
267
268
  | Name | Signature | Meaning | Rules |
268
269
  | --- | --- | --- | --- |
269
270
  | `description` | `string` | The human description carried onto every generated surface. | 16.28 |
270
- | `middleware` | `{ [At in keyof Answers]: (next: (ctx?: { request: Request; args: () => Args }) => Promise<Reached>, ctx: { request: Request; args: () => Args }) => Answers[At] \| Promise<Answers[At]> }` | The rungs run per call. | 16.8, 16.11, 16.12, 16.16, 16.17, 16.18, 16.19, 16.20, 16.86 |
271
+ | `middleware` | `{ [At in keyof Answers]: (next: (ctx?: { request: Request; args: () => Args }) => Promise<Reached>, ctx: { request: Request; args: () => Args }) => Answers[At] \| Promise<Answers[At]> } \| readonly ((next: (ctx?: { request: Request; args: () => NoInfer<Args> }) => Promise<Reached>, ctx: { request: Request; args: () => NoInfer<Args> }) => unknown)[]` | The rungs run per call. | 16.8, 16.11, 16.12, 16.16, 16.17, 16.18, 16.19, 16.20, 16.86 |
271
272
  | `timeout` | `number` | How long the call may go without progress. | 16.22, 16.58, 16.59, 16.60, 16.61, 16.77 |
272
273
  | `clients` | `Clients` | Which surfaces carry this handler. | 16.37, 20.3 |
273
274
  | `crossOrigin` | `boolean \| string[]` | Whether other origins may call it, or follow it over the mux. | 16.24, 18.2, 21.19 |
@@ -365,7 +366,7 @@ type Reached = { readonly [REACHED]: true }
365
366
  | `Toolbox` | `{ list: ToolDefinition[]; call(name: string, input: unknown): Promise<ToolAnswer> }` | The app's MCP tools, called as the asker. | 44.6, 44.7, 44.35 |
366
367
  | `ToolAnswer` | `{ content: AssistantContent[]; failed: boolean }` | What a tool call answered: its pieces, and whether it failed. | 20.21, 20.22, 44.35 |
367
368
  | `ToolDefinition` | `{ name: string; description: string; inputSchema: unknown; annotations: { readOnlyHint: boolean; destructiveHint: boolean; idempotentHint: boolean }; outputSchema: unknown }` | A tool as the app's MCP surface lists it. | 20.14, 20.16, 20.19, 44.6 |
368
- | `resultsOf` | `<Value>(events: readonly AssistantEvent[], handler: Rpc<Value, never, unknown, unknown>) => Value[]` | What a conversation's calls to one handler answered. | 44.51 |
369
+ | `resultsOf` | `<Value>(events: readonly AssistantEvent[], handler: ((...args: never) => Reactive<Value, never, unknown, unknown>) & { method: string; address: string \| undefined }) => Value[]` | What a conversation's calls to one handler answered. | 44.51 |
369
370
  | `imagePiece` | `(image: Blob) => Promise<{ type: 'image'; data: string; mimeType: string }>` | An image as a turn's image piece, drawn down to what a model looks at. | 44.52, 44.53, 44.54 |
370
371
  | `imageFiles` | `(data: DataTransfer \| null) => File[]` | The images a paste or a drop carries that a model is shown. | 44.55 |
371
372
 
package/docs/RULEBOOK.md CHANGED
@@ -1183,6 +1183,10 @@ declaration, and a rung naming no refusal, one typed to answer anything or a `Mi
1183
1183
  16.87 A handler whose parameter carries no annotation MUST take its `Args` from the `args` its options
1184
1184
  declare.
1185
1185
 
1186
+ 16.88 A handler file's `rpc.address` MUST be its route (23.14) under the mount (16.10) once the file is
1187
+ loaded in a process serving the app's compilation, `bun test`'s under `@abide/abide/test` included
1188
+ (38.109), whether or not `createApp` has run.
1189
+
1186
1190
  # 17. Response helpers
1187
1191
 
1188
1192
  17.1 Every body helper MUST take `Values<T>`.
package/docs/abide.md CHANGED
@@ -1774,6 +1774,7 @@ A handler that returns a `Blob` answers with the blob's own type, so this one is
1774
1774
  | --- | --- |
1775
1775
  | `rpc.url` | the address, with the args written in, for a link, a form or a webhook |
1776
1776
  | `rpc.method` | the verb it was declared with |
1777
+ | `rpc.address` | its route, once its file has loaded, in a test as in the app |
1777
1778
  | `rpc.raw` | the `Response` itself, for a header or a body you read yourself |
1778
1779
  | `rpc.description` | the sentence it was declared with |
1779
1780
 
@@ -8418,6 +8419,7 @@ Reactive primitives: what is written in place rather than imported.
8418
8419
  | `rpc.raw` | `(args: Args, init?: RequestInit) => Promise<Response>` | The same call handed back as the raw response. | [16.11](/rulebook#16.11), [16.42](/rulebook#16.42) |
8419
8420
  | `rpc.method` | `string` | The HTTP method. | [16.2](/rulebook#16.2) |
8420
8421
  | `rpc.url` | `(args?: Args) => string` | The address, resolved against the mount. | [16.9](/rulebook#16.9), [16.10](/rulebook#16.10), [30.28](/rulebook#30.28) |
8422
+ | `rpc.address` | `string \| undefined` | Where the handler answers. | [16.88](/rulebook#16.88) |
8421
8423
  | `rpc.description` | `string \| undefined` | The human description. | [16.28](/rulebook#16.28) |
8422
8424
 
8423
8425
  ## `outbox`
@@ -8445,7 +8447,7 @@ Reactive primitives: what is written in place rather than imported.
8445
8447
 
8446
8448
  | Name | Signature | Meaning | Rules |
8447
8449
  | --- | --- | --- | --- |
8448
- | `Middleware` | `Middleware<Answer = never, Extra extends object = object, Refused extends Failed<string, unknown> = Failed<string, unknown>> = <Given extends { request: Request } & Extra, Result>(next: (ctx?: Given) => Promise<Result>, ctx: Given) => Result \| Answer \| Refused \| Promise<Result \| Answer \| Refused>` | One rung, written once for every lane whose context carries `Extra`. | [16.8](/rulebook#16.8), [16.14](/rulebook#16.14), [16.15](/rulebook#16.15), [16.16](/rulebook#16.16), [16.21](/rulebook#16.21), [16.86](/rulebook#16.86), [37.14](/rulebook#37.14), [37.15](/rulebook#37.15) |
8450
+ | `Middleware` | `Middleware<Answer = never, Extra extends object = object, Refused extends Failed<string, unknown> = Failed<string, unknown>> = <Given extends { request: Request } & Extra, Result extends {} \| null \| undefined>(next: (ctx?: Given) => Promise<Result>, ctx: Given) => Result \| Answer \| Refused \| Promise<Result \| Answer \| Refused>` | One rung, written once for every lane whose context carries `Extra`. | [16.8](/rulebook#16.8), [16.14](/rulebook#16.14), [16.15](/rulebook#16.15), [16.16](/rulebook#16.16), [16.21](/rulebook#16.21), [16.86](/rulebook#16.86), [37.14](/rulebook#37.14), [37.15](/rulebook#37.15) |
8449
8451
 
8450
8452
  ## Taught in
8451
8453
 
@@ -8526,7 +8528,7 @@ Reactive primitives: what is written in place rather than imported.
8526
8528
  | Name | Signature | Meaning | Rules |
8527
8529
  | --- | --- | --- | --- |
8528
8530
  | `description` | `string` | The human description carried onto every generated surface. | [16.28](/rulebook#16.28) |
8529
- | `middleware` | `{ [At in keyof Answers]: (next: (ctx?: { request: Request; args: () => Args }) => Promise<Reached>, ctx: { request: Request; args: () => Args }) => Answers[At] \| Promise<Answers[At]> }` | The rungs run per call. | [16.8](/rulebook#16.8), [16.11](/rulebook#16.11), [16.12](/rulebook#16.12), [16.16](/rulebook#16.16), [16.17](/rulebook#16.17), [16.18](/rulebook#16.18), [16.19](/rulebook#16.19), [16.20](/rulebook#16.20), [16.86](/rulebook#16.86) |
8531
+ | `middleware` | `{ [At in keyof Answers]: (next: (ctx?: { request: Request; args: () => Args }) => Promise<Reached>, ctx: { request: Request; args: () => Args }) => Answers[At] \| Promise<Answers[At]> } \| readonly ((next: (ctx?: { request: Request; args: () => NoInfer<Args> }) => Promise<Reached>, ctx: { request: Request; args: () => NoInfer<Args> }) => unknown)[]` | The rungs run per call. | [16.8](/rulebook#16.8), [16.11](/rulebook#16.11), [16.12](/rulebook#16.12), [16.16](/rulebook#16.16), [16.17](/rulebook#16.17), [16.18](/rulebook#16.18), [16.19](/rulebook#16.19), [16.20](/rulebook#16.20), [16.86](/rulebook#16.86) |
8530
8532
  | `timeout` | `number` | How long the call may go without progress. | [16.22](/rulebook#16.22), [16.58](/rulebook#16.58), [16.59](/rulebook#16.59), [16.60](/rulebook#16.60), [16.61](/rulebook#16.61), [16.77](/rulebook#16.77) |
8531
8533
  | `clients` | `Clients` | Which surfaces carry this handler. | [16.37](/rulebook#16.37), [20.3](/rulebook#20.3) |
8532
8534
  | `crossOrigin` | `boolean \| string[]` | Whether other origins may call it, or follow it over the mux. | [16.24](/rulebook#16.24), [18.2](/rulebook#18.2), [21.19](/rulebook#21.19) |
@@ -9075,7 +9077,7 @@ Generated surfaces: what is written in place rather than imported.
9075
9077
 
9076
9078
  | Name | Signature | Meaning | Rules |
9077
9079
  | --- | --- | --- | --- |
9078
- | `resultsOf` | `<Value>(events: readonly AssistantEvent[], handler: Rpc<Value, never, unknown, unknown>) => Value[]` | What a conversation's calls to one handler answered. | [44.51](/rulebook#44.51) |
9080
+ | `resultsOf` | `<Value>(events: readonly AssistantEvent[], handler: ((...args: never) => Reactive<Value, never, unknown, unknown>) & { method: string; address: string \| undefined }) => Value[]` | What a conversation's calls to one handler answered. | [44.51](/rulebook#44.51) |
9079
9081
 
9080
9082
  ## Taught in
9081
9083
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abide/abide",
3
- "version": "0.125.0",
3
+ "version": "0.125.2",
4
4
  "type": "module",
5
5
  "description": "Isomorphic type-safe framework for reactive async interfaces for humans and machines",
6
6
  "license": "MIT",
@@ -404,6 +404,7 @@ async function emitApp(
404
404
  if (!mount.private)
405
405
  handlers[mount.source] = {
406
406
  scope: mount.scope,
407
+ address: mount.address,
407
408
  client: standIns.get(mount.source) ?? '',
408
409
  args: mount.args,
409
410
  }
@@ -135,6 +135,8 @@ export function serveCompilation(root: string, compilation: Compilation): void {
135
135
  // plugin per compilation because the filter is fixed at registration, and the runtime's `onLoad`
136
136
  // cannot fall through, so the filter names the handler files and nothing else. The face it names
137
137
  // is resolved by `modulesPlugin`, registered once.
138
+ // 16.88 — AND GIVES IT ITS ADDRESS, so a test importing the file calls, links and matches it (44.51)
139
+ // with no app booted.
138
140
  // 15.8, 19.9 — AND HOLDS ITS HANDLER TO THE SCHEMA ITS ANNOTATION DERIVES, once the face has built it:
139
141
  // this module is what every import of the file is answered with — the server's mount, a test's own
140
142
  // import, a component's under `mount` — so a call is held to it whoever makes it.
@@ -153,15 +155,20 @@ function handlerFilesPlugin(
153
155
  },
154
156
  (args) => {
155
157
  const face = JSON.stringify(`${args.path}${HANDLER_SUFFIX}`)
156
- const schema = faces.get(args.path)?.args
157
- return {
158
- contents:
159
- `export { default } from ${face}\n` +
160
- (schema === undefined
161
- ? ''
162
- : `import __handler from ${face}\nimport { derivedArgs as __derivedArgs } from '${SERVER_SPECIFIER}'\n__derivedArgs(__handler, ${JSON.stringify(schema)})\n`),
163
- loader: 'js',
164
- }
158
+ const { address, args: schema } = faces.get(
159
+ args.path,
160
+ ) as HandlerFace
161
+ const lines = [
162
+ `export { default } from ${face}`,
163
+ `import __handler from ${face}`,
164
+ `import { addressed as __addressed, derivedArgs as __derivedArgs } from '${SERVER_SPECIFIER}'`,
165
+ `__addressed(__handler, ${JSON.stringify(address)})`,
166
+ ]
167
+ if (schema !== undefined)
168
+ lines.push(
169
+ `__derivedArgs(__handler, ${JSON.stringify(schema)})`,
170
+ )
171
+ return { contents: `${lines.join('\n')}\n`, loader: 'js' }
165
172
  },
166
173
  )
167
174
  },
@@ -0,0 +1,14 @@
1
+ import { mounted } from '#shared/mount.ts'
2
+
3
+ /**
4
+ * Gives a handler its address: `route` under the mount, read when it is reported.
5
+ *
6
+ * 16.88 — ONE WRITER, READ LATE: the mount is the configuration's, so the address a file's module
7
+ * is given as it loads is the one a boot would give it, and a later call only names the route again.
8
+ */
9
+ export function addressed(handler: object, route: string): void {
10
+ Object.defineProperty(handler, 'address', {
11
+ get: () => mounted(route),
12
+ configurable: true,
13
+ })
14
+ }
@@ -43,10 +43,12 @@ export type Asked =
43
43
 
44
44
  /**
45
45
  * One handler file, as `Compilation.handlers` holds it — and 19.9's schema its default's annotation
46
- * derives, which the module at the file's path holds it to (`derivedArgs`).
46
+ * derives, which the module at the file's path holds it to (`derivedArgs`), and the address it is
47
+ * stamped with on load (16.2).
47
48
  */
48
49
  export type HandlerFace = {
49
50
  scope: string
51
+ address: string
50
52
  client: string
51
53
  args: JsonValue | undefined
52
54
  }
@@ -56,6 +56,9 @@ export { type App, createApp } from './createApp.ts'
56
56
  // 41.28's run-time half: an emitted component reads it on either side of its setup, and one whose
57
57
  // setup made a call answers as a page does. Not an app's name.
58
58
  export { callsInRender } from './PageRender.ts'
59
+ // 16.88's run-time half: what the module a handler file is loaded through calls to give it its
60
+ // address. Not an app's name.
61
+ export { addressed } from './addressed.ts'
59
62
  // 19.9's run-time half: what the module a handler is loaded through calls to hold it to the schema
60
63
  // its annotation derives (15.8). Not an app's name.
61
64
  export { derivedArgs } from './argsContract.ts'
@@ -3,11 +3,11 @@
3
3
 
4
4
  import type { CopyOptions } from '#shared/CopyOptions.ts'
5
5
  import type { JsonValue } from '#shared/JsonValue.ts'
6
- import { mounted as underMount } from '#shared/mount.ts'
7
6
  import type { Gated, HeldToAnnotation } from '#shared/reactive/argsContract.ts'
8
7
  import type { Defaults } from '#shared/reactive/parameterDefaults.ts'
9
8
  import type { Method, Rpc } from '#shared/rpc.ts'
10
9
 
10
+ import { addressed } from './addressed.ts'
11
11
  import { answered } from './answerFailure.ts'
12
12
  import { derivedArgs } from './argsContract.ts'
13
13
  import { type LaneRung, rungsOver } from './hooks.ts'
@@ -116,11 +116,7 @@ export function mountedOwn(
116
116
  name: string,
117
117
  handler: object,
118
118
  ): [string, Mounted] {
119
- // 16.10 — READ WHEN IT IS REPORTED, where a mounted handler's is stamped at boot: the mount is
120
- // the configuration's, and nothing boots this handler.
121
- Object.defineProperty(handler, 'address', {
122
- get: () => underMount(address),
123
- })
119
+ addressed(handler, address)
124
120
  return [
125
121
  address,
126
122
  {
@@ -168,7 +164,7 @@ export async function mountHandlers(
168
164
  const handler = exported as Mounted['handler']
169
165
  // 16.9, 16.10 — the address `rpc.url` and `rpc.raw` build on, under the mount — and what a
170
166
  // page's render turns a call it made into the browser's request by (41.20).
171
- handler.address = underMount(mount.address)
167
+ addressed(handler, mount.address)
172
168
  // 19.9 — the schema the build derived off its annotation, for a handler whose module did not
173
169
  // file it on load: a bundled executable's faces carry no `derivedArgs` call.
174
170
  if (mount.args !== undefined) derivedArgs(handler, mount.args)
@@ -10,12 +10,19 @@ import type { Failed } from './Failed.ts'
10
10
  *
11
11
  * `Refused` is what it may refuse with; a rung naming its refusals carries them onto the `Rpc` its
12
12
  * handler is declared with (16.86), where one left at `Failed<string, unknown>` carries none.
13
+ *
14
+ * `Result` IS BOUNDED BY EVERYTHING BUT `unknown`. A rung read outside a context — listed by a
15
+ * helper, or beside the plain array arm of `RpcOptions['middleware']` — is read at its erased
16
+ * signature, where `Result` is its bound, and an `unknown` there absorbs `Refused` in the union.
13
17
  */
14
18
  export type Middleware<
15
19
  Answer = never,
16
20
  Extra extends object = object,
17
21
  Refused extends Failed<string, unknown> = Failed<string, unknown>,
18
- > = <Given extends { request: Request } & Extra, Result>(
22
+ > = <
23
+ Given extends { request: Request } & Extra,
24
+ Result extends {} | null | undefined,
25
+ >(
19
26
  next: (ctx?: Given) => Promise<Result>,
20
27
  ctx: Given,
21
28
  ) => Result | Answer | Refused | Promise<Result | Answer | Refused>
@@ -1,11 +1,13 @@
1
1
  import type { AssistantEvent } from './AssistantEvent.ts'
2
+ import type { Reactive } from './reactive/Reactive.ts'
2
3
  import type { Rpc } from './rpc.ts'
3
4
 
4
5
  /** What every call to `handler` in `events` answered, in order, a failed call's left out. 44.51. */
5
6
  export function resultsOf<Value>(
6
7
  events: readonly AssistantEvent[],
7
- // Any handler at all: its args, failures and production are no part of what it answered.
8
- handler: Rpc<Value, never, unknown, unknown>,
8
+ // Any `Rpc`, whatever its `Args`: only the value is read off its call.
9
+ handler: ((...args: never) => Reactive<Value, never, unknown, unknown>) &
10
+ Pick<Rpc<Value, never>, 'method' | 'address'>,
9
11
  ): Value[] {
10
12
  const values: Value[] = []
11
13
  for (const event of events)
package/src/shared/rpc.ts CHANGED
@@ -122,7 +122,7 @@ export type Rpc<Value, Args, Failures = never, Produced = Value> = ((
122
122
  url(...args: undefined extends Args ? [args?: Args] : [args: Args]): string
123
123
  /** The same call, handed back as the `Response` (16.42). */
124
124
  raw(args?: Args, init?: RequestInit): Promise<Response>
125
- /** Where the handler is mounted, stamped by whoever mounted it — `undefined` until then. */
125
+ /** Where the handler answers: its route under the mount, once its file has loaded (16.88). */
126
126
  address: string | undefined
127
127
  keyed: boolean
128
128
  timeout: number | undefined
@@ -206,6 +206,12 @@ type RungRefusals<Answers extends readonly unknown[]> =
206
206
  : never
207
207
  : never
208
208
 
209
+ /** One rung of a handler's `middleware`, answering `Answer` in the handler's place or passing on. */
210
+ type HandlerRung<Args, Answer> = (
211
+ next: (ctx?: RpcContext<Args>) => Promise<Reached>,
212
+ ctx: RpcContext<Args>,
213
+ ) => Answer | Promise<Answer>
214
+
209
215
  export type RpcOptions<
210
216
  Args = unknown,
211
217
  Answers extends readonly unknown[] = unknown[],
@@ -214,13 +220,16 @@ export type RpcOptions<
214
220
  /**
215
221
  * 16.86 — one rung per answer in `Answers`, each read in context: a rung's own refusals are
216
222
  * inferred where the list is written at the declaration.
223
+ *
224
+ * The plain array arm is the context a list built by a GENERIC helper is typed in: the mapped
225
+ * arm is over `Answers`, still being inferred, so the helper's own `Args` read nothing there and
226
+ * are `unknown`. It adds no refusal of its own — `Answers` is still read off the list — and
227
+ * no `Args` either: a `Middleware` asking `args: () => unknown` of its context would otherwise
228
+ * widen a keyed handler's to `unknown`, which its `Keyed` then refuses.
217
229
  */
218
- middleware?: {
219
- [At in keyof Answers]: (
220
- next: (ctx?: RpcContext<Args>) => Promise<Reached>,
221
- ctx: RpcContext<Args>,
222
- ) => Answers[At] | Promise<Answers[At]>
223
- }
230
+ middleware?:
231
+ | { [At in keyof Answers]: HandlerRung<Args, Answers[At]> }
232
+ | readonly HandlerRung<NoInfer<Args>, unknown>[]
224
233
  timeout?: number
225
234
  clients?: Clients
226
235
  crossOrigin?: boolean | string[]
@@ -386,6 +395,23 @@ type Declare = {
386
395
  | RungRefusals<Answers>,
387
396
  AdoptedProduced<Computed, Kept<Shaped>>
388
397
  >
398
+ // 16.1 — a value, `reactive`'s value overload given an address. Ahead of the arm below rather
399
+ // than last, so a body refused for its options is reported by that arm, naming them, and not as
400
+ // a value it never was: `Held` already turns every function away.
401
+ <
402
+ Accepted,
403
+ Shaped = Accepted,
404
+ Gate = Schema<Accepted>,
405
+ Answers extends readonly unknown[] = unknown[],
406
+ >(
407
+ handler: Held<Accepted>,
408
+ options?: Shaping<Accepted, Shaped, never, Gate> &
409
+ RpcOptions<undefined, Answers>,
410
+ ): Rpc<
411
+ Kept<Shaped>,
412
+ undefined,
413
+ Refusals<Shaped, Gate, Accepted> | RungRefusals<Answers>
414
+ >
389
415
  <
390
416
  Computed,
391
417
  Args,
@@ -405,21 +431,6 @@ type Declare = {
405
431
  | RungRefusals<Answers>,
406
432
  AdoptedProduced<Computed, Kept<Shaped>>
407
433
  >
408
- // 16.1 — a value, `reactive`'s value overload given an address. Last, so a function is a body.
409
- <
410
- Accepted,
411
- Shaped = Accepted,
412
- Gate = Schema<Accepted>,
413
- Answers extends readonly unknown[] = unknown[],
414
- >(
415
- handler: Held<Accepted>,
416
- options?: Shaping<Accepted, Shaped, never, Gate> &
417
- RpcOptions<undefined, Answers>,
418
- ): Rpc<
419
- Kept<Shaped>,
420
- undefined,
421
- Refusals<Shaped, Gate, Accepted> | RungRefusals<Answers>
422
- >
423
434
  }
424
435
 
425
436
  /** What a handler's selection members answer before a call has built its value: no entries' answers. */