stitchkit 0.50.0 → 0.51.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 +3 -3
- package/dist/{index-a4qmxybq.js → index-8mt47qk7.js} +5 -154
- package/dist/{index-jewp9r0a.js → index-8qghvg18.js} +1 -1
- package/dist/{index-h05ygjqx.js → index-bmnhqtya.js} +4 -1
- package/dist/{index-p9d4cxt5.js → index-gff2mxzk.js} +1 -1
- package/dist/{index-0hj37z43.js → index-h55e1wyq.js} +2 -2
- package/dist/{index-v58mwa19.js → index-psrxjvbw.js} +2 -2
- package/dist/{index-03j2t778.js → index-tw7mhqgy.js} +15 -4
- package/dist/index-ynts85ew.js +246 -0
- package/dist/node.d.ts +1 -1
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +7 -6
- package/dist/observability/index.js +3 -3
- package/dist/server/implement.d.ts +30 -3
- package/dist/server/implement.d.ts.map +1 -1
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +14 -14
- package/dist/server/middleware/auth.d.ts +63 -3
- package/dist/server/middleware/auth.d.ts.map +1 -1
- package/dist/testing.js +2 -2
- package/dist/tools/list-names.d.ts +13 -1
- package/dist/tools/list-names.d.ts.map +1 -1
- package/dist/tools.d.ts +1 -1
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +13 -8
- package/llms-full.txt +145 -4
- package/package.json +1 -1
- package/dist/index-r1qp4rve.js +0 -57
package/dist/cli.js
CHANGED
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
emitResult,
|
|
5
5
|
parseCliArgs,
|
|
6
6
|
pollUntilDone
|
|
7
|
-
} from "./index-
|
|
7
|
+
} from "./index-h55e1wyq.js";
|
|
8
8
|
import"./index-yxpe3phd.js";
|
|
9
|
-
import"./index-
|
|
10
|
-
import"./index-
|
|
9
|
+
import"./index-8qghvg18.js";
|
|
10
|
+
import"./index-bmnhqtya.js";
|
|
11
11
|
export {
|
|
12
12
|
pollUntilDone,
|
|
13
13
|
parseCliArgs,
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
assertCorsConfig,
|
|
3
3
|
corsHeaders,
|
|
4
4
|
corsPreflightResponse
|
|
5
|
-
} from "./index-
|
|
5
|
+
} from "./index-ynts85ew.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-
|
|
25
|
+
} from "./index-8qghvg18.js";
|
|
27
26
|
import {
|
|
28
27
|
__require,
|
|
29
|
-
callRuntimeHandler,
|
|
30
28
|
isRecord,
|
|
31
29
|
parseTrailingWildcard,
|
|
32
|
-
resolveTraceContext
|
|
33
|
-
|
|
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,
|
|
2155
|
+
export { parseMultipart, createHandler, ShutdownStateSchema, ShutdownOptionsSchema, ShutdownStatusSchema, ShutdownResultSchema, createServerLifecycle, bindProcessSignals, bindRealtimeServer, webSocketLane, composeWebSocketHandlers, createSocketIOServer, socketIoLane };
|
|
@@ -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 };
|
|
@@ -14,10 +14,10 @@ import {
|
|
|
14
14
|
runWithRequestContext,
|
|
15
15
|
safeJsonParse,
|
|
16
16
|
validateDeclaredOutput
|
|
17
|
-
} from "./index-
|
|
17
|
+
} from "./index-8qghvg18.js";
|
|
18
18
|
import {
|
|
19
19
|
isRecord
|
|
20
|
-
} from "./index-
|
|
20
|
+
} from "./index-bmnhqtya.js";
|
|
21
21
|
|
|
22
22
|
// src/tools/coerce.ts
|
|
23
23
|
import { z } from "zod";
|
|
@@ -5,10 +5,10 @@ import {
|
|
|
5
5
|
isUnsafeKey,
|
|
6
6
|
safeJsonParse,
|
|
7
7
|
unauthorized
|
|
8
|
-
} from "./index-
|
|
8
|
+
} from "./index-8qghvg18.js";
|
|
9
9
|
import {
|
|
10
10
|
isRecord
|
|
11
|
-
} from "./index-
|
|
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
|
|
163
|
-
if (!
|
|
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) {
|
|
@@ -0,0 +1,246 @@
|
|
|
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
|
+
return Object.assign(implementScoped, { stream });
|
|
193
|
+
}
|
|
194
|
+
function createScopedImplementRegistry() {
|
|
195
|
+
return (contracts, handlers) => transportResult(bindRegistry(contracts, handlers));
|
|
196
|
+
}
|
|
197
|
+
function isImplementationContract(value) {
|
|
198
|
+
return isRecord(value) && isRecord(value.meta) && typeof value.meta.prefix === "string" && isRecord(value.endpoints);
|
|
199
|
+
}
|
|
200
|
+
function bindRegistry(contracts, handlers) {
|
|
201
|
+
const contractKeys = Object.keys(contracts);
|
|
202
|
+
const handlerKeys = Object.keys(handlers);
|
|
203
|
+
const missing = contractKeys.filter((key) => !Object.hasOwn(handlers, key));
|
|
204
|
+
const extra = handlerKeys.filter((key) => !Object.hasOwn(contracts, key));
|
|
205
|
+
if (missing.length > 0 || extra.length > 0) {
|
|
206
|
+
throw new Error(`[stitchkit] implementRegistry: registry mismatch (missing: ${missing.join(", ") || "none"}; extra: ${extra.join(", ") || "none"})`);
|
|
207
|
+
}
|
|
208
|
+
const prefixes = new Map;
|
|
209
|
+
const services = [];
|
|
210
|
+
const byKey = {};
|
|
211
|
+
for (const [key, candidate] of Object.entries(contracts)) {
|
|
212
|
+
if (!isImplementationContract(candidate)) {
|
|
213
|
+
throw new TypeError(`[stitchkit] implementRegistry: registry entry "${key}" must be one contract; composed arrays and namespaces are not supported`);
|
|
214
|
+
}
|
|
215
|
+
const contract = candidate;
|
|
216
|
+
const previousKey = prefixes.get(contract.meta.prefix);
|
|
217
|
+
if (previousKey !== undefined) {
|
|
218
|
+
throw new Error(`[stitchkit] implementRegistry: duplicate contract prefix "${contract.meta.prefix}" at "${previousKey}" and "${key}"`);
|
|
219
|
+
}
|
|
220
|
+
prefixes.set(contract.meta.prefix, key);
|
|
221
|
+
const entryHandlers = handlers[key];
|
|
222
|
+
if (!isRecord(entryHandlers)) {
|
|
223
|
+
throw new TypeError(`[stitchkit] implementRegistry: handlers for "${key}" must be an object`);
|
|
224
|
+
}
|
|
225
|
+
const endpointKeys = Object.keys(contract.endpoints);
|
|
226
|
+
const handlerEntryKeys = Object.keys(entryHandlers);
|
|
227
|
+
const missingEndpoints = endpointKeys.filter((endpointKey) => !Object.hasOwn(entryHandlers, endpointKey));
|
|
228
|
+
const extraEndpoints = handlerEntryKeys.filter((endpointKey) => !Object.hasOwn(contract.endpoints, endpointKey));
|
|
229
|
+
if (missingEndpoints.length > 0 || extraEndpoints.length > 0) {
|
|
230
|
+
throw new Error(`[stitchkit] implementRegistry: handlers for "${key}" mismatch (missing: ${missingEndpoints.join(", ") || "none"}; extra: ${extraEndpoints.join(", ") || "none"})`);
|
|
231
|
+
}
|
|
232
|
+
const service = bindContract(contract, entryHandlers);
|
|
233
|
+
services.push(service);
|
|
234
|
+
byKey[key] = service;
|
|
235
|
+
}
|
|
236
|
+
Object.defineProperty(services, "byKey", { value: byKey, enumerable: false });
|
|
237
|
+
return transportResult(services);
|
|
238
|
+
}
|
|
239
|
+
function implementRegistry(contracts, handlers) {
|
|
240
|
+
return transportResult(bindRegistry(contracts, handlers));
|
|
241
|
+
}
|
|
242
|
+
function createImplementRegistry() {
|
|
243
|
+
return (contracts, handlers) => transportResult(bindRegistry(contracts, handlers));
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export { DEFAULT_CORS_ALLOW_HEADERS, DEFAULT_CORS_EXPOSE_HEADERS, assertCorsConfig, corsHeaders, corsPreflightResponse, defineMultipartStream, createMultipartStream, contractOnlyService, implement, createImplement, createScopedImplement, createScopedImplementRegistry, implementRegistry, createImplementRegistry };
|
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';
|
package/dist/node.d.ts.map
CHANGED
|
@@ -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-8mt47qk7.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-
|
|
19
|
-
import"./index-r1qp4rve.js";
|
|
20
|
+
} from "./index-ynts85ew.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-
|
|
30
|
-
import"./index-
|
|
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-
|
|
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-
|
|
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-
|
|
27
|
+
} from "../index-bmnhqtya.js";
|
|
28
28
|
|
|
29
29
|
// src/observability/status.ts
|
|
30
30
|
import { z } from "zod";
|