@nwire/handler 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +64 -0
- package/dist/__tests__/define-handler.test.d.ts +18 -0
- package/dist/__tests__/define-handler.test.d.ts.map +1 -0
- package/dist/__tests__/define-handler.test.js +112 -0
- package/dist/__tests__/define-handler.test.js.map +1 -0
- package/dist/define-error.d.ts +59 -0
- package/dist/define-error.d.ts.map +1 -0
- package/dist/define-error.js +88 -0
- package/dist/define-error.js.map +1 -0
- package/dist/define-handler.d.ts +168 -0
- package/dist/define-handler.d.ts.map +1 -0
- package/dist/define-handler.js +116 -0
- package/dist/define-handler.js.map +1 -0
- package/dist/define-middleware.d.ts +76 -0
- package/dist/define-middleware.d.ts.map +1 -0
- package/dist/define-middleware.js +81 -0
- package/dist/define-middleware.js.map +1 -0
- package/dist/define-resource.d.ts +66 -0
- package/dist/define-resource.d.ts.map +1 -0
- package/dist/define-resource.js +65 -0
- package/dist/define-resource.js.map +1 -0
- package/dist/handler-index.d.ts +41 -0
- package/dist/handler-index.d.ts.map +1 -0
- package/dist/handler-index.js +41 -0
- package/dist/handler-index.js.map +1 -0
- package/dist/response.d.ts +57 -0
- package/dist/response.d.ts.map +1 -0
- package/dist/response.js +69 -0
- package/dist/response.js.map +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `defineHandler(name, config)` — the operation primitive.
|
|
3
|
+
*
|
|
4
|
+
* const GetStation = defineHandler("GetStation", {
|
|
5
|
+
* input: z.object({ stationId: z.string() }),
|
|
6
|
+
* returns: Station,
|
|
7
|
+
* handler: async ({ input, db }) => db.stations.findById(input.stationId),
|
|
8
|
+
* });
|
|
9
|
+
*
|
|
10
|
+
* The shape is universal — same primitive whether the operation is exposed
|
|
11
|
+
* via HTTP, queue, MCP, CLI, or invoked directly via `execute(handler, input)`.
|
|
12
|
+
* Every transport binds via its own `(binding, handler, options)` signature;
|
|
13
|
+
* the binding narrows the input contract at the boundary, the handler is
|
|
14
|
+
* the lowest-common-denominator inside.
|
|
15
|
+
*
|
|
16
|
+
* See: architecture-sketch.html §08.
|
|
17
|
+
*
|
|
18
|
+
* Field placement follows the freq table:
|
|
19
|
+
* first-class — input, handler, returns, errors, summary, policy, emits
|
|
20
|
+
* meta — tags, description, version, deprecated, successor
|
|
21
|
+
* settings — retry, timeout, cache, idempotency
|
|
22
|
+
*
|
|
23
|
+
* `meta` + `settings` are open bags; adapters extend them via TS declaration
|
|
24
|
+
* merging without touching the core type.
|
|
25
|
+
*
|
|
26
|
+
* NOTE: forge has its own `defineHandler(action, fn)` that binds a handler
|
|
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.
|
|
30
|
+
*/
|
|
31
|
+
import { NoopLogger } from "@nwire/logger";
|
|
32
|
+
/**
|
|
33
|
+
* Define a handler — the operation primitive.
|
|
34
|
+
*
|
|
35
|
+
* const ListTodos = defineHandler("listTodos", {
|
|
36
|
+
* input: z.object({ status: z.string().optional() }),
|
|
37
|
+
* handler: async ({ input, resolve }) => {
|
|
38
|
+
* const db = resolve<DB>("db");
|
|
39
|
+
* return db.todos.findMany(input);
|
|
40
|
+
* },
|
|
41
|
+
* });
|
|
42
|
+
*
|
|
43
|
+
* // Direct invocation:
|
|
44
|
+
* const todos = await execute(ListTodos, { status: "open" });
|
|
45
|
+
*
|
|
46
|
+
* // HTTP transport:
|
|
47
|
+
* api.wire(get("/todos"), ListTodos);
|
|
48
|
+
*
|
|
49
|
+
* // Queue transport (same handler):
|
|
50
|
+
* workers.wire(job("listTodos"), ListTodos);
|
|
51
|
+
*/
|
|
52
|
+
export function defineHandler(name, config) {
|
|
53
|
+
return {
|
|
54
|
+
$kind: "handler",
|
|
55
|
+
name,
|
|
56
|
+
config,
|
|
57
|
+
run(ctx) {
|
|
58
|
+
return config.handler(ctx);
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/** Type narrow. */
|
|
63
|
+
export function isHandlerDefinition(x) {
|
|
64
|
+
return typeof x === "object" && x !== null && x.$kind === "handler";
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Invoke a handler directly with input. The framework validates input
|
|
68
|
+
* against the handler's `input` schema (if declared), constructs the
|
|
69
|
+
* handler context, runs the handler, and returns its result.
|
|
70
|
+
*
|
|
71
|
+
* const todos = await execute(ListTodos, { status: "open" });
|
|
72
|
+
*
|
|
73
|
+
* This is the universal "any host can mount it" entry point. Transports
|
|
74
|
+
* call into `execute()` after parsing the binding's payload; tests call
|
|
75
|
+
* `execute()` directly to bypass transport machinery.
|
|
76
|
+
*/
|
|
77
|
+
export async function execute(handler,
|
|
78
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
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;
|
|
111
|
+
}
|
|
112
|
+
/** Minimal stub logger. No-op every method. */
|
|
113
|
+
function minimalLogger() {
|
|
114
|
+
return new NoopLogger();
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=define-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define-handler.js","sourceRoot":"","sources":["../src/define-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAKH,OAAO,EAAE,UAAU,EAAe,MAAM,eAAe,CAAC;AAkGxD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,aAAa,CAC3B,IAAY,EACZ,MAA4C;IAE5C,OAAO;QACL,KAAK,EAAE,SAAS;QAChB,IAAI;QACJ,MAAM;QACN,GAAG,CAAC,GAAG;YACL,OAAO,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC7B,CAAC;KACF,CAAC;AACJ,CAAC;AAED,mBAAmB;AACnB,MAAM,UAAU,mBAAmB,CAAC,CAAU;IAC5C,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAK,CAAyB,CAAC,KAAK,KAAK,SAAS,CAAC;AAC/F,CAAC;AAeD;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,OAAiD;AACjD,8DAA8D;AAC9D,KAAoE,EACpE,UAA0B,EAAE;IAE5B,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC;IACpC,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAEpD,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,gBAAgB,EAAE,CAAC;IAC1D,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,aAAa,EAAE,CAAC;IAEjD,MAAM,GAAG,GAAmB;QAC1B,KAAK,EAAE,MAAM;QACb,SAAS;QACT,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC;QAC1C,MAAM;QACN,SAAS,EAAE,OAAO,CAAC,SAAS;KAC7B,CAAC;IAEF,OAAO,OAAO,CAAC,GAAG,CAChB,GAA2F,CAC5F,CAAC;AACJ,CAAC;AAED,sFAAsF;AACtF,SAAS,gBAAgB;IACvB,MAAM,IAAI,GAAc;QACtB,OAAO,CAAI,IAAY;YACrB,MAAM,IAAI,KAAK,CACb,sDAAsD,IAAI,KAAK;gBAC7D,2DAA2D,CAC9D,CAAC;QACJ,CAAC;QACD,QAAQ;YACN,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;QAC/D,CAAC;QACD,WAAW;YACT,OAAO,gBAAgB,EAAE,CAAC;QAC5B,CAAC;QACD,GAAG;YACD,OAAO,KAAK,CAAC;QACf,CAAC;KACF,CAAC;IACF,OAAO,IAAI,CAAC;AACd,CAAC;AAED,+CAA+C;AAC/C,SAAS,aAAa;IACpB,OAAO,IAAI,UAAU,EAAE,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Middleware primitives — `defineMiddleware`, `defineHook`, `pipe`.
|
|
3
|
+
*
|
|
4
|
+
* const authenticate = defineMiddleware(async (ctx, next) => {
|
|
5
|
+
* ctx.user = await verifyToken(ctx.header("authorization"));
|
|
6
|
+
* await next();
|
|
7
|
+
* });
|
|
8
|
+
*
|
|
9
|
+
* const stationPipeline = pipe(
|
|
10
|
+
* authenticate,
|
|
11
|
+
* authorize("operator", "admin"),
|
|
12
|
+
* withActiveStation,
|
|
13
|
+
* auditLog,
|
|
14
|
+
* );
|
|
15
|
+
*
|
|
16
|
+
* Middleware augments the resolver context before/after the handler runs.
|
|
17
|
+
* `pipe` composes middleware into a reusable chain. `defineHook("after", fn)`
|
|
18
|
+
* declares a post-handler-only step (cleaner than a middleware that
|
|
19
|
+
* forgets to do anything before next()).
|
|
20
|
+
*
|
|
21
|
+
* Status: types are open (RestrictedCtx = the resolver's ctx after enrichment).
|
|
22
|
+
* Type-narrowing middleware composition is a follow-up.
|
|
23
|
+
*/
|
|
24
|
+
export type ResolverCtx = Record<string, any>;
|
|
25
|
+
export type Next = () => Promise<void> | void;
|
|
26
|
+
export interface MiddlewareDefinition {
|
|
27
|
+
readonly $kind: "middleware";
|
|
28
|
+
readonly name?: string;
|
|
29
|
+
readonly fn: (ctx: ResolverCtx, next: Next) => Promise<void> | void;
|
|
30
|
+
}
|
|
31
|
+
export interface HookDefinition {
|
|
32
|
+
readonly $kind: "hook";
|
|
33
|
+
readonly when: "before" | "after";
|
|
34
|
+
readonly name?: string;
|
|
35
|
+
readonly fn: (ctx: ResolverCtx, result?: unknown) => Promise<void> | void;
|
|
36
|
+
}
|
|
37
|
+
export interface MiddlewarePipe {
|
|
38
|
+
readonly $kind: "pipe";
|
|
39
|
+
readonly steps: ReadonlyArray<MiddlewareDefinition | HookDefinition>;
|
|
40
|
+
readonly name?: string;
|
|
41
|
+
}
|
|
42
|
+
export type PipeStep = MiddlewareDefinition | HookDefinition | MiddlewarePipe;
|
|
43
|
+
/**
|
|
44
|
+
* `ChainFn<Ctx>` — the executable shape of a middleware chain. The base
|
|
45
|
+
* `pipe(...)` returns a `MiddlewarePipe` value (data); transports
|
|
46
|
+
* `unwindPipe()` it into onion-runnable middlewares + hooks. Exposed as
|
|
47
|
+
* a type for downstream packages that want to declare "an opaque chain
|
|
48
|
+
* over Ctx" without leaking the underlying step shape.
|
|
49
|
+
*/
|
|
50
|
+
export type ChainFn<Ctx extends ResolverCtx = ResolverCtx> = MiddlewarePipe & {
|
|
51
|
+
readonly _ctx?: Ctx;
|
|
52
|
+
};
|
|
53
|
+
export declare function defineMiddleware(fn: (ctx: ResolverCtx, next: Next) => Promise<void> | void): MiddlewareDefinition;
|
|
54
|
+
export declare function defineMiddleware(name: string, fn: (ctx: ResolverCtx, next: Next) => Promise<void> | void): MiddlewareDefinition;
|
|
55
|
+
export declare function defineHook(when: "before" | "after", fn: (ctx: ResolverCtx, result?: unknown) => Promise<void> | void): HookDefinition;
|
|
56
|
+
export declare function defineHook(when: "before" | "after", name: string, fn: (ctx: ResolverCtx, result?: unknown) => Promise<void> | void): HookDefinition;
|
|
57
|
+
/**
|
|
58
|
+
* Compose middleware + hooks into a reusable pipe value. The pipe runs in
|
|
59
|
+
* declared order: middleware wraps the handler (onion style), `after` hooks
|
|
60
|
+
* run after the handler succeeds.
|
|
61
|
+
*/
|
|
62
|
+
export declare function pipe(...steps: PipeStep[]): MiddlewarePipe;
|
|
63
|
+
/**
|
|
64
|
+
* Walk a list of pipe steps and split into middleware + hooks. Transports
|
|
65
|
+
* use this when mounting a resolver.
|
|
66
|
+
*/
|
|
67
|
+
export declare function unwindPipe(steps: ReadonlyArray<PipeStep>): {
|
|
68
|
+
middlewares: MiddlewareDefinition[];
|
|
69
|
+
beforeHooks: HookDefinition[];
|
|
70
|
+
afterHooks: HookDefinition[];
|
|
71
|
+
};
|
|
72
|
+
/** Type narrows. */
|
|
73
|
+
export declare const isMiddleware: (x: unknown) => x is MiddlewareDefinition;
|
|
74
|
+
export declare const isHook: (x: unknown) => x is HookDefinition;
|
|
75
|
+
export declare const isPipe: (x: unknown) => x is MiddlewarePipe;
|
|
76
|
+
//# sourceMappingURL=define-middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define-middleware.d.ts","sourceRoot":"","sources":["../src/define-middleware.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Middleware primitives — `defineMiddleware`, `defineHook`, `pipe`.
|
|
3
|
+
*
|
|
4
|
+
* const authenticate = defineMiddleware(async (ctx, next) => {
|
|
5
|
+
* ctx.user = await verifyToken(ctx.header("authorization"));
|
|
6
|
+
* await next();
|
|
7
|
+
* });
|
|
8
|
+
*
|
|
9
|
+
* const stationPipeline = pipe(
|
|
10
|
+
* authenticate,
|
|
11
|
+
* authorize("operator", "admin"),
|
|
12
|
+
* withActiveStation,
|
|
13
|
+
* auditLog,
|
|
14
|
+
* );
|
|
15
|
+
*
|
|
16
|
+
* Middleware augments the resolver context before/after the handler runs.
|
|
17
|
+
* `pipe` composes middleware into a reusable chain. `defineHook("after", fn)`
|
|
18
|
+
* declares a post-handler-only step (cleaner than a middleware that
|
|
19
|
+
* forgets to do anything before next()).
|
|
20
|
+
*
|
|
21
|
+
* Status: types are open (RestrictedCtx = the resolver's ctx after enrichment).
|
|
22
|
+
* Type-narrowing middleware composition is a follow-up.
|
|
23
|
+
*/
|
|
24
|
+
export function defineMiddleware(nameOrFn, fn) {
|
|
25
|
+
if (typeof nameOrFn === "string") {
|
|
26
|
+
return { $kind: "middleware", name: nameOrFn, fn: fn };
|
|
27
|
+
}
|
|
28
|
+
return { $kind: "middleware", fn: nameOrFn };
|
|
29
|
+
}
|
|
30
|
+
export function defineHook(when, nameOrFn, maybeFn) {
|
|
31
|
+
if (typeof nameOrFn === "string") {
|
|
32
|
+
return { $kind: "hook", when, name: nameOrFn, fn: maybeFn };
|
|
33
|
+
}
|
|
34
|
+
return { $kind: "hook", when, fn: nameOrFn };
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Compose middleware + hooks into a reusable pipe value. The pipe runs in
|
|
38
|
+
* declared order: middleware wraps the handler (onion style), `after` hooks
|
|
39
|
+
* run after the handler succeeds.
|
|
40
|
+
*/
|
|
41
|
+
export function pipe(...steps) {
|
|
42
|
+
const flattened = [];
|
|
43
|
+
for (const s of steps) {
|
|
44
|
+
if (s.$kind === "pipe")
|
|
45
|
+
flattened.push(...s.steps);
|
|
46
|
+
else
|
|
47
|
+
flattened.push(s);
|
|
48
|
+
}
|
|
49
|
+
return { $kind: "pipe", steps: flattened };
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Walk a list of pipe steps and split into middleware + hooks. Transports
|
|
53
|
+
* use this when mounting a resolver.
|
|
54
|
+
*/
|
|
55
|
+
export function unwindPipe(steps) {
|
|
56
|
+
const middlewares = [];
|
|
57
|
+
const beforeHooks = [];
|
|
58
|
+
const afterHooks = [];
|
|
59
|
+
const visit = (s) => {
|
|
60
|
+
if (s.$kind === "middleware")
|
|
61
|
+
middlewares.push(s);
|
|
62
|
+
else if (s.$kind === "hook") {
|
|
63
|
+
if (s.when === "before")
|
|
64
|
+
beforeHooks.push(s);
|
|
65
|
+
else
|
|
66
|
+
afterHooks.push(s);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
for (const inner of s.steps)
|
|
70
|
+
visit(inner);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
for (const s of steps)
|
|
74
|
+
visit(s);
|
|
75
|
+
return { middlewares, beforeHooks, afterHooks };
|
|
76
|
+
}
|
|
77
|
+
/** Type narrows. */
|
|
78
|
+
export const isMiddleware = (x) => typeof x === "object" && x !== null && x.$kind === "middleware";
|
|
79
|
+
export const isHook = (x) => typeof x === "object" && x !== null && x.$kind === "hook";
|
|
80
|
+
export const isPipe = (x) => typeof x === "object" && x !== null && x.$kind === "pipe";
|
|
81
|
+
//# sourceMappingURL=define-middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define-middleware.js","sourceRoot":"","sources":["../src/define-middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AA8CH,MAAM,UAAU,gBAAgB,CAC9B,QAA2E,EAC3E,EAA2D;IAE3D,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAG,EAAE,CAAC;IAC1D,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC;AAC/C,CAAC;AAWD,MAAM,UAAU,UAAU,CACxB,IAAwB,EACxB,QAAiF,EACjF,OAAsE;IAEtE,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAQ,EAAE,CAAC;IAC/D,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC;AAC/C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,IAAI,CAAC,GAAG,KAAiB;IACvC,MAAM,SAAS,GAAiD,EAAE,CAAC;IACnE,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM;YAAE,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;;YAC9C,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACzB,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;AAC7C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,KAA8B;IAKvD,MAAM,WAAW,GAA2B,EAAE,CAAC;IAC/C,MAAM,WAAW,GAAqB,EAAE,CAAC;IACzC,MAAM,UAAU,GAAqB,EAAE,CAAC;IACxC,MAAM,KAAK,GAAG,CAAC,CAAW,EAAE,EAAE;QAC5B,IAAI,CAAC,CAAC,KAAK,KAAK,YAAY;YAAE,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;aAC7C,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;YAC5B,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ;gBAAE,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;;gBACxC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC1B,CAAC;aAAM,CAAC;YACN,KAAK,MAAM,KAAK,IAAI,CAAC,CAAC,KAAK;gBAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC,CAAC;IACF,KAAK,MAAM,CAAC,IAAI,KAAK;QAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IAChC,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC;AAClD,CAAC;AAED,oBAAoB;AACpB,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAU,EAA6B,EAAE,CACpE,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAK,CAAyB,CAAC,KAAK,KAAK,YAAY,CAAC;AAE3F,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,CAAU,EAAuB,EAAE,CACxD,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAK,CAAyB,CAAC,KAAK,KAAK,MAAM,CAAC;AAErF,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,CAAU,EAAuB,EAAE,CACxD,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAK,CAAyB,CAAC,KAAK,KAAK,MAAM,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `defineResource` — a named, typed, exampled, view-projecting data shape
|
|
3
|
+
* used by the interface / app layer (resolvers + transports).
|
|
4
|
+
*
|
|
5
|
+
* const Wash = defineResource("Wash", {
|
|
6
|
+
* summary: "A wash session recorded at a station",
|
|
7
|
+
* schema: z.object({
|
|
8
|
+
* id: z.string().uuid(),
|
|
9
|
+
* washType: z.enum(["basic", "premium", "full"]),
|
|
10
|
+
* plateNumber: z.string().nullable(),
|
|
11
|
+
* completedAt: z.string().datetime(),
|
|
12
|
+
* }),
|
|
13
|
+
* public: ["id", "washType", "plateNumber", "completedAt"],
|
|
14
|
+
* examples: {
|
|
15
|
+
* premium: { washType: "premium", plateNumber: "51-234-67" },
|
|
16
|
+
* quick: { washType: "basic", plateNumber: null },
|
|
17
|
+
* },
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* Wash.example("premium") // typed partial — used in tests
|
|
21
|
+
* Wash.project(record) // strip non-public fields
|
|
22
|
+
*
|
|
23
|
+
* Resources are distinct from actor schemas: a resource is the *external*
|
|
24
|
+
* contract (what the API client sees). The actor's `defineSchema` is the
|
|
25
|
+
* *internal* stored state.
|
|
26
|
+
*/
|
|
27
|
+
import type { z } from "zod";
|
|
28
|
+
import type { ZodTypeAny } from "@nwire/messages";
|
|
29
|
+
export interface DefineResourceOptions<TSchema extends ZodTypeAny> {
|
|
30
|
+
readonly summary?: string;
|
|
31
|
+
readonly description?: string;
|
|
32
|
+
readonly schema: TSchema;
|
|
33
|
+
/**
|
|
34
|
+
* Field paths exposed by the *public* projection. The resolver's response
|
|
35
|
+
* renderer uses these to strip non-public fields when serializing.
|
|
36
|
+
*/
|
|
37
|
+
readonly public?: ReadonlyArray<keyof z.output<TSchema> & string>;
|
|
38
|
+
/**
|
|
39
|
+
* Named example fixtures used by tests + OpenAPI examples. Each value is a
|
|
40
|
+
* partial of the resource — tests merge with sensible defaults.
|
|
41
|
+
*/
|
|
42
|
+
readonly examples?: Readonly<Record<string, Partial<z.output<TSchema>>>>;
|
|
43
|
+
/** Studio-aware: who reads this resource? `['product']`, `['ops']`, etc. */
|
|
44
|
+
readonly audience?: readonly string[];
|
|
45
|
+
}
|
|
46
|
+
export interface ResourceDefinition<TSchema extends ZodTypeAny = ZodTypeAny> {
|
|
47
|
+
readonly $kind: "resource";
|
|
48
|
+
readonly name: string;
|
|
49
|
+
readonly summary?: string;
|
|
50
|
+
readonly description?: string;
|
|
51
|
+
readonly schema: TSchema;
|
|
52
|
+
readonly public: ReadonlyArray<string>;
|
|
53
|
+
readonly examples: Readonly<Record<string, Partial<z.output<TSchema>>>>;
|
|
54
|
+
readonly audience?: readonly string[];
|
|
55
|
+
/** Return the named example as a typed partial. Throws if not declared. */
|
|
56
|
+
example(variant: string): Partial<z.output<TSchema>>;
|
|
57
|
+
/**
|
|
58
|
+
* Project a full record through the `public` field list. If `public` was
|
|
59
|
+
* not declared, returns the input unchanged.
|
|
60
|
+
*/
|
|
61
|
+
project(record: z.output<TSchema>): Partial<z.output<TSchema>>;
|
|
62
|
+
}
|
|
63
|
+
export declare function defineResource<TSchema extends ZodTypeAny>(name: string, options: DefineResourceOptions<TSchema>): ResourceDefinition<TSchema>;
|
|
64
|
+
/** Type narrow. */
|
|
65
|
+
export declare function isResourceDefinition(x: unknown): x is ResourceDefinition;
|
|
66
|
+
//# sourceMappingURL=define-resource.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define-resource.d.ts","sourceRoot":"","sources":["../src/define-resource.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,MAAM,WAAW,qBAAqB,CAAC,OAAO,SAAS,UAAU;IAC/D,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,MAAM,CAAC,CAAC;IAClE;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IACzE,4EAA4E;IAC5E,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC;AAED,MAAM,WAAW,kBAAkB,CAAC,OAAO,SAAS,UAAU,GAAG,UAAU;IACzE,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACvC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IACxE,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAEtC,2EAA2E;IAC3E,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IAErD;;;OAGG;IACH,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;CAChE;AAED,wBAAgB,cAAc,CAAC,OAAO,SAAS,UAAU,EACvD,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,qBAAqB,CAAC,OAAO,CAAC,GACtC,kBAAkB,CAAC,OAAO,CAAC,CAsC7B;AAED,mBAAmB;AACnB,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,kBAAkB,CAExE"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `defineResource` — a named, typed, exampled, view-projecting data shape
|
|
3
|
+
* used by the interface / app layer (resolvers + transports).
|
|
4
|
+
*
|
|
5
|
+
* const Wash = defineResource("Wash", {
|
|
6
|
+
* summary: "A wash session recorded at a station",
|
|
7
|
+
* schema: z.object({
|
|
8
|
+
* id: z.string().uuid(),
|
|
9
|
+
* washType: z.enum(["basic", "premium", "full"]),
|
|
10
|
+
* plateNumber: z.string().nullable(),
|
|
11
|
+
* completedAt: z.string().datetime(),
|
|
12
|
+
* }),
|
|
13
|
+
* public: ["id", "washType", "plateNumber", "completedAt"],
|
|
14
|
+
* examples: {
|
|
15
|
+
* premium: { washType: "premium", plateNumber: "51-234-67" },
|
|
16
|
+
* quick: { washType: "basic", plateNumber: null },
|
|
17
|
+
* },
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* Wash.example("premium") // typed partial — used in tests
|
|
21
|
+
* Wash.project(record) // strip non-public fields
|
|
22
|
+
*
|
|
23
|
+
* Resources are distinct from actor schemas: a resource is the *external*
|
|
24
|
+
* contract (what the API client sees). The actor's `defineSchema` is the
|
|
25
|
+
* *internal* stored state.
|
|
26
|
+
*/
|
|
27
|
+
export function defineResource(name, options) {
|
|
28
|
+
const examples = options.examples ?? {};
|
|
29
|
+
const publicFields = (options.public ?? []);
|
|
30
|
+
const resource = {
|
|
31
|
+
$kind: "resource",
|
|
32
|
+
name,
|
|
33
|
+
summary: options.summary,
|
|
34
|
+
description: options.description,
|
|
35
|
+
schema: options.schema,
|
|
36
|
+
public: publicFields,
|
|
37
|
+
examples,
|
|
38
|
+
audience: options.audience,
|
|
39
|
+
example(variant) {
|
|
40
|
+
const ex = examples[variant];
|
|
41
|
+
if (!ex) {
|
|
42
|
+
throw new Error(`Resource "${name}" has no example "${String(variant)}". ` +
|
|
43
|
+
`Declared examples: ${Object.keys(examples).join(", ") || "(none)"}.`);
|
|
44
|
+
}
|
|
45
|
+
return ex;
|
|
46
|
+
},
|
|
47
|
+
project(record) {
|
|
48
|
+
if (publicFields.length === 0)
|
|
49
|
+
return record;
|
|
50
|
+
const out = {};
|
|
51
|
+
for (const k of publicFields) {
|
|
52
|
+
if (k in record) {
|
|
53
|
+
out[k] = record[k];
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return out;
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
return resource;
|
|
60
|
+
}
|
|
61
|
+
/** Type narrow. */
|
|
62
|
+
export function isResourceDefinition(x) {
|
|
63
|
+
return typeof x === "object" && x !== null && x.$kind === "resource";
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=define-resource.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define-resource.js","sourceRoot":"","sources":["../src/define-resource.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AA2CH,MAAM,UAAU,cAAc,CAC5B,IAAY,EACZ,OAAuC;IAEvC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC;IACxC,MAAM,YAAY,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAA0B,CAAC;IAErE,MAAM,QAAQ,GAAgC;QAC5C,KAAK,EAAE,UAAU;QACjB,IAAI;QACJ,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,MAAM,EAAE,YAAY;QACpB,QAAQ;QACR,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAE1B,OAAO,CAAC,OAAO;YACb,MAAM,EAAE,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;YAC7B,IAAI,CAAC,EAAE,EAAE,CAAC;gBACR,MAAM,IAAI,KAAK,CACb,aAAa,IAAI,qBAAqB,MAAM,CAAC,OAAO,CAAC,KAAK;oBACxD,sBAAsB,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,QAAQ,GAAG,CACxE,CAAC;YACJ,CAAC;YACD,OAAO,EAAgC,CAAC;QAC1C,CAAC;QAED,OAAO,CAAC,MAAM;YACZ,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,MAAoC,CAAC;YAC3E,MAAM,GAAG,GAA4B,EAAE,CAAC;YACxC,KAAK,MAAM,CAAC,IAAI,YAAY,EAAE,CAAC;gBAC7B,IAAI,CAAC,IAAK,MAAkC,EAAE,CAAC;oBAC7C,GAAG,CAAC,CAAC,CAAC,GAAI,MAAkC,CAAC,CAAC,CAAC,CAAC;gBAClD,CAAC;YACH,CAAC;YACD,OAAO,GAAiC,CAAC;QAC3C,CAAC;KACF,CAAC;IAEF,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,mBAAmB;AACnB,MAAM,UAAU,oBAAoB,CAAC,CAAU;IAC7C,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAK,CAAyB,CAAC,KAAK,KAAK,UAAU,CAAC;AAChG,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@nwire/handler` — the operation primitive.
|
|
3
|
+
*
|
|
4
|
+
* Per the sealed architecture (§05), every transport (HTTP, queue,
|
|
5
|
+
* MCP, CLI, …) speaks the same handler shape. This package ships that
|
|
6
|
+
* shape — the operation as value — without dragging in forge's action /
|
|
7
|
+
* actor / event machinery. A NestJS team can import `defineHandler`
|
|
8
|
+
* from here, mount it on Express, and never touch `@nwire/forge`.
|
|
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.
|
|
14
|
+
*
|
|
15
|
+
* Public surface:
|
|
16
|
+
*
|
|
17
|
+
* Primitives
|
|
18
|
+
* defineHandler(name, config) — the operation primitive
|
|
19
|
+
* execute(handler, input) — direct invocation
|
|
20
|
+
* defineError(meta) — typed throwable
|
|
21
|
+
* defineResource(name, opts) — interface/response shape
|
|
22
|
+
* defineMiddleware — chain step
|
|
23
|
+
* defineHook — before/after-only step
|
|
24
|
+
* pipe(...steps) — compose chain
|
|
25
|
+
*
|
|
26
|
+
* Response factories
|
|
27
|
+
* ok / ok.list / created / accepted / noContent / notModified / gone
|
|
28
|
+
*
|
|
29
|
+
* Framework errors
|
|
30
|
+
* Unauthorized / Forbidden / NotFound / Conflict / Gone / BadRequest
|
|
31
|
+
*
|
|
32
|
+
* Types
|
|
33
|
+
* HandlerDefinition / HandlerConfig / HandlerContext / HandlerLike
|
|
34
|
+
* ResponseInstance / ResponseSpec / ChainFn / NwireError
|
|
35
|
+
*/
|
|
36
|
+
export { defineHandler, execute, isHandlerDefinition, type HandlerDefinition, type HandlerConfig, type HandlerContext, type HandlerMeta, type HandlerSettings, type HandlerReturnsSpec, type HandlerPolicy, type HandlerLike, type ExecuteOptions, } from "./define-handler.js";
|
|
37
|
+
export { defineError, isNwireError, NwireError, Unauthorized, Forbidden, NotFound, Gone, BadRequest, Conflict, type ErrorDefinition, type ErrorMeta, } from "./define-error.js";
|
|
38
|
+
export { defineResource, isResourceDefinition, type ResourceDefinition, type DefineResourceOptions, } from "./define-resource.js";
|
|
39
|
+
export { defineMiddleware, defineHook, pipe, unwindPipe, isMiddleware, isHook, isPipe, type MiddlewareDefinition, type HookDefinition, type MiddlewarePipe, type PipeStep, type ChainFn, type ResolverCtx, type Next, } from "./define-middleware.js";
|
|
40
|
+
export { response, isResponseSpec, isResponseInstance, ok, created, accepted, noContent, notModified, gone, type ResponseSpec, type ListResponseSpec, type ResponseInstance, type ResponseSpecBase, type ResponseKind, } from "./response.js";
|
|
41
|
+
//# sourceMappingURL=handler-index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler-index.d.ts","sourceRoot":"","sources":["../src/handler-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,EACL,aAAa,EACb,OAAO,EACP,mBAAmB,EACnB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,cAAc,GACpB,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,gBAAgB,EAChB,UAAU,EACV,IAAI,EACJ,UAAU,EACV,YAAY,EACZ,MAAM,EACN,MAAM,EACN,KAAK,oBAAoB,EACzB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,KAAK,OAAO,EACZ,KAAK,WAAW,EAChB,KAAK,IAAI,GACV,MAAM,wBAAwB,CAAC;AAEhC,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,gBAAgB,EACrB,KAAK,YAAY,GAClB,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@nwire/handler` — the operation primitive.
|
|
3
|
+
*
|
|
4
|
+
* Per the sealed architecture (§05), every transport (HTTP, queue,
|
|
5
|
+
* MCP, CLI, …) speaks the same handler shape. This package ships that
|
|
6
|
+
* shape — the operation as value — without dragging in forge's action /
|
|
7
|
+
* actor / event machinery. A NestJS team can import `defineHandler`
|
|
8
|
+
* from here, mount it on Express, and never touch `@nwire/forge`.
|
|
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.
|
|
14
|
+
*
|
|
15
|
+
* Public surface:
|
|
16
|
+
*
|
|
17
|
+
* Primitives
|
|
18
|
+
* defineHandler(name, config) — the operation primitive
|
|
19
|
+
* execute(handler, input) — direct invocation
|
|
20
|
+
* defineError(meta) — typed throwable
|
|
21
|
+
* defineResource(name, opts) — interface/response shape
|
|
22
|
+
* defineMiddleware — chain step
|
|
23
|
+
* defineHook — before/after-only step
|
|
24
|
+
* pipe(...steps) — compose chain
|
|
25
|
+
*
|
|
26
|
+
* Response factories
|
|
27
|
+
* ok / ok.list / created / accepted / noContent / notModified / gone
|
|
28
|
+
*
|
|
29
|
+
* Framework errors
|
|
30
|
+
* Unauthorized / Forbidden / NotFound / Conflict / Gone / BadRequest
|
|
31
|
+
*
|
|
32
|
+
* Types
|
|
33
|
+
* HandlerDefinition / HandlerConfig / HandlerContext / HandlerLike
|
|
34
|
+
* ResponseInstance / ResponseSpec / ChainFn / NwireError
|
|
35
|
+
*/
|
|
36
|
+
export { defineHandler, execute, isHandlerDefinition, } from "./define-handler.js";
|
|
37
|
+
export { defineError, isNwireError, NwireError, Unauthorized, Forbidden, NotFound, Gone, BadRequest, Conflict, } from "./define-error.js";
|
|
38
|
+
export { defineResource, isResourceDefinition, } from "./define-resource.js";
|
|
39
|
+
export { defineMiddleware, defineHook, pipe, unwindPipe, isMiddleware, isHook, isPipe, } from "./define-middleware.js";
|
|
40
|
+
export { response, isResponseSpec, isResponseInstance, ok, created, accepted, noContent, notModified, gone, } from "./response.js";
|
|
41
|
+
//# sourceMappingURL=handler-index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler-index.js","sourceRoot":"","sources":["../src/handler-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,EACL,aAAa,EACb,OAAO,EACP,mBAAmB,GAUpB,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,gBAAgB,EAChB,UAAU,EACV,IAAI,EACJ,UAAU,EACV,YAAY,EACZ,MAAM,EACN,MAAM,GAQP,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,QAAQ,EACR,cAAc,EACd,kBAAkB,EAClB,EAAE,EACF,OAAO,EACP,QAAQ,EACR,SAAS,EACT,WAAW,EACX,IAAI,GAML,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `response(status, Resource)` — typed response builder.
|
|
3
|
+
*
|
|
4
|
+
* const washCreated = response(201, Wash);
|
|
5
|
+
* const washAccepted = response(202);
|
|
6
|
+
* const washList = response.list(200, Wash);
|
|
7
|
+
*
|
|
8
|
+
* // In a resolver handler:
|
|
9
|
+
* return washCreated(wash);
|
|
10
|
+
* return washAccepted();
|
|
11
|
+
* return washList([wash1, wash2]);
|
|
12
|
+
*
|
|
13
|
+
* Each response spec is BOTH metadata (status + body resource — used by
|
|
14
|
+
* OpenAPI/GraphQL schema generation + response validation) AND a builder
|
|
15
|
+
* (callable to produce a typed response instance). When the spec carries
|
|
16
|
+
* a `ResourceDefinition` with declared `public` fields, the body is run
|
|
17
|
+
* through `resource.project(...)` so non-public fields never leak — the
|
|
18
|
+
* builder owns the shaping, handlers don't hand-map.
|
|
19
|
+
*/
|
|
20
|
+
import type { z } from "zod";
|
|
21
|
+
import type { ResourceDefinition } from "./define-resource.js";
|
|
22
|
+
export type ResponseKind = "single" | "list" | "empty";
|
|
23
|
+
export interface ResponseInstance<TBody = unknown> {
|
|
24
|
+
readonly $kind: "response-instance";
|
|
25
|
+
readonly status: number;
|
|
26
|
+
readonly body?: TBody;
|
|
27
|
+
}
|
|
28
|
+
export interface ResponseSpecBase {
|
|
29
|
+
readonly $kind: "response";
|
|
30
|
+
readonly status: number;
|
|
31
|
+
readonly kind: ResponseKind;
|
|
32
|
+
readonly resource?: ResourceDefinition;
|
|
33
|
+
}
|
|
34
|
+
export type ResponseSpec<TResource extends ResourceDefinition | undefined = ResourceDefinition | undefined> = ResponseSpecBase & (TResource extends ResourceDefinition<infer S> ? (data: z.output<S>) => ResponseInstance<z.output<S>> : () => ResponseInstance<undefined>);
|
|
35
|
+
export type ListResponseSpec<TResource extends ResourceDefinition> = ResponseSpecBase & (TResource extends ResourceDefinition<infer S> ? (items: ReadonlyArray<z.output<S>>) => ResponseInstance<ReadonlyArray<z.output<S>>> : never);
|
|
36
|
+
declare function singleSpec<TResource extends ResourceDefinition | undefined>(status: number, resource?: TResource): ResponseSpec<TResource>;
|
|
37
|
+
declare function listSpec<TResource extends ResourceDefinition>(status: number, resource: TResource): ListResponseSpec<TResource>;
|
|
38
|
+
/**
|
|
39
|
+
* `response(status, Resource?)` — single-body response spec (or empty body).
|
|
40
|
+
* Has `.list(status, Resource)` for array responses.
|
|
41
|
+
*/
|
|
42
|
+
export declare const response: typeof singleSpec & {
|
|
43
|
+
list: typeof listSpec;
|
|
44
|
+
};
|
|
45
|
+
export declare const ok: (<TResource extends ResourceDefinition | undefined>(resource?: TResource) => ResponseSpec<TResource>) & {
|
|
46
|
+
list: <TResource extends ResourceDefinition>(resource: TResource) => ListResponseSpec<TResource>;
|
|
47
|
+
};
|
|
48
|
+
export declare const created: <TResource extends ResourceDefinition | undefined>(resource?: TResource) => ResponseSpec<TResource>;
|
|
49
|
+
export declare const accepted: <TResource extends ResourceDefinition | undefined>(resource?: TResource) => ResponseSpec<TResource>;
|
|
50
|
+
export declare const noContent: () => ResponseSpec<undefined>;
|
|
51
|
+
export declare const notModified: () => ResponseSpec<undefined>;
|
|
52
|
+
export declare const gone: () => ResponseSpec<undefined>;
|
|
53
|
+
/** Type narrows. */
|
|
54
|
+
export declare const isResponseSpec: (x: unknown) => x is ResponseSpecBase;
|
|
55
|
+
export declare const isResponseInstance: (x: unknown) => x is ResponseInstance;
|
|
56
|
+
export type { ResourceDefinition } from "./define-resource.js";
|
|
57
|
+
//# sourceMappingURL=response.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response.d.ts","sourceRoot":"","sources":["../src/response.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE/D,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;AAEvD,MAAM,WAAW,gBAAgB,CAAC,KAAK,GAAG,OAAO;IAC/C,QAAQ,CAAC,KAAK,EAAE,mBAAmB,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,kBAAkB,CAAC;CACxC;AAGD,MAAM,MAAM,YAAY,CACtB,SAAS,SAAS,kBAAkB,GAAG,SAAS,GAAG,kBAAkB,GAAG,SAAS,IAC/E,gBAAgB,GAClB,CAAC,SAAS,SAAS,kBAAkB,CAAC,MAAM,CAAC,CAAC,GAE1C,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,gBAAgB,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GACpD,MAAM,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAC;AAEzC,MAAM,MAAM,gBAAgB,CAAC,SAAS,SAAS,kBAAkB,IAAI,gBAAgB,GACnF,CAAC,SAAS,SAAS,kBAAkB,CAAC,MAAM,CAAC,CAAC,GAE1C,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,gBAAgB,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,GACnF,KAAK,CAAC,CAAC;AAEb,iBAAS,UAAU,CAAC,SAAS,SAAS,kBAAkB,GAAG,SAAS,EAClE,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,SAAS,GACnB,YAAY,CAAC,SAAS,CAAC,CAazB;AAED,iBAAS,QAAQ,CAAC,SAAS,SAAS,kBAAkB,EACpD,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,SAAS,GAClB,gBAAgB,CAAC,SAAS,CAAC,CAa7B;AAED;;;GAGG;AACH,eAAO,MAAM,QAAQ;;CAAgD,CAAC;AAQtE,eAAO,MAAM,EAAE,IACZ,SAAS,SAAS,kBAAkB,GAAG,SAAS,aAAa,SAAS;WAG9D,SAAS,SAAS,kBAAkB,YAAY,SAAS;CAGnE,CAAC;AACF,eAAO,MAAM,OAAO,GAAI,SAAS,SAAS,kBAAkB,GAAG,SAAS,EAAE,WAAW,SAAS,4BACrE,CAAC;AAC1B,eAAO,MAAM,QAAQ,GAAI,SAAS,SAAS,kBAAkB,GAAG,SAAS,EAAE,WAAW,SAAS,4BACtE,CAAC;AAC1B,eAAO,MAAM,SAAS,QAAO,YAAY,CAAC,SAAS,CAAkB,CAAC;AACtE,eAAO,MAAM,WAAW,QAAO,YAAY,CAAC,SAAS,CAAkB,CAAC;AACxE,eAAO,MAAM,IAAI,QAAO,YAAY,CAAC,SAAS,CAAkB,CAAC;AAEjE,oBAAoB;AACpB,eAAO,MAAM,cAAc,GAAI,GAAG,OAAO,KAAG,CAAC,IAAI,gBACuC,CAAC;AAEzF,eAAO,MAAM,kBAAkB,GAAI,GAAG,OAAO,KAAG,CAAC,IAAI,gBAC4C,CAAC;AAIlG,YAAY,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC"}
|