stitchkit 0.50.0 → 0.52.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/cli.js CHANGED
@@ -4,10 +4,10 @@ import {
4
4
  emitResult,
5
5
  parseCliArgs,
6
6
  pollUntilDone
7
- } from "./index-0hj37z43.js";
7
+ } from "./index-h55e1wyq.js";
8
8
  import"./index-yxpe3phd.js";
9
- import"./index-jewp9r0a.js";
10
- import"./index-h05ygjqx.js";
9
+ import"./index-8qghvg18.js";
10
+ import"./index-bmnhqtya.js";
11
11
  export {
12
12
  pollUntilDone,
13
13
  parseCliArgs,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  isRecord,
3
3
  resolveTraceContext
4
- } from "./index-h05ygjqx.js";
4
+ } from "./index-bmnhqtya.js";
5
5
 
6
6
  // src/contract/errors.ts
7
7
  var APP_ERROR_BRAND = Symbol.for("stitchkit.AppError");
@@ -136,6 +136,9 @@ function callRuntimeHandler(handler, context) {
136
136
  }
137
137
  return Reflect.apply(handler, undefined, [context]);
138
138
  }
139
+ function transportResult(value) {
140
+ return value;
141
+ }
139
142
  function mapObject(source, mapper) {
140
143
  const result = {};
141
144
  for (const [key, value] of typedEntries(source)) {
@@ -146,4 +149,4 @@ function mapObject(source, mapper) {
146
149
  return result;
147
150
  }
148
151
 
149
- export { __require, parseTrailingWildcard, typedEntries, isRecord, callRuntimeHandler, mapObject, createTraceContext, parseTraceparent, formatTraceparent, resolveTraceContext, childSpan, resolvePropagationContext };
152
+ export { __require, parseTrailingWildcard, typedEntries, isRecord, callRuntimeHandler, transportResult, mapObject, createTraceContext, parseTraceparent, formatTraceparent, resolveTraceContext, childSpan, resolvePropagationContext };
@@ -2,7 +2,7 @@ import {
2
2
  assertCorsConfig,
3
3
  corsHeaders,
4
4
  corsPreflightResponse
5
- } from "./index-r1qp4rve.js";
5
+ } from "./index-drsnz8gw.js";
6
6
  import {
7
7
  AppError,
8
8
  badRequest,
@@ -11,7 +11,6 @@ import {
11
11
  getClientInfo,
12
12
  getRequestContext,
13
13
  isUnsafeKey,
14
- mergeMeta,
15
14
  normalizeError,
16
15
  parseQueryParams,
17
16
  recordedErrorMessage,
@@ -23,15 +22,13 @@ import {
23
22
  setRequestError,
24
23
  validateDeclaredOutput,
25
24
  zodIssues
26
- } from "./index-jewp9r0a.js";
25
+ } from "./index-8qghvg18.js";
27
26
  import {
28
27
  __require,
29
- callRuntimeHandler,
30
28
  isRecord,
31
29
  parseTrailingWildcard,
32
- resolveTraceContext,
33
- typedEntries
34
- } from "./index-h05ygjqx.js";
30
+ resolveTraceContext
31
+ } from "./index-bmnhqtya.js";
35
32
 
36
33
  // src/server/multipart.ts
37
34
  var DEFAULT_MAX_REQUEST_BYTES = 25 * 1024 * 1024;
@@ -1607,152 +1604,6 @@ function createServerLifecycle(getAdapter) {
1607
1604
  };
1608
1605
  }
1609
1606
 
