@nwire/handler 0.8.17 → 0.9.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/README.md +183 -68
- package/dist/define-error.d.ts +33 -27
- package/dist/define-error.d.ts.map +1 -1
- package/dist/define-error.js +27 -19
- package/dist/define-error.js.map +1 -1
- package/dist/define-handler.d.ts +146 -107
- package/dist/define-handler.d.ts.map +1 -1
- package/dist/define-handler.js +139 -83
- package/dist/define-handler.js.map +1 -1
- package/dist/handler-index.d.ts +17 -22
- package/dist/handler-index.d.ts.map +1 -1
- package/dist/handler-index.js +16 -21
- package/dist/handler-index.js.map +1 -1
- package/package.json +4 -7
package/README.md
CHANGED
|
@@ -1,88 +1,203 @@
|
|
|
1
1
|
# @nwire/handler
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
`defineHandler(name, config)` describes a single operation: input
|
|
6
|
-
schema, response shape, the function. `execute(handler, input, ctx?)`
|
|
7
|
-
runs one. Every Nwire transport (`@nwire/http`, `@nwire/queue`,
|
|
8
|
-
`@nwire/mcp`) speaks this exact shape; `@nwire/forge`'s `defineAction`
|
|
9
|
-
and `defineQuery` are flavored sugar on top.
|
|
3
|
+
The operation primitive — one typed, callable, hookable value usable from every transport (HTTP, queue, MCP, CLI, direct test).
|
|
10
4
|
|
|
11
5
|
```bash
|
|
12
|
-
pnpm add @nwire/handler
|
|
6
|
+
pnpm add @nwire/handler
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Why
|
|
10
|
+
|
|
11
|
+
Every backend ends up with the same shape: parse input, run a function with some deps, return a typed response or throw a typed error. Different teams wrap it in different abstractions (controllers, route handlers, command handlers, tRPC procedures, NestJS providers). They're all the same thing.
|
|
12
|
+
|
|
13
|
+
`@nwire/handler` is that thing, distilled: a callable, type-generic value. It's a `Hook` from `@nwire/hooks` underneath, so it gets `.use()` middleware, `.on()` listeners, `.tap()` observation, `signal` cancellation, and replay — for free.
|
|
14
|
+
|
|
15
|
+
Three rules:
|
|
16
|
+
|
|
17
|
+
1. **Ctx is generic.** Whatever you pass to `.run(ctx)` is what the handler body destructures, fully typed.
|
|
18
|
+
2. **App pins ctx once.** `defineHandlerWith<AppExtras>()` returns a factory you import everywhere; handlers never annotate ctx.
|
|
19
|
+
3. **No global pollution.** Plugin contributions are imported types, intersected at the app boundary. Nothing is `declare module`-ed.
|
|
20
|
+
|
|
21
|
+
## Surface
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
// The primitive
|
|
25
|
+
function defineHandler<TInputSchema, TOutput, TExtras extends object = object>(
|
|
26
|
+
name: string,
|
|
27
|
+
config: HandlerConfig<TInputSchema, TOutput, TExtras>,
|
|
28
|
+
): HandlerDefinition<TInputSchema, TOutput, TExtras>;
|
|
29
|
+
|
|
30
|
+
// The app-boundary factory — pins TExtras once
|
|
31
|
+
function defineHandlerWith<TExtras extends object>(): typeof defineHandler bound to TExtras;
|
|
32
|
+
|
|
33
|
+
// Type alias for declaring a handler's ctx shape inline
|
|
34
|
+
type Ctx<TInput, TExtras extends object = object> =
|
|
35
|
+
{ input: TInput; signal: AbortSignal; set: … } & TExtras;
|
|
36
|
+
|
|
37
|
+
// Resources, errors, response factories
|
|
38
|
+
function defineResource(name, { schema, public, examples });
|
|
39
|
+
function defineError({ code, status, summary }); // callable: throw NotFound({ resourceId });
|
|
40
|
+
ok / created / accepted / noContent / notModified / gone
|
|
41
|
+
|
|
42
|
+
// Built-in errors
|
|
43
|
+
Unauthorized / Forbidden / NotFound / Conflict / Gone / BadRequest
|
|
44
|
+
|
|
45
|
+
// Type helpers
|
|
46
|
+
HandlerInput<H> / HandlerOutput<H> / HandlerExtras<H>
|
|
13
47
|
```
|
|
14
48
|
|
|
15
|
-
##
|
|
49
|
+
## Consumer example
|
|
50
|
+
|
|
51
|
+
`app/define.ts` — one file, pinned once:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import { defineHandlerWith, type Ctx } from "@nwire/handler";
|
|
55
|
+
import type { AuthCradle } from "@nwire/auth";
|
|
56
|
+
import type { DbCradle } from "@nwire/data-drizzle";
|
|
57
|
+
|
|
58
|
+
// Plugins export type fragments; app composes its cradle:
|
|
59
|
+
export type AppCradle = AuthCradle &
|
|
60
|
+
DbCradle & {
|
|
61
|
+
config: AppConfig;
|
|
62
|
+
logger: Logger;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// Extras the wires put onto ctx at request time:
|
|
66
|
+
type AppExtras = {
|
|
67
|
+
logger: Logger;
|
|
68
|
+
pg: PgClient;
|
|
69
|
+
user: { id: string; role: string; balance: number };
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export const defineAction = defineHandlerWith<AppExtras>();
|
|
73
|
+
export type AppCtx<I = unknown> = Ctx<I, AppExtras>;
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`app/orders/buy-wash.action.ts` — a complete handler:
|
|
16
77
|
|
|
17
78
|
```ts
|
|
18
79
|
import { z } from "zod";
|
|
19
|
-
import {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
80
|
+
import { defineResource, defineError, NotFound } from "@nwire/handler";
|
|
81
|
+
import { defineAction } from "@/define";
|
|
82
|
+
|
|
83
|
+
const Wash = defineResource("Wash", {
|
|
84
|
+
schema: z.object({ id: z.string(), name: z.string(), price: z.number() }),
|
|
85
|
+
public: ["id", "name", "price"],
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const InsufficientFunds = defineError({
|
|
89
|
+
code: "INSUFFICIENT_FUNDS",
|
|
90
|
+
status: 402,
|
|
91
|
+
summary: "balance too low for this wash",
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
export const BuyWash = defineAction("buyWash", {
|
|
95
|
+
input: z.object({ washId: z.string() }),
|
|
96
|
+
returns: Wash,
|
|
97
|
+
errors: [NotFound, InsufficientFunds],
|
|
98
|
+
handler: async ({ input, logger, pg, user, signal }) => {
|
|
99
|
+
const row = await pg.query("SELECT * FROM washes WHERE id=$1", [input.washId], { signal });
|
|
100
|
+
if (!row) throw NotFound({ resourceId: input.washId });
|
|
101
|
+
if (user.balance < row.price) throw InsufficientFunds({ have: user.balance, need: row.price });
|
|
102
|
+
logger.log(`${user.id} bought ${row.id}`);
|
|
103
|
+
return row;
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Per-handler middleware via the hook substrate
|
|
108
|
+
BuyWash.use(async (ctx, next) => {
|
|
109
|
+
const t = performance.now();
|
|
31
110
|
await next();
|
|
111
|
+
ctx.logger.log(`buyWash ${(performance.now() - t).toFixed(1)}ms`);
|
|
32
112
|
});
|
|
113
|
+
```
|
|
33
114
|
|
|
34
|
-
|
|
35
|
-
|
|
115
|
+
`wires/api.wire.ts` — boot, container, request composition:
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
import { createContainer } from "@nwire/container";
|
|
119
|
+
import type { AppCradle } from "@/define";
|
|
120
|
+
import { BuyWash } from "@/orders/buy-wash.action";
|
|
121
|
+
|
|
122
|
+
const root = createContainer<AppCradle>();
|
|
123
|
+
root.register("config", { port: 3000 });
|
|
124
|
+
root.register("logger", () => ({ log: (s) => console.log(s) }));
|
|
125
|
+
root.register("db.pg", () => new PgClient(root.cradle.config.port));
|
|
126
|
+
|
|
127
|
+
const server = createServer(async (req, res) => {
|
|
128
|
+
const user = await authenticate(req);
|
|
129
|
+
|
|
130
|
+
const result = await BuyWash(JSON.parse(await readBody(req))).run({
|
|
131
|
+
ctx: {
|
|
132
|
+
logger: root.cradle.logger,
|
|
133
|
+
pg: root.cradle["db.pg"],
|
|
134
|
+
user,
|
|
135
|
+
},
|
|
136
|
+
signal: req.signal,
|
|
137
|
+
});
|
|
138
|
+
res.writeHead(201).end(JSON.stringify(result));
|
|
36
139
|
});
|
|
140
|
+
```
|
|
37
141
|
|
|
38
|
-
|
|
142
|
+
`app/orders/buy-wash.test.ts` — unit test, no container:
|
|
39
143
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
144
|
+
```ts
|
|
145
|
+
import { expect, test } from "vitest";
|
|
146
|
+
import { BuyWash, InsufficientFunds } from "./buy-wash.action";
|
|
147
|
+
|
|
148
|
+
test("rejects when balance < price", async () => {
|
|
149
|
+
await expect(
|
|
150
|
+
BuyWash({ washId: "w1" }).run({
|
|
151
|
+
ctx: {
|
|
152
|
+
logger: { log: () => {} },
|
|
153
|
+
pg: { query: async () => ({ id: "w1", name: "Quick", price: 100 }) },
|
|
154
|
+
user: { id: "u-1", role: "user", balance: 5 },
|
|
155
|
+
},
|
|
156
|
+
}),
|
|
157
|
+
).rejects.toMatchObject({
|
|
158
|
+
code: "INSUFFICIENT_FUNDS",
|
|
159
|
+
status: 402,
|
|
160
|
+
context: { have: 5, need: 100 },
|
|
161
|
+
});
|
|
49
162
|
});
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## The four contracts in one model
|
|
166
|
+
|
|
167
|
+
| Concern | How |
|
|
168
|
+
| ---------------- | ------------------------------------------------------------------------------------------- |
|
|
169
|
+
| Input validation | `input: zodSchema` — parsed before the handler body runs |
|
|
170
|
+
| Output shape | `returns: Resource \| ResponseSpec[]` — narrows the handler's return type, drives OpenAPI |
|
|
171
|
+
| Throws | `errors: [NotFound, …]` — typed throwables, drive 4xx OpenAPI bodies |
|
|
172
|
+
| Cancellation | `signal: AbortSignal` on ctx — forward to fetch/pg/etc., bail via `signal.throwIfAborted()` |
|
|
173
|
+
|
|
174
|
+
## Cancellation in practice
|
|
50
175
|
|
|
51
|
-
|
|
176
|
+
The signal flows through nested `.run()` calls automatically:
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
const ParentOp = defineAction("parent", {
|
|
180
|
+
handler: async ({ signal }) => ChildOp().run({ signal }),
|
|
181
|
+
});
|
|
182
|
+
// caller-supplied signal → ParentOp.run({ signal }) → ChildOp.run({ signal })
|
|
183
|
+
// abort the caller's controller → both bail
|
|
52
184
|
```
|
|
53
185
|
|
|
54
|
-
|
|
186
|
+
When no signal is supplied, ctx gets a non-aborting placeholder — code can always pass `ctx.signal` through to `fetch`/`pg`/`redis` without null checks.
|
|
187
|
+
|
|
188
|
+
## Errors
|
|
189
|
+
|
|
190
|
+
```ts
|
|
191
|
+
throw NotFound; // bare value (it IS an Error)
|
|
192
|
+
throw NotFound({ resourceId: input.id }); // callable, contextualised clone
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Caught at the transport layer — REST → `status` + `{ code, message, context }`, GraphQL → `extensions.code`, CLI → exit status + stderr.
|
|
196
|
+
|
|
197
|
+
## Built on @nwire/hooks
|
|
198
|
+
|
|
199
|
+
Every handler is a `Hook<HandlerRunCtx>` underneath. `.use()`, `.on()`, `.tap()`, `.runDetailed()` all work. The user's handler function is the innermost chain step, registered at construction with `Number.MIN_SAFE_INTEGER` priority so every `.use()` you add wraps around it. Telemetry, replay, observation come along for free.
|
|
200
|
+
|
|
201
|
+
## Scope
|
|
55
202
|
|
|
56
|
-
|
|
57
|
-
| -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
58
|
-
| `defineHandler(name, config)` | The operation primitive: `{ input, handler, returns?, errors?, summary?, policy?, middleware? }`. |
|
|
59
|
-
| `execute(handler, input, ctx?)` | Run one handler; returns a response envelope. |
|
|
60
|
-
| `defineMiddleware(name?, fn)` | Chain step `(ctx, next)`. Captures `$source`. Reusable. |
|
|
61
|
-
| `defineHook("before" \| "after", name?, fn)` | Before-or-after-only step (cleaner than mw that forgets `next()`). |
|
|
62
|
-
| `pipe(name?, ...steps)` | Compose middleware + hooks into a reusable pipe value. Attaches a per-pipe `$hook` (`@nwire/hooks`) so transports adopt it for telemetry. |
|
|
63
|
-
| `unwindPipe(steps)` | Walk a pipe into `{ middlewares, beforeHooks, afterHooks }` — transports call this when mounting. |
|
|
64
|
-
| `defineResource(name, opts)` | Public response shape (field allowlist, OpenAPI schema). |
|
|
65
|
-
| `defineError(meta)` | Typed throwable with status code. |
|
|
66
|
-
| Response factories | `ok` / `created` / `accepted` / `noContent` / `notModified` / `gone`. |
|
|
67
|
-
| Framework errors | `Unauthorized` / `Forbidden` / `NotFound` / `Conflict` / `Gone` / `BadRequest`. |
|
|
68
|
-
| Type guards | `isHandlerDefinition` / `isMiddleware` / `isHook` / `isPipe` / `isResourceDefinition` / `isNwireError` / `isResponseSpec` / `isResponseInstance`. |
|
|
69
|
-
|
|
70
|
-
### `$source` + `$hook`
|
|
71
|
-
|
|
72
|
-
- Every `defineMiddleware` / `pipe` captures its call site as `$source`
|
|
73
|
-
via `@nwire/messages.captureSourceLocation`, so Studio can render
|
|
74
|
-
click-to-open chips.
|
|
75
|
-
- Every `pipe(...)` creates a per-pipe `Hook<ResolverCtx>` (one
|
|
76
|
-
`.use()` per middleware step). Runtimes adopt it via
|
|
77
|
-
`runtime.adoptHook($hook)` to route taps into telemetry; consumers
|
|
78
|
-
that don't know about `$hook` ignore the field.
|
|
79
|
-
|
|
80
|
-
## Related
|
|
81
|
-
|
|
82
|
-
- `@nwire/forge` — re-exports this surface and layers `defineAction` (`defineHandler` + `emits`) + `defineQuery` on top.
|
|
83
|
-
- `@nwire/hooks` — backs `pipe()`'s per-pipe `$hook` and the resolver-level dispatch chain.
|
|
84
|
-
- `@nwire/http` — mounts handlers by `unwindPipe`-ing their middleware.
|
|
85
|
-
|
|
86
|
-
## Status
|
|
87
|
-
|
|
88
|
-
v0.x — handler shape, middleware/hook/pipe primitives, response factories, and core errors are locked. Per-pipe `$hook` adoption is the canonical observation seam.
|
|
203
|
+
Standalone — no DI, no events, no logger, no transport opinions. App composes ctx by value; transports compose ctx at boot. Forge / HTTP / queue / MCP wires layer on top.
|
package/dist/define-error.d.ts
CHANGED
|
@@ -7,14 +7,20 @@
|
|
|
7
7
|
* summary: "Station already has an unfinished wash",
|
|
8
8
|
* });
|
|
9
9
|
*
|
|
10
|
-
* //
|
|
11
|
-
*
|
|
10
|
+
* // Throw it with context — callable form, symmetric with defineEvent/defineAction:
|
|
11
|
+
* throw WashInProgress({ stationId: input.id });
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* -
|
|
13
|
+
* // Throw it without context — works because the definition IS an Error:
|
|
14
|
+
* throw WashInProgress;
|
|
15
|
+
*
|
|
16
|
+
* Each `defineError(...)` result is BOTH:
|
|
17
|
+
* - an `Error` instance you can `throw` directly (zero context),
|
|
18
|
+
* - a callable factory `Boom(context)` that returns a contextualised clone.
|
|
19
|
+
*
|
|
20
|
+
* Transports detect `instanceof NwireError`, read `.code` + `.status` + `.summary`
|
|
21
|
+
* + `.context`, render the right shape per medium:
|
|
22
|
+
* - REST → JSON body `{ code, message, context }` with the declared status
|
|
23
|
+
* - GraphQL → error with `extensions: { code, context }`
|
|
18
24
|
* - CLI → stderr message + exit status
|
|
19
25
|
*/
|
|
20
26
|
export interface ErrorMeta {
|
|
@@ -24,36 +30,36 @@ export interface ErrorMeta {
|
|
|
24
30
|
readonly description?: string;
|
|
25
31
|
readonly tags?: readonly string[];
|
|
26
32
|
}
|
|
27
|
-
export interface ErrorDefinition extends ErrorMeta {
|
|
28
|
-
readonly $kind: "error";
|
|
29
|
-
}
|
|
30
33
|
/**
|
|
31
34
|
* Concrete Error subclass — thrown by domain code, caught by transport.
|
|
32
|
-
* The `defineError(...)` result is an instance of this class
|
|
35
|
+
* The `defineError(...)` result is an instance of this class, AND callable
|
|
36
|
+
* so `Boom(context)` returns a contextualised clone.
|
|
33
37
|
*/
|
|
34
|
-
export declare class NwireError extends Error implements
|
|
38
|
+
export declare class NwireError extends Error implements ErrorMeta {
|
|
35
39
|
readonly $kind: "error";
|
|
36
40
|
readonly code: string;
|
|
37
41
|
readonly status: number;
|
|
38
42
|
readonly summary: string;
|
|
39
43
|
readonly description?: string;
|
|
40
44
|
readonly tags?: readonly string[];
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
readonly context?: Readonly<Record<string, unknown>>;
|
|
46
|
+
constructor(meta: ErrorMeta, context?: Readonly<Record<string, unknown>>);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The value returned by `defineError(meta)` — callable to build a contextual
|
|
50
|
+
* clone, and itself an Error instance you can throw directly.
|
|
51
|
+
*/
|
|
52
|
+
export interface ErrorDefinition extends NwireError {
|
|
53
|
+
/** Build a contextualised clone — `throw NotFound({ resourceId: id })`. */
|
|
54
|
+
(context: Readonly<Record<string, unknown>>): NwireError;
|
|
49
55
|
}
|
|
50
|
-
export declare function defineError(meta: ErrorMeta):
|
|
56
|
+
export declare function defineError(meta: ErrorMeta): ErrorDefinition;
|
|
51
57
|
/** Type narrow. */
|
|
52
58
|
export declare function isNwireError(x: unknown): x is NwireError;
|
|
53
|
-
export declare const Unauthorized:
|
|
54
|
-
export declare const Forbidden:
|
|
55
|
-
export declare const NotFound:
|
|
56
|
-
export declare const Gone:
|
|
57
|
-
export declare const BadRequest:
|
|
58
|
-
export declare const Conflict:
|
|
59
|
+
export declare const Unauthorized: ErrorDefinition;
|
|
60
|
+
export declare const Forbidden: ErrorDefinition;
|
|
61
|
+
export declare const NotFound: ErrorDefinition;
|
|
62
|
+
export declare const Gone: ErrorDefinition;
|
|
63
|
+
export declare const BadRequest: ErrorDefinition;
|
|
64
|
+
export declare const Conflict: ErrorDefinition;
|
|
59
65
|
//# sourceMappingURL=define-error.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define-error.d.ts","sourceRoot":"","sources":["../src/define-error.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"define-error.d.ts","sourceRoot":"","sources":["../src/define-error.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAED;;;;GAIG;AACH,qBAAa,UAAW,SAAQ,KAAM,YAAW,SAAS;IACxD,QAAQ,CAAC,KAAK,EAAG,OAAO,CAAU;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;gBAEzC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAUzE;AAED;;;GAGG;AACH,MAAM,WAAW,eAAgB,SAAQ,UAAU;IACjD,2EAA2E;IAC3E,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,UAAU,CAAC;CAC1D;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,GAAG,eAAe,CAW5D;AAED,mBAAmB;AACnB,wBAAgB,YAAY,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,UAAU,CAExD;AAGD,eAAO,MAAM,YAAY,iBAIvB,CAAC;AAEH,eAAO,MAAM,SAAS,iBAIpB,CAAC;AAEH,eAAO,MAAM,QAAQ,iBAInB,CAAC;AAEH,eAAO,MAAM,IAAI,iBAIf,CAAC;AAEH,eAAO,MAAM,UAAU,iBAIrB,CAAC;AAEH,eAAO,MAAM,QAAQ,iBAInB,CAAC"}
|
package/dist/define-error.js
CHANGED
|
@@ -7,19 +7,26 @@
|
|
|
7
7
|
* summary: "Station already has an unfinished wash",
|
|
8
8
|
* });
|
|
9
9
|
*
|
|
10
|
-
* //
|
|
11
|
-
*
|
|
10
|
+
* // Throw it with context — callable form, symmetric with defineEvent/defineAction:
|
|
11
|
+
* throw WashInProgress({ stationId: input.id });
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* -
|
|
13
|
+
* // Throw it without context — works because the definition IS an Error:
|
|
14
|
+
* throw WashInProgress;
|
|
15
|
+
*
|
|
16
|
+
* Each `defineError(...)` result is BOTH:
|
|
17
|
+
* - an `Error` instance you can `throw` directly (zero context),
|
|
18
|
+
* - a callable factory `Boom(context)` that returns a contextualised clone.
|
|
19
|
+
*
|
|
20
|
+
* Transports detect `instanceof NwireError`, read `.code` + `.status` + `.summary`
|
|
21
|
+
* + `.context`, render the right shape per medium:
|
|
22
|
+
* - REST → JSON body `{ code, message, context }` with the declared status
|
|
23
|
+
* - GraphQL → error with `extensions: { code, context }`
|
|
18
24
|
* - CLI → stderr message + exit status
|
|
19
25
|
*/
|
|
20
26
|
/**
|
|
21
27
|
* Concrete Error subclass — thrown by domain code, caught by transport.
|
|
22
|
-
* The `defineError(...)` result is an instance of this class
|
|
28
|
+
* The `defineError(...)` result is an instance of this class, AND callable
|
|
29
|
+
* so `Boom(context)` returns a contextualised clone.
|
|
23
30
|
*/
|
|
24
31
|
export class NwireError extends Error {
|
|
25
32
|
$kind = "error";
|
|
@@ -28,7 +35,8 @@ export class NwireError extends Error {
|
|
|
28
35
|
summary;
|
|
29
36
|
description;
|
|
30
37
|
tags;
|
|
31
|
-
|
|
38
|
+
context;
|
|
39
|
+
constructor(meta, context) {
|
|
32
40
|
super(meta.summary);
|
|
33
41
|
this.name = meta.code;
|
|
34
42
|
this.code = meta.code;
|
|
@@ -36,19 +44,19 @@ export class NwireError extends Error {
|
|
|
36
44
|
this.summary = meta.summary;
|
|
37
45
|
this.description = meta.description;
|
|
38
46
|
this.tags = meta.tags;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
* Build a fresh error instance with the same metadata but additional
|
|
42
|
-
* runtime context — e.g., the resource id that triggered it.
|
|
43
|
-
*/
|
|
44
|
-
with(context) {
|
|
45
|
-
const next = new NwireError(this);
|
|
46
|
-
Object.assign(next, { context });
|
|
47
|
-
return next;
|
|
47
|
+
if (context !== undefined)
|
|
48
|
+
this.context = context;
|
|
48
49
|
}
|
|
49
50
|
}
|
|
50
51
|
export function defineError(meta) {
|
|
51
|
-
|
|
52
|
+
// Build the canonical (no-context) instance + make it callable.
|
|
53
|
+
const base = new NwireError(meta);
|
|
54
|
+
const factory = ((context) => new NwireError(meta, context));
|
|
55
|
+
// Copy every own property + the prototype-resident fields onto the factory
|
|
56
|
+
// so `factory instanceof Error`, `factory.code`, `factory.message`, etc.
|
|
57
|
+
// all behave like the underlying NwireError instance.
|
|
58
|
+
Object.setPrototypeOf(factory, base);
|
|
59
|
+
return factory;
|
|
52
60
|
}
|
|
53
61
|
/** Type narrow. */
|
|
54
62
|
export function isNwireError(x) {
|
package/dist/define-error.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define-error.js","sourceRoot":"","sources":["../src/define-error.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"define-error.js","sourceRoot":"","sources":["../src/define-error.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAUH;;;;GAIG;AACH,MAAM,OAAO,UAAW,SAAQ,KAAK;IAC1B,KAAK,GAAG,OAAgB,CAAC;IACzB,IAAI,CAAS;IACb,MAAM,CAAS;IACf,OAAO,CAAS;IAChB,WAAW,CAAU;IACrB,IAAI,CAAqB;IACzB,OAAO,CAAqC;IAErD,YAAY,IAAe,EAAE,OAA2C;QACtE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACtB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACtB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QAC5B,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;QACpC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACtB,IAAI,OAAO,KAAK,SAAS;YAAE,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACpD,CAAC;CACF;AAWD,MAAM,UAAU,WAAW,CAAC,IAAe;IACzC,gEAAgE;IAChE,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,OAAO,GAAoB,CAAC,CAAC,OAA0C,EAAE,EAAE,CAC/E,IAAI,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,CAA+B,CAAC;IAE/D,2EAA2E;IAC3E,yEAAyE;IACzE,sDAAsD;IACtD,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACrC,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,mBAAmB;AACnB,MAAM,UAAU,YAAY,CAAC,CAAU;IACrC,OAAO,CAAC,YAAY,UAAU,CAAC;AACjC,CAAC;AAED,+CAA+C;AAC/C,MAAM,CAAC,MAAM,YAAY,GAAG,WAAW,CAAC;IACtC,IAAI,EAAE,cAAc;IACpB,MAAM,EAAE,GAAG;IACX,OAAO,EAAE,0BAA0B;CACpC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,SAAS,GAAG,WAAW,CAAC;IACnC,IAAI,EAAE,WAAW;IACjB,MAAM,EAAE,GAAG;IACX,OAAO,EAAE,kDAAkD;CAC5D,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,QAAQ,GAAG,WAAW,CAAC;IAClC,IAAI,EAAE,WAAW;IACjB,MAAM,EAAE,GAAG;IACX,OAAO,EAAE,+BAA+B;CACzC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,IAAI,GAAG,WAAW,CAAC;IAC9B,IAAI,EAAE,MAAM;IACZ,MAAM,EAAE,GAAG;IACX,OAAO,EAAE,iDAAiD;CAC3D,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,UAAU,GAAG,WAAW,CAAC;IACpC,IAAI,EAAE,aAAa;IACnB,MAAM,EAAE,GAAG;IACX,OAAO,EAAE,8EAA8E;CACxF,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,QAAQ,GAAG,WAAW,CAAC;IAClC,IAAI,EAAE,UAAU;IAChB,MAAM,EAAE,GAAG;IACX,OAAO,EAAE,2DAA2D;CACrE,CAAC,CAAC"}
|
package/dist/define-handler.d.ts
CHANGED
|
@@ -3,166 +3,205 @@
|
|
|
3
3
|
*
|
|
4
4
|
* const GetStation = defineHandler("GetStation", {
|
|
5
5
|
* input: z.object({ stationId: z.string() }),
|
|
6
|
+
* handler: async ({ input }) => loadStation(input.stationId),
|
|
6
7
|
* returns: Station,
|
|
7
|
-
*
|
|
8
|
+
* errors: [NotFound],
|
|
8
9
|
* });
|
|
9
10
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
* GetStation({ stationId: "abc" }) // mint a prepared op
|
|
12
|
+
* await GetStation({ stationId: "abc" }).run() // run it
|
|
13
|
+
* await GetStation.run({ input, signal }) // low-level hook surface
|
|
14
|
+
* GetStation.use(myMiddleware) // attach a hook chain step
|
|
15
|
+
* GetStation.on(observer) // attach a listener
|
|
15
16
|
*
|
|
16
|
-
*
|
|
17
|
+
* The handler IS a `Hook` from `@nwire/hooks`. `.use()` attaches chain steps;
|
|
18
|
+
* `.on()` attaches parallel listeners; `.run()` executes. The user's handler
|
|
19
|
+
* function is the innermost chain step — registered at construction with
|
|
20
|
+
* `Number.MIN_SAFE_INTEGER` priority so every `.use(...)` wraps around it.
|
|
17
21
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
+
* **Ctx is generic.** The handler's ctx is `BaseHookCtx & { input } & TExtras`
|
|
23
|
+
* where `TExtras` is inferred from the handler's param annotation, or pinned
|
|
24
|
+
* once via `defineHandlerWith<TExtras>()` at the app boundary. Whatever you
|
|
25
|
+
* pass to `.run(ctx)` is what reaches the handler body, fully typed. No
|
|
26
|
+
* declaration merging on ctx — composition by value.
|
|
22
27
|
*
|
|
23
|
-
* `meta` + `settings`
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* to an existing `ActionDefinition`. That signature stays inside `@nwire/forge`
|
|
28
|
-
* because it depends on the action machinery. The standalone version lives
|
|
29
|
-
* here because every transport needs it without the forge surface.
|
|
28
|
+
* `meta` + `settings` ARE globally augmentable open interfaces. Those are
|
|
29
|
+
* declarative per-handler tags (policy, retry, persona, SLO) that plugins
|
|
30
|
+
* must discover across every handler — different role from ctx, different
|
|
31
|
+
* mechanism by design.
|
|
30
32
|
*/
|
|
31
33
|
import type { z } from "zod";
|
|
32
34
|
import type { ZodTypeAny } from "@nwire/messages";
|
|
33
|
-
import type
|
|
34
|
-
import { type Logger } from "@nwire/logger";
|
|
35
|
+
import { type BaseHookCtx, type ChainFn, type ListenerFn, type RunOptions, type RunResult, type UseOptions, type OnOptions, type TapFn } from "@nwire/hooks";
|
|
35
36
|
import type { ErrorDefinition } from "./define-error.js";
|
|
36
37
|
import type { ResponseSpec, ListResponseSpec } from "./response.js";
|
|
37
38
|
import type { ResourceDefinition } from "./define-resource.js";
|
|
38
|
-
/**
|
|
39
|
+
/**
|
|
40
|
+
* Empty bags — plugins extend via TS declaration merging.
|
|
41
|
+
*
|
|
42
|
+
* declare module "@nwire/handler" {
|
|
43
|
+
* interface HandlerSettings { retry?: { attempts: number } }
|
|
44
|
+
* }
|
|
45
|
+
*
|
|
46
|
+
* These hold *declarative config* (RBAC policies, retry strategies,
|
|
47
|
+
* SLO budgets, persona tags) — NOT runtime ctx. Different role, different
|
|
48
|
+
* mechanism: ctx is per-invocation runtime data composed by value;
|
|
49
|
+
* meta/settings are per-handler design-time tags plugins discover globally.
|
|
50
|
+
*/
|
|
39
51
|
export interface HandlerMeta {
|
|
40
|
-
readonly tags?: readonly string[];
|
|
41
|
-
readonly description?: string;
|
|
42
|
-
readonly version?: number;
|
|
43
|
-
readonly deprecated?: boolean;
|
|
44
|
-
readonly successor?: string;
|
|
45
52
|
}
|
|
46
53
|
export interface HandlerSettings {
|
|
47
|
-
readonly retry?: {
|
|
48
|
-
attempts: number;
|
|
49
|
-
backoff?: "linear" | "exponential";
|
|
50
|
-
};
|
|
51
|
-
readonly timeout?: number;
|
|
52
|
-
readonly cache?: {
|
|
53
|
-
ttl: number;
|
|
54
|
-
};
|
|
55
|
-
readonly idempotency?: (input: unknown) => string;
|
|
56
54
|
}
|
|
55
|
+
/** Resolve a zod schema (or `undefined`) to the parsed input type. */
|
|
56
|
+
export type InferInput<S extends ZodTypeAny | undefined> = S extends ZodTypeAny ? z.output<S> : undefined;
|
|
57
57
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
58
|
+
* The base ctx every handler receives — hooks' `BaseHookCtx` (`signal` + typed
|
|
59
|
+
* `set`) plus the parsed `input`. Apps extend with their own `TExtras` via
|
|
60
|
+
* the `Ctx<TInput, TExtras>` alias below.
|
|
61
61
|
*/
|
|
62
|
-
export interface
|
|
63
|
-
|
|
64
|
-
readonly input: TInput;
|
|
65
|
-
/** Look up a dependency by name. Most handlers destructure instead. */
|
|
66
|
-
resolve<T = unknown>(name: string): T;
|
|
67
|
-
/** Per-execution container scope. */
|
|
68
|
-
readonly container: Container;
|
|
69
|
-
/** Logger scoped to this execution (correlation/causation already attached). */
|
|
70
|
-
readonly logger: Logger;
|
|
71
|
-
/** Free-form bag for transport-specific extras (raw request, MCP progress, …). */
|
|
72
|
-
readonly transport?: Record<string, unknown>;
|
|
62
|
+
export interface HandlerBaseCtx<TInput> extends BaseHookCtx {
|
|
63
|
+
input: TInput;
|
|
73
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Sugar for declaring a handler's ctx shape:
|
|
67
|
+
*
|
|
68
|
+
* handler: async ({ input, cradle, user }: Ctx<MyInput, { cradle: Cradle; user: User }>) => …
|
|
69
|
+
*/
|
|
70
|
+
export type Ctx<TInput, TExtras extends object = object> = HandlerBaseCtx<TInput> & TExtras;
|
|
74
71
|
/**
|
|
75
72
|
* `returns` may be a single resource, a single response spec, or an array
|
|
76
73
|
* of response specs (e.g. `[created(Station), accepted(StationHandle)]`).
|
|
77
74
|
*/
|
|
78
75
|
export type HandlerReturnsSpec = ResourceDefinition | ResponseSpec | ListResponseSpec<ResourceDefinition> | ReadonlyArray<ResponseSpec | ListResponseSpec<ResourceDefinition>>;
|
|
79
|
-
|
|
80
|
-
* Policy: a tag (`"admin"`), a list of tags (`["admin", "operator"]`), or
|
|
81
|
-
* a tuple of `[action, subject]` for RBAC-style policies. Adapters
|
|
82
|
-
* interpret the value per their authorization model.
|
|
83
|
-
*/
|
|
84
|
-
export type HandlerPolicy = string | readonly string[] | readonly [string, string];
|
|
85
|
-
export interface HandlerConfig<TInputSchema extends ZodTypeAny | undefined = ZodTypeAny | undefined, TOutput = unknown> {
|
|
76
|
+
export interface HandlerConfig<TInputSchema extends ZodTypeAny | undefined = undefined, TOutput = unknown, TExtras extends object = object> {
|
|
86
77
|
/** Input schema. Optional for zero-arg handlers (health checks, listings without filters). */
|
|
87
78
|
readonly input?: TInputSchema;
|
|
88
|
-
/** The implementation. Receives the typed input +
|
|
89
|
-
readonly handler: (ctx:
|
|
79
|
+
/** The implementation. Receives the typed input + ctx fields contributed upstream. */
|
|
80
|
+
readonly handler: (ctx: Ctx<InferInput<TInputSchema>, TExtras>) => TOutput | Promise<TOutput>;
|
|
90
81
|
/** Documented response shape(s). Drives OpenAPI + runtime body validation. */
|
|
91
82
|
readonly returns?: HandlerReturnsSpec;
|
|
92
83
|
/** Declared error values this handler may throw. */
|
|
93
84
|
readonly errors?: readonly ErrorDefinition[];
|
|
94
|
-
/**
|
|
95
|
-
readonly emits?: readonly unknown[];
|
|
96
|
-
/** One-line intent. Surfaces in OpenAPI/MCP descriptions. */
|
|
85
|
+
/** One-line intent. Surfaces in OpenAPI / MCP / Studio. */
|
|
97
86
|
readonly summary?: string;
|
|
98
|
-
/**
|
|
99
|
-
readonly
|
|
100
|
-
/**
|
|
87
|
+
/** Multi-line prose. Surfaces in OpenAPI / MCP / Studio. */
|
|
88
|
+
readonly description?: string;
|
|
89
|
+
/** Open bag — plugins contribute fields via declaration merging. */
|
|
101
90
|
readonly meta?: HandlerMeta;
|
|
102
|
-
/**
|
|
91
|
+
/** Open bag — plugins contribute fields via declaration merging. */
|
|
103
92
|
readonly settings?: HandlerSettings;
|
|
104
93
|
}
|
|
105
|
-
|
|
94
|
+
/**
|
|
95
|
+
* Hook ctx that flows through the chain when a handler runs. Extends the
|
|
96
|
+
* consumer's `TExtras` so middleware that wrote to ctx (or used `.set()`)
|
|
97
|
+
* is visible to subsequent steps + listeners.
|
|
98
|
+
*/
|
|
99
|
+
export type HandlerRunCtx<TInput = unknown, TOutput = unknown, TExtras extends object = object> = Ctx<TInput, TExtras> & {
|
|
100
|
+
result?: TOutput;
|
|
101
|
+
};
|
|
102
|
+
/** Options passed to `PreparedOp.run()` — the high-level callable form. */
|
|
103
|
+
export interface PreparedOpRunOptions<TExtras extends object = object> {
|
|
104
|
+
readonly signal?: AbortSignal;
|
|
105
|
+
/**
|
|
106
|
+
* Extras to populate on ctx before the handler body runs. Required when
|
|
107
|
+
* `TExtras` declares fields the runtime must supply (`cradle`, `user`, …).
|
|
108
|
+
* Optional `{}` for handlers that declared no extras.
|
|
109
|
+
*/
|
|
110
|
+
readonly ctx?: TExtras;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* The value returned by calling a handler as a factory: `Handler(input)`.
|
|
114
|
+
* Holds the validated input and exposes `.run(opts?)` to execute.
|
|
115
|
+
*/
|
|
116
|
+
export interface PreparedOp<TInput = unknown, TOutput = unknown, TExtras extends object = object> {
|
|
117
|
+
readonly input: TInput;
|
|
118
|
+
run(opts?: PreparedOpRunOptions<TExtras>): Promise<TOutput>;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* The handler definition. Composes a `Hook<HandlerRunCtx>` for the chain
|
|
122
|
+
* surface (`.use` / `.on` / `.off` / `.run` / `.runDetailed` / `.tap`) and
|
|
123
|
+
* is callable as a factory that mints a `PreparedOp`.
|
|
124
|
+
*/
|
|
125
|
+
export interface HandlerDefinition<TInputSchema extends ZodTypeAny | undefined = undefined, TOutput = unknown, TExtras extends object = object> {
|
|
106
126
|
readonly $kind: "handler";
|
|
107
127
|
readonly name: string;
|
|
108
|
-
readonly config: HandlerConfig<TInputSchema, TOutput>;
|
|
128
|
+
readonly config: HandlerConfig<TInputSchema, TOutput, TExtras>;
|
|
129
|
+
(input: TInputSchema extends ZodTypeAny ? z.input<TInputSchema> : void): PreparedOp<InferInput<TInputSchema>, TOutput, TExtras>;
|
|
130
|
+
/** Hook chain attach — runs sequentially, can short-circuit. */
|
|
131
|
+
use(fn: ChainFn<HandlerRunCtx<InferInput<TInputSchema>, TOutput, TExtras>>, opts?: UseOptions): HandlerDefinition<TInputSchema, TOutput, TExtras>;
|
|
132
|
+
/** Listener attach — observes the final ctx, cannot mutate. */
|
|
133
|
+
on(fn: ListenerFn<HandlerRunCtx<InferInput<TInputSchema>, TOutput, TExtras>>, opts?: OnOptions): HandlerDefinition<TInputSchema, TOutput, TExtras>;
|
|
134
|
+
/** Detach a previously attached `.use()` or `.on()` fn. */
|
|
135
|
+
off(fn: ChainFn<HandlerRunCtx<InferInput<TInputSchema>, TOutput, TExtras>> | ListenerFn<HandlerRunCtx<InferInput<TInputSchema>, TOutput, TExtras>>): HandlerDefinition<TInputSchema, TOutput, TExtras>;
|
|
109
136
|
/**
|
|
110
|
-
*
|
|
111
|
-
*
|
|
137
|
+
* Run the chain + listeners. The caller supplies the ctx — `input` +
|
|
138
|
+
* `signal` are filled in automatically; everything in `TExtras` must
|
|
139
|
+
* be present on the ctx object.
|
|
112
140
|
*/
|
|
113
|
-
run(ctx:
|
|
141
|
+
run(ctx: HandlerRunCtx<InferInput<TInputSchema>, TOutput, TExtras>, opts?: RunOptions): Promise<HandlerRunCtx<InferInput<TInputSchema>, TOutput, TExtras>>;
|
|
142
|
+
/** Same as `.run()` but returns the full observation trace. */
|
|
143
|
+
runDetailed(ctx: HandlerRunCtx<InferInput<TInputSchema>, TOutput, TExtras>, opts?: RunOptions): Promise<RunResult<HandlerRunCtx<InferInput<TInputSchema>, TOutput, TExtras>>>;
|
|
144
|
+
/** Subscribe to per-step observations. */
|
|
145
|
+
tap(observer: TapFn): () => void;
|
|
146
|
+
/** Step counts on the underlying hook. */
|
|
147
|
+
stepCounts(): {
|
|
148
|
+
readonly chain: number;
|
|
149
|
+
readonly listeners: number;
|
|
150
|
+
};
|
|
114
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* `HandlerLike` — what `interface-builder.wire()` accepts.
|
|
154
|
+
*
|
|
155
|
+
* - a full `HandlerDefinition` (typed, hookable, has metadata) — the canonical path.
|
|
156
|
+
* - a bare async function (one-liners, health checks, escape hatch).
|
|
157
|
+
*/
|
|
158
|
+
export type HandlerLike<TInput = unknown, TOutput = unknown> = HandlerDefinition<ZodTypeAny | undefined, TOutput, object> | ((input: TInput) => Promise<TOutput> | TOutput);
|
|
115
159
|
/**
|
|
116
160
|
* Define a handler — the operation primitive.
|
|
117
161
|
*
|
|
118
162
|
* const ListTodos = defineHandler("listTodos", {
|
|
119
163
|
* input: z.object({ status: z.string().optional() }),
|
|
120
|
-
* handler: async ({ input
|
|
121
|
-
* const db = resolve<DB>("db");
|
|
122
|
-
* return db.todos.findMany(input);
|
|
123
|
-
* },
|
|
164
|
+
* handler: async ({ input }) => todos.findMany(input),
|
|
124
165
|
* });
|
|
125
166
|
*
|
|
126
167
|
* // Direct invocation:
|
|
127
|
-
* const
|
|
168
|
+
* const result = await ListTodos({ status: "open" }).run();
|
|
128
169
|
*
|
|
129
|
-
* //
|
|
130
|
-
*
|
|
170
|
+
* // Hook composition:
|
|
171
|
+
* ListTodos.use(withTimeout(5000));
|
|
172
|
+
* ListTodos.on(record => otel.span(...));
|
|
131
173
|
*
|
|
132
|
-
* //
|
|
133
|
-
*
|
|
134
|
-
*/
|
|
135
|
-
export declare function defineHandler<TInputSchema extends ZodTypeAny | undefined, TOutput>(name: string, config: HandlerConfig<TInputSchema, TOutput>): HandlerDefinition<TInputSchema, TOutput>;
|
|
136
|
-
/** Type narrow. */
|
|
137
|
-
export declare function isHandlerDefinition(x: unknown): x is HandlerDefinition;
|
|
138
|
-
/**
|
|
139
|
-
* Options for `execute()` — transport-agnostic. Adapters can pass a
|
|
140
|
-
* container so the handler can `resolve()` its deps; if omitted, a
|
|
141
|
-
* minimal stub container is supplied that throws on resolve.
|
|
174
|
+
* // Low-level hook surface (transports use this):
|
|
175
|
+
* await ListTodos.run({ input: { status: "open" }, signal });
|
|
142
176
|
*/
|
|
143
|
-
export
|
|
144
|
-
readonly container?: Container;
|
|
145
|
-
readonly logger?: Logger;
|
|
146
|
-
readonly transport?: Record<string, unknown>;
|
|
147
|
-
}
|
|
177
|
+
export declare function defineHandler<TInputSchema extends ZodTypeAny | undefined, TOutput, TExtras extends object = object>(name: string, config: HandlerConfig<TInputSchema, TOutput, TExtras>): HandlerDefinition<TInputSchema, TOutput, TExtras>;
|
|
148
178
|
/**
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
* handler context, runs the handler, and returns its result.
|
|
179
|
+
* App-boundary sugar — pin `TExtras` once so every handler in the app reads
|
|
180
|
+
* `ctx` as the bound shape without per-call annotations.
|
|
152
181
|
*
|
|
153
|
-
*
|
|
182
|
+
* // app/action.ts
|
|
183
|
+
* type AppExtras = { cradle: AppCradle; user: User };
|
|
184
|
+
* export const action = defineHandlerWith<AppExtras>();
|
|
154
185
|
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
186
|
+
* // every handler file:
|
|
187
|
+
* import { action } from "@/action";
|
|
188
|
+
* const PlaceOrder = action("placeOrder", {
|
|
189
|
+
* input: z.object({ id: z.string() }),
|
|
190
|
+
* handler: async ({ input, cradle, user }) => … // fully typed, no annotation
|
|
191
|
+
* });
|
|
192
|
+
*
|
|
193
|
+
* // every transport / .run() call:
|
|
194
|
+
* PlaceOrder.run({ input, signal, cradle, user });
|
|
164
195
|
*/
|
|
165
|
-
export
|
|
166
|
-
/**
|
|
196
|
+
export declare function defineHandlerWith<TExtras extends object>(): <TInputSchema extends ZodTypeAny | undefined, TOutput>(name: string, config: HandlerConfig<TInputSchema, TOutput, TExtras>) => HandlerDefinition<TInputSchema, TOutput, TExtras>;
|
|
197
|
+
/** Type narrow. */
|
|
198
|
+
export declare function isHandlerDefinition(x: unknown): x is HandlerDefinition;
|
|
199
|
+
/** Extract the input type of a handler's schema (post-parse). */
|
|
200
|
+
export type HandlerInput<H> = H extends HandlerDefinition<infer S, unknown, object> ? InferInput<S> : never;
|
|
201
|
+
/** Extract the output type of a handler. */
|
|
202
|
+
export type HandlerOutput<H> = H extends HandlerDefinition<ZodTypeAny | undefined, infer O, object> ? O : never;
|
|
203
|
+
/** Extract the extras (TExtras) a handler requires on ctx. */
|
|
204
|
+
export type HandlerExtras<H> = H extends HandlerDefinition<ZodTypeAny | undefined, unknown, infer E> ? E : never;
|
|
205
|
+
/** Re-export from the response builders for ergonomics. */
|
|
167
206
|
export type { ResponseInstance } from "./response.js";
|
|
168
207
|
//# sourceMappingURL=define-handler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define-handler.d.ts","sourceRoot":"","sources":["../src/define-handler.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"define-handler.d.ts","sourceRoot":"","sources":["../src/define-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,OAAO,EACZ,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,KAAK,EACX,MAAM,cAAc,CAAC;AAEtB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAoB,MAAM,eAAe,CAAC;AACtF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE/D;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,WAAW;CAAG;AAC/B,MAAM,WAAW,eAAe;CAAG;AAEnC,sEAAsE;AACtE,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,UAAU,GAAG,SAAS,IAAI,CAAC,SAAS,UAAU,GAC3E,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GACX,SAAS,CAAC;AAEd;;;;GAIG;AACH,MAAM,WAAW,cAAc,CAAC,MAAM,CAAE,SAAQ,WAAW;IACzD,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,MAAM,MAAM,GAAG,CAAC,MAAM,EAAE,OAAO,SAAS,MAAM,GAAG,MAAM,IAAI,cAAc,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC;AAE5F;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAC1B,kBAAkB,GAClB,YAAY,GACZ,gBAAgB,CAAC,kBAAkB,CAAC,GACpC,aAAa,CAAC,YAAY,GAAG,gBAAgB,CAAC,kBAAkB,CAAC,CAAC,CAAC;AAEvE,MAAM,WAAW,aAAa,CAC5B,YAAY,SAAS,UAAU,GAAG,SAAS,GAAG,SAAS,EACvD,OAAO,GAAG,OAAO,EACjB,OAAO,SAAS,MAAM,GAAG,MAAM;IAE/B,8FAA8F;IAC9F,QAAQ,CAAC,KAAK,CAAC,EAAE,YAAY,CAAC;IAC9B,sFAAsF;IACtF,QAAQ,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9F,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,CAAC,EAAE,kBAAkB,CAAC;IACtC,oDAAoD;IACpD,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IAC7C,2DAA2D;IAC3D,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,4DAA4D;IAC5D,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,oEAAoE;IACpE,QAAQ,CAAC,IAAI,CAAC,EAAE,WAAW,CAAC;IAC5B,oEAAoE;IACpE,QAAQ,CAAC,QAAQ,CAAC,EAAE,eAAe,CAAC;CACrC;AAED;;;;GAIG;AACH,MAAM,MAAM,aAAa,CACvB,MAAM,GAAG,OAAO,EAChB,OAAO,GAAG,OAAO,EACjB,OAAO,SAAS,MAAM,GAAG,MAAM,IAC7B,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAEhD,2EAA2E;AAC3E,MAAM,WAAW,oBAAoB,CAAC,OAAO,SAAS,MAAM,GAAG,MAAM;IACnE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;;GAGG;AACH,MAAM,WAAW,UAAU,CAAC,MAAM,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO,EAAE,OAAO,SAAS,MAAM,GAAG,MAAM;IAC9F,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,GAAG,CAAC,IAAI,CAAC,EAAE,oBAAoB,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC7D;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB,CAChC,YAAY,SAAS,UAAU,GAAG,SAAS,GAAG,SAAS,EACvD,OAAO,GAAG,OAAO,EACjB,OAAO,SAAS,MAAM,GAAG,MAAM;IAE/B,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,YAAY,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAG/D,CACE,KAAK,EAAE,YAAY,SAAS,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,IAAI,GACpE,UAAU,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAE1D,gEAAgE;IAChE,GAAG,CACD,EAAE,EAAE,OAAO,CAAC,aAAa,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,EACtE,IAAI,CAAC,EAAE,UAAU,GAChB,iBAAiB,CAAC,YAAY,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAErD,+DAA+D;IAC/D,EAAE,CACA,EAAE,EAAE,UAAU,CAAC,aAAa,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,EACzE,IAAI,CAAC,EAAE,SAAS,GACf,iBAAiB,CAAC,YAAY,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAErD,2DAA2D;IAC3D,GAAG,CACD,EAAE,EACE,OAAO,CAAC,aAAa,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,GAClE,UAAU,CAAC,aAAa,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,GACxE,iBAAiB,CAAC,YAAY,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAErD;;;;OAIG;IACH,GAAG,CACD,GAAG,EAAE,aAAa,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,EAC9D,IAAI,CAAC,EAAE,UAAU,GAChB,OAAO,CAAC,aAAa,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;IAEtE,+DAA+D;IAC/D,WAAW,CACT,GAAG,EAAE,aAAa,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,EAC9D,IAAI,CAAC,EAAE,UAAU,GAChB,OAAO,CAAC,SAAS,CAAC,aAAa,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IAEjF,0CAA0C;IAC1C,GAAG,CAAC,QAAQ,EAAE,KAAK,GAAG,MAAM,IAAI,CAAC;IAEjC,0CAA0C;IAC1C,UAAU,IAAI;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;CACtE;AAED;;;;;GAKG;AACH,MAAM,MAAM,WAAW,CAAC,MAAM,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO,IACvD,iBAAiB,CAAC,UAAU,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,CAAC,GAC1D,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC;AASpD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,aAAa,CAC3B,YAAY,SAAS,UAAU,GAAG,SAAS,EAC3C,OAAO,EACP,OAAO,SAAS,MAAM,GAAG,MAAM,EAE/B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,aAAa,CAAC,YAAY,EAAE,OAAO,EAAE,OAAO,CAAC,GACpD,iBAAiB,CAAC,YAAY,EAAE,OAAO,EAAE,OAAO,CAAC,CAkGnD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,SAAS,MAAM,MAC9C,YAAY,SAAS,UAAU,GAAG,SAAS,EAAE,OAAO,EAC1D,MAAM,MAAM,EACZ,QAAQ,aAAa,CAAC,YAAY,EAAE,OAAO,EAAE,OAAO,CAAC,KACpD,iBAAiB,CAAC,YAAY,EAAE,OAAO,EAAE,OAAO,CAAC,CAErD;AAED,mBAAmB;AACnB,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,iBAAiB,CAEtE;AAED,iEAAiE;AACjE,MAAM,MAAM,YAAY,CAAC,CAAC,IACxB,CAAC,SAAS,iBAAiB,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC;AAEhF,4CAA4C;AAC5C,MAAM,MAAM,aAAa,CAAC,CAAC,IACzB,CAAC,SAAS,iBAAiB,CAAC,UAAU,GAAG,SAAS,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;AAEnF,8DAA8D;AAC9D,MAAM,MAAM,aAAa,CAAC,CAAC,IACzB,CAAC,SAAS,iBAAiB,CAAC,UAAU,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;AAEpF,2DAA2D;AAC3D,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/define-handler.js
CHANGED
|
@@ -3,114 +3,170 @@
|
|
|
3
3
|
*
|
|
4
4
|
* const GetStation = defineHandler("GetStation", {
|
|
5
5
|
* input: z.object({ stationId: z.string() }),
|
|
6
|
+
* handler: async ({ input }) => loadStation(input.stationId),
|
|
6
7
|
* returns: Station,
|
|
7
|
-
*
|
|
8
|
+
* errors: [NotFound],
|
|
8
9
|
* });
|
|
9
10
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
* GetStation({ stationId: "abc" }) // mint a prepared op
|
|
12
|
+
* await GetStation({ stationId: "abc" }).run() // run it
|
|
13
|
+
* await GetStation.run({ input, signal }) // low-level hook surface
|
|
14
|
+
* GetStation.use(myMiddleware) // attach a hook chain step
|
|
15
|
+
* GetStation.on(observer) // attach a listener
|
|
15
16
|
*
|
|
16
|
-
*
|
|
17
|
+
* The handler IS a `Hook` from `@nwire/hooks`. `.use()` attaches chain steps;
|
|
18
|
+
* `.on()` attaches parallel listeners; `.run()` executes. The user's handler
|
|
19
|
+
* function is the innermost chain step — registered at construction with
|
|
20
|
+
* `Number.MIN_SAFE_INTEGER` priority so every `.use(...)` wraps around it.
|
|
17
21
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
+
* **Ctx is generic.** The handler's ctx is `BaseHookCtx & { input } & TExtras`
|
|
23
|
+
* where `TExtras` is inferred from the handler's param annotation, or pinned
|
|
24
|
+
* once via `defineHandlerWith<TExtras>()` at the app boundary. Whatever you
|
|
25
|
+
* pass to `.run(ctx)` is what reaches the handler body, fully typed. No
|
|
26
|
+
* declaration merging on ctx — composition by value.
|
|
22
27
|
*
|
|
23
|
-
* `meta` + `settings`
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
* `meta` + `settings` ARE globally augmentable open interfaces. Those are
|
|
29
|
+
* declarative per-handler tags (policy, retry, persona, SLO) that plugins
|
|
30
|
+
* must discover across every handler — different role from ctx, different
|
|
31
|
+
* mechanism by design.
|
|
32
|
+
*/
|
|
33
|
+
import { hook, } from "@nwire/hooks";
|
|
34
|
+
/**
|
|
35
|
+
* Tag for the terminal chain step that calls the user's handler function.
|
|
36
|
+
* Registered with `Number.MIN_SAFE_INTEGER` priority so every `.use()` from
|
|
37
|
+
* the consumer wraps around it (higher priority = runs first / outermost).
|
|
30
38
|
*/
|
|
31
|
-
|
|
39
|
+
const TERMINAL_STEP_NAME = "@nwire/handler/terminal";
|
|
32
40
|
/**
|
|
33
41
|
* Define a handler — the operation primitive.
|
|
34
42
|
*
|
|
35
43
|
* const ListTodos = defineHandler("listTodos", {
|
|
36
44
|
* input: z.object({ status: z.string().optional() }),
|
|
37
|
-
* handler: async ({ input
|
|
38
|
-
* const db = resolve<DB>("db");
|
|
39
|
-
* return db.todos.findMany(input);
|
|
40
|
-
* },
|
|
45
|
+
* handler: async ({ input }) => todos.findMany(input),
|
|
41
46
|
* });
|
|
42
47
|
*
|
|
43
48
|
* // Direct invocation:
|
|
44
|
-
* const
|
|
49
|
+
* const result = await ListTodos({ status: "open" }).run();
|
|
45
50
|
*
|
|
46
|
-
* //
|
|
47
|
-
*
|
|
51
|
+
* // Hook composition:
|
|
52
|
+
* ListTodos.use(withTimeout(5000));
|
|
53
|
+
* ListTodos.on(record => otel.span(...));
|
|
48
54
|
*
|
|
49
|
-
* //
|
|
50
|
-
*
|
|
55
|
+
* // Low-level hook surface (transports use this):
|
|
56
|
+
* await ListTodos.run({ input: { status: "open" }, signal });
|
|
51
57
|
*/
|
|
52
58
|
export function defineHandler(name, config) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
const h = hook(name);
|
|
60
|
+
// Innermost step — runs the user's handler after every `.use()` has had
|
|
61
|
+
// its chance to wrap. Validation happens here (parse against config.input)
|
|
62
|
+
// so middleware that mutates ctx.input does so BEFORE validation. The
|
|
63
|
+
// chain ctx is forwarded so middleware-contributed fields (TExtras —
|
|
64
|
+
// cradle, user, tenant, logger, …) flow through.
|
|
65
|
+
h.use(async (ctx, next) => {
|
|
66
|
+
ctx.signal?.throwIfAborted();
|
|
67
|
+
const parsed = config.input ? config.input.parse(ctx.input) : ctx.input;
|
|
68
|
+
ctx.input = parsed;
|
|
69
|
+
const result = await config.handler(ctx);
|
|
70
|
+
ctx.result = result;
|
|
71
|
+
await next();
|
|
72
|
+
}, {
|
|
73
|
+
name: TERMINAL_STEP_NAME,
|
|
74
|
+
priority: Number.MIN_SAFE_INTEGER,
|
|
75
|
+
});
|
|
76
|
+
// The factory is a callable function. `Object.defineProperty` attaches the
|
|
77
|
+
// metadata + delegated hook methods + the `$kind` discriminator.
|
|
78
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
79
|
+
const factory = (input) => {
|
|
80
|
+
const prepared = {
|
|
81
|
+
input: input,
|
|
82
|
+
async run(opts) {
|
|
83
|
+
const ctx = {
|
|
84
|
+
input,
|
|
85
|
+
...(opts?.ctx ?? {}),
|
|
86
|
+
};
|
|
87
|
+
const out = await h.run(ctx, { signal: opts?.signal });
|
|
88
|
+
return out.result;
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
return prepared;
|
|
60
92
|
};
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
93
|
+
Object.defineProperties(factory, {
|
|
94
|
+
$kind: { value: "handler", enumerable: true },
|
|
95
|
+
name: { value: name, enumerable: true, configurable: true },
|
|
96
|
+
config: { value: config, enumerable: true },
|
|
97
|
+
toString: { value: () => name, enumerable: false },
|
|
98
|
+
$hook: { value: h, enumerable: false, configurable: true },
|
|
99
|
+
use: {
|
|
100
|
+
value: function use(fn, opts) {
|
|
101
|
+
h.use(fn, opts);
|
|
102
|
+
return factory;
|
|
103
|
+
},
|
|
104
|
+
enumerable: false,
|
|
105
|
+
},
|
|
106
|
+
on: {
|
|
107
|
+
value: function on(fn, opts) {
|
|
108
|
+
h.on(fn, opts);
|
|
109
|
+
return factory;
|
|
110
|
+
},
|
|
111
|
+
enumerable: false,
|
|
112
|
+
},
|
|
113
|
+
off: {
|
|
114
|
+
value: function off(fn) {
|
|
115
|
+
h.off(fn);
|
|
116
|
+
return factory;
|
|
117
|
+
},
|
|
118
|
+
enumerable: false,
|
|
119
|
+
},
|
|
120
|
+
run: {
|
|
121
|
+
value: function run(ctx, opts) {
|
|
122
|
+
return h.run(ctx, opts);
|
|
123
|
+
},
|
|
124
|
+
enumerable: false,
|
|
125
|
+
},
|
|
126
|
+
runDetailed: {
|
|
127
|
+
value: function runDetailed(ctx, opts) {
|
|
128
|
+
return h.runDetailed(ctx, opts);
|
|
129
|
+
},
|
|
130
|
+
enumerable: false,
|
|
131
|
+
},
|
|
132
|
+
tap: {
|
|
133
|
+
value: function tap(observer) {
|
|
134
|
+
return h.tap(observer);
|
|
135
|
+
},
|
|
136
|
+
enumerable: false,
|
|
137
|
+
},
|
|
138
|
+
stepCounts: {
|
|
139
|
+
value: function stepCounts() {
|
|
140
|
+
return h.stepCounts();
|
|
141
|
+
},
|
|
142
|
+
enumerable: false,
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
return factory;
|
|
65
146
|
}
|
|
66
147
|
/**
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
148
|
+
* App-boundary sugar — pin `TExtras` once so every handler in the app reads
|
|
149
|
+
* `ctx` as the bound shape without per-call annotations.
|
|
150
|
+
*
|
|
151
|
+
* // app/action.ts
|
|
152
|
+
* type AppExtras = { cradle: AppCradle; user: User };
|
|
153
|
+
* export const action = defineHandlerWith<AppExtras>();
|
|
70
154
|
*
|
|
71
|
-
*
|
|
155
|
+
* // every handler file:
|
|
156
|
+
* import { action } from "@/action";
|
|
157
|
+
* const PlaceOrder = action("placeOrder", {
|
|
158
|
+
* input: z.object({ id: z.string() }),
|
|
159
|
+
* handler: async ({ input, cradle, user }) => … // fully typed, no annotation
|
|
160
|
+
* });
|
|
72
161
|
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* `execute()` directly to bypass transport machinery.
|
|
162
|
+
* // every transport / .run() call:
|
|
163
|
+
* PlaceOrder.run({ input, signal, cradle, user });
|
|
76
164
|
*/
|
|
77
|
-
export
|
|
78
|
-
|
|
79
|
-
input, options = {}) {
|
|
80
|
-
const schema = handler.config.input;
|
|
81
|
-
const parsed = schema ? schema.parse(input) : input;
|
|
82
|
-
const container = options.container ?? minimalContainer();
|
|
83
|
-
const logger = options.logger ?? minimalLogger();
|
|
84
|
-
const ctx = {
|
|
85
|
-
input: parsed,
|
|
86
|
-
container,
|
|
87
|
-
resolve: (name) => container.resolve(name),
|
|
88
|
-
logger,
|
|
89
|
-
transport: options.transport,
|
|
90
|
-
};
|
|
91
|
-
return handler.run(ctx);
|
|
92
|
-
}
|
|
93
|
-
/** Minimal stub container — every resolve throws. Used when no container supplied. */
|
|
94
|
-
function minimalContainer() {
|
|
95
|
-
const stub = {
|
|
96
|
-
resolve(name) {
|
|
97
|
-
throw new Error(`execute(): no container provided — cannot resolve "${name}". ` +
|
|
98
|
-
`Pass options.container or use a transport that wires one.`);
|
|
99
|
-
},
|
|
100
|
-
register() {
|
|
101
|
-
throw new Error("execute(): minimal container is read-only");
|
|
102
|
-
},
|
|
103
|
-
createScope() {
|
|
104
|
-
return minimalContainer();
|
|
105
|
-
},
|
|
106
|
-
has() {
|
|
107
|
-
return false;
|
|
108
|
-
},
|
|
109
|
-
};
|
|
110
|
-
return stub;
|
|
165
|
+
export function defineHandlerWith() {
|
|
166
|
+
return (name, config) => defineHandler(name, config);
|
|
111
167
|
}
|
|
112
|
-
/**
|
|
113
|
-
function
|
|
114
|
-
return
|
|
168
|
+
/** Type narrow. */
|
|
169
|
+
export function isHandlerDefinition(x) {
|
|
170
|
+
return typeof x === "function" && x.$kind === "handler";
|
|
115
171
|
}
|
|
116
172
|
//# sourceMappingURL=define-handler.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define-handler.js","sourceRoot":"","sources":["../src/define-handler.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"define-handler.js","sourceRoot":"","sources":["../src/define-handler.ts"],"names":[],"mappings":"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"}
|
package/dist/handler-index.d.ts
CHANGED
|
@@ -1,27 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `@nwire/handler` — the operation primitive.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* `@nwire/forge` re-exports the same names so existing call sites keep
|
|
11
|
-
* compiling unchanged. Forge layers `defineAction = defineHandler +
|
|
12
|
-
* { flavor: "action", emits }` on top — same primitive, additional
|
|
13
|
-
* semantics for the event-driven path.
|
|
4
|
+
* A handler is a typed, callable, hookable value built on `@nwire/hooks`.
|
|
5
|
+
* One value, every transport (HTTP, queue, MCP, CLI, direct test) —
|
|
6
|
+
* standalone, no DI, no events, no logger, no transport opinions. Ctx is
|
|
7
|
+
* generic; the app pins `TExtras` via `defineHandlerWith<TExtras>()` once
|
|
8
|
+
* at the boot site and every handler reads `ctx` as the bound shape with
|
|
9
|
+
* full autocomplete, zero per-call annotations.
|
|
14
10
|
*
|
|
15
11
|
* Public surface:
|
|
16
12
|
*
|
|
17
13
|
* Primitives
|
|
18
|
-
* defineHandler(name, config)
|
|
19
|
-
*
|
|
20
|
-
* defineError(meta)
|
|
21
|
-
* defineResource(name, opts)
|
|
22
|
-
* defineMiddleware — chain step
|
|
23
|
-
* defineHook — before/after-only step
|
|
24
|
-
* pipe(...steps) — compose chain
|
|
14
|
+
* defineHandler(name, config) — the operation primitive
|
|
15
|
+
* defineHandlerWith<TExtras>() — app-boundary factory binding
|
|
16
|
+
* defineError(meta) — typed throwable
|
|
17
|
+
* defineResource(name, opts) — interface/response shape
|
|
25
18
|
*
|
|
26
19
|
* Response factories
|
|
27
20
|
* ok / ok.list / created / accepted / noContent / notModified / gone
|
|
@@ -30,12 +23,14 @@
|
|
|
30
23
|
* Unauthorized / Forbidden / NotFound / Conflict / Gone / BadRequest
|
|
31
24
|
*
|
|
32
25
|
* Types
|
|
33
|
-
* HandlerDefinition / HandlerConfig /
|
|
34
|
-
*
|
|
26
|
+
* HandlerDefinition / HandlerConfig / Ctx / HandlerBaseCtx
|
|
27
|
+
* HandlerRunCtx / PreparedOp / HandlerLike
|
|
28
|
+
* HandlerMeta / HandlerSettings (empty bags — extend via declaration merging)
|
|
29
|
+
* HandlerInput<H> / HandlerOutput<H> / HandlerExtras<H>
|
|
30
|
+
* ResponseInstance / ResponseSpec / NwireError
|
|
35
31
|
*/
|
|
36
|
-
export { defineHandler,
|
|
32
|
+
export { defineHandler, defineHandlerWith, isHandlerDefinition, type HandlerDefinition, type HandlerConfig, type HandlerBaseCtx, type Ctx, type HandlerMeta, type HandlerSettings, type HandlerReturnsSpec, type HandlerRunCtx, type HandlerLike, type PreparedOp, type PreparedOpRunOptions, type HandlerInput, type HandlerOutput, type HandlerExtras, type InferInput, type ResponseInstance, } from "./define-handler.js";
|
|
37
33
|
export { defineError, isNwireError, NwireError, Unauthorized, Forbidden, NotFound, Gone, BadRequest, Conflict, type ErrorDefinition, type ErrorMeta, } from "./define-error.js";
|
|
38
34
|
export { defineResource, isResourceDefinition, type ResourceDefinition, type DefineResourceOptions, } from "./define-resource.js";
|
|
39
|
-
export {
|
|
40
|
-
export { response, isResponseSpec, isResponseInstance, ok, created, accepted, noContent, notModified, gone, type ResponseSpec, type ListResponseSpec, type ResponseInstance, type ResponseSpecBase, type ResponseKind, } from "./response.js";
|
|
35
|
+
export { response, isResponseSpec, isResponseInstance, ok, created, accepted, noContent, notModified, gone, type ResponseSpec, type ListResponseSpec, type ResponseSpecBase, type ResponseKind, } from "./response.js";
|
|
41
36
|
//# sourceMappingURL=handler-index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handler-index.d.ts","sourceRoot":"","sources":["../src/handler-index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"handler-index.d.ts","sourceRoot":"","sources":["../src/handler-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,mBAAmB,EACnB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,GAAG,EACR,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,oBAAoB,EACzB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,gBAAgB,GACtB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,WAAW,EACX,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,SAAS,EACT,QAAQ,EACR,IAAI,EACJ,UAAU,EACV,QAAQ,EACR,KAAK,eAAe,EACpB,KAAK,SAAS,GACf,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,cAAc,EACd,oBAAoB,EACpB,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,GAC3B,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,QAAQ,EACR,cAAc,EACd,kBAAkB,EAClB,EAAE,EACF,OAAO,EACP,QAAQ,EACR,SAAS,EACT,WAAW,EACX,IAAI,EACJ,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,YAAY,GAClB,MAAM,eAAe,CAAC"}
|
package/dist/handler-index.js
CHANGED
|
@@ -1,27 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `@nwire/handler` — the operation primitive.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* `@nwire/forge` re-exports the same names so existing call sites keep
|
|
11
|
-
* compiling unchanged. Forge layers `defineAction = defineHandler +
|
|
12
|
-
* { flavor: "action", emits }` on top — same primitive, additional
|
|
13
|
-
* semantics for the event-driven path.
|
|
4
|
+
* A handler is a typed, callable, hookable value built on `@nwire/hooks`.
|
|
5
|
+
* One value, every transport (HTTP, queue, MCP, CLI, direct test) —
|
|
6
|
+
* standalone, no DI, no events, no logger, no transport opinions. Ctx is
|
|
7
|
+
* generic; the app pins `TExtras` via `defineHandlerWith<TExtras>()` once
|
|
8
|
+
* at the boot site and every handler reads `ctx` as the bound shape with
|
|
9
|
+
* full autocomplete, zero per-call annotations.
|
|
14
10
|
*
|
|
15
11
|
* Public surface:
|
|
16
12
|
*
|
|
17
13
|
* Primitives
|
|
18
|
-
* defineHandler(name, config)
|
|
19
|
-
*
|
|
20
|
-
* defineError(meta)
|
|
21
|
-
* defineResource(name, opts)
|
|
22
|
-
* defineMiddleware — chain step
|
|
23
|
-
* defineHook — before/after-only step
|
|
24
|
-
* pipe(...steps) — compose chain
|
|
14
|
+
* defineHandler(name, config) — the operation primitive
|
|
15
|
+
* defineHandlerWith<TExtras>() — app-boundary factory binding
|
|
16
|
+
* defineError(meta) — typed throwable
|
|
17
|
+
* defineResource(name, opts) — interface/response shape
|
|
25
18
|
*
|
|
26
19
|
* Response factories
|
|
27
20
|
* ok / ok.list / created / accepted / noContent / notModified / gone
|
|
@@ -30,12 +23,14 @@
|
|
|
30
23
|
* Unauthorized / Forbidden / NotFound / Conflict / Gone / BadRequest
|
|
31
24
|
*
|
|
32
25
|
* Types
|
|
33
|
-
* HandlerDefinition / HandlerConfig /
|
|
34
|
-
*
|
|
26
|
+
* HandlerDefinition / HandlerConfig / Ctx / HandlerBaseCtx
|
|
27
|
+
* HandlerRunCtx / PreparedOp / HandlerLike
|
|
28
|
+
* HandlerMeta / HandlerSettings (empty bags — extend via declaration merging)
|
|
29
|
+
* HandlerInput<H> / HandlerOutput<H> / HandlerExtras<H>
|
|
30
|
+
* ResponseInstance / ResponseSpec / NwireError
|
|
35
31
|
*/
|
|
36
|
-
export { defineHandler,
|
|
32
|
+
export { defineHandler, defineHandlerWith, isHandlerDefinition, } from "./define-handler.js";
|
|
37
33
|
export { defineError, isNwireError, NwireError, Unauthorized, Forbidden, NotFound, Gone, BadRequest, Conflict, } from "./define-error.js";
|
|
38
34
|
export { defineResource, isResourceDefinition, } from "./define-resource.js";
|
|
39
|
-
export { defineMiddleware, defineHook, pipe, unwindPipe, isMiddleware, isHook, isPipe, } from "./define-middleware.js";
|
|
40
35
|
export { response, isResponseSpec, isResponseInstance, ok, created, accepted, noContent, notModified, gone, } from "./response.js";
|
|
41
36
|
//# sourceMappingURL=handler-index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handler-index.js","sourceRoot":"","sources":["../src/handler-index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"handler-index.js","sourceRoot":"","sources":["../src/handler-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,mBAAmB,GAiBpB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,WAAW,EACX,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,SAAS,EACT,QAAQ,EACR,IAAI,EACJ,UAAU,EACV,QAAQ,GAGT,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,cAAc,EACd,oBAAoB,GAGrB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,QAAQ,EACR,cAAc,EACd,kBAAkB,EAClB,EAAE,EACF,OAAO,EACP,QAAQ,EACR,SAAS,EACT,WAAW,EACX,IAAI,GAKL,MAAM,eAAe,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nwire/handler",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Nwire — the operation primitive.
|
|
3
|
+
"version": "0.9.2",
|
|
4
|
+
"description": "Nwire — the operation primitive. Typed, callable, hookable value built on @nwire/hooks. defineHandler / defineError / defineResource / response factories / framework errors. Standalone; every transport (HTTP, queue, MCP, CLI) speaks the same handler shape.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"execute",
|
|
7
7
|
"handler",
|
|
@@ -28,11 +28,8 @@
|
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"zod": "^4.0.0",
|
|
31
|
-
"@nwire/
|
|
32
|
-
"@nwire/
|
|
33
|
-
"@nwire/hooks": "0.8.17",
|
|
34
|
-
"@nwire/logger": "0.8.17",
|
|
35
|
-
"@nwire/messages": "0.8.17"
|
|
31
|
+
"@nwire/hooks": "0.9.2",
|
|
32
|
+
"@nwire/messages": "0.9.2"
|
|
36
33
|
},
|
|
37
34
|
"devDependencies": {
|
|
38
35
|
"@types/node": "^22.19.9",
|