stitchkit 0.0.1 → 0.1.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 +373 -4
- package/dist/browser/client.d.ts +30 -0
- package/dist/browser/client.d.ts.map +1 -0
- package/dist/browser/http.d.ts +77 -0
- package/dist/browser/http.d.ts.map +1 -0
- package/dist/browser/socket-io.d.ts +69 -0
- package/dist/browser/socket-io.d.ts.map +1 -0
- package/dist/browser/stream.d.ts +2 -0
- package/dist/browser/stream.d.ts.map +1 -0
- package/dist/contract/define.d.ts +94 -0
- package/dist/contract/define.d.ts.map +1 -0
- package/dist/contract/errors.d.ts +46 -0
- package/dist/contract/errors.d.ts.map +1 -0
- package/dist/contract/index.d.ts +4 -0
- package/dist/contract/index.d.ts.map +1 -0
- package/dist/contract/index.js +26 -0
- package/dist/contract/pagination.d.ts +19 -0
- package/dist/contract/pagination.d.ts.map +1 -0
- package/dist/index-5sxnvwb1.js +368 -0
- package/dist/index-7wfkbvss.js +92 -0
- package/dist/index-809wc1tt.js +18 -0
- package/dist/index-gfzn1n4n.js +29 -0
- package/dist/index-ke4mx4ea.js +38 -0
- package/dist/index-n7bmdwmz.js +68 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +111 -0
- package/dist/internal/errors.d.ts +5 -0
- package/dist/internal/errors.d.ts.map +1 -0
- package/dist/internal/typed.d.ts +9 -0
- package/dist/internal/typed.d.ts.map +1 -0
- package/dist/observability/audit.d.ts +32 -0
- package/dist/observability/audit.d.ts.map +1 -0
- package/dist/observability/context.d.ts +60 -0
- package/dist/observability/context.d.ts.map +1 -0
- package/dist/observability/event.d.ts +49 -0
- package/dist/observability/event.d.ts.map +1 -0
- package/dist/observability/index.d.ts +14 -0
- package/dist/observability/index.d.ts.map +1 -0
- package/dist/observability/index.js +266 -0
- package/dist/observability/sanitize.d.ts +42 -0
- package/dist/observability/sanitize.d.ts.map +1 -0
- package/dist/observability/trace.d.ts +42 -0
- package/dist/observability/trace.d.ts.map +1 -0
- package/dist/react/cache-bridge.d.ts +53 -0
- package/dist/react/cache-bridge.d.ts.map +1 -0
- package/dist/react/cursor-query.d.ts +37 -0
- package/dist/react/cursor-query.d.ts.map +1 -0
- package/dist/react.d.ts +3 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +52 -0
- package/dist/server/cache.d.ts +15 -0
- package/dist/server/cache.d.ts.map +1 -0
- package/dist/server/context.d.ts +9 -0
- package/dist/server/context.d.ts.map +1 -0
- package/dist/server/create.d.ts +4 -0
- package/dist/server/create.d.ts.map +1 -0
- package/dist/server/event-bus.d.ts +24 -0
- package/dist/server/event-bus.d.ts.map +1 -0
- package/dist/server/implement.d.ts +16 -0
- package/dist/server/implement.d.ts.map +1 -0
- package/dist/server/index.d.ts +17 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +965 -0
- package/dist/server/logger.d.ts +21 -0
- package/dist/server/logger.d.ts.map +1 -0
- package/dist/server/middleware/auth.d.ts +50 -0
- package/dist/server/middleware/auth.d.ts.map +1 -0
- package/dist/server/middleware/cookies.d.ts +34 -0
- package/dist/server/middleware/cookies.d.ts.map +1 -0
- package/dist/server/middleware/cors.d.ts +9 -0
- package/dist/server/middleware/cors.d.ts.map +1 -0
- package/dist/server/multipart.d.ts +14 -0
- package/dist/server/multipart.d.ts.map +1 -0
- package/dist/server/rate-limit.d.ts +17 -0
- package/dist/server/rate-limit.d.ts.map +1 -0
- package/dist/server/request.d.ts +22 -0
- package/dist/server/request.d.ts.map +1 -0
- package/dist/server/router.d.ts +42 -0
- package/dist/server/router.d.ts.map +1 -0
- package/dist/server/socket-io.d.ts +42 -0
- package/dist/server/socket-io.d.ts.map +1 -0
- package/dist/server/stream.d.ts +18 -0
- package/dist/server/stream.d.ts.map +1 -0
- package/dist/server/swept-map.d.ts +22 -0
- package/dist/server/swept-map.d.ts.map +1 -0
- package/dist/server/types.d.ts +124 -0
- package/dist/server/types.d.ts.map +1 -0
- package/dist/tools/agent.d.ts +14 -0
- package/dist/tools/agent.d.ts.map +1 -0
- package/dist/tools/execute.d.ts +21 -0
- package/dist/tools/execute.d.ts.map +1 -0
- package/dist/tools/mcp-handler.d.ts +21 -0
- package/dist/tools/mcp-handler.d.ts.map +1 -0
- package/dist/tools/mcp-stdio.d.ts +25 -0
- package/dist/tools/mcp-stdio.d.ts.map +1 -0
- package/dist/tools/mcp.d.ts +42 -0
- package/dist/tools/mcp.d.ts.map +1 -0
- package/dist/tools/mount.d.ts +61 -0
- package/dist/tools/mount.d.ts.map +1 -0
- package/dist/tools/names.d.ts +2 -0
- package/dist/tools/names.d.ts.map +1 -0
- package/dist/tools/remote.d.ts +22 -0
- package/dist/tools/remote.d.ts.map +1 -0
- package/dist/tools/schema.d.ts +4 -0
- package/dist/tools/schema.d.ts.map +1 -0
- package/dist/tools/view-file.d.ts +57 -0
- package/dist/tools/view-file.d.ts.map +1 -0
- package/dist/tools.d.ts +8 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +654 -0
- package/package.json +136 -4
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isRecord
|
|
3
|
+
} from "../index-809wc1tt.js";
|
|
4
|
+
import {
|
|
5
|
+
getClientInfo
|
|
6
|
+
} from "../index-ke4mx4ea.js";
|
|
7
|
+
|
|
8
|
+
// src/observability/context.ts
|
|
9
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
10
|
+
|
|
11
|
+
// src/observability/trace.ts
|
|
12
|
+
var TRACEPARENT_RE = /^00-([0-9a-f]{32})-([0-9a-f]{16})-[0-9a-f]{2}$/i;
|
|
13
|
+
function randomHex(bytes) {
|
|
14
|
+
const arr = new Uint8Array(bytes);
|
|
15
|
+
crypto.getRandomValues(arr);
|
|
16
|
+
let hex = "";
|
|
17
|
+
for (const byte of arr)
|
|
18
|
+
hex += byte.toString(16).padStart(2, "0");
|
|
19
|
+
return hex;
|
|
20
|
+
}
|
|
21
|
+
function createTraceContext() {
|
|
22
|
+
return { traceId: randomHex(16), spanId: randomHex(8) };
|
|
23
|
+
}
|
|
24
|
+
function parseTraceparent(header) {
|
|
25
|
+
if (!header)
|
|
26
|
+
return null;
|
|
27
|
+
const match = TRACEPARENT_RE.exec(header.trim());
|
|
28
|
+
if (!match?.[1] || !match[2])
|
|
29
|
+
return null;
|
|
30
|
+
return {
|
|
31
|
+
traceId: match[1].toLowerCase(),
|
|
32
|
+
spanId: randomHex(8),
|
|
33
|
+
parentSpanId: match[2].toLowerCase()
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function formatTraceparent(ctx) {
|
|
37
|
+
return `00-${ctx.traceId}-${ctx.spanId}-01`;
|
|
38
|
+
}
|
|
39
|
+
function resolveTraceContext(req) {
|
|
40
|
+
return parseTraceparent(req.headers.get("traceparent")) ?? createTraceContext();
|
|
41
|
+
}
|
|
42
|
+
function childSpan(parent) {
|
|
43
|
+
return {
|
|
44
|
+
traceId: parent.traceId,
|
|
45
|
+
spanId: randomHex(8),
|
|
46
|
+
parentSpanId: parent.spanId
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// src/observability/context.ts
|
|
51
|
+
var storage = new AsyncLocalStorage;
|
|
52
|
+
function runWithRequestContext(ctx, fn) {
|
|
53
|
+
return storage.run(ctx, fn);
|
|
54
|
+
}
|
|
55
|
+
function getRequestContext() {
|
|
56
|
+
return storage.getStore();
|
|
57
|
+
}
|
|
58
|
+
function getTraceId() {
|
|
59
|
+
return storage.getStore()?.trace.traceId;
|
|
60
|
+
}
|
|
61
|
+
function getUserId() {
|
|
62
|
+
return storage.getStore()?.userId;
|
|
63
|
+
}
|
|
64
|
+
function setRequestUser(userId) {
|
|
65
|
+
const ctx = storage.getStore();
|
|
66
|
+
if (ctx)
|
|
67
|
+
ctx.userId = userId;
|
|
68
|
+
}
|
|
69
|
+
function setRequestError(error) {
|
|
70
|
+
const ctx = storage.getStore();
|
|
71
|
+
if (ctx)
|
|
72
|
+
ctx.error = error;
|
|
73
|
+
}
|
|
74
|
+
function wrapInRequestContext(handler) {
|
|
75
|
+
return (req, server) => {
|
|
76
|
+
const ctx = {
|
|
77
|
+
trace: resolveTraceContext(req),
|
|
78
|
+
source: "http",
|
|
79
|
+
method: req.method,
|
|
80
|
+
path: new URL(req.url).pathname,
|
|
81
|
+
startedAt: process.hrtime.bigint(),
|
|
82
|
+
...getClientInfo(req)
|
|
83
|
+
};
|
|
84
|
+
return runWithRequestContext(ctx, () => handler(req, server));
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// src/observability/sanitize.ts
|
|
89
|
+
var DEFAULT_SENSITIVE_KEYS = /(password|passwd|pwd|secret|token|apikey|api[-_ ]?key|auth|authorization|bearer|session|cookie|init[-_ ]?data|credential|private[-_ ]?key)/i;
|
|
90
|
+
var DEFAULT_MAX_BYTES = 16000;
|
|
91
|
+
var DEFAULT_MAX_DEPTH = 20;
|
|
92
|
+
var MASK = "[redacted]";
|
|
93
|
+
function redact(value, options = {}) {
|
|
94
|
+
const sensitive = options.sensitiveKeys ?? DEFAULT_SENSITIVE_KEYS;
|
|
95
|
+
const maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH;
|
|
96
|
+
const walk = (input, depth) => {
|
|
97
|
+
if (depth > maxDepth)
|
|
98
|
+
return "[max depth]";
|
|
99
|
+
if (input === null || input === undefined)
|
|
100
|
+
return null;
|
|
101
|
+
if (typeof input === "string" || typeof input === "number" || typeof input === "boolean") {
|
|
102
|
+
return input;
|
|
103
|
+
}
|
|
104
|
+
if (input instanceof Uint8Array || input instanceof ArrayBuffer) {
|
|
105
|
+
return { _type: "binary", size: input.byteLength };
|
|
106
|
+
}
|
|
107
|
+
if (input instanceof Blob) {
|
|
108
|
+
return { _type: "blob", size: input.size, mime: input.type };
|
|
109
|
+
}
|
|
110
|
+
if (typeof FormData !== "undefined" && input instanceof FormData) {
|
|
111
|
+
return { _type: "formdata", keys: [...input.keys()] };
|
|
112
|
+
}
|
|
113
|
+
if (Array.isArray(input)) {
|
|
114
|
+
return input.map((item) => walk(item, depth + 1));
|
|
115
|
+
}
|
|
116
|
+
if (isRecord(input)) {
|
|
117
|
+
const out = {};
|
|
118
|
+
for (const [key, val] of Object.entries(input)) {
|
|
119
|
+
out[key] = sensitive.test(key) ? MASK : walk(val, depth + 1);
|
|
120
|
+
}
|
|
121
|
+
return out;
|
|
122
|
+
}
|
|
123
|
+
return String(input);
|
|
124
|
+
};
|
|
125
|
+
return walk(value, 0);
|
|
126
|
+
}
|
|
127
|
+
function truncatePreview(value, maxBytes = DEFAULT_MAX_BYTES) {
|
|
128
|
+
const json = JSON.stringify(value);
|
|
129
|
+
if (json.length <= maxBytes)
|
|
130
|
+
return value;
|
|
131
|
+
return {
|
|
132
|
+
_truncated: true,
|
|
133
|
+
_originalBytes: json.length,
|
|
134
|
+
preview: `${json.slice(0, maxBytes)}…`
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
function sanitizePayload(value, options = {}) {
|
|
138
|
+
if (value === undefined || value === null)
|
|
139
|
+
return null;
|
|
140
|
+
return truncatePreview(redact(value, options), options.maxBytes ?? DEFAULT_MAX_BYTES);
|
|
141
|
+
}
|
|
142
|
+
function measureSize(value) {
|
|
143
|
+
if (value === undefined || value === null) {
|
|
144
|
+
return { resultSize: null, responseBytes: 0 };
|
|
145
|
+
}
|
|
146
|
+
const responseBytes = Buffer.byteLength(JSON.stringify(value), "utf8");
|
|
147
|
+
if (Array.isArray(value)) {
|
|
148
|
+
return { resultSize: value.length, responseBytes };
|
|
149
|
+
}
|
|
150
|
+
if (typeof value === "object" && "items" in value && Array.isArray(value.items)) {
|
|
151
|
+
return { resultSize: value.items.length, responseBytes };
|
|
152
|
+
}
|
|
153
|
+
return { resultSize: null, responseBytes };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// src/observability/audit.ts
|
|
157
|
+
var BODY_METHODS = new Set(["POST", "PUT", "PATCH", "DELETE"]);
|
|
158
|
+
function toolErrorMessage(result) {
|
|
159
|
+
const { details, hint } = result;
|
|
160
|
+
if (isRecord(details) && typeof details.message === "string") {
|
|
161
|
+
return details.message;
|
|
162
|
+
}
|
|
163
|
+
return hint;
|
|
164
|
+
}
|
|
165
|
+
function readString(value) {
|
|
166
|
+
return typeof value === "string" ? value : undefined;
|
|
167
|
+
}
|
|
168
|
+
function createAuditHook(config) {
|
|
169
|
+
const { write, filter, sanitize } = config;
|
|
170
|
+
const emit = async (event) => {
|
|
171
|
+
try {
|
|
172
|
+
if (filter && !filter(event))
|
|
173
|
+
return;
|
|
174
|
+
await write(event);
|
|
175
|
+
} catch {}
|
|
176
|
+
};
|
|
177
|
+
const http = (handler) => {
|
|
178
|
+
return async (req, server) => {
|
|
179
|
+
const bodyClone = BODY_METHODS.has(req.method) ? req.clone() : null;
|
|
180
|
+
const res = await handler(req, server);
|
|
181
|
+
const ctx = getRequestContext();
|
|
182
|
+
if (ctx) {
|
|
183
|
+
const durationMs = Math.round(Number(process.hrtime.bigint() - ctx.startedAt) / 1e6);
|
|
184
|
+
(async () => {
|
|
185
|
+
let body;
|
|
186
|
+
if (bodyClone) {
|
|
187
|
+
try {
|
|
188
|
+
body = await bodyClone.json();
|
|
189
|
+
} catch {
|
|
190
|
+
body = undefined;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
await emit({
|
|
194
|
+
source: ctx.source,
|
|
195
|
+
method: ctx.method,
|
|
196
|
+
path: ctx.path,
|
|
197
|
+
traceId: ctx.trace.traceId,
|
|
198
|
+
spanId: ctx.trace.spanId,
|
|
199
|
+
parentSpanId: ctx.trace.parentSpanId,
|
|
200
|
+
ok: res.status < 400,
|
|
201
|
+
statusCode: res.status,
|
|
202
|
+
durationMs,
|
|
203
|
+
errorCode: ctx.error?.code,
|
|
204
|
+
errorMessage: ctx.error?.message,
|
|
205
|
+
payload: sanitizePayload(body, sanitize),
|
|
206
|
+
resultSize: null,
|
|
207
|
+
responseBytes: 0,
|
|
208
|
+
userId: ctx.userId,
|
|
209
|
+
ipAddress: ctx.ipAddress,
|
|
210
|
+
userAgent: ctx.userAgent,
|
|
211
|
+
startedAt: new Date(Date.now() - durationMs)
|
|
212
|
+
});
|
|
213
|
+
})();
|
|
214
|
+
}
|
|
215
|
+
return res;
|
|
216
|
+
};
|
|
217
|
+
};
|
|
218
|
+
const toolCall = {
|
|
219
|
+
afterToolCall: (toolName, args, result, durationMs, context) => {
|
|
220
|
+
const parent = getRequestContext()?.trace;
|
|
221
|
+
const span = parent ? childSpan(parent) : createTraceContext();
|
|
222
|
+
const measure = result.ok ? measureSize(result.data) : { resultSize: null, responseBytes: 0 };
|
|
223
|
+
emit({
|
|
224
|
+
source: context.source,
|
|
225
|
+
method: "TOOL",
|
|
226
|
+
path: `/${context.source}/${toolName}`,
|
|
227
|
+
toolName,
|
|
228
|
+
traceId: span.traceId,
|
|
229
|
+
spanId: span.spanId,
|
|
230
|
+
parentSpanId: span.parentSpanId,
|
|
231
|
+
ok: result.ok,
|
|
232
|
+
statusCode: result.ok ? 200 : 400,
|
|
233
|
+
durationMs,
|
|
234
|
+
errorCode: result.ok ? undefined : result.code,
|
|
235
|
+
errorMessage: result.ok ? undefined : toolErrorMessage(result),
|
|
236
|
+
payload: sanitizePayload(args, sanitize),
|
|
237
|
+
resultSize: measure.resultSize,
|
|
238
|
+
responseBytes: measure.responseBytes,
|
|
239
|
+
userId: readString(context.userId),
|
|
240
|
+
ipAddress: readString(context.ipAddress),
|
|
241
|
+
userAgent: readString(context.userAgent),
|
|
242
|
+
startedAt: new Date(Date.now() - durationMs)
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
return { http, toolCall };
|
|
247
|
+
}
|
|
248
|
+
export {
|
|
249
|
+
wrapInRequestContext,
|
|
250
|
+
truncatePreview,
|
|
251
|
+
setRequestUser,
|
|
252
|
+
setRequestError,
|
|
253
|
+
sanitizePayload,
|
|
254
|
+
runWithRequestContext,
|
|
255
|
+
resolveTraceContext,
|
|
256
|
+
redact,
|
|
257
|
+
parseTraceparent,
|
|
258
|
+
measureSize,
|
|
259
|
+
getUserId,
|
|
260
|
+
getTraceId,
|
|
261
|
+
getRequestContext,
|
|
262
|
+
formatTraceparent,
|
|
263
|
+
createTraceContext,
|
|
264
|
+
createAuditHook,
|
|
265
|
+
childSpan
|
|
266
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** A JSON-serialisable value — what a sanitised payload always reduces to. */
|
|
2
|
+
export type JsonValue = string | number | boolean | null | JsonValue[] | {
|
|
3
|
+
[key: string]: JsonValue;
|
|
4
|
+
};
|
|
5
|
+
/** Tuning for `redact` / `sanitizePayload`. */
|
|
6
|
+
export interface SanitizeOptions {
|
|
7
|
+
/** Keys whose VALUE is masked. Default covers passwords, tokens, secrets… */
|
|
8
|
+
sensitiveKeys?: RegExp;
|
|
9
|
+
/** Cap on the serialised payload — anything larger collapses to a preview. Default 16 KB. */
|
|
10
|
+
maxBytes?: number;
|
|
11
|
+
/** Recursion limit — deeper nesting collapses to a marker. Default 20. */
|
|
12
|
+
maxDepth?: number;
|
|
13
|
+
}
|
|
14
|
+
/** The result of `measureSize`. */
|
|
15
|
+
export interface SizeMeasure {
|
|
16
|
+
/** Item count, when the value is a list (or a `{ items: [] }` page). */
|
|
17
|
+
resultSize: number | null;
|
|
18
|
+
/** Serialised byte length. */
|
|
19
|
+
responseBytes: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Deep-copy `value` into a JSON-safe shape: secret-named keys masked, binary
|
|
23
|
+
* blobs reduced to `{ _type, size }` metadata, branches past `maxDepth`
|
|
24
|
+
* truncated. Always returns a `JsonValue` — safe to store directly.
|
|
25
|
+
*/
|
|
26
|
+
export declare function redact(value: unknown, options?: SanitizeOptions): JsonValue;
|
|
27
|
+
/**
|
|
28
|
+
* Cap a `JsonValue` by serialised size — anything over `maxBytes` collapses to
|
|
29
|
+
* a `{ _truncated, _originalBytes, preview }` marker.
|
|
30
|
+
*/
|
|
31
|
+
export declare function truncatePreview(value: JsonValue, maxBytes?: number): JsonValue;
|
|
32
|
+
/**
|
|
33
|
+
* Sanitise an arbitrary payload for an audit row — `redact` then
|
|
34
|
+
* `truncatePreview`. `null` / `undefined` reduce to `null`.
|
|
35
|
+
*/
|
|
36
|
+
export declare function sanitizePayload(value: unknown, options?: SanitizeOptions): JsonValue | null;
|
|
37
|
+
/**
|
|
38
|
+
* Measure a result for the audit row — item count when it is a list (a bare
|
|
39
|
+
* array or a `{ items: [] }` page), and the serialised byte length.
|
|
40
|
+
*/
|
|
41
|
+
export declare function measureSize(value: unknown): SizeMeasure;
|
|
42
|
+
//# sourceMappingURL=sanitize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../../src/observability/sanitize.ts"],"names":[],"mappings":"AAMA,8EAA8E;AAC9E,MAAM,MAAM,SAAS,GACjB,MAAM,GACN,MAAM,GACN,OAAO,GACP,IAAI,GACJ,SAAS,EAAE,GACX;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAEjC,+CAA+C;AAC/C,MAAM,WAAW,eAAe;IAC9B,6EAA6E;IAC7E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,6FAA6F;IAC7F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,mCAAmC;AACnC,MAAM,WAAW,WAAW;IAC1B,wEAAwE;IACxE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,8BAA8B;IAC9B,aAAa,EAAE,MAAM,CAAC;CACvB;AASD;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,GAAE,eAAoB,GAAG,SAAS,CAsC/E;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,SAAoB,GAAG,SAAS,CAQzF;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,eAAoB,GAC5B,SAAS,GAAG,IAAI,CAGlB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,CAYvD"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* W3C Trace Context — `traceparent` parsing, formatting and span chaining.
|
|
3
|
+
*
|
|
4
|
+
* `traceparent` format: `00-{traceId:32hex}-{spanId:16hex}-{flags:2hex}`.
|
|
5
|
+
* Spec: https://www.w3.org/TR/trace-context/
|
|
6
|
+
*
|
|
7
|
+
* A trace id is stable across every span of one logical request — it ties the
|
|
8
|
+
* front-end call, the HTTP handler and each nested tool call into one timeline.
|
|
9
|
+
* A span id is unique to a single operation within that trace.
|
|
10
|
+
*/
|
|
11
|
+
/** A point in a distributed trace — one trace, one span, an optional parent. */
|
|
12
|
+
export interface TraceContext {
|
|
13
|
+
/** 32-hex trace id — stable across every span of one logical request. */
|
|
14
|
+
traceId: string;
|
|
15
|
+
/** 16-hex span id — unique to this operation. */
|
|
16
|
+
spanId: string;
|
|
17
|
+
/** 16-hex id of the span that caused this one, when there is one. */
|
|
18
|
+
parentSpanId?: string;
|
|
19
|
+
}
|
|
20
|
+
/** A fresh root trace — a new trace id, a new span id, no parent. */
|
|
21
|
+
export declare function createTraceContext(): TraceContext;
|
|
22
|
+
/**
|
|
23
|
+
* Parse a `traceparent` header into a `TraceContext`. The inbound span id
|
|
24
|
+
* becomes THIS context's `parentSpanId` and a fresh span id is minted — the
|
|
25
|
+
* receiver opens a new span under the caller's trace. Returns `null` for a
|
|
26
|
+
* missing or malformed header.
|
|
27
|
+
*/
|
|
28
|
+
export declare function parseTraceparent(header: string | null | undefined): TraceContext | null;
|
|
29
|
+
/** Render a `TraceContext` as a `traceparent` header value (sampled flag `01`). */
|
|
30
|
+
export declare function formatTraceparent(ctx: TraceContext): string;
|
|
31
|
+
/**
|
|
32
|
+
* The trace for an incoming request — the caller's `traceparent` continued
|
|
33
|
+
* when present and valid, else a fresh root trace.
|
|
34
|
+
*/
|
|
35
|
+
export declare function resolveTraceContext(req: Request): TraceContext;
|
|
36
|
+
/**
|
|
37
|
+
* A child span of `parent` — the same trace id, a fresh span id, `parentSpanId`
|
|
38
|
+
* set to the parent's span. Open one per nested operation (e.g. each tool call
|
|
39
|
+
* inside an HTTP request).
|
|
40
|
+
*/
|
|
41
|
+
export declare function childSpan(parent: TraceContext): TraceContext;
|
|
42
|
+
//# sourceMappingURL=trace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trace.d.ts","sourceRoot":"","sources":["../../src/observability/trace.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,gFAAgF;AAChF,MAAM,WAAW,YAAY;IAC3B,yEAAyE;IACzE,OAAO,EAAE,MAAM,CAAC;IAChB,iDAAiD;IACjD,MAAM,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAaD,qEAAqE;AACrE,wBAAgB,kBAAkB,IAAI,YAAY,CAEjD;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,YAAY,GAAG,IAAI,CASvF;AAED,mFAAmF;AACnF,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAE3D;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG,YAAY,CAE9D;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,YAAY,CAM5D"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache bridge — wire socket events into the TanStack Query cache.
|
|
3
|
+
*
|
|
4
|
+
* Transport-agnostic: works with ANY emitter exposing `on(event, handler) =>
|
|
5
|
+
* unsubscribe` — socket.io, stitchkit's `SocketClient`, an `EventTarget`
|
|
6
|
+
* wrapper. `TEvents` is a socket.io-style event map (`{ event: (payload) =>
|
|
7
|
+
* void }`, i.e. `ServerToClientEvents`) — so the whole family (all on
|
|
8
|
+
* socket.io) shares one cache-sync helper.
|
|
9
|
+
*
|
|
10
|
+
* Each handler updates `queryClient` in response to a typed server event.
|
|
11
|
+
* `markFresh(key)` (called from a mutation's onSuccess) records a key as
|
|
12
|
+
* just-mutated; a handler calls `ctx.isFresh(key)` to skip a stale socket echo
|
|
13
|
+
* and avoid a double update (mutation result + socket event).
|
|
14
|
+
*/
|
|
15
|
+
import type { QueryClient, QueryKey } from '@tanstack/react-query';
|
|
16
|
+
/** Minimal emitter — subscribe to an event, get an unsubscribe back. */
|
|
17
|
+
export interface CacheBridgeSocket<TEvents> {
|
|
18
|
+
on<K extends keyof TEvents & string>(event: K, handler: TEvents[K]): () => void;
|
|
19
|
+
}
|
|
20
|
+
export interface CacheBridgeContext {
|
|
21
|
+
queryClient: QueryClient;
|
|
22
|
+
/** True if `markFresh(key)` was called for this key within `freshWindow` ms. */
|
|
23
|
+
isFresh(key: QueryKey): boolean;
|
|
24
|
+
}
|
|
25
|
+
/** Payload of a socket.io-style event entry `(payload) => void`. */
|
|
26
|
+
type EventPayload<T> = T extends (payload: infer P) => unknown ? P : never;
|
|
27
|
+
export type CacheBridgeHandler<TPayload> = (data: TPayload, ctx: CacheBridgeContext) => void;
|
|
28
|
+
export type CacheBridgeHandlers<TEvents> = {
|
|
29
|
+
[K in keyof TEvents & string]?: CacheBridgeHandler<EventPayload<TEvents[K]>>;
|
|
30
|
+
};
|
|
31
|
+
export interface CacheBridgeConfig<TEvents> {
|
|
32
|
+
socket: CacheBridgeSocket<TEvents>;
|
|
33
|
+
/**
|
|
34
|
+
* The TanStack Query client, or a thunk returning it. A thunk is resolved
|
|
35
|
+
* lazily at `connect()` time — keeps the bridge SSR-safe when it lives in a
|
|
36
|
+
* module-level `const` (client components are still evaluated on the server,
|
|
37
|
+
* where a browser-only `QueryClient` does not exist yet).
|
|
38
|
+
*/
|
|
39
|
+
queryClient: QueryClient | (() => QueryClient);
|
|
40
|
+
handlers: CacheBridgeHandlers<TEvents>;
|
|
41
|
+
/** Window (ms) after `markFresh()` during which `isFresh()` returns true. Default 500. */
|
|
42
|
+
freshWindow?: number;
|
|
43
|
+
}
|
|
44
|
+
export interface CacheBridge {
|
|
45
|
+
connect(): void;
|
|
46
|
+
disconnect(): void;
|
|
47
|
+
/** Mark a query key as just-mutated. Call from a mutation's onSuccess so
|
|
48
|
+
* handlers can skip a stale socket echo via `ctx.isFresh()`. */
|
|
49
|
+
markFresh(key: QueryKey): void;
|
|
50
|
+
}
|
|
51
|
+
export declare function createCacheBridge<TEvents>(config: CacheBridgeConfig<TEvents>): CacheBridge;
|
|
52
|
+
export {};
|
|
53
|
+
//# sourceMappingURL=cache-bridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache-bridge.d.ts","sourceRoot":"","sources":["../../src/react/cache-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEnE,wEAAwE;AACxE,MAAM,WAAW,iBAAiB,CAAC,OAAO;IACxC,EAAE,CAAC,CAAC,SAAS,MAAM,OAAO,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,CAAC;CACjF;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,WAAW,CAAC;IACzB,gFAAgF;IAChF,OAAO,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC;CACjC;AAED,oEAAoE;AACpE,KAAK,YAAY,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,OAAO,GAAG,CAAC,GAAG,KAAK,CAAC;AAE3E,MAAM,MAAM,kBAAkB,CAAC,QAAQ,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,kBAAkB,KAAK,IAAI,CAAC;AAE7F,MAAM,MAAM,mBAAmB,CAAC,OAAO,IAAI;KACxC,CAAC,IAAI,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,EAAE,kBAAkB,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;CAC7E,CAAC;AAEF,MAAM,WAAW,iBAAiB,CAAC,OAAO;IACxC,MAAM,EAAE,iBAAiB,CAAC,OAAO,CAAC,CAAC;IACnC;;;;;OAKG;IACH,WAAW,EAAE,WAAW,GAAG,CAAC,MAAM,WAAW,CAAC,CAAC;IAC/C,QAAQ,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC;IACvC,0FAA0F;IAC1F,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,IAAI,IAAI,CAAC;IAChB,UAAU,IAAI,IAAI,CAAC;IACnB;qEACiE;IACjE,SAAS,CAAC,GAAG,EAAE,QAAQ,GAAG,IAAI,CAAC;CAChC;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,iBAAiB,CAAC,OAAO,CAAC,GAAG,WAAW,CA8C1F"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cursor-paginated infinite query — the family's canonical pagination hook.
|
|
3
|
+
*
|
|
4
|
+
* Built on `react-query-kit`'s `createInfiniteQuery` (the family's hook layer),
|
|
5
|
+
* so the result keeps the full rqk surface: `.getKey()`, `.fetcher`,
|
|
6
|
+
* `useSuspenseInfiniteQuery`, every option. Cursor pagination is immune to
|
|
7
|
+
* concurrent inserts — offset paging drops or duplicates rows when a new row
|
|
8
|
+
* lands on top of the list.
|
|
9
|
+
*
|
|
10
|
+
* Pass the typed contract method as `endpoint` (e.g. `apiClient.media.list`).
|
|
11
|
+
* The helper injects `cursor` from the page param and bakes in
|
|
12
|
+
* `getNextPageParam` / `initialPageParam`. An infinite hook is therefore just
|
|
13
|
+
* `queryKey + endpoint` — no per-hook fetcher, no pagination plumbing. Every
|
|
14
|
+
* other rqk option (`staleTime`, `gcTime`, `refetch*`, …) passes through.
|
|
15
|
+
*
|
|
16
|
+
* Page size is NOT a client concern: the helper never sends `limit`, so the
|
|
17
|
+
* server applies the contract's `limit` default (`z.coerce.number().default(N)`).
|
|
18
|
+
* That default is the single source of truth — the client cannot diverge.
|
|
19
|
+
*/
|
|
20
|
+
import { type CompatibleError, type CreateInfiniteQueryOptions } from 'react-query-kit';
|
|
21
|
+
/** The cursor field the helper owns — the caller never passes it. */
|
|
22
|
+
interface CursorParam {
|
|
23
|
+
cursor?: string;
|
|
24
|
+
}
|
|
25
|
+
/** Options the helper computes itself; the caller cannot override them. */
|
|
26
|
+
type ControlledKeys = 'fetcher' | 'initialPageParam' | 'getNextPageParam';
|
|
27
|
+
export type CursorQueryConfig<TVars, TPage extends {
|
|
28
|
+
nextCursor: string | null;
|
|
29
|
+
}, TError> = Omit<CreateInfiniteQueryOptions<TPage, TVars, TError, string | null>, ControlledKeys> & {
|
|
30
|
+
/** Contract method, e.g. `apiClient.media.list`. */
|
|
31
|
+
endpoint: (args: TVars & CursorParam) => Promise<TPage>;
|
|
32
|
+
};
|
|
33
|
+
export declare function createCursorQuery<TVars, TPage extends {
|
|
34
|
+
nextCursor: string | null;
|
|
35
|
+
}, TError = CompatibleError>(config: CursorQueryConfig<TVars, TPage, TError>): import("react-query-kit").InfiniteQueryHook<TPage, TVars, TError, string | null>;
|
|
36
|
+
export {};
|
|
37
|
+
//# sourceMappingURL=cursor-query.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor-query.d.ts","sourceRoot":"","sources":["../../src/react/cursor-query.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,0BAA0B,EAEhC,MAAM,iBAAiB,CAAC;AAEzB,qEAAqE;AACrE,UAAU,WAAW;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,2EAA2E;AAC3E,KAAK,cAAc,GAAG,SAAS,GAAG,kBAAkB,GAAG,kBAAkB,CAAC;AAE1E,MAAM,MAAM,iBAAiB,CAC3B,KAAK,EACL,KAAK,SAAS;IAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EAC3C,MAAM,IACJ,IAAI,CAAC,0BAA0B,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,EAAE,cAAc,CAAC,GAAG;IAC1F,oDAAoD;IACpD,QAAQ,EAAE,CAAC,IAAI,EAAE,KAAK,GAAG,WAAW,KAAK,OAAO,CAAC,KAAK,CAAC,CAAC;CACzD,CAAC;AAEF,wBAAgB,iBAAiB,CAC/B,KAAK,EACL,KAAK,SAAS;IAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EAC3C,MAAM,GAAG,eAAe,EACxB,MAAM,EAAE,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,oFAYhD"}
|
package/dist/react.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { type CacheBridge, type CacheBridgeConfig, type CacheBridgeContext, type CacheBridgeHandler, type CacheBridgeHandlers, type CacheBridgeSocket, createCacheBridge, } from './react/cache-bridge';
|
|
2
|
+
export { type CursorQueryConfig, createCursorQuery } from './react/cursor-query';
|
|
3
|
+
//# sourceMappingURL=react.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,iBAAiB,GAClB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,KAAK,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC"}
|
package/dist/react.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// src/react/cache-bridge.ts
|
|
2
|
+
function createCacheBridge(config) {
|
|
3
|
+
const freshWindow = config.freshWindow ?? 500;
|
|
4
|
+
const freshAt = new Map;
|
|
5
|
+
let unsubscribers = [];
|
|
6
|
+
const isFresh = (key) => {
|
|
7
|
+
const ts = freshAt.get(JSON.stringify(key));
|
|
8
|
+
return ts !== undefined && Date.now() - ts < freshWindow;
|
|
9
|
+
};
|
|
10
|
+
return {
|
|
11
|
+
connect() {
|
|
12
|
+
if (unsubscribers.length > 0)
|
|
13
|
+
return;
|
|
14
|
+
const queryClient = typeof config.queryClient === "function" ? config.queryClient() : config.queryClient;
|
|
15
|
+
const ctx = { queryClient, isFresh };
|
|
16
|
+
const handlers = config.handlers;
|
|
17
|
+
const socket = config.socket;
|
|
18
|
+
for (const event of Object.keys(handlers)) {
|
|
19
|
+
const handler = handlers[event];
|
|
20
|
+
if (!handler)
|
|
21
|
+
continue;
|
|
22
|
+
const off = socket.on(event, (data) => handler(data, ctx));
|
|
23
|
+
unsubscribers.push(off);
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
disconnect() {
|
|
27
|
+
for (const off of unsubscribers)
|
|
28
|
+
off();
|
|
29
|
+
unsubscribers = [];
|
|
30
|
+
},
|
|
31
|
+
markFresh(key) {
|
|
32
|
+
freshAt.set(JSON.stringify(key), Date.now());
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
// src/react/cursor-query.ts
|
|
37
|
+
import {
|
|
38
|
+
createInfiniteQuery
|
|
39
|
+
} from "react-query-kit";
|
|
40
|
+
function createCursorQuery(config) {
|
|
41
|
+
const { endpoint, ...rest } = config;
|
|
42
|
+
return createInfiniteQuery({
|
|
43
|
+
...rest,
|
|
44
|
+
fetcher: (variables, { pageParam }) => endpoint({ ...variables, cursor: pageParam ?? undefined }),
|
|
45
|
+
initialPageParam: null,
|
|
46
|
+
getNextPageParam: (lastPage) => lastPage.nextCursor
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
export {
|
|
50
|
+
createCursorQuery,
|
|
51
|
+
createCacheBridge
|
|
52
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory key/value cache with a per-entry TTL. Expired entries are swept on
|
|
3
|
+
* a 30-second timer; `destroy()` stops it. For response `Cache-Control`
|
|
4
|
+
* headers use `cacheHeaders()` instead.
|
|
5
|
+
*/
|
|
6
|
+
export declare function createCache(): {
|
|
7
|
+
destroy: () => void;
|
|
8
|
+
get(key: string): unknown | null;
|
|
9
|
+
set(key: string, data: unknown, maxAgeSeconds: number): void;
|
|
10
|
+
invalidate(prefix: string): void;
|
|
11
|
+
clear(): void;
|
|
12
|
+
};
|
|
13
|
+
/** Build a `Cache-Control` header capping freshness at `maxAge` seconds. */
|
|
14
|
+
export declare function cacheHeaders(maxAge: number, scope?: 'public' | 'private'): Record<string, string>;
|
|
15
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/server/cache.ts"],"names":[],"mappings":"AAOA;;;;GAIG;AACH,wBAAgB,WAAW;;aAQd,MAAM,GAAG,OAAO,GAAG,IAAI;aAUvB,MAAM,QAAQ,OAAO,iBAAiB,MAAM;uBAIlC,MAAM;;EAU5B;AAED,4EAA4E;AAC5E,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,EACd,KAAK,GAAE,QAAQ,GAAG,SAAoB,GACrC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAIxB"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `RuntimeContext` assembly — parses params / body / multipart against the
|
|
3
|
+
* endpoint schemas and gathers request metadata (trace id, client info).
|
|
4
|
+
*/
|
|
5
|
+
import { type RuntimeContext } from '../contract';
|
|
6
|
+
import type { MethodDef } from './types';
|
|
7
|
+
export declare function buildContext(req: Request, url: URL, method: MethodDef, pathParams: Record<string, string>, traceId: string): Promise<RuntimeContext>;
|
|
8
|
+
export declare function buildErrorContext(req: Request, url: URL, traceId: string): RuntimeContext;
|
|
9
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +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,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AA2BzC,wBAAsB,YAAY,CAChC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,SAAS,EACjB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAClC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,cAAc,CAAC,CAyCzB;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,GAAG,cAAc,CAWzF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/server/create.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAwB,YAAY,EAAgB,MAAM,SAAS,CAAC;AAEhF,wBAAgB,aAAa,CAAC,MAAM,EAAE,YAAY,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CA+KvF;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,uBAsBhD"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
type EventHandler<T = unknown> = (data: T) => void | Promise<void>;
|
|
2
|
+
/** Default event map — untyped string-keyed events (ad-hoc buses). */
|
|
3
|
+
export type DefaultEventMap = Record<string, unknown>;
|
|
4
|
+
/**
|
|
5
|
+
* In-process pub/sub bus.
|
|
6
|
+
*
|
|
7
|
+
* Pass an event map for full type safety — `emit`/`on`/`once`/`off` are then
|
|
8
|
+
* typed per event:
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* const bus = createEventBus<{ 'user:created': { id: string } }>();
|
|
12
|
+
* bus.emit('user:created', { id: '1' }); // typed
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export interface EventBus<M extends Record<string, unknown> = DefaultEventMap> {
|
|
16
|
+
emit<K extends keyof M & string>(event: K, data: M[K]): void;
|
|
17
|
+
on<K extends keyof M & string>(event: K, handler: EventHandler<M[K]>): () => void;
|
|
18
|
+
once<K extends keyof M & string>(event: K, handler: EventHandler<M[K]>): () => void;
|
|
19
|
+
off<K extends keyof M & string>(event: K, handler: EventHandler<M[K]>): void;
|
|
20
|
+
clear(): void;
|
|
21
|
+
}
|
|
22
|
+
export declare function createEventBus<M extends Record<string, unknown> = DefaultEventMap>(): EventBus<M>;
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=event-bus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-bus.d.ts","sourceRoot":"","sources":["../../src/server/event-bus.ts"],"names":[],"mappings":"AAAA,KAAK,YAAY,CAAC,CAAC,GAAG,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnE,sEAAsE;AACtE,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEtD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,QAAQ,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,eAAe;IAC3E,IAAI,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAC7D,EAAE,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,CAAC;IAClF,IAAI,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,CAAC;IACpF,GAAG,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAC7E,KAAK,IAAI,IAAI,CAAC;CACf;AAYD,wBAAgB,cAAc,CAC5B,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,eAAe,KAChD,QAAQ,CAAC,CAAC,CAAC,CAuDf"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ContractDef, EndpointDef, RuntimeContext } from '../contract';
|
|
2
|
+
import type { Handlers, ServiceDef } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Bind a contract to its typed `handlers`, producing a `ServiceDef` to mount on
|
|
5
|
+
* `createServer`. Every handler is type-checked against its endpoint's schemas.
|
|
6
|
+
* Pass `TCtx` for a typed handler context — or use `createImplement` to fix it
|
|
7
|
+
* once.
|
|
8
|
+
*/
|
|
9
|
+
export declare function implement<T extends Record<string, EndpointDef>, TCtx extends RuntimeContext = RuntimeContext>(contract: ContractDef<T, string>, handlers: Handlers<T, TCtx>): ServiceDef;
|
|
10
|
+
/**
|
|
11
|
+
* Fix the handler context type once — `const implement =
|
|
12
|
+
* createImplement<MyContext>()` — so each `implement()` call site stays free of
|
|
13
|
+
* the generic. The application declares its context shape in a single place.
|
|
14
|
+
*/
|
|
15
|
+
export declare function createImplement<TCtx extends RuntimeContext>(): <T extends Record<string, EndpointDef>>(contract: ContractDef<T, string>, handlers: Handlers<T, TCtx>) => ServiceDef;
|
|
16
|
+
//# sourceMappingURL=implement.d.ts.map
|
|
@@ -0,0 +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;AAC5E,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,CAoC3E;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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export { AppError, appError, badRequest, conflict, forbidden, notFound, rateLimited, unauthorized, } from '../contract';
|
|
2
|
+
export { cacheHeaders, createCache } from './cache';
|
|
3
|
+
export { createHandler, createServer } from './create';
|
|
4
|
+
export { createEventBus, type EventBus } from './event-bus';
|
|
5
|
+
export { createImplement, implement } from './implement';
|
|
6
|
+
export { type AuthHook, type AuthHookConfig, type AuthRule, type BearerResolverConfig, createAuthHook, createBearerResolver, extractToken, type JwtPayload, verifyJwt, } from './middleware/auth';
|
|
7
|
+
export { type CookieDef, type CookieOptions, defineCookie, parseCookies, serializeCookie, } from './middleware/cookies';
|
|
8
|
+
export { type CorsConfig, corsHeaders, corsPreflightResponse, } from './middleware/cors';
|
|
9
|
+
export { parseMultipart } from './multipart';
|
|
10
|
+
export { createRateLimiter, type RateLimitConfig } from './rate-limit';
|
|
11
|
+
export { extractIp, generateTraceId, getClientInfo, resolveTraceId, } from './request';
|
|
12
|
+
export { staticRoute } from './router';
|
|
13
|
+
export type { SocketIOServerConfig, SocketIOServerHandle } from './socket-io';
|
|
14
|
+
export { createSocketIOServer } from './socket-io';
|
|
15
|
+
export { type ParseSSEOptions, parseSSE, streamSSE } from './stream';
|
|
16
|
+
export type { BunServer, Handlers, LifecycleHooks, MethodDef, RawRoute, RawRouteContext, RouteGroup, ServerConfig, ServerPassthrough, ServiceDef, StitchLogger, } from './types';
|
|
17
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,WAAW,EACX,YAAY,GACb,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,KAAK,oBAAoB,EACzB,cAAc,EACd,oBAAoB,EACpB,YAAY,EACZ,KAAK,UAAU,EACf,SAAS,GACV,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,YAAY,EACZ,YAAY,EACZ,eAAe,GAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,KAAK,UAAU,EACf,WAAW,EACX,qBAAqB,GACtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,iBAAiB,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,EACL,SAAS,EACT,eAAe,EACf,aAAa,EACb,cAAc,GACf,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,YAAY,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,KAAK,eAAe,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrE,YAAY,EACV,SAAS,EACT,QAAQ,EACR,cAAc,EACd,SAAS,EACT,QAAQ,EACR,eAAe,EACf,UAAU,EACV,YAAY,EACZ,iBAAiB,EACjB,UAAU,EACV,YAAY,GACb,MAAM,SAAS,CAAC"}
|