1610
- // src/server/implement.ts
1611
- function isStreamingImplementation(value) {
1612
- return typeof value === "object" && value !== null && "kind" in value && value.kind === "stitchkit.multipart.stream";
1613
- }
1614
- function defineMultipartStream(endpoint, config) {
1615
- return buildMultipartStream(endpoint, config.files, config.handler);
1616
- }
1617
- function buildMultipartStream(endpoint, files, handler) {
1618
- const receivers = {};
1619
- for (const [key, receiver] of typedEntries(files)) {
1620
- receivers[String(key)] = receiver;
1621
- }
1622
- const declared = Object.keys(endpoint.multipart.files);
1623
- const configured = Object.keys(receivers);
1624
- if (declared.length !== configured.length || declared.some((field) => !Object.hasOwn(receivers, field))) {
1625
- throw new Error("Streaming multipart receivers must exactly match declared file fields");
1626
- }
1627
- return {
1628
- kind: "stitchkit.multipart.stream",
1629
- receivers,
1630
- execute(ctx, streamedFiles) {
1631
- return callRuntimeHandler(handler, { ...ctx, files: streamedFiles });
1632
- }
1633
- };
1634
- }
1635
- function createMultipartStream() {
1636
- return (endpoint, config) => buildMultipartStream(endpoint, config.files, config.handler);
1637
- }
1638
- var HTTP_ONLY = Object.freeze(["HTTP"]);
1639
- function bindContract(contract, handlers) {
1640
- const methods = {};
1641
- const groupScope = contract.meta.scope ?? "public";
1642
- for (const [key, endpoint] of typedEntries(contract.endpoints)) {
1643
- const typedHandler = handlers[String(key)];
1644
- const isStreaming = endpoint.multipart?.delivery === "stream";
1645
- if (!isStreaming && typeof typedHandler !== "function") {
1646
- throw new Error(`[stitchkit] implement: missing handler for "${contract.meta.prefix}.${String(key)}"`);
1647
- }
1648
- if (isStreaming && !isStreamingImplementation(typedHandler)) {
1649
- throw new Error(`[stitchkit] implement: streaming multipart endpoint "${contract.meta.prefix}.${String(key)}" must use defineMultipartStream()`);
1650
- }
1651
- const streamingHandler = isStreamingImplementation(typedHandler) ? typedHandler : undefined;
1652
- const regularHandler = typeof typedHandler === "function" ? typedHandler : undefined;
1653
- methods[String(key)] = {
1654
- method: endpoint.method,
1655
- path: endpoint.path,
1656
- desc: endpoint.desc,
1657
- serviceName: contract.meta.prefix,
1658
- key: String(key),
1659
- toolName: "toolName" in endpoint ? endpoint.toolName : undefined,
1660
- expose: endpoint.rawResponse || endpoint.rawBody || endpoint.responseMeta ? HTTP_ONLY : endpoint.expose,
1661
- scope: endpoint.scope ?? groupScope,
1662
- paramsSchema: endpoint.params,
1663
- inputSchema: endpoint.input,
1664
- outputSchema: endpoint.output,
1665
- multipart: endpoint.multipart,
1666
- multipartReceivers: streamingHandler?.receivers,
1667
- maxJsonBodyBytes: endpoint.maxJsonBodyBytes,
1668
- idempotent: endpoint.idempotent,
1669
- ui: "ui" in endpoint ? endpoint.ui : undefined,
1670
- annotations: "annotations" in endpoint ? endpoint.annotations : undefined,
1671
- mcp: "mcp" in endpoint ? endpoint.mcp : undefined,
1672
- meta: mergeMeta(contract.meta.meta, endpoint.meta),
1673
- rawResponse: endpoint.rawResponse,
1674
- rawBody: endpoint.rawBody,
1675
- responseMeta: endpoint.responseMeta,
1676
- contentType: "contentType" in endpoint ? endpoint.contentType : undefined,
1677
- handler: streamingHandler ? (ctx) => streamingHandler.execute(ctx, ctx.files ?? {}) : (ctx) => {
1678
- if (!regularHandler) {
1679
- throw new Error(`[stitchkit] implement: missing handler for "${contract.meta.prefix}.${String(key)}"`);
1680
- }
1681
- return callRuntimeHandler(regularHandler, ctx);
1682
- }
1683
- };
1684
- }
1685
- return {
1686
- name: contract.meta.prefix,
1687
- prefix: contract.meta.prefix,
1688
- scope: groupScope,
1689
- methods
1690
- };
1691
- }
1692
- function implement(contract, handlers) {
1693
- return bindContract(contract, handlers);
1694
- }
1695
- function createImplement() {
1696
- return (contract, handlers) => implement(contract, handlers);
1697
- }
1698
- function createScopedImplement() {
1699
- const implementScoped = (contract, handlers) => bindContract(contract, handlers);
1700
- const stream = (scope, endpoint, config) => {
1701
- if (endpoint.scope !== undefined && endpoint.scope !== scope) {
1702
- throw new Error(`[stitchkit] createScopedImplement.stream: endpoint declares scope "${endpoint.scope}" but "${String(scope)}" was given`);
1703
- }
1704
- return buildMultipartStream(endpoint, config.files, config.handler);
1705
- };
1706
- return Object.assign(implementScoped, { stream });
1707
- }
1708
- function createScopedImplementRegistry() {
1709
- return (contracts, handlers) => bindRegistry(contracts, handlers);
1710
- }
1711
- function isImplementationContract(value) {
1712
- return isRecord(value) && isRecord(value.meta) && typeof value.meta.prefix === "string" && isRecord(value.endpoints);
1713
- }
1714
- function bindRegistry(contracts, handlers) {
1715
- const contractKeys = Object.keys(contracts);
1716
- const handlerKeys = Object.keys(handlers);
1717
- const missing = contractKeys.filter((key) => !Object.hasOwn(handlers, key));
1718
- const extra = handlerKeys.filter((key) => !Object.hasOwn(contracts, key));
1719
- if (missing.length > 0 || extra.length > 0) {
1720
- throw new Error(`[stitchkit] implementRegistry: registry mismatch (missing: ${missing.join(", ") || "none"}; extra: ${extra.join(", ") || "none"})`);
1721
- }
1722
- const prefixes = new Map;
1723
- const services = [];
1724
- for (const [key, candidate] of Object.entries(contracts)) {
1725
- if (!isImplementationContract(candidate)) {
1726
- throw new TypeError(`[stitchkit] implementRegistry: registry entry "${key}" must be one contract; composed arrays and namespaces are not supported`);
1727
- }
1728
- const contract = candidate;
1729
- const previousKey = prefixes.get(contract.meta.prefix);
1730
- if (previousKey !== undefined) {
1731
- throw new Error(`[stitchkit] implementRegistry: duplicate contract prefix "${contract.meta.prefix}" at "${previousKey}" and "${key}"`);
1732
- }
1733
- prefixes.set(contract.meta.prefix, key);
1734
- const entryHandlers = handlers[key];
1735
- if (!isRecord(entryHandlers)) {
1736
- throw new TypeError(`[stitchkit] implementRegistry: handlers for "${key}" must be an object`);
1737
- }
1738
- const endpointKeys = Object.keys(contract.endpoints);
1739
- const handlerEntryKeys = Object.keys(entryHandlers);
1740
- const missingEndpoints = endpointKeys.filter((endpointKey) => !Object.hasOwn(entryHandlers, endpointKey));
1741
- const extraEndpoints = handlerEntryKeys.filter((endpointKey) => !Object.hasOwn(contract.endpoints, endpointKey));
1742
- if (missingEndpoints.length > 0 || extraEndpoints.length > 0) {
1743
- throw new Error(`[stitchkit] implementRegistry: handlers for "${key}" mismatch (missing: ${missingEndpoints.join(", ") || "none"}; extra: ${extraEndpoints.join(", ") || "none"})`);
1744
- }
1745
- services.push(bindContract(contract, entryHandlers));
1746
- }
1747
- return services;
1748
- }
1749
- function implementRegistry(contracts, handlers) {
1750
- return bindRegistry(contracts, handlers);
1751
- }
1752
- function createImplementRegistry() {
1753
- return (contracts, handlers) => bindRegistry(contracts, handlers);
1754
- }
1755
-
1756
1607
  // src/server/process-signals.ts
