stitchkit 0.8.1 → 0.10.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/dist/browser/client.d.ts.map +1 -1
- package/dist/browser/socket-io.d.ts +11 -2
- package/dist/browser/socket-io.d.ts.map +1 -1
- package/dist/contract/define.d.ts +60 -2
- package/dist/contract/define.d.ts.map +1 -1
- package/dist/contract/index.d.ts +1 -1
- package/dist/contract/index.d.ts.map +1 -1
- package/dist/{index-svvpy5bz.js → index-s8gtzt8w.js} +37 -37
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +55 -4
- package/dist/node.js +1 -1
- package/dist/observability/audit.d.ts.map +1 -1
- package/dist/observability/context.d.ts +6 -1
- package/dist/observability/context.d.ts.map +1 -1
- package/dist/observability/event.d.ts +6 -0
- package/dist/observability/event.d.ts.map +1 -1
- package/dist/observability/index.js +1 -0
- package/dist/retained.d.ts +30 -0
- package/dist/retained.d.ts.map +1 -0
- package/dist/server/context.d.ts +18 -1
- package/dist/server/context.d.ts.map +1 -1
- package/dist/server/create.d.ts.map +1 -1
- package/dist/server/implement.d.ts.map +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/logger.d.ts +3 -2
- package/dist/server/logger.d.ts.map +1 -1
- package/dist/server/types.d.ts +7 -0
- package/dist/server/types.d.ts.map +1 -1
- package/dist/tools/remote.d.ts.map +1 -1
- package/dist/tools.js +15 -4
- package/llms-full.txt +92 -3
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/browser/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/browser/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,WAAW,EAGX,gBAAgB,EAChB,eAAe,EAChB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAEL,KAAK,UAAU,IAAI,WAAW,EAG/B,MAAM,QAAQ,CAAC;AA8ChB,kFAAkF;AAClF,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAClE,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACnD;AAED;;;GAGG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GAC1D,OAAO,GACP;KAAG,CAAC,IAAI,CAAC,GAAG,MAAM;CAAE,CAAC;AAEzB;;;;;GAKG;AACH,MAAM,WAAW,oBAAoB,CAAC,CAAC,SAAS,MAAM,GAAG,KAAK;IAC5D,UAAU,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,CAAC,CAAC;IAClE,eAAe,CAAC,EAAE,SAAS,CAAC,EAAE,CAAC;CAChC;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACrC,KAAK,CAAC,CAAC,SAAS,MAAM,GAAG,KAAK,EAE9B,QAAQ,EAAE,WAAW,CAAC,CAAC,EAAE,MAAM,CAAC,EAChC,cAAc,EAAE,YAAY,GAAG,WAAW,EAC1C,cAAc,CAAC,EAAE,oBAAoB,CAAC,CAAC,CAAC,GACvC,gBAAgB,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAepC;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,CAAC,CAAC,EAC1E,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,WAAW,GAAG;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;CAAE,CAKzF"}
|
|
@@ -29,7 +29,7 @@ import type { EventsMap } from '@socket.io/component-emitter';
|
|
|
29
29
|
* public `on` / `emit` signatures.
|
|
30
30
|
*/
|
|
31
31
|
export type SocketEventMap = EventsMap;
|
|
32
|
-
export interface SocketIOClientConfig {
|
|
32
|
+
export interface SocketIOClientConfig<TServerEvents extends SocketEventMap = SocketEventMap> {
|
|
33
33
|
/** Server origin, e.g. `https://api.example.com`. */
|
|
34
34
|
url: string;
|
|
35
35
|
/** Socket.IO endpoint path. Default `/socket.io/`. */
|
|
@@ -63,6 +63,15 @@ export interface SocketIOClientConfig {
|
|
|
63
63
|
reconnectionDelayMax?: number;
|
|
64
64
|
/** Connection timeout (ms). Default `20000`. */
|
|
65
65
|
timeout?: number;
|
|
66
|
+
/**
|
|
67
|
+
* Server → client events to **retain** ("sticky events"). The client keeps the
|
|
68
|
+
* last payload of each listed event and replays it to any handler subscribed
|
|
69
|
+
* afterwards (and on the next subscribe after a re-render), so a late
|
|
70
|
+
* subscriber catches up to the current state at once instead of waiting for the
|
|
71
|
+
* next emission. The retained value survives a `disconnect()` / `connect()`
|
|
72
|
+
* cycle. Only the first emitted argument of an event is retained.
|
|
73
|
+
*/
|
|
74
|
+
retain?: Array<keyof TServerEvents & string>;
|
|
66
75
|
}
|
|
67
76
|
export interface SocketIOClient<TServerEvents extends SocketEventMap, TClientEvents extends SocketEventMap> {
|
|
68
77
|
/** Create the socket (if absent) and connect. Idempotent. */
|
|
@@ -82,5 +91,5 @@ export interface SocketIOClient<TServerEvents extends SocketEventMap, TClientEve
|
|
|
82
91
|
/** Subscribe to connection up/down changes. Returns an unsubscribe. */
|
|
83
92
|
onConnectionChange(listener: (connected: boolean) => void): () => void;
|
|
84
93
|
}
|
|
85
|
-
export declare function createSocketIOClient<TServerEvents extends SocketEventMap, TClientEvents extends SocketEventMap>(config: SocketIOClientConfig): SocketIOClient<TServerEvents, TClientEvents>;
|
|
94
|
+
export declare function createSocketIOClient<TServerEvents extends SocketEventMap, TClientEvents extends SocketEventMap>(config: SocketIOClientConfig<TServerEvents>): SocketIOClient<TServerEvents, TClientEvents>;
|
|
86
95
|
//# sourceMappingURL=socket-io.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"socket-io.d.ts","sourceRoot":"","sources":["../../src/browser/socket-io.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"socket-io.d.ts","sourceRoot":"","sources":["../../src/browser/socket-io.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AA2B9D;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG,SAAS,CAAC;AAEvC,MAAM,WAAW,oBAAoB,CAAC,aAAa,SAAS,cAAc,GAAG,cAAc;IACzF,qDAAqD;IACrD,GAAG,EAAE,MAAM,CAAC;IACZ,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yEAAyE;IACzE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;;OAMG;IACH,IAAI,CAAC,EACD,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACvB,CAAC,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IACvE,kFAAkF;IAClF,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;IAClD;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,2EAA2E;IAC3E,UAAU,CAAC,EAAE,KAAK,CAAC,WAAW,GAAG,SAAS,CAAC,CAAC;IAC5C,iDAAiD;IACjD,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,uDAAuD;IACvD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,mDAAmD;IACnD,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,gDAAgD;IAChD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,KAAK,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC,CAAC;CAC9C;AAED,MAAM,WAAW,cAAc,CAC7B,aAAa,SAAS,cAAc,EACpC,aAAa,SAAS,cAAc;IAEpC,6DAA6D;IAC7D,OAAO,IAAI,IAAI,CAAC;IAChB,+CAA+C;IAC/C,UAAU,IAAI,IAAI,CAAC;IACnB,qDAAqD;IACrD,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B;;;;OAIG;IACH,EAAE,CAAC,CAAC,SAAS,MAAM,aAAa,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,CAAC;IAC5F,8DAA8D;IAC9D,IAAI,CAAC,CAAC,SAAS,MAAM,aAAa,GAAG,MAAM,EACzC,KAAK,EAAE,CAAC,EACR,GAAG,IAAI,EAAE,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,GACpC,IAAI,CAAC;IACR,uEAAuE;IACvE,kBAAkB,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CACxE;AAED,wBAAgB,oBAAoB,CAClC,aAAa,SAAS,cAAc,EACpC,aAAa,SAAS,cAAc,EACpC,MAAM,EAAE,oBAAoB,CAAC,aAAa,CAAC,GAAG,cAAc,CAAC,aAAa,EAAE,aAAa,CAAC,CA8G3F"}
|
|
@@ -2,7 +2,14 @@ import type { ZodType, z } from 'zod';
|
|
|
2
2
|
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
3
3
|
export declare const ALL_TRANSPORTS: readonly ["HTTP", "MCP", "AGENT", "CLI"];
|
|
4
4
|
export type Transport = (typeof ALL_TRANSPORTS)[number];
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* The transport tag on `ctx.source`. The four built-ins keep autocomplete, but
|
|
7
|
+
* the union is **open** (`string & {}`) so a bring-your-own transport — e.g. a
|
|
8
|
+
* raw-WebSocket lane that runs a contract through the app's own dispatch loop —
|
|
9
|
+
* can tag its own calls (`source: 'local-ws'`). `source` is transport-only and
|
|
10
|
+
* carries no framework behaviour (→ ADR 0002).
|
|
11
|
+
*/
|
|
12
|
+
export type TransportSource = 'http' | 'mcp' | 'agent' | 'cli' | (string & {});
|
|
6
13
|
interface EndpointDefBase {
|
|
7
14
|
method: HttpMethod;
|
|
8
15
|
path: string;
|
|
@@ -25,6 +32,20 @@ interface EndpointDefBase {
|
|
|
25
32
|
* property of the endpoint — declared once, the typed client applies it.
|
|
26
33
|
*/
|
|
27
34
|
timeout?: number;
|
|
35
|
+
/**
|
|
36
|
+
* Whether calling this operation twice with the same input is safe (the
|
|
37
|
+
* second call yields the same result and no extra side effect) — a
|
|
38
|
+
* transport-neutral property of the operation, like HTTP `PUT`/`DELETE`.
|
|
39
|
+
*
|
|
40
|
+
* The core attaches **no** behaviour to it (it stays generic — ADR 0002): it
|
|
41
|
+
* rides through to `MethodDef.idempotent`, where a transport that can retry
|
|
42
|
+
* reads it. A reliable bring-your-own-transport lane (e.g. a raw-WebSocket
|
|
43
|
+
* client) replays an `idempotent` call after a reconnect — that is the
|
|
44
|
+
* durability guarantee — while a non-idempotent one is rejected rather than
|
|
45
|
+
* re-sent (a duplicate would be a second side effect). Unset means "unknown" —
|
|
46
|
+
* a careful transport treats it as non-idempotent.
|
|
47
|
+
*/
|
|
48
|
+
idempotent?: boolean;
|
|
28
49
|
/**
|
|
29
50
|
* Opaque, app-defined per-endpoint metadata. The core attaches **no** meaning
|
|
30
51
|
* to it (like `scope`, it is a free escape-hatch — ADR 0002/0021): it rides
|
|
@@ -108,6 +129,17 @@ export interface RuntimeContext {
|
|
|
108
129
|
input: unknown;
|
|
109
130
|
file?: File;
|
|
110
131
|
source: TransportSource;
|
|
132
|
+
/**
|
|
133
|
+
* The raw Web `Request`, its parsed `URL` and `Headers` — set on the HTTP
|
|
134
|
+
* transport (and reachable in every lifecycle hook, including `onError` on a
|
|
135
|
+
* validation failure). Absent on the non-HTTP transports (MCP / agent / CLI /
|
|
136
|
+
* a bring-your-own lane), which carry no `Request`, so they are optional and a
|
|
137
|
+
* reader narrows them. Web Fetch types only — the core stays Fetch-clean
|
|
138
|
+
* (→ ADR 0013).
|
|
139
|
+
*/
|
|
140
|
+
req?: Request;
|
|
141
|
+
url?: URL;
|
|
142
|
+
headers?: Headers;
|
|
111
143
|
traceId?: string;
|
|
112
144
|
spanId?: string;
|
|
113
145
|
ipAddress?: string;
|
|
@@ -119,6 +151,11 @@ export interface HandlerContext<TParams = undefined, TInput = undefined> {
|
|
|
119
151
|
input: TInput;
|
|
120
152
|
file?: File;
|
|
121
153
|
source: TransportSource;
|
|
154
|
+
/** Raw Web `Request` / `URL` / `Headers` — set on the HTTP transport, absent
|
|
155
|
+
* on the tool transports (see {@link RuntimeContext}). */
|
|
156
|
+
req?: Request;
|
|
157
|
+
url?: URL;
|
|
158
|
+
headers?: Headers;
|
|
122
159
|
traceId?: string;
|
|
123
160
|
spanId?: string;
|
|
124
161
|
ipAddress?: string;
|
|
@@ -127,10 +164,31 @@ export interface HandlerContext<TParams = undefined, TInput = undefined> {
|
|
|
127
164
|
}
|
|
128
165
|
type Prop<T, K extends string> = K extends keyof T ? T[K] : undefined;
|
|
129
166
|
type InferInput<S> = S extends ZodType ? z.input<S> : unknown;
|
|
167
|
+
/**
|
|
168
|
+
* A platform file descriptor accepted by the typed client's multipart methods
|
|
169
|
+
* where a file is not a `Blob`. React Native / Expo represent a file as
|
|
170
|
+
* `{ uri, name, type }` and their `FormData.append` streams it from disk by
|
|
171
|
+
* `uri`; reading it into a `Blob` first (`fetch(uri).blob()`) would load the
|
|
172
|
+
* whole media into memory. The web / Bun path still uses `Blob`.
|
|
173
|
+
*/
|
|
174
|
+
export interface FileDescriptor {
|
|
175
|
+
/** Local file URI the platform streams from (e.g. `file:///…`). */
|
|
176
|
+
uri: string;
|
|
177
|
+
/** File name for the multipart part. */
|
|
178
|
+
name: string;
|
|
179
|
+
/** MIME type for the multipart part. */
|
|
180
|
+
type: string;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* What a `multipart` endpoint's file field accepts on the typed client — a
|
|
184
|
+
* `Blob` (web / Bun) or a platform {@link FileDescriptor} (React Native / Expo).
|
|
185
|
+
* Exported so a consumer can type its own upload helpers without a cast.
|
|
186
|
+
*/
|
|
187
|
+
export type MultipartFile = Blob | FileDescriptor;
|
|
130
188
|
type MultipartArgs<E> = E extends {
|
|
131
189
|
multipart: infer K extends string;
|
|
132
190
|
} ? {
|
|
133
|
-
[P in K]:
|
|
191
|
+
[P in K]: MultipartFile;
|
|
134
192
|
} : unknown;
|
|
135
193
|
type EndpointArgs<E> = InferInput<Prop<E, 'params'>> & InferInput<Prop<E, 'input'>> & MultipartArgs<E>;
|
|
136
194
|
type EndpointOutput<E> = Prop<E, 'output'> extends ZodType<infer O> ? O : undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define.d.ts","sourceRoot":"","sources":["../../src/contract/define.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAEtC,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;AAErE,eAAO,MAAM,cAAc,0CAA2C,CAAC;AACvE,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAExD,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,KAAK,CAAC;AAE/
|
|
1
|
+
{"version":3,"file":"define.d.ts","sourceRoot":"","sources":["../../src/contract/define.ts"],"names":[],"mappings":"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"}
|
package/dist/contract/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { ALL_TRANSPORTS, type ContractDef, type ContractMeta, defineContract, type EndpointDef, type EndpointFn, type EndpointToolAnnotations, type EndpointUiMeta, type HandlerContext, type HttpMethod, type RuntimeContext, type ScopedEndpointFn, type ScopedHttpClient, type Transport, type TransportSource, type TypedClient, type TypedHttpClient, } from './define';
|
|
1
|
+
export { ALL_TRANSPORTS, type ContractDef, type ContractMeta, defineContract, type EndpointDef, type EndpointFn, type EndpointToolAnnotations, type EndpointUiMeta, type FileDescriptor, type HandlerContext, type HttpMethod, type MultipartFile, type RuntimeContext, type ScopedEndpointFn, type ScopedHttpClient, type Transport, type TransportSource, type TypedClient, type TypedHttpClient, } from './define';
|
|
2
2
|
export { AppError, appError, badRequest, conflict, type ErrorEnvelope, forbidden, isStitchErrorCode, notFound, rateLimited, STITCH_ERROR_STATUS, type StitchErrorCode, unauthorized, } from './errors';
|
|
3
3
|
export { decodeCursor, encodeCursor, type Paginated, paginatedSchema, } from './pagination';
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/contract/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,cAAc,EACd,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,uBAAuB,EAC5B,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,SAAS,EACd,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,eAAe,GACrB,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,KAAK,aAAa,EAClB,SAAS,EACT,iBAAiB,EACjB,QAAQ,EACR,WAAW,EACX,mBAAmB,EACnB,KAAK,eAAe,EACpB,YAAY,GACb,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,KAAK,SAAS,EACd,eAAe,GAChB,MAAM,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/contract/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,cAAc,EACd,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,uBAAuB,EAC5B,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,SAAS,EACd,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,eAAe,GACrB,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,KAAK,aAAa,EAClB,SAAS,EACT,iBAAiB,EACjB,QAAQ,EACR,WAAW,EACX,mBAAmB,EACnB,KAAK,eAAe,EACpB,YAAY,GACb,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,KAAK,SAAS,EACd,eAAe,GAChB,MAAM,cAAc,CAAC"}
|
|
@@ -343,38 +343,15 @@ async function readJsonBody(req) {
|
|
|
343
343
|
badRequest("Invalid JSON body");
|
|
344
344
|
}
|
|
345
345
|
}
|
|
346
|
-
|
|
347
|
-
const parsedParams = method.paramsSchema ? method.paramsSchema.parse(pathParams) : undefined;
|
|
348
|
-
let parsedInput;
|
|
349
|
-
let file;
|
|
350
|
-
if (method.multipart) {
|
|
351
|
-
const cap = method.maxUploadBytes ?? maxUploadBytes;
|
|
352
|
-
const multipart = await parseMultipart(req, method.multipart, method.inputSchema, cap);
|
|
353
|
-
parsedInput = multipart.fields;
|
|
354
|
-
file = multipart.file;
|
|
355
|
-
} else if (method.inputSchema) {
|
|
356
|
-
if (req.method === "GET") {
|
|
357
|
-
parsedInput = method.inputSchema.parse(parseQueryParams(url));
|
|
358
|
-
} else if (req.method === "DELETE") {
|
|
359
|
-
const ct = req.headers.get("content-type");
|
|
360
|
-
if (ct?.includes("application/json")) {
|
|
361
|
-
parsedInput = method.inputSchema.parse(await readJsonBody(req));
|
|
362
|
-
} else {
|
|
363
|
-
parsedInput = method.inputSchema.parse(parseQueryParams(url));
|
|
364
|
-
}
|
|
365
|
-
} else {
|
|
366
|
-
parsedInput = method.inputSchema.parse(await readJsonBody(req));
|
|
367
|
-
}
|
|
368
|
-
}
|
|
346
|
+
function buildBaseContext(req, url, pathParams, traceId, clientIp) {
|
|
369
347
|
const safePathParams = {};
|
|
370
348
|
for (const [k, v] of Object.entries(pathParams)) {
|
|
371
349
|
if (!RESERVED_KEYS.has(k) && !isUnsafeKey(k))
|
|
372
350
|
safePathParams[k] = v;
|
|
373
351
|
}
|
|
374
352
|
return {
|
|
375
|
-
params:
|
|
376
|
-
input:
|
|
377
|
-
...file && { file },
|
|
353
|
+
params: pathParams,
|
|
354
|
+
input: undefined,
|
|
378
355
|
source: "http",
|
|
379
356
|
req,
|
|
380
357
|
url,
|
|
@@ -384,6 +361,27 @@ async function buildContext(req, url, method, pathParams, traceId, clientIp, max
|
|
|
384
361
|
...getClientInfo(req, clientIp)
|
|
385
362
|
};
|
|
386
363
|
}
|
|
364
|
+
async function parseRequestInto(ctx, req, url, method, maxUploadBytes) {
|
|
365
|
+
if (method.paramsSchema) {
|
|
366
|
+
ctx.params = method.paramsSchema.parse(ctx.params);
|
|
367
|
+
}
|
|
368
|
+
if (method.multipart) {
|
|
369
|
+
const cap = method.maxUploadBytes ?? maxUploadBytes;
|
|
370
|
+
const multipart = await parseMultipart(req, method.multipart, method.inputSchema, cap);
|
|
371
|
+
ctx.input = multipart.fields;
|
|
372
|
+
if (multipart.file)
|
|
373
|
+
ctx.file = multipart.file;
|
|
374
|
+
} else if (method.inputSchema) {
|
|
375
|
+
if (req.method === "GET") {
|
|
376
|
+
ctx.input = method.inputSchema.parse(parseQueryParams(url));
|
|
377
|
+
} else if (req.method === "DELETE") {
|
|
378
|
+
const ct = req.headers.get("content-type");
|
|
379
|
+
ctx.input = ct?.includes("application/json") ? method.inputSchema.parse(await readJsonBody(req)) : method.inputSchema.parse(parseQueryParams(url));
|
|
380
|
+
} else {
|
|
381
|
+
ctx.input = method.inputSchema.parse(await readJsonBody(req));
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
387
385
|
function buildErrorContext(req, url, traceId, clientIp) {
|
|
388
386
|
return {
|
|
389
387
|
params: undefined,
|
|
@@ -447,8 +445,8 @@ function levelForStatus(status) {
|
|
|
447
445
|
return "warn";
|
|
448
446
|
return "info";
|
|
449
447
|
}
|
|
450
|
-
function buildLogFields(method, path, status, durationMs, traceId) {
|
|
451
|
-
return { traceId, method, path, status, durationMs };
|
|
448
|
+
function buildLogFields(method, path, status, durationMs, traceId, errorCode) {
|
|
449
|
+
return { traceId, method, path, status, durationMs, ...errorCode && { errorCode } };
|
|
452
450
|
}
|
|
453
451
|
function formatMs(ms) {
|
|
454
452
|
if (ms >= 1000)
|
|
@@ -492,20 +490,21 @@ function logIncoming(req, pathname, traceId, ipAddress) {
|
|
|
492
490
|
}
|
|
493
491
|
return log;
|
|
494
492
|
}
|
|
495
|
-
function logOutgoing(req, pathname, status, log, ipAddress) {
|
|
493
|
+
function logOutgoing(req, pathname, status, log, ipAddress, errorCode) {
|
|
496
494
|
const ms = elapsedMs(log.startTime);
|
|
497
495
|
if (isProd) {
|
|
498
496
|
console.log(JSON.stringify({
|
|
499
497
|
ts: new Date().toISOString(),
|
|
500
498
|
level: levelForStatus(status),
|
|
501
499
|
msg: `${req.method} ${pathname} ${status}`,
|
|
502
|
-
...buildLogFields(req.method, pathname, status, Math.round(ms), log.traceId),
|
|
500
|
+
...buildLogFields(req.method, pathname, status, Math.round(ms), log.traceId, errorCode),
|
|
503
501
|
ip: ipAddress
|
|
504
502
|
}));
|
|
505
503
|
return;
|
|
506
504
|
}
|
|
507
505
|
const mc = METHOD_COLOR[req.method] ?? c.dim;
|
|
508
|
-
|
|
506
|
+
const code = errorCode ? ` ${c.red}${errorCode}${c.reset}` : "";
|
|
507
|
+
console.log(`${c.gray}[${timestamp()}]${c.reset} ${mc}${req.method}${c.reset} ${c.dim}${log.traceId}${c.reset} ${c.cyan}←${c.reset} ${safePath(pathname)} ${statusColor(status)}${status}${c.reset}${code} ${durationColor(ms)}${formatMs(ms)}${c.reset} ${ipLabel(ipAddress ?? "")}`);
|
|
509
508
|
}
|
|
510
509
|
|
|
511
510
|
// src/server/create.ts
|
|
@@ -534,15 +533,15 @@ function createHandler(config) {
|
|
|
534
533
|
});
|
|
535
534
|
reqLog = { traceId, startTime: performance.now() };
|
|
536
535
|
}
|
|
537
|
-
const logDone = (status) => {
|
|
536
|
+
const logDone = (status, errorCode) => {
|
|
538
537
|
if (!reqLog)
|
|
539
538
|
return;
|
|
540
539
|
if (useDefaultLog)
|
|
541
|
-
logOutgoing(req, url.pathname, status, reqLog, ipAddress);
|
|
540
|
+
logOutgoing(req, url.pathname, status, reqLog, ipAddress, errorCode);
|
|
542
541
|
if (customLogger) {
|
|
543
542
|
const durationMs = Math.round(elapsedMs(reqLog.startTime));
|
|
544
543
|
const level = levelForStatus(status);
|
|
545
|
-
customLogger[level](`${req.method} ${url.pathname} ${status} ${durationMs}ms`, buildLogFields(req.method, url.pathname, status, durationMs, reqLog.traceId));
|
|
544
|
+
customLogger[level](`${req.method} ${url.pathname} ${status}${errorCode ? ` ${errorCode}` : ""} ${durationMs}ms`, buildLogFields(req.method, url.pathname, status, durationMs, reqLog.traceId, errorCode));
|
|
546
545
|
}
|
|
547
546
|
};
|
|
548
547
|
const respondError = async (err, errCtx, endpoint) => {
|
|
@@ -557,7 +556,7 @@ function createHandler(config) {
|
|
|
557
556
|
} catch {}
|
|
558
557
|
}
|
|
559
558
|
const appErr = normalizeError(err);
|
|
560
|
-
logDone(appErr.status);
|
|
559
|
+
logDone(appErr.status, appErr.code);
|
|
561
560
|
return json(appErr.toJSON(), appErr.status, cors, req);
|
|
562
561
|
};
|
|
563
562
|
if (cors && req.method === "OPTIONS") {
|
|
@@ -600,9 +599,9 @@ function createHandler(config) {
|
|
|
600
599
|
return respondError(new AppError("NOT_FOUND", "Not found", 404));
|
|
601
600
|
}
|
|
602
601
|
const { method, pathParams, groupHooks } = match;
|
|
603
|
-
|
|
602
|
+
const ctx = buildBaseContext(req, url, pathParams, traceId, clientIp);
|
|
604
603
|
try {
|
|
605
|
-
|
|
604
|
+
await parseRequestInto(ctx, req, url, method, config.maxUploadBytes);
|
|
606
605
|
if (hooks?.beforeHandle) {
|
|
607
606
|
await hooks.beforeHandle(ctx, method);
|
|
608
607
|
}
|
|
@@ -729,6 +728,7 @@ function implement(contract, handlers) {
|
|
|
729
728
|
outputSchema: endpoint.output,
|
|
730
729
|
multipart: endpoint.multipart,
|
|
731
730
|
maxUploadBytes: endpoint.maxUploadBytes,
|
|
731
|
+
idempotent: endpoint.idempotent,
|
|
732
732
|
ui: "ui" in endpoint ? endpoint.ui : undefined,
|
|
733
733
|
annotations: "annotations" in endpoint ? endpoint.annotations : undefined,
|
|
734
734
|
meta: endpoint.meta,
|
package/dist/index.d.ts
CHANGED
|
@@ -4,4 +4,5 @@ export type { SocketEventMap, SocketIOClient, SocketIOClientConfig, } from './br
|
|
|
4
4
|
export { createSocketIOClient } from './browser/socket-io';
|
|
5
5
|
export { type ParseSSEOptions, parseSSE } from './browser/stream';
|
|
6
6
|
export * from './contract';
|
|
7
|
+
export { createRetainedTopics, type RetainedTopics } from './retained';
|
|
7
8
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,oBAAoB,EACzB,YAAY,EACZ,aAAa,GACd,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,QAAQ,EACR,KAAK,QAAQ,EACb,KAAK,gBAAgB,EACrB,gBAAgB,EAChB,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,gBAAgB,EACrB,KAAK,cAAc,GACpB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,cAAc,EACd,cAAc,EACd,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,KAAK,eAAe,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAClE,cAAc,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,oBAAoB,EACzB,YAAY,EACZ,aAAa,GACd,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,QAAQ,EACR,KAAK,QAAQ,EACb,KAAK,gBAAgB,EACrB,gBAAgB,EAChB,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,gBAAgB,EACrB,KAAK,cAAc,GACpB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,cAAc,EACd,cAAc,EACd,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,KAAK,eAAe,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAClE,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -263,11 +263,11 @@ function createHttpMethod(endpoint, prefix, client, config) {
|
|
|
263
263
|
url = url.slice(0, -1);
|
|
264
264
|
if (endpoint.multipart) {
|
|
265
265
|
const file = firstArg[endpoint.multipart];
|
|
266
|
-
if (!(file
|
|
266
|
+
if (!isMultipartFile(file)) {
|
|
267
267
|
throw new Error(`Missing multipart file field: ${endpoint.multipart}`);
|
|
268
268
|
}
|
|
269
269
|
const formData = new FormData;
|
|
270
|
-
formData
|
|
270
|
+
appendMultipartFile(formData, endpoint.multipart, file);
|
|
271
271
|
appendFormFields(formData, firstArg, new Set([...prefixKeys, endpoint.multipart]));
|
|
272
272
|
return client.post(url, formData, withTimeout(undefined, endpoint.timeout));
|
|
273
273
|
}
|
|
@@ -324,11 +324,11 @@ function createFetchMethod(endpoint, prefix, config, contractConfig) {
|
|
|
324
324
|
headers["Content-Type"] = "application/json";
|
|
325
325
|
if (endpoint.multipart && args) {
|
|
326
326
|
const file = args[endpoint.multipart];
|
|
327
|
-
if (!(file
|
|
327
|
+
if (!isMultipartFile(file)) {
|
|
328
328
|
throw new Error(`Missing multipart file field: ${endpoint.multipart}`);
|
|
329
329
|
}
|
|
330
330
|
const formData = new FormData;
|
|
331
|
-
formData
|
|
331
|
+
appendMultipartFile(formData, endpoint.multipart, file);
|
|
332
332
|
appendFormFields(formData, stripParams(args, endpoint.path, prefixKeys), new Set([endpoint.multipart]));
|
|
333
333
|
const res2 = await fetch(url, {
|
|
334
334
|
method: endpoint.method,
|
|
@@ -361,6 +361,16 @@ function createFetchMethod(endpoint, prefix, config, contractConfig) {
|
|
|
361
361
|
return endpoint.output ? endpoint.output.parse(json) : json;
|
|
362
362
|
};
|
|
363
363
|
}
|
|
364
|
+
function isFileDescriptor(value) {
|
|
365
|
+
return typeof value === "object" && value !== null && !(value instanceof Blob) && "uri" in value && typeof value.uri === "string" && "name" in value && typeof value.name === "string" && "type" in value && typeof value.type === "string";
|
|
366
|
+
}
|
|
367
|
+
function isMultipartFile(value) {
|
|
368
|
+
return value instanceof Blob || isFileDescriptor(value);
|
|
369
|
+
}
|
|
370
|
+
function appendMultipartFile(form, field, file) {
|
|
371
|
+
const sink = form;
|
|
372
|
+
sink.append(field, file);
|
|
373
|
+
}
|
|
364
374
|
function appendFormFields(formData, values, skipKeys) {
|
|
365
375
|
for (const [key, value] of Object.entries(values)) {
|
|
366
376
|
if (skipKeys.has(key) || value === undefined || value === null)
|
|
@@ -409,6 +419,34 @@ function stripParams(args, path, extra) {
|
|
|
409
419
|
}
|
|
410
420
|
// src/browser/socket-io.ts
|
|
411
421
|
import { io } from "socket.io-client";
|
|
422
|
+
|
|
423
|
+
// src/retained.ts
|
|
424
|
+
function createRetainedTopics() {
|
|
425
|
+
const last = {};
|
|
426
|
+
return {
|
|
427
|
+
record(topic, payload) {
|
|
428
|
+
last[topic] = payload;
|
|
429
|
+
},
|
|
430
|
+
replay(topic, handler) {
|
|
431
|
+
const value = last[topic];
|
|
432
|
+
if (value !== undefined)
|
|
433
|
+
handler(value);
|
|
434
|
+
},
|
|
435
|
+
get(topic) {
|
|
436
|
+
return last[topic];
|
|
437
|
+
},
|
|
438
|
+
clear(topic) {
|
|
439
|
+
if (topic !== undefined) {
|
|
440
|
+
delete last[topic];
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
for (const key of Object.keys(last))
|
|
444
|
+
Reflect.deleteProperty(last, key);
|
|
445
|
+
}
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// src/browser/socket-io.ts
|
|
412
450
|
function toIoAuth(auth) {
|
|
413
451
|
if (typeof auth !== "function")
|
|
414
452
|
return auth;
|
|
@@ -420,6 +458,9 @@ function createSocketIOClient(config) {
|
|
|
420
458
|
let socket = null;
|
|
421
459
|
const connectionListeners = new Set;
|
|
422
460
|
const subscriptions = new Set;
|
|
461
|
+
const retainNames = config.retain ? config.retain.map(String) : [];
|
|
462
|
+
const retainSet = new Set(retainNames);
|
|
463
|
+
const retained = retainNames.length > 0 ? createRetainedTopics() : null;
|
|
423
464
|
function notifyConnection(connected) {
|
|
424
465
|
for (const listener of connectionListeners)
|
|
425
466
|
listener(connected);
|
|
@@ -447,6 +488,11 @@ function createSocketIOClient(config) {
|
|
|
447
488
|
});
|
|
448
489
|
socket.on("connect", () => notifyConnection(true));
|
|
449
490
|
socket.on("disconnect", () => notifyConnection(false));
|
|
491
|
+
if (retained) {
|
|
492
|
+
for (const name of retainNames) {
|
|
493
|
+
socket.on(name, (payload) => retained.record(name, payload));
|
|
494
|
+
}
|
|
495
|
+
}
|
|
450
496
|
for (const attach of subscriptions)
|
|
451
497
|
attach(socket);
|
|
452
498
|
socket.connect();
|
|
@@ -469,6 +515,10 @@ function createSocketIOClient(config) {
|
|
|
469
515
|
subscriptions.add(attach);
|
|
470
516
|
if (socket)
|
|
471
517
|
attach(socket);
|
|
518
|
+
if (retained && retainSet.has(name)) {
|
|
519
|
+
const fn = handler;
|
|
520
|
+
retained.replay(name, (payload) => fn(payload));
|
|
521
|
+
}
|
|
472
522
|
return () => {
|
|
473
523
|
subscriptions.delete(attach);
|
|
474
524
|
socket?.off(name, handler);
|
|
@@ -535,6 +585,7 @@ export {
|
|
|
535
585
|
defineContract,
|
|
536
586
|
decodeCursor,
|
|
537
587
|
createSocketIOClient,
|
|
588
|
+
createRetainedTopics,
|
|
538
589
|
createHttpClient,
|
|
539
590
|
createClients,
|
|
540
591
|
createClient,
|
package/dist/node.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/observability/audit.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,aAAa,EAAc,MAAM,kBAAkB,CAAC;AAElE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAe,KAAK,eAAe,EAAmB,MAAM,YAAY,CAAC;AAGhF,oCAAoC;AACpC,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,KAAK,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,iFAAiF;IACjF,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC;IAC1C,yEAAyE;IACzE,QAAQ,CAAC,EAAE,eAAe,CAAC;CAC5B;AAED,qEAAqE;AACrE,MAAM,WAAW,SAAS;IACxB;;;;OAIG;IACH,IAAI,EAAE,CAAC,CAAC,EACN,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,KACpD,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IACpD;;;OAGG;IACH,QAAQ,EAAE,aAAa,CAAC;CACzB;AAmBD,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/observability/audit.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,aAAa,EAAc,MAAM,kBAAkB,CAAC;AAElE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAe,KAAK,eAAe,EAAmB,MAAM,YAAY,CAAC;AAGhF,oCAAoC;AACpC,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,KAAK,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,iFAAiF;IACjF,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC;IAC1C,yEAAyE;IACzE,QAAQ,CAAC,EAAE,eAAe,CAAC;CAC5B;AAED,qEAAqE;AACrE,MAAM,WAAW,SAAS;IACxB;;;;OAIG;IACH,IAAI,EAAE,CAAC,CAAC,EACN,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,KACpD,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IACpD;;;OAGG;IACH,QAAQ,EAAE,aAAa,CAAC;CACzB;AAmBD,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,CAgG9D"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { TransportSource } from '../contract';
|
|
2
|
+
import type { JsonValue } from './sanitize';
|
|
2
3
|
import { type TraceContext } from './trace';
|
|
3
4
|
/** Everything known about the request in flight. */
|
|
4
5
|
export interface RequestContext {
|
|
@@ -22,6 +23,7 @@ export interface RequestContext {
|
|
|
22
23
|
error?: {
|
|
23
24
|
code?: string;
|
|
24
25
|
message?: string;
|
|
26
|
+
details?: JsonValue;
|
|
25
27
|
};
|
|
26
28
|
}
|
|
27
29
|
/** Run `fn` with `ctx` as the active request context. */
|
|
@@ -40,11 +42,14 @@ export declare function getUserId(): string | undefined;
|
|
|
40
42
|
export declare function setRequestUser(userId: string): void;
|
|
41
43
|
/**
|
|
42
44
|
* Record the error outcome on the active context. Call this from the error
|
|
43
|
-
* handler — the audit hook reads it when the request completes.
|
|
45
|
+
* handler — the audit hook reads it when the request completes. Optional
|
|
46
|
+
* `details` carries structure the message string flattens (e.g. the failing
|
|
47
|
+
* Zod issues) onto `RequestEvent.errorDetail`.
|
|
44
48
|
*/
|
|
45
49
|
export declare function setRequestError(error: {
|
|
46
50
|
code?: string;
|
|
47
51
|
message?: string;
|
|
52
|
+
details?: JsonValue;
|
|
48
53
|
}): void;
|
|
49
54
|
/** Options for `wrapInRequestContext`. */
|
|
50
55
|
export interface WrapRequestContextOptions {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/observability/context.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,SAAS,CAAC;AAEjE,oDAAoD;AACpD,MAAM,WAAW,cAAc;IAC7B,sCAAsC;IACtC,KAAK,EAAE,YAAY,CAAC;IACpB,4CAA4C;IAC5C,MAAM,EAAE,eAAe,CAAC;IACxB,iBAAiB;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,oBAAoB;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAC;IAClB,kCAAkC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sDAAsD;IACtD,KAAK,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/observability/context.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,SAAS,CAAC;AAEjE,oDAAoD;AACpD,MAAM,WAAW,cAAc;IAC7B,sCAAsC;IACtC,KAAK,EAAE,YAAY,CAAC;IACpB,4CAA4C;IAC5C,MAAM,EAAE,eAAe,CAAC;IACxB,iBAAiB;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,oBAAoB;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAC;IAClB,kCAAkC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sDAAsD;IACtD,KAAK,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,SAAS,CAAA;KAAE,CAAC;CAClE;AAID,yDAAyD;AACzD,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,GAAG,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAE5E;AAED,sEAAsE;AACtE,wBAAgB,iBAAiB,IAAI,cAAc,GAAG,SAAS,CAE9D;AAED,mEAAmE;AACnE,wBAAgB,UAAU,IAAI,MAAM,GAAG,SAAS,CAE/C;AAED,qDAAqD;AACrD,wBAAgB,SAAS,IAAI,MAAM,GAAG,SAAS,CAE9C;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAGnD;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,SAAS,CAAC;CACrB,GAAG,IAAI,CAGP;AAED,0CAA0C;AAC1C,MAAM,WAAW,yBAAyB;IACxC;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EACpC,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,EACvD,OAAO,GAAE,yBAA8B,GACtC,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,CAiBhD"}
|
|
@@ -31,6 +31,12 @@ export interface RequestEvent {
|
|
|
31
31
|
errorCode?: string;
|
|
32
32
|
/** Error message — failures only. */
|
|
33
33
|
errorMessage?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Structured error detail — failures only, when the error handler recorded it
|
|
36
|
+
* via `setRequestError({ details })` (e.g. the failing validation issues the
|
|
37
|
+
* `errorMessage` string flattens). HTTP path only.
|
|
38
|
+
*/
|
|
39
|
+
errorDetail?: JsonValue;
|
|
34
40
|
/** Sanitised request payload — the HTTP body or the tool arguments. */
|
|
35
41
|
payload: JsonValue | null;
|
|
36
42
|
/** Item count of the result, when it is a list. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event.d.ts","sourceRoot":"","sources":["../../src/observability/event.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,mCAAmC;IACnC,MAAM,EAAE,eAAe,CAAC;IACxB,4CAA4C;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,OAAO,EAAE,MAAM,CAAC;IAChB,yCAAyC;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,8DAA8D;IAC9D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kCAAkC;IAClC,EAAE,EAAE,OAAO,CAAC;IACZ,6EAA6E;IAC7E,UAAU,EAAE,MAAM,CAAC;IACnB,2BAA2B;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,kCAAkC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qCAAqC;IACrC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uEAAuE;IACvE,OAAO,EAAE,SAAS,GAAG,IAAI,CAAC;IAC1B,mDAAmD;IACnD,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,4CAA4C;IAC5C,aAAa,EAAE,MAAM,CAAC;IACtB,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iBAAiB;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yBAAyB;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6BAA6B;IAC7B,SAAS,EAAE,IAAI,CAAC;CACjB"}
|
|
1
|
+
{"version":3,"file":"event.d.ts","sourceRoot":"","sources":["../../src/observability/event.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,mCAAmC;IACnC,MAAM,EAAE,eAAe,CAAC;IACxB,4CAA4C;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,OAAO,EAAE,MAAM,CAAC;IAChB,yCAAyC;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,8DAA8D;IAC9D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kCAAkC;IAClC,EAAE,EAAE,OAAO,CAAC;IACZ,6EAA6E;IAC7E,UAAU,EAAE,MAAM,CAAC;IACnB,2BAA2B;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,kCAAkC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qCAAqC;IACrC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,uEAAuE;IACvE,OAAO,EAAE,SAAS,GAAG,IAAI,CAAC;IAC1B,mDAAmD;IACnD,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,4CAA4C;IAC5C,aAAa,EAAE,MAAM,CAAC;IACtB,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iBAAiB;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yBAAyB;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6BAA6B;IAC7B,SAAS,EAAE,IAAI,CAAC;CACjB"}
|
|
@@ -148,6 +148,7 @@ function createAuditHook(config) {
|
|
|
148
148
|
durationMs,
|
|
149
149
|
errorCode: ctx.error?.code,
|
|
150
150
|
errorMessage: ctx.error?.message,
|
|
151
|
+
...ctx.error?.details !== undefined && { errorDetail: ctx.error.details },
|
|
151
152
|
payload: sanitizePayload(body, sanitize),
|
|
152
153
|
resultSize: null,
|
|
153
154
|
responseBytes: 0,
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retained-last-value memory for pub/sub topics — "sticky events".
|
|
3
|
+
*
|
|
4
|
+
* A subscriber that connects (or a component that mounts / re-renders) *after* an
|
|
5
|
+
* event was published would otherwise miss it and show stale state until the next
|
|
6
|
+
* publish. Record each topic's last payload and replay it to a fresh subscriber,
|
|
7
|
+
* so a late one catches up at once — the pub/sub analogue of MQTT's "retained"
|
|
8
|
+
* message or an RxJS `BehaviorSubject`.
|
|
9
|
+
*
|
|
10
|
+
* Transport-agnostic: wrap it around any pub/sub. `createSocketIOClient`'s
|
|
11
|
+
* `retain` option uses it internally, and a bring-your-own-transport lane (a raw
|
|
12
|
+
* WebSocket driving a contract through the app's own dispatch loop) can use it
|
|
13
|
+
* directly for its own event channel. Browser-safe — no Node built-ins.
|
|
14
|
+
*/
|
|
15
|
+
export interface RetainedTopics<Events extends Record<string, unknown>> {
|
|
16
|
+
/** Record a topic's latest payload — call on every publish / receive. */
|
|
17
|
+
record<K extends keyof Events & string>(topic: K, payload: Events[K]): void;
|
|
18
|
+
/** Replay the retained payload, if any, to a just-subscribed handler. */
|
|
19
|
+
replay<K extends keyof Events & string>(topic: K, handler: (payload: Events[K]) => void): void;
|
|
20
|
+
/** The retained payload for a topic, or `undefined` if none recorded yet. */
|
|
21
|
+
get<K extends keyof Events & string>(topic: K): Events[K] | undefined;
|
|
22
|
+
/** Forget a topic's retained value, or every topic when `topic` is omitted. */
|
|
23
|
+
clear(topic?: keyof Events & string): void;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Create a {@link RetainedTopics} store. `Events` maps each topic name to its
|
|
27
|
+
* payload type, so `record` / `replay` / `get` are typed per topic.
|
|
28
|
+
*/
|
|
29
|
+
export declare function createRetainedTopics<Events extends Record<string, unknown>>(): RetainedTopics<Events>;
|
|
30
|
+
//# sourceMappingURL=retained.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retained.d.ts","sourceRoot":"","sources":["../src/retained.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,cAAc,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACpE,yEAAyE;IACzE,MAAM,CAAC,CAAC,SAAS,MAAM,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAC5E,yEAAyE;IACzE,MAAM,CAAC,CAAC,SAAS,MAAM,MAAM,GAAG,MAAM,EACpC,KAAK,EAAE,CAAC,EACR,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,GACpC,IAAI,CAAC;IACR,6EAA6E;IAC7E,GAAG,CAAC,CAAC,SAAS,MAAM,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IACtE,+EAA+E;IAC/E,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;CAC5C;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KACnC,cAAc,CAAC,MAAM,CAAC,CAqB1B"}
|
package/dist/server/context.d.ts
CHANGED
|
@@ -5,6 +5,23 @@
|
|
|
5
5
|
import { type RuntimeContext } from '../contract';
|
|
6
6
|
import { type ClientIpOptions } from './request';
|
|
7
7
|
import type { MethodDef } from './types';
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Assemble everything knowable from the URL alone — path params, the request,
|
|
10
|
+
* trace id and client info — **before** any schema parsing. Bound first so a
|
|
11
|
+
* later validation failure still hands `onError` the path params and the
|
|
12
|
+
* request (an empty context loses both); `parseRequestInto` then enriches it
|
|
13
|
+
* with the schema-validated `params` / `input`.
|
|
14
|
+
*
|
|
15
|
+
* `params` starts as the raw matched path params (a property of the URL, known
|
|
16
|
+
* the moment the route matched) and is replaced by the validated value when the
|
|
17
|
+
* endpoint declares a `paramsSchema`.
|
|
18
|
+
*/
|
|
19
|
+
export declare function buildBaseContext(req: Request, url: URL, pathParams: Record<string, string>, traceId: string, clientIp: ClientIpOptions): RuntimeContext;
|
|
20
|
+
/**
|
|
21
|
+
* Parse `params` / `input` against the endpoint schemas and write them onto an
|
|
22
|
+
* already-assembled base context. A validation failure throws a `ZodError`
|
|
23
|
+
* here — the base context (path params, request) is preserved for `onError`.
|
|
24
|
+
*/
|
|
25
|
+
export declare function parseRequestInto(ctx: RuntimeContext, req: Request, url: URL, method: MethodDef, maxUploadBytes?: number): Promise<void>;
|
|
9
26
|
export declare function buildErrorContext(req: Request, url: URL, traceId: string, clientIp: ClientIpOptions): RuntimeContext;
|
|
10
27
|
//# sourceMappingURL=context.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/server/context.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAG9D,OAAO,EAAE,KAAK,eAAe,EAAmC,MAAM,WAAW,CAAC;AAClF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAmCzC,
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/server/context.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAG9D,OAAO,EAAE,KAAK,eAAe,EAAmC,MAAM,WAAW,CAAC;AAClF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAmCzC;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,GAAG,EACR,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAClC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,eAAe,GACxB,cAAc,CAmBhB;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,cAAc,EACnB,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,SAAS,EACjB,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,IAAI,CAAC,CAwBf;AAED,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,GAAG,EACR,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,eAAe,GACxB,cAAc,CAWhB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/server/create.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAEV,eAAe,EACf,aAAa,EAGd,MAAM,SAAS,CAAC;AAEjB,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/server/create.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAEV,eAAe,EACf,aAAa,EAGd,MAAM,SAAS,CAAC;AAEjB,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAkNxF;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,eAAe,uBAsBnD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"implement.d.ts","sourceRoot":"","sources":["../../src/server/implement.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE5E,OAAO,KAAK,EAAE,QAAQ,EAAa,UAAU,EAAE,MAAM,SAAS,CAAC;AAE/D;;;;;GAKG;AACH,wBAAgB,SAAS,CACvB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACrC,IAAI,SAAS,cAAc,GAAG,cAAc,EAC5C,QAAQ,EAAE,WAAW,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,UAAU,
|
|
1
|
+
{"version":3,"file":"implement.d.ts","sourceRoot":"","sources":["../../src/server/implement.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE5E,OAAO,KAAK,EAAE,QAAQ,EAAa,UAAU,EAAE,MAAM,SAAS,CAAC;AAE/D;;;;;GAKG;AACH,wBAAgB,SAAS,CACvB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACrC,IAAI,SAAS,cAAc,GAAG,cAAc,EAC5C,QAAQ,EAAE,WAAW,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,UAAU,CA+C3E;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,SAAS,cAAc,MACjD,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EAC3C,UAAU,WAAW,CAAC,CAAC,EAAE,MAAM,CAAC,EAChC,UAAU,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,KAC1B,UAAU,CACd"}
|
package/dist/server/index.js
CHANGED
package/dist/server/logger.d.ts
CHANGED
|
@@ -3,12 +3,13 @@ export declare function elapsedMs(startTime: number): number;
|
|
|
3
3
|
/** Map an HTTP status to a log level. */
|
|
4
4
|
export declare function levelForStatus(status: number): 'error' | 'warn' | 'info';
|
|
5
5
|
/** The structured fields shared by every completed-request log line. */
|
|
6
|
-
export declare function buildLogFields(method: string, path: string, status: number, durationMs: number, traceId: string): {
|
|
6
|
+
export declare function buildLogFields(method: string, path: string, status: number, durationMs: number, traceId: string, errorCode?: string): {
|
|
7
7
|
traceId: string;
|
|
8
8
|
method: string;
|
|
9
9
|
path: string;
|
|
10
10
|
status: number;
|
|
11
11
|
durationMs: number;
|
|
12
|
+
errorCode?: string;
|
|
12
13
|
};
|
|
13
14
|
export interface RequestLog {
|
|
14
15
|
traceId: string;
|
|
@@ -18,5 +19,5 @@ export declare function shouldLog(pathname: string, method: string): boolean;
|
|
|
18
19
|
/** Open the timing window for a request. Development prints a `→` line. */
|
|
19
20
|
export declare function logIncoming(req: Request, pathname: string, traceId: string, ipAddress?: string): RequestLog;
|
|
20
21
|
/** Close a request. Development: `←` line. Production: one structured JSON line. */
|
|
21
|
-
export declare function logOutgoing(req: Request, pathname: string, status: number, log: RequestLog, ipAddress?: string): void;
|
|
22
|
+
export declare function logOutgoing(req: Request, pathname: string, status: number, log: RequestLog, ipAddress?: string, errorCode?: string): void;
|
|
22
23
|
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/server/logger.ts"],"names":[],"mappings":"AAwCA,6DAA6D;AAC7D,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEnD;AAoBD,yCAAyC;AACzC,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAIxE;AAED,wEAAwE;AACxE,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/server/logger.ts"],"names":[],"mappings":"AAwCA,6DAA6D;AAC7D,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEnD;AAoBD,yCAAyC;AACzC,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAIxE;AAED,wEAAwE;AACxE,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,SAAS,CAAC,EAAE,MAAM,GACjB;IACD,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAEA;AA0BD,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAID,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAGnE;AAED,2EAA2E;AAC3E,wBAAgB,WAAW,CACzB,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,SAAS,CAAC,EAAE,MAAM,GACjB,UAAU,CASZ;AAED,oFAAoF;AACpF,wBAAgB,WAAW,CACzB,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,UAAU,EACf,SAAS,CAAC,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,GACjB,IAAI,CA4BN"}
|
package/dist/server/types.d.ts
CHANGED
|
@@ -37,6 +37,13 @@ export interface MethodDef<TParams = unknown, TInput = unknown, TOutput = unknow
|
|
|
37
37
|
/** Per-route upload ceiling (bytes) for a multipart endpoint — overrides the
|
|
38
38
|
* server `maxUploadBytes` default; from `EndpointDef.maxUploadBytes`. */
|
|
39
39
|
maxUploadBytes?: number;
|
|
40
|
+
/**
|
|
41
|
+
* Whether the operation is safe to call twice with the same input — from
|
|
42
|
+
* `EndpointDef.idempotent`. The core attaches no behaviour; a retrying
|
|
43
|
+
* transport reads it to decide whether to replay a call after a reconnect.
|
|
44
|
+
* → ADR 0027.
|
|
45
|
+
*/
|
|
46
|
+
idempotent?: boolean;
|
|
40
47
|
/** MCP Apps widget metadata — carried onto the MCP tool's `_meta.ui`. */
|
|
41
48
|
ui?: EndpointUiMeta;
|
|
42
49
|
/** MCP behavioural hints — carried onto the MCP tool's `annotations`. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/server/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AACnC,OAAO,KAAK,EACV,WAAW,EACX,uBAAuB,EACvB,cAAc,EACd,cAAc,EACd,UAAU,EACV,cAAc,EACd,SAAS,EACV,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,KAAK,IAAI,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,IAAI,CAAC,SAAS,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;AACtE,KAAK,WAAW,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,EAAE,QAAQ,CAAC,SAAS,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;AACjF,KAAK,UAAU,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,EAAE,OAAO,CAAC,SAAS,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;AAC/E,KAAK,WAAW,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,EAAE,QAAQ,CAAC,SAAS,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;AAE7E,MAAM,MAAM,QAAQ,CAClB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACrC,IAAI,SAAS,cAAc,GAAG,cAAc,IAC1C;KACD,CAAC,IAAI,MAAM,CAAC,GAAG,CACd,GAAG,EAAE,IAAI,GAAG;QAAE,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;KAAE,KAC/D,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,SAAS,OAAO,GACrC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAC9C,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;CACzB,CAAC;AAEF,MAAM,WAAW,SAAS,CAAC,OAAO,GAAG,OAAO,EAAE,MAAM,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO;IAC/E,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,SAAS,SAAS,EAAE,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAChC,WAAW,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9B,YAAY,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;8EAC0E;IAC1E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,yEAAyE;IACzE,EAAE,CAAC,EAAE,cAAc,CAAC;IACpB,yEAAyE;IACzE,WAAW,CAAC,EAAE,uBAAuB,CAAC;IACtC;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,OAAO,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;CAC9D;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;CAC/D;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC,SAAS,GAAG,QAAQ,CAAC,CAAC;IACnF,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,EAAE,QAAQ,EAAE,SAAS,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClF,WAAW,CAAC,EAAE,CACZ,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,OAAO,EACf,QAAQ,EAAE,SAAS,KAChB,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAChC,OAAO,CAAC,EAAE,CACR,GAAG,EAAE,cAAc,EACnB,KAAK,EAAE,OAAO,EACd,QAAQ,CAAC,EAAE,SAAS,KACjB,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,SAAS,CAAC;CAC/C;AAED,MAAM,WAAW,UAAU;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,KAAK,CAAC,EAAE,cAAc,CAAC;CACxB;AAED;;;;;;;;GAQG;AACH,gFAAgF;AAChF,MAAM,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC;AAErD,0EAA0E;AAC1E,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B;;;OAGG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,UAAU,GAAG,KAAK,CAAC;IAC3B;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,eAAe,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;CAC/E;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxD,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxD,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACzD,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC1D;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,UAAU,EAAE,CAAC;IACxB,MAAM,CAAC,EAAE,UAAU,EAAE,CAAC;IACtB;;;;;;;;;OASG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC;IACvB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,GAAG,YAAY,CAAC;IACjC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,MAAM,CAAC;IACnC;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAID,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AACvD,KAAK,oBAAoB,GAAG,eAAe,SAAS;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC,CAAA;CAAE,GAAG,CAAC,GAAG,KAAK,CAAC;AACxF,KAAK,SAAS,GAAG,eAAe,SAAS;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC,CAAA;CAAE,GAAG,CAAC,GAAG,KAAK,CAAC;AAC1E,KAAK,qBAAqB,GAAG,eAAe,SAAS;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC,CAAA;CAAE,GAAG,CAAC,GAAG,KAAK,CAAC;AAE3F,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAClC,eAAe,EACf,OAAO,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,WAAW,GAAG,aAAa,CAChF,CAAC;AAEF;;;GAGG;AACH,MAAM,WAAW,eAAgB,SAAQ,aAAa;IACpD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,SAAS,CAAC,EAAE,oBAAoB,CAAC;IACjC,WAAW,CAAC,EAAE,qBAAqB,CAAC;IACpC,GAAG,CAAC,EAAE,iBAAiB,CAAC;CACzB;AAED,MAAM,MAAM,kBAAkB,GAAG,iBAAiB,GAAG;IACnD,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,SAAS,CAAC,EAAE,oBAAoB,CAAC;IACjC,WAAW,CAAC,EAAE,qBAAqB,CAAC;IACpC,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;CACxC,CAAC"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/server/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote.d.ts","sourceRoot":"","sources":["../../src/tools/remote.ts"],"names":[],"mappings":"AACA,OAAO,EAAY,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,WAAW,EAEjB,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAa,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAc7D,MAAM,WAAW,sBAAsB;IACrC;;;;OAIG;IACH,aAAa,CAAC,EAAE,CACd,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC1B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACjE;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACnE,QAAQ,EAAE,WAAW,CAAC,CAAC,EAAE,MAAM,CAAC,EAChC,IAAI,EAAE,UAAU,EAChB,OAAO,CAAC,EAAE,sBAAsB,GAC/B,UAAU,
|
|
1
|
+
{"version":3,"file":"remote.d.ts","sourceRoot":"","sources":["../../src/tools/remote.ts"],"names":[],"mappings":"AACA,OAAO,EAAY,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,WAAW,EAEjB,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAa,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAc7D,MAAM,WAAW,sBAAsB;IACrC;;;;OAIG;IACH,aAAa,CAAC,EAAE,CACd,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC1B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACjE;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACnE,QAAQ,EAAE,WAAW,CAAC,CAAC,EAAE,MAAM,CAAC,EAChC,IAAI,EAAE,UAAU,EAChB,OAAO,CAAC,EAAE,sBAAsB,GAC/B,UAAU,CAqEZ"}
|
package/dist/tools.js
CHANGED
|
@@ -923,11 +923,11 @@ function createHttpMethod(endpoint, prefix, client, config) {
|
|
|
923
923
|
url = url.slice(0, -1);
|
|
924
924
|
if (endpoint.multipart) {
|
|
925
925
|
const file = firstArg[endpoint.multipart];
|
|
926
|
-
if (!(file
|
|
926
|
+
if (!isMultipartFile(file)) {
|
|
927
927
|
throw new Error(`Missing multipart file field: ${endpoint.multipart}`);
|
|
928
928
|
}
|
|
929
929
|
const formData = new FormData;
|
|
930
|
-
formData
|
|
930
|
+
appendMultipartFile(formData, endpoint.multipart, file);
|
|
931
931
|
appendFormFields(formData, firstArg, new Set([...prefixKeys, endpoint.multipart]));
|
|
932
932
|
return client.post(url, formData, withTimeout(undefined, endpoint.timeout));
|
|
933
933
|
}
|
|
@@ -984,11 +984,11 @@ function createFetchMethod(endpoint, prefix, config, contractConfig) {
|
|
|
984
984
|
headers["Content-Type"] = "application/json";
|
|
985
985
|
if (endpoint.multipart && args) {
|
|
986
986
|
const file = args[endpoint.multipart];
|
|
987
|
-
if (!(file
|
|
987
|
+
if (!isMultipartFile(file)) {
|
|
988
988
|
throw new Error(`Missing multipart file field: ${endpoint.multipart}`);
|
|
989
989
|
}
|
|
990
990
|
const formData = new FormData;
|
|
991
|
-
formData
|
|
991
|
+
appendMultipartFile(formData, endpoint.multipart, file);
|
|
992
992
|
appendFormFields(formData, stripParams(args, endpoint.path, prefixKeys), new Set([endpoint.multipart]));
|
|
993
993
|
const res2 = await fetch(url, {
|
|
994
994
|
method: endpoint.method,
|
|
@@ -1021,6 +1021,16 @@ function createFetchMethod(endpoint, prefix, config, contractConfig) {
|
|
|
1021
1021
|
return endpoint.output ? endpoint.output.parse(json2) : json2;
|
|
1022
1022
|
};
|
|
1023
1023
|
}
|
|
1024
|
+
function isFileDescriptor(value) {
|
|
1025
|
+
return typeof value === "object" && value !== null && !(value instanceof Blob) && "uri" in value && typeof value.uri === "string" && "name" in value && typeof value.name === "string" && "type" in value && typeof value.type === "string";
|
|
1026
|
+
}
|
|
1027
|
+
function isMultipartFile(value) {
|
|
1028
|
+
return value instanceof Blob || isFileDescriptor(value);
|
|
1029
|
+
}
|
|
1030
|
+
function appendMultipartFile(form, field, file) {
|
|
1031
|
+
const sink = form;
|
|
1032
|
+
sink.append(field, file);
|
|
1033
|
+
}
|
|
1024
1034
|
function appendFormFields(formData, values, skipKeys) {
|
|
1025
1035
|
for (const [key, value] of Object.entries(values)) {
|
|
1026
1036
|
if (skipKeys.has(key) || value === undefined || value === null)
|
|
@@ -1097,6 +1107,7 @@ function implementRemote(contract, http, options) {
|
|
|
1097
1107
|
inputSchema: endpoint.input,
|
|
1098
1108
|
outputSchema: endpoint.output,
|
|
1099
1109
|
multipart: endpoint.multipart,
|
|
1110
|
+
idempotent: endpoint.idempotent,
|
|
1100
1111
|
handler: async (ctx) => {
|
|
1101
1112
|
const call = client[key];
|
|
1102
1113
|
if (!call) {
|
package/llms-full.txt
CHANGED
|
@@ -236,6 +236,7 @@ export const users = defineContract({ prefix: 'users' }, {
|
|
|
236
236
|
| `toolName` | no | explicit MCP / agent tool name (defaults to `prefix_key`) |
|
|
237
237
|
| `multipart` | no | field name of a file upload — see [below](#file-uploads) |
|
|
238
238
|
| `timeout` | no | per-endpoint client timeout in ms, for slow endpoints |
|
|
239
|
+
| `idempotent` | no | safe to call twice with the same input (like `PUT`/`DELETE`); a retrying transport reads it — see [Realtime](./realtime.md#bring-your-own-transport) |
|
|
239
240
|
| `meta` | no | opaque app metadata — read in hooks / on tool mounts, never in OpenAPI ([below](#endpoint-metadata-meta)) |
|
|
240
241
|
|
|
241
242
|
## `params` vs `input` vs `output`
|
|
@@ -861,7 +862,10 @@ contract:
|
|
|
861
862
|
- for `GET` / `DELETE`, the remaining fields become the **query string**
|
|
862
863
|
(arrays become repeated keys),
|
|
863
864
|
- for `POST` / `PUT` / `PATCH`, they become the **JSON body**,
|
|
864
|
-
- a `multipart` field
|
|
865
|
+
- a `multipart` field is a `Blob` (web / Bun) or a platform `FileDescriptor`
|
|
866
|
+
(`{ uri, name, type }`, for React Native / Expo) and is sent as `form-data`.
|
|
867
|
+
The exported `MultipartFile` / `FileDescriptor` types let you annotate your own
|
|
868
|
+
upload helpers.
|
|
865
869
|
|
|
866
870
|
### Many contracts at once
|
|
867
871
|
|
|
@@ -1668,7 +1672,41 @@ subscribe once; reconnection is the wrapper's problem, not yours.
|
|
|
1668
1672
|
|
|
1669
1673
|
`SocketIOClientConfig` takes `url`, `path`, `withCredentials` (cookies on the
|
|
1670
1674
|
handshake — default `true`), `auth`, `query`, `extraHeaders`, `transports`,
|
|
1671
|
-
`reconnectionAttempts` and `
|
|
1675
|
+
`reconnectionAttempts`, `reconnectionDelay` and `retain` (below).
|
|
1676
|
+
|
|
1677
|
+
### Sticky events
|
|
1678
|
+
|
|
1679
|
+
A handler that subscribes **after** an event was emitted misses it — the UI stays
|
|
1680
|
+
on stale state until the next emission. List those events in **`retain`** and the
|
|
1681
|
+
client keeps each one's last payload and replays it to a handler the moment it
|
|
1682
|
+
subscribes (and on the next subscribe after a re-render). It is the pub/sub
|
|
1683
|
+
analogue of an MQTT *retained* message or an RxJS `BehaviorSubject`.
|
|
1684
|
+
|
|
1685
|
+
```ts
|
|
1686
|
+
const client = createSocketIOClient<ServerEvents, ClientEvents>({
|
|
1687
|
+
url,
|
|
1688
|
+
retain: ['presence:changed', 'job:state'], // events to keep the last value of
|
|
1689
|
+
})
|
|
1690
|
+
client.connect()
|
|
1691
|
+
|
|
1692
|
+
// Later — even after the event already fired — this handler fires at once with
|
|
1693
|
+
// the last value, then on every future change:
|
|
1694
|
+
client.on('job:state', (s) => render(s))
|
|
1695
|
+
```
|
|
1696
|
+
|
|
1697
|
+
The retained value survives a `disconnect()` / `connect()` cycle (the store lives
|
|
1698
|
+
outside the socket). Only an event's **first** argument is retained.
|
|
1699
|
+
|
|
1700
|
+
For a pub/sub channel that is **not** Socket.IO (your own transport — see
|
|
1701
|
+
[below](#bring-your-own-transport)), use the same memory directly:
|
|
1702
|
+
|
|
1703
|
+
```ts
|
|
1704
|
+
import { createRetainedTopics } from 'stitchkit'
|
|
1705
|
+
|
|
1706
|
+
const topics = createRetainedTopics<{ 'job:state': JobState }>()
|
|
1707
|
+
topics.record('job:state', state) // on every publish
|
|
1708
|
+
topics.replay('job:state', (s) => render(s)) // for a late subscriber
|
|
1709
|
+
```
|
|
1672
1710
|
|
|
1673
1711
|
### Handshake auth — cookie or token
|
|
1674
1712
|
|
|
@@ -1834,6 +1872,53 @@ Notes:
|
|
|
1834
1872
|
- For high throughput, handle backpressure in the raw lane: `ws.send()` returns
|
|
1835
1873
|
`-1` under pressure; resume on the `drain` callback.
|
|
1836
1874
|
|
|
1875
|
+
## Bring-your-own transport
|
|
1876
|
+
|
|
1877
|
+
Sometimes the transport is neither HTTP nor Socket.IO — a desktop app whose UI
|
|
1878
|
+
webview talks to its own local Bun sidecar over a raw WebSocket, an IPC channel,
|
|
1879
|
+
a queue worker. You can still drive it from one `defineContract`: share the
|
|
1880
|
+
contract's Zod schemas to validate each inbound frame, run your handlers, and
|
|
1881
|
+
reuse the contract metadata below. You own **both** the wire (framing,
|
|
1882
|
+
handshake, reconnect) *and* the per-call validate-run loop — stitchkit ships the
|
|
1883
|
+
contract and its metadata, not a transport engine. A reliable-RPC-over-raw-
|
|
1884
|
+
WebSocket engine would be a competing WebSocket transport
|
|
1885
|
+
([ADR 0008](../decisions/0008-thin-wrappers.md)); the wire stays yours.
|
|
1886
|
+
|
|
1887
|
+
Two pieces of the contract carry straight over to a bring-your-own lane:
|
|
1888
|
+
|
|
1889
|
+
- **`source` is an open tag.** `TransportSource` is `'http' | 'mcp' | 'agent' |
|
|
1890
|
+
'cli' | (string & {})`, so a handler or hook can tell your transport's calls
|
|
1891
|
+
apart — tag them `source: 'local-ws'` and read `ctx.source`.
|
|
1892
|
+
- **`idempotent` drives replay-on-reconnect** — see below.
|
|
1893
|
+
|
|
1894
|
+
### Durability — `idempotent` + replay
|
|
1895
|
+
|
|
1896
|
+
If the sidecar can restart mid-call, the client decides what to do with an
|
|
1897
|
+
in-flight request on reconnect from the operation's **idempotency**, declared on
|
|
1898
|
+
the contract:
|
|
1899
|
+
|
|
1900
|
+
```ts
|
|
1901
|
+
export const runtimeContract = defineContract({ prefix: 'runtime' }, {
|
|
1902
|
+
'tasks.setDone': { method: 'POST', path: '/done', desc: '…',
|
|
1903
|
+
idempotent: true, input: taskDone, output: ok }, // re-send after reconnect — same result
|
|
1904
|
+
'capture.start': { method: 'POST', path: '/start', desc: '…',
|
|
1905
|
+
output: snapshot }, // unset → one-shot, do not re-send
|
|
1906
|
+
})
|
|
1907
|
+
```
|
|
1908
|
+
|
|
1909
|
+
`idempotent` rides through to `MethodDef.idempotent`; the core attaches no
|
|
1910
|
+
behaviour. Your reconnect logic reads it: replay an idempotent call (the
|
|
1911
|
+
durability guarantee — the user's action is not lost), reject a non-idempotent
|
|
1912
|
+
one rather than fire a second side effect. Pair it with
|
|
1913
|
+
[sticky events](#sticky-events) (`createRetainedTopics`) so a reconnected client
|
|
1914
|
+
also catches up on the latest pushed state.
|
|
1915
|
+
|
|
1916
|
+
This is deliberately *not* a reliable-RPC engine — that would be a competing
|
|
1917
|
+
WebSocket transport ([ADR 0008](../decisions/0008-thin-wrappers.md)). stitchkit
|
|
1918
|
+
gives you the contract and the metadata (`idempotent`, the open `source` tag,
|
|
1919
|
+
`createRetainedTopics`); the wire and the per-call execution stay yours. See
|
|
1920
|
+
[ADR 0028](../decisions/0028-revert-contract-dispatcher.md).
|
|
1921
|
+
|
|
1837
1922
|
|
|
1838
1923
|
==============================================================================
|
|
1839
1924
|
# Guide: Auth & errors (docs/guide/auth-and-errors.md)
|
|
@@ -2718,10 +2803,12 @@ The browser-and-server entrypoint. Re-exports everything from
|
|
|
2718
2803
|
| Export | Kind | Summary |
|
|
2719
2804
|
|--------|------|---------|
|
|
2720
2805
|
| `createSocketIOClient` | function | the typed Socket.IO client — [guide](../guide/realtime.md#client--createsocketioclient) |
|
|
2806
|
+
| `createRetainedTopics` | function | retained last-value store for sticky events — [guide](../guide/realtime.md#sticky-events) |
|
|
2721
2807
|
| `parseSSE` | function | parse an SSE `Response` into an async generator — [guide](../guide/client.md#sse) |
|
|
2722
2808
|
| `SocketIOClient` | _type_ | the client handle |
|
|
2723
|
-
| `SocketIOClientConfig` | _type_ | config for `createSocketIOClient` |
|
|
2809
|
+
| `SocketIOClientConfig` | _type_ | config for `createSocketIOClient` (incl. `retain`) |
|
|
2724
2810
|
| `SocketEventMap` | _type_ | the shape of an event map |
|
|
2811
|
+
| `RetainedTopics` | _type_ | the `createRetainedTopics` handle |
|
|
2725
2812
|
| `ParseSSEOptions` | _type_ | options for `parseSSE` |
|
|
2726
2813
|
|
|
2727
2814
|
---
|
|
@@ -2750,6 +2837,8 @@ from the root `stitchkit`.
|
|
|
2750
2837
|
| `TypedHttpClient` | _type_ | the typed client, HTTP endpoints only (`= ScopedHttpClient<C, unknown>`) |
|
|
2751
2838
|
| `ScopedHttpClient` | _type_ | a client whose `stripPrefixKeys` become required args ([guide](../guide/multi-tenant.md)) |
|
|
2752
2839
|
| `ScopedEndpointFn` | _type_ | one method's signature with the consumed keys folded in |
|
|
2840
|
+
| `MultipartFile` | _type_ | a `multipart` file field — `Blob \| FileDescriptor` |
|
|
2841
|
+
| `FileDescriptor` | _type_ | a React Native / Expo file — `{ uri, name, type }` |
|
|
2753
2842
|
|
|
2754
2843
|
### Errors
|
|
2755
2844
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stitchkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Contract-first backend framework — one defineContract() into an HTTP API, MCP tools, AI-agent tools and a typed client. Bun and Node.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bun",
|
|
@@ -155,8 +155,8 @@
|
|
|
155
155
|
"@tanstack/react-query": "5.101.0",
|
|
156
156
|
"@types/bun": "^1.3.14",
|
|
157
157
|
"@types/json-schema": "^7.0.15",
|
|
158
|
-
"@types/react": "^19.2.
|
|
159
|
-
"ai": "^6.0.
|
|
158
|
+
"@types/react": "^19.2.17",
|
|
159
|
+
"ai": "^6.0.207",
|
|
160
160
|
"react": "^19.2.7",
|
|
161
161
|
"react-query-kit": "^3.3.4",
|
|
162
162
|
"socket.io": "^4.8.3",
|