1757
1608
  var DEFAULT_SIGNALS = ["SIGINT", "SIGTERM"];
1758
1609
  var defaultSignalSource = {
@@ -2301,4 +2152,4 @@ function socketIoLane(websocket) {
2301
2152
  });
2302
2153
  }
2303
2154
 
2304
- export { parseMultipart, createHandler, ShutdownStateSchema, ShutdownOptionsSchema, ShutdownStatusSchema, ShutdownResultSchema, createServerLifecycle, defineMultipartStream, createMultipartStream, implement, createImplement, createScopedImplement, createScopedImplementRegistry, implementRegistry, createImplementRegistry, bindProcessSignals, bindRealtimeServer, webSocketLane, composeWebSocketHandlers, createSocketIOServer, socketIoLane };
2155
+ export { parseMultipart, createHandler, ShutdownStateSchema, ShutdownOptionsSchema, ShutdownStatusSchema, ShutdownResultSchema, createServerLifecycle, bindProcessSignals, bindRealtimeServer, webSocketLane, composeWebSocketHandlers, createSocketIOServer, socketIoLane };
@@ -0,0 +1,249 @@
1
+ import {
2
+ mergeMeta
3
+ } from "./index-8qghvg18.js";
4
+ import {
5
+ callRuntimeHandler,
6
+ isRecord,
7
+ transportResult,
8
+ typedEntries
9
+ } from "./index-bmnhqtya.js";
10
+
11
+ // src/server/middleware/cors.ts
12
+ var DEFAULT_CORS_ALLOW_HEADERS = "Content-Type, Authorization, X-Trace-Id, traceparent, tracestate";
13
+ var DEFAULT_CORS_EXPOSE_HEADERS = "Content-Disposition, Content-Length, Content-Range, Accept-Ranges, ETag, Last-Modified, X-Request-Id";
14
+ function assertCorsConfig(config) {
15
+ if (config.credentials && (config.origin === undefined || config.origin === "*")) {
16
+ throw new Error("[stitchkit] cors: `credentials: true` cannot be combined with a wildcard origin. " + "Set `origin` to an explicit string or list.");
17
+ }
18
+ if (config.origin === undefined) {
19
+ throw new Error("[stitchkit] cors: `origin` is required. Pass an explicit origin (or list), or '*' to deliberately allow every origin — omit `cors` entirely to emit no CORS headers.");
20
+ }
21
+ if (Array.isArray(config.origin) && config.origin.length === 0) {
22
+ throw new Error("[stitchkit] cors: `origin` cannot be an empty list. Pass explicit origins, or '*' to deliberately allow every origin.");
23
+ }
24
+ }
25
+ function resolveOrigin(config, requestOrigin) {
26
+ if (config.origin === undefined)
27
+ return;
28
+ if (config.origin === "*") {
29
+ return "*";
30
+ }
31
+ if (Array.isArray(config.origin)) {
32
+ if (!requestOrigin)
33
+ return;
34
+ const lower = requestOrigin.toLowerCase();
35
+ return config.origin.some((o) => o.toLowerCase() === lower) ? requestOrigin : undefined;
36
+ }
37
+ return config.origin;
38
+ }
39
+ function corsHeaders(config, requestOrigin) {
40
+ const allowOrigin = resolveOrigin(config, requestOrigin);
41
+ const headers = {
42
+ "Access-Control-Allow-Methods": config.methods ?? "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS",
43
+ "Access-Control-Allow-Headers": config.headers ?? DEFAULT_CORS_ALLOW_HEADERS
44
+ };
45
+ const expose = Array.isArray(config.exposeHeaders) ? config.exposeHeaders.join(", ") : config.exposeHeaders ?? DEFAULT_CORS_EXPOSE_HEADERS;
46
+ if (expose !== "") {
47
+ headers["Access-Control-Expose-Headers"] = expose;
48
+ }
49
+ if (allowOrigin !== undefined) {
50
+ headers["Access-Control-Allow-Origin"] = allowOrigin;
51
+ if (config.credentials) {
52
+ headers["Access-Control-Allow-Credentials"] = "true";
53
+ }
54
+ }
55
+ if (Array.isArray(config.origin)) {
56
+ headers.Vary = "Origin";
57
+ }
58
+ return headers;
59
+ }
60
+ function corsPreflightResponse(config, req) {
61
+ return new Response(null, {
62
+ status: 204,
63
+ headers: corsHeaders(config, req.headers.get("origin"))
64
+ });
65
+ }
66
+
67
+ // src/server/implement.ts
68
+ function isStreamingImplementation(value) {
69
+ return typeof value === "object" && value !== null && "kind" in value && value.kind === "stitchkit.multipart.stream";
70
+ }
71
+ function defineMultipartStream(endpoint, config) {
72
+ return buildMultipartStream(endpoint, config.files, config.handler);
73
+ }
74
+ function buildMultipartStream(endpoint, files, handler) {
75
+ const receivers = {};
76
+ for (const [key, receiver] of typedEntries(files)) {
77
+ receivers[String(key)] = receiver;
78
+ }
79
+ const declared = Object.keys(endpoint.multipart.files);
80
+ const configured = Object.keys(receivers);
81
+ if (declared.length !== configured.length || declared.some((field) => !Object.hasOwn(receivers, field))) {
82
+ throw new Error("Streaming multipart receivers must exactly match declared file fields");
83
+ }
84
+ return {
85
+ kind: "stitchkit.multipart.stream",
86
+ receivers,
87
+ execute(ctx, streamedFiles) {
88
+ return callRuntimeHandler(handler, { ...ctx, files: streamedFiles });
89
+ }
90
+ };
91
+ }
92
+ function createMultipartStream() {
93
+ return (endpoint, config) => buildMultipartStream(endpoint, config.files, config.handler);
94
+ }
95
+ function isStreamingEndpoint(endpoint) {
96
+ return endpoint.multipart?.delivery === "stream";
97
+ }
98
+ function contractOnlyService(contract) {
99
+ const handlers = {};
100
+ for (const [key, endpoint] of Object.entries(contract.endpoints)) {
101
+ if (isStreamingEndpoint(endpoint)) {
102
+ const receivers = {};
103
+ for (const field of Object.keys(endpoint.multipart.files)) {
104
+ receivers[field] = () => {
105
+ throw new Error("[stitchkit] contract-only service: handlers are not callable");
106
+ };
107
+ }
108
+ const streaming = {
109
+ kind: "stitchkit.multipart.stream",
110
+ receivers,
111
+ execute: () => {
112
+ throw new Error("[stitchkit] contract-only service: handlers are not callable");
113
+ }
114
+ };
115
+ handlers[key] = streaming;
116
+ continue;
117
+ }
118
+ handlers[key] = () => {
119
+ throw new Error("[stitchkit] contract-only service: handlers are not callable");
120
+ };
121
+ }
122
+ return bindContract(contract, handlers);
123
+ }
124
+ var HTTP_ONLY = Object.freeze(["HTTP"]);
125
+ function bindContract(contract, handlers) {
126
+ const methods = {};
127
+ const groupScope = contract.meta.scope ?? "public";
128
+ for (const [key, endpoint] of typedEntries(contract.endpoints)) {
129
+ const typedHandler = handlers[String(key)];
130
+ const isStreaming = endpoint.multipart?.delivery === "stream";
131
+ if (!isStreaming && typeof typedHandler !== "function") {
132
+ throw new Error(`[stitchkit] implement: missing handler for "${contract.meta.prefix}.${String(key)}"`);
133
+ }
134
+ if (isStreaming && !isStreamingImplementation(typedHandler)) {
135
+ throw new Error(`[stitchkit] implement: streaming multipart endpoint "${contract.meta.prefix}.${String(key)}" must use defineMultipartStream()`);
136
+ }
137
+ const streamingHandler = isStreamingImplementation(typedHandler) ? typedHandler : undefined;
138
+ const regularHandler = typeof typedHandler === "function" ? typedHandler : undefined;
139
+ methods[String(key)] = {
140
+ method: endpoint.method,
141
+ path: endpoint.path,
142
+ desc: endpoint.desc,
143
+ serviceName: contract.meta.prefix,
144
+ key: String(key),
145
+ toolName: "toolName" in endpoint ? endpoint.toolName : undefined,
146
+ expose: endpoint.rawResponse || endpoint.rawBody || endpoint.responseMeta ? HTTP_ONLY : endpoint.expose,
147
+ scope: endpoint.scope ?? groupScope,
148
+ paramsSchema: endpoint.params,
149
+ inputSchema: endpoint.input,
150
+ outputSchema: endpoint.output,
151
+ multipart: endpoint.multipart,
152
+ multipartReceivers: streamingHandler?.receivers,
153
+ maxJsonBodyBytes: endpoint.maxJsonBodyBytes,
154
+ idempotent: endpoint.idempotent,
155
+ ui: "ui" in endpoint ? endpoint.ui : undefined,
156
+ annotations: "annotations" in endpoint ? endpoint.annotations : undefined,
157
+ mcp: "mcp" in endpoint ? endpoint.mcp : undefined,
158
+ meta: mergeMeta(contract.meta.meta, endpoint.meta),
159
+ rawResponse: endpoint.rawResponse,
160
+ rawBody: endpoint.rawBody,
161
+ responseMeta: endpoint.responseMeta,
162
+ contentType: "contentType" in endpoint ? endpoint.contentType : undefined,
163
+ handler: streamingHandler ? (ctx) => streamingHandler.execute(ctx, ctx.files ?? {}) : (ctx) => {
164
+ if (!regularHandler) {
165
+ throw new Error(`[stitchkit] implement: missing handler for "${contract.meta.prefix}.${String(key)}"`);
166
+ }
167
+ return callRuntimeHandler(regularHandler, ctx);
168
+ }
169
+ };
170
+ }
171
+ return {
172
+ name: contract.meta.prefix,
173
+ prefix: contract.meta.prefix,
174
+ scope: groupScope,
175
+ methods
176
+ };
177
+ }
178
+ function implement(contract, handlers) {
179
+ return bindContract(contract, handlers);
180
+ }
181
+ function createImplement() {
182
+ return (contract, handlers) => implement(contract, handlers);
183
+ }
184
+ function createScopedImplement() {
185
+ const implementScoped = (contract, handlers) => bindContract(contract, handlers);
186
+ const stream = (scope, endpoint, config) => {
187
+ if (endpoint.scope !== undefined && endpoint.scope !== scope) {
188
+ throw new Error(`[stitchkit] createScopedImplement.stream: endpoint declares scope "${endpoint.scope}" but "${String(scope)}" was given`);
189
+ }
190
+ return buildMultipartStream(endpoint, config.files, config.handler);
191
+ };
192
+ const declare = (_contract) => (handlers) => handlers;
193
+ return Object.assign(implementScoped, { stream, declare });
194
+ }
195
+ function createScopedImplementRegistry() {
196
+ return (contracts, handlers) => transportResult(bindRegistry(contracts, handlers));
197
+ }
198
+ function isImplementationContract(value) {
199
+ return isRecord(value) && isRecord(value.meta) && typeof value.meta.prefix === "string" && isRecord(value.endpoints);
200
+ }
201
+ function bindRegistry(contracts, handlers) {
202
+ const contractKeys = Object.keys(contracts);
203
+ const handlerKeys = Object.keys(handlers);
204
+ const missing = contractKeys.filter((key) => !Object.hasOwn(handlers, key));
205
+ const extra = handlerKeys.filter((key) => !Object.hasOwn(contracts, key));
206
+ if (missing.length > 0 || extra.length > 0) {
207
+ throw new Error(`[stitchkit] implementRegistry: registry mismatch (missing: ${missing.join(", ") || "none"}; extra: ${extra.join(", ") || "none"})`);
208
+ }
209
+ const prefixes = new Map;
210
+ const services = [];
211
+ const byKey = {};
212
+ for (const [key, candidate] of Object.entries(contracts)) {
213
+ if (!isImplementationContract(candidate)) {
214
+ throw new TypeError(`[stitchkit] implementRegistry: registry entry "${key}" must be one contract; composed arrays and namespaces are not supported`);
215
+ }
216
+ const contract = candidate;
217
+ const groupScope = contract.meta.scope ?? "public";
218
+ const identity = `${groupScope}\x00${contract.meta.prefix}`;
219
+ const previousKey = prefixes.get(identity);
220
+ if (previousKey !== undefined) {
221
+ throw new Error(`[stitchkit] implementRegistry: duplicate contract prefix "${contract.meta.prefix}" in scope "${groupScope}" at "${previousKey}" and "${key}"`);
222
+ }
223
+ prefixes.set(identity, key);
224
+ const entryHandlers = handlers[key];
225
+ if (!isRecord(entryHandlers)) {
226
+ throw new TypeError(`[stitchkit] implementRegistry: handlers for "${key}" must be an object`);
227
+ }
228
+ const endpointKeys = Object.keys(contract.endpoints);
229
+ const handlerEntryKeys = Object.keys(entryHandlers);
230
+ const missingEndpoints = endpointKeys.filter((endpointKey) => !Object.hasOwn(entryHandlers, endpointKey));
231
+ const extraEndpoints = handlerEntryKeys.filter((endpointKey) => !Object.hasOwn(contract.endpoints, endpointKey));
232
+ if (missingEndpoints.length > 0 || extraEndpoints.length > 0) {
233
+ throw new Error(`[stitchkit] implementRegistry: handlers for "${key}" mismatch (missing: ${missingEndpoints.join(", ") || "none"}; extra: ${extraEndpoints.join(", ") || "none"})`);
234
+ }
235
+ const service = bindContract(contract, entryHandlers);
236
+ services.push(service);
237
+ byKey[key] = service;
238
+ }
239
+ Object.defineProperty(services, "byKey", { value: byKey, enumerable: false });
240
+ return transportResult(services);
241
+ }
242
+ function implementRegistry(contracts, handlers) {
243
+ return transportResult(bindRegistry(contracts, handlers));
244
+ }
245
+ function createImplementRegistry() {
246
+ return (contracts, handlers) => transportResult(bindRegistry(contracts, handlers));
247
+ }
248
+
249
+ export { DEFAULT_CORS_ALLOW_HEADERS, DEFAULT_CORS_EXPOSE_HEADERS, assertCorsConfig, corsHeaders, corsPreflightResponse, defineMultipartStream, createMultipartStream, contractOnlyService, implement, createImplement, createScopedImplement, createScopedImplementRegistry, implementRegistry, createImplementRegistry };
@@ -6,7 +6,7 @@ import {
6
6
  mapObject,
7
7
  parseTrailingWildcard,
8
8
  typedEntries
9
- } from "./index-h05ygjqx.js";
9
+ } from "./index-bmnhqtya.js";
10
10
 
11
11
  // src/browser/cancellation.ts
12
12
  class RequestCancellationError extends Error {
@@ -14,10 +14,10 @@ import {
14
14
  runWithRequestContext,
15
15
  safeJsonParse,
16
16
  validateDeclaredOutput
17
- } from "./index-jewp9r0a.js";
17
+ } from "./index-8qghvg18.js";
18
18
  import {
19
19
  isRecord
20
- } from "./index-h05ygjqx.js";
20
+ } from "./index-bmnhqtya.js";
21
21
 
22
22
  // src/tools/coerce.ts
23
23
  import { z } from "zod";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  isUnsafeKey
3
- } from "./index-jewp9r0a.js";
3
+ } from "./index-8qghvg18.js";
4
4
  import {
5
5
  isRecord
6
- } from "./index-h05ygjqx.js";
6
+ } from "./index-bmnhqtya.js";
7
7
 
8
8
  // src/observability/sanitize.ts
9
9
  var SENSITIVE_WORDS = new Set([
@@ -5,10 +5,10 @@ import {
5
5
  isUnsafeKey,
6
6
  safeJsonParse,
7
7
  unauthorized
8
- } from "./index-jewp9r0a.js";
8
+ } from "./index-8qghvg18.js";
9
9
  import {
10
10
  isRecord
11
- } from "./index-h05ygjqx.js";
11
+ } from "./index-bmnhqtya.js";
12
12
 
13
13
  // src/server/middleware/cookies.ts
14
14
  function parseCookies(header) {
@@ -150,6 +150,9 @@ function extractToken(req, cookieName) {
150
150
  }
151
151
  return null;
152
152
  }
153
+ function isThenable(value) {
154
+ return "then" in value && typeof value.then === "function";
155
+ }
153
156
  function createAuthHook(config) {
154
157
  const onAnonymous = config.onAnonymous ?? (() => unauthorized());
155
158
  const onForbidden = config.onForbidden ?? (() => forbidden());
@@ -159,10 +162,18 @@ function createAuthHook(config) {
159
162
  const scope = endpoint.scope ?? config.defaultScope;
160
163
  if (!scope)
161
164
  return;
162
- const rule = Object.hasOwn(config.rules, scope) ? config.rules[scope] : undefined;
163
- if (!rule) {
165
+ const entry = Object.hasOwn(config.rules, scope) ? config.rules[scope] : undefined;
166
+ if (!entry) {
164
167
  throw new Error(`[stitchkit] auth: no rule for scope "${scope}"`);
165
168
  }
169
+ const rule = typeof entry === "object" ? entry.rule : entry;
170
+ if (typeof entry === "object" && entry.inject && identity !== null) {
171
+ const fields = entry.inject(identity, ctx);
172
+ if (isThenable(fields)) {
173
+ throw new Error(`[stitchkit] auth: the inject of scope "${scope}" must be synchronous — an async inject merges a Promise, not fields`);
174
+ }
175
+ Object.assign(ctx, fields);
176
+ }
166
177
  if (rule === "public")
167
178
  return;
168
179
  if (!identity) {
package/dist/node.d.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  */
7
7
  export { AppError, appError, badRequest, conflict, forbidden, notFound, rateLimited, unauthorized, } from './contract';
8
8
  export { createHandler } from './server/create';
9
- export { createImplement, createImplementRegistry, createMultipartStream, createScopedImplement, createScopedImplementRegistry, type ExactRegistryHandlers, type ExactScopedRegistryHandlers, type ImplementationRegistry, implement, implementRegistry, type MultipartStreamConfig, type RegistryHandlers, type ScopedImplementationRegistry, type ScopedRegistryHandlers, type StreamScope, } from './server/implement';
9
+ export { createImplement, createImplementRegistry, createMultipartStream, createScopedImplement, createScopedImplementRegistry, type ExactRegistryHandlers, type ExactScopedRegistryHandlers, type ImplementationRegistry, implement, implementRegistry, type KeyedServices, type MultipartStreamConfig, type RegistryHandlers, type ScopedImplementationRegistry, type ScopedRegistryHandlers, type StreamScope, } from './server/implement';
10
10
  export type { LogFormat } from './server/logger';
11
11
  export { type NodeRuntimeServer, type NodeServerConfig, type NodeServerHandle, type NodeSocketLifecycle, serveNode, } from './server/node';
12
12
  export { bindProcessSignals, type ProcessSignalName, type ProcessSignalsBinding, type ProcessSignalsErrorPhase, type ProcessSignalsOptions, type ShutdownTarget, type SignalSource, } from './server/process-signals';
@@ -1 +1 @@
1
- {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,WAAW,EACX,YAAY,GACb,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EACL,eAAe,EACf,uBAAuB,EACvB,qBAAqB,EACrB,qBAAqB,EACrB,6BAA6B,EAC7B,KAAK,qBAAqB,EAC1B,KAAK,2BAA2B,EAChC,KAAK,sBAAsB,EAC3B,SAAS,EACT,iBAAiB,EACjB,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,KAAK,4BAA4B,EACjC,KAAK,sBAAsB,EAC3B,KAAK,WAAW,GACjB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,SAAS,GACV,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,kBAAkB,EAClB,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAC1B,KAAK,wBAAwB,EAC7B,KAAK,qBAAqB,EAC1B,KAAK,cAAc,EACnB,KAAK,YAAY,GAClB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,kBAAkB,EAClB,KAAK,cAAc,EACnB,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,GAC1B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,qBAAqB,EACrB,KAAK,cAAc,EACnB,oBAAoB,EACpB,KAAK,aAAa,EAClB,mBAAmB,EACnB,KAAK,cAAc,EACnB,oBAAoB,GACrB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,wBAAwB,IAAI,oBAAoB,EAChD,KAAK,wBAAwB,IAAI,oBAAoB,EACrD,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,GAC1B,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,cAAc,EACd,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,aAAa,EACb,UAAU,EACV,QAAQ,EACR,eAAe,EACf,aAAa,EACb,cAAc,EACd,UAAU,GACX,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,WAAW,EACX,YAAY,GACb,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EACL,eAAe,EACf,uBAAuB,EACvB,qBAAqB,EACrB,qBAAqB,EACrB,6BAA6B,EAC7B,KAAK,qBAAqB,EAC1B,KAAK,2BAA2B,EAChC,KAAK,sBAAsB,EAC3B,SAAS,EACT,iBAAiB,EACjB,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,KAAK,4BAA4B,EACjC,KAAK,sBAAsB,EAC3B,KAAK,WAAW,GACjB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,SAAS,GACV,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,kBAAkB,EAClB,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAC1B,KAAK,wBAAwB,EAC7B,KAAK,qBAAqB,EAC1B,KAAK,cAAc,EACnB,KAAK,YAAY,GAClB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,kBAAkB,EAClB,KAAK,cAAc,EACnB,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,GAC1B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,qBAAqB,EACrB,KAAK,cAAc,EACnB,oBAAoB,EACpB,KAAK,aAAa,EAClB,mBAAmB,EACnB,KAAK,cAAc,EACnB,oBAAoB,GACrB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,wBAAwB,IAAI,oBAAoB,EAChD,KAAK,wBAAwB,IAAI,oBAAoB,EACrD,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,GAC1B,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,cAAc,EACd,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,aAAa,EACb,UAAU,EACV,QAAQ,EACR,eAAe,EACf,aAAa,EACb,cAAc,EACd,UAAU,GACX,MAAM,gBAAgB,CAAC"}
package/dist/node.js CHANGED
@@ -6,17 +6,18 @@ import {
6
6
  bindProcessSignals,
7
7
  bindRealtimeServer,
8
8
  createHandler,
9
+ createServerLifecycle,
10
+ createSocketIOServer
11
+ } from "./index-d6jj3cp5.js";
12
+ import {
9
13
  createImplement,
10
14
  createImplementRegistry,
11
15
  createMultipartStream,
12
16
  createScopedImplement,
13
17
  createScopedImplementRegistry,
14
- createServerLifecycle,
15
- createSocketIOServer,
16
18
  implement,
17
19
  implementRegistry
18
- } from "./index-a4qmxybq.js";
19
- import"./index-r1qp4rve.js";
20
+ } from "./index-drsnz8gw.js";
20
21
  import {
21
22
  AppError,
22
23
  appError,
@@ -26,8 +27,8 @@ import {
26
27
  notFound,
27
28
  rateLimited,
28
29
  unauthorized
29
- } from "./index-jewp9r0a.js";
30
- import"./index-h05ygjqx.js";
30
+ } from "./index-8qghvg18.js";
31
+ import"./index-bmnhqtya.js";
31
32
  // src/server/node.ts
32
33
  import { serve } from "srvx/node";
33
34
  async function serveNode(config) {
@@ -3,7 +3,7 @@ import {
3
3
  redact,
4
4
  sanitizePayload,
5
5
  truncatePreview
6
- } from "../index-v58mwa19.js";
6
+ } from "../index-psrxjvbw.js";
7
7
  import {
8
8
  getRequestContext,
9
9
  getTraceId,
@@ -15,7 +15,7 @@ import {
15
15
  setRequestError,
16
16
  setRequestUser,
17
17
  wrapInRequestContext
18
- } from "../index-jewp9r0a.js";
18
+ } from "../index-8qghvg18.js";
19
19
  import {
20
20
  childSpan,
21
21
  createTraceContext,
@@ -24,7 +24,7 @@ import {
24
24
  parseTraceparent,
25
25
  resolvePropagationContext,
26
26
  resolveTraceContext
27
- } from "../index-h05ygjqx.js";
27
+ } from "../index-bmnhqtya.js";
28
28
 
29
29
  // src/observability/status.ts
30
30
  import { z } from "zod";