stitchkit 0.80.1 → 0.81.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/agent-runtime/provider-failure.d.ts.map +1 -1
- package/dist/agent-runtime-coding-tools.js +1 -1
- package/dist/agent-runtime-harness.js +7 -5
- package/dist/agent-runtime.js +15 -19
- package/dist/application/notification-outbox.d.ts +80 -0
- package/dist/application/notification-outbox.d.ts.map +1 -0
- package/dist/application/process-lifecycle.d.ts +166 -0
- package/dist/application/process-lifecycle.d.ts.map +1 -0
- package/dist/application/state-store.d.ts +18 -0
- package/dist/application/state-store.d.ts.map +1 -0
- package/dist/application.d.ts +3 -0
- package/dist/application.d.ts.map +1 -1
- package/dist/application.js +568 -11
- package/dist/browser/http.d.ts +14 -0
- package/dist/browser/http.d.ts.map +1 -1
- package/dist/browser/resumable.d.ts +6 -0
- package/dist/browser/resumable.d.ts.map +1 -1
- package/dist/cli.js +9 -7
- package/dist/contract/define.d.ts +27 -4
- package/dist/contract/define.d.ts.map +1 -1
- package/dist/contract/errors-factory.d.ts +22 -3
- package/dist/contract/errors-factory.d.ts.map +1 -1
- package/dist/contract/index.d.ts +2 -2
- package/dist/contract/index.d.ts.map +1 -1
- package/dist/contract/index.js +1 -1
- package/dist/geo/index.d.ts +4 -0
- package/dist/geo/index.d.ts.map +1 -0
- package/dist/geo/maxmind.d.ts +4 -0
- package/dist/geo/maxmind.d.ts.map +1 -0
- package/dist/geo/resolver.d.ts +9 -0
- package/dist/geo/resolver.d.ts.map +1 -0
- package/dist/geo/types.d.ts +59 -0
- package/dist/geo/types.d.ts.map +1 -0
- package/dist/geo.d.ts +3 -0
- package/dist/geo.d.ts.map +1 -0
- package/dist/geo.js +245 -0
- package/dist/{index-9s6z8vgf.js → index-1dd82z59.js} +1 -1
- package/dist/{index-vwp69871.js → index-27487ay5.js} +4 -4
- package/dist/{index-7ag81v2f.js → index-27gm3d6g.js} +10 -321
- package/dist/{index-y01f4223.js → index-2bxcmqkb.js} +127 -43
- package/dist/{index-dxesg10d.js → index-3ds42w1w.js} +3 -3
- package/dist/index-3xwxfj4z.js +334 -0
- package/dist/{index-egaabxkz.js → index-5z8tezpx.js} +2 -2
- package/dist/{index-fxz0v7vg.js → index-7evdsyez.js} +1 -83
- package/dist/{index-xcetq6j3.js → index-90d7h2cr.js} +32 -13
- package/dist/{index-t0z5p5w6.js → index-apn8repm.js} +1 -1
- package/dist/{index-758zx2ha.js → index-dggwnnjv.js} +2 -2
- package/dist/{index-dwf1f2ap.js → index-f9mb610r.js} +1 -62
- package/dist/{index-5mtc969c.js → index-fwbkp2jn.js} +1 -1
- package/dist/index-p13mwz16.js +16 -0
- package/dist/{index-m9kzax52.js → index-p18f59zy.js} +21 -5
- package/dist/{index-hb5pf3s4.js → index-qmgs7y68.js} +1 -1
- package/dist/{index-2rwwmr7v.js → index-s4ckmrac.js} +1 -1
- package/dist/{index-h4pj6fta.js → index-s8cnx02s.js} +8 -5
- package/dist/{index-k9ay6v6d.js → index-vchzar7e.js} +74 -23
- package/dist/index-vsbzgd7b.js +84 -0
- package/dist/{index-dxjjv399.js → index-xbppf54a.js} +95 -15
- package/dist/index-y91ry9h2.js +4 -0
- package/dist/index-zcgf3gqf.js +83 -0
- package/dist/index.js +17 -13
- package/dist/internal/error-message.d.ts +3 -0
- package/dist/internal/error-message.d.ts.map +1 -0
- package/dist/internal/media-type.d.ts +8 -0
- package/dist/internal/media-type.d.ts.map +1 -0
- package/dist/internal/route-pattern.d.ts +21 -0
- package/dist/internal/route-pattern.d.ts.map +1 -1
- package/dist/node.js +9 -6
- package/dist/observability/audit.d.ts +28 -1
- package/dist/observability/audit.d.ts.map +1 -1
- package/dist/observability/bounded-logger.d.ts +20 -0
- package/dist/observability/bounded-logger.d.ts.map +1 -0
- package/dist/observability/context.d.ts +12 -3
- package/dist/observability/context.d.ts.map +1 -1
- package/dist/observability/index.d.ts +3 -2
- package/dist/observability/index.d.ts.map +1 -1
- package/dist/observability/index.js +138 -8
- package/dist/observability/sanitize.d.ts +17 -0
- package/dist/observability/sanitize.d.ts.map +1 -1
- package/dist/primitives.js +1 -1
- package/dist/react/query-client.d.ts +33 -0
- package/dist/react/query-client.d.ts.map +1 -0
- package/dist/react.d.ts +1 -0
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +83 -1
- package/dist/realtime/registry.d.ts +93 -0
- package/dist/realtime/registry.d.ts.map +1 -0
- package/dist/release/header.d.ts +9 -0
- package/dist/release/header.d.ts.map +1 -0
- package/dist/release/marker.d.ts +39 -0
- package/dist/release/marker.d.ts.map +1 -0
- package/dist/release/socket.d.ts +44 -0
- package/dist/release/socket.d.ts.map +1 -0
- package/dist/release/watcher.d.ts +66 -0
- package/dist/release/watcher.d.ts.map +1 -0
- package/dist/release.d.ts +15 -0
- package/dist/release.d.ts.map +1 -0
- package/dist/release.js +196 -0
- package/dist/remote.js +12 -7
- package/dist/server/context.d.ts +2 -1
- package/dist/server/context.d.ts.map +1 -1
- package/dist/server/create.d.ts.map +1 -1
- package/dist/server/error-hook.d.ts +17 -16
- package/dist/server/error-hook.d.ts.map +1 -1
- package/dist/server/file-state-store.d.ts +23 -0
- package/dist/server/file-state-store.d.ts.map +1 -0
- package/dist/server/implement.d.ts.map +1 -1
- package/dist/server/index.d.ts +5 -2
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +469 -11
- package/dist/server/middleware/cookies.d.ts +33 -1
- package/dist/server/middleware/cookies.d.ts.map +1 -1
- package/dist/server/middleware/cors.d.ts +16 -1
- package/dist/server/middleware/cors.d.ts.map +1 -1
- package/dist/server/openapi.d.ts.map +1 -1
- package/dist/server/release-signal.d.ts +35 -0
- package/dist/server/release-signal.d.ts.map +1 -0
- package/dist/server/router.d.ts.map +1 -1
- package/dist/server/types.d.ts +26 -2
- package/dist/server/types.d.ts.map +1 -1
- package/dist/testing/surface-manifest.d.ts.map +1 -1
- package/dist/testing.js +7 -4
- package/dist/tool-invoker.js +8 -6
- package/dist/tools/download-core.d.ts.map +1 -1
- package/dist/tools/remote.d.ts.map +1 -1
- package/dist/tools/view-file.d.ts.map +1 -1
- package/dist/tools.js +20 -16
- package/dist/tracking/attribution.d.ts +51 -0
- package/dist/tracking/attribution.d.ts.map +1 -0
- package/dist/tracking/beacon.d.ts +14 -0
- package/dist/tracking/beacon.d.ts.map +1 -0
- package/dist/tracking/clicks.d.ts +46 -0
- package/dist/tracking/clicks.d.ts.map +1 -0
- package/dist/tracking/client.d.ts +121 -0
- package/dist/tracking/client.d.ts.map +1 -0
- package/dist/tracking/contract.d.ts +46 -0
- package/dist/tracking/contract.d.ts.map +1 -0
- package/dist/tracking/delivery.d.ts +24 -0
- package/dist/tracking/delivery.d.ts.map +1 -0
- package/dist/tracking/host.d.ts +43 -0
- package/dist/tracking/host.d.ts.map +1 -0
- package/dist/tracking/once-per-page.d.ts +17 -0
- package/dist/tracking/once-per-page.d.ts.map +1 -0
- package/dist/tracking/outbox-storage-indexeddb.d.ts +12 -0
- package/dist/tracking/outbox-storage-indexeddb.d.ts.map +1 -0
- package/dist/tracking/outbox-storage-memory.d.ts +12 -0
- package/dist/tracking/outbox-storage-memory.d.ts.map +1 -0
- package/dist/tracking/outbox.d.ts +98 -0
- package/dist/tracking/outbox.d.ts.map +1 -0
- package/dist/tracking/schemas.d.ts +183 -0
- package/dist/tracking/schemas.d.ts.map +1 -0
- package/dist/tracking/scroll.d.ts +22 -0
- package/dist/tracking/scroll.d.ts.map +1 -0
- package/dist/tracking/sequence-reserve.d.ts +34 -0
- package/dist/tracking/sequence-reserve.d.ts.map +1 -0
- package/dist/tracking/server/active-interval.d.ts +26 -0
- package/dist/tracking/server/active-interval.d.ts.map +1 -0
- package/dist/tracking/server/bot.d.ts +9 -0
- package/dist/tracking/server/bot.d.ts.map +1 -0
- package/dist/tracking/server/disposition.d.ts +55 -0
- package/dist/tracking/server/disposition.d.ts.map +1 -0
- package/dist/tracking/server/hash.d.ts +13 -0
- package/dist/tracking/server/hash.d.ts.map +1 -0
- package/dist/tracking/server/presence.d.ts +27 -0
- package/dist/tracking/server/presence.d.ts.map +1 -0
- package/dist/tracking/server/visit-lease.d.ts +92 -0
- package/dist/tracking/server/visit-lease.d.ts.map +1 -0
- package/dist/tracking/visible-time.d.ts +38 -0
- package/dist/tracking/visible-time.d.ts.map +1 -0
- package/dist/tracking-server.d.ts +14 -0
- package/dist/tracking-server.d.ts.map +1 -0
- package/dist/tracking-server.js +173 -0
- package/dist/tracking.d.ts +24 -0
- package/dist/tracking.d.ts.map +1 -0
- package/dist/tracking.js +1090 -0
- package/llms-full.txt +938 -29
- package/llms.txt +4 -0
- package/package.json +25 -4
|
@@ -1,77 +1,104 @@
|
|
|
1
1
|
import {
|
|
2
|
-
AppError
|
|
2
|
+
AppError,
|
|
3
|
+
STITCH_ERROR_STATUS,
|
|
4
|
+
isStitchErrorCode
|
|
3
5
|
} from "./index-0w9abg87.js";
|
|
4
6
|
import {
|
|
5
7
|
isUnsafeKey
|
|
6
8
|
} from "./index-kzfs85xp.js";
|
|
7
9
|
import {
|
|
8
|
-
mapObjectTypeBoundary
|
|
10
|
+
mapObjectTypeBoundary,
|
|
11
|
+
transportResult,
|
|
12
|
+
typedEntries
|
|
9
13
|
} from "./index-77fekveh.js";
|
|
10
14
|
|
|
11
|
-
// src/contract/define.ts
|
|
12
|
-
import { z } from "zod";
|
|
13
|
-
|
|
14
15
|
// src/internal/route-pattern.ts
|
|
16
|
+
import { z } from "zod";
|
|
15
17
|
var PARAM_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
16
18
|
function joinRoutePath(...parts) {
|
|
17
19
|
const joined = parts.filter((part) => Boolean(part)).map((part) => part.replace(/^\/+|\/+$/g, "")).filter(Boolean).join("/");
|
|
18
20
|
return `/${joined}`;
|
|
19
21
|
}
|
|
20
|
-
function
|
|
22
|
+
function parseRoutePattern(path) {
|
|
21
23
|
const segments = path.split("/").filter(Boolean);
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
+
const parameters = [];
|
|
25
|
+
const names = new Set;
|
|
24
26
|
for (const [segmentIndex, segment] of segments.entries()) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (!PARAM_IDENTIFIER.test(name2)) {
|
|
28
|
-
throw new Error(`Invalid route parameter name "${name2}" in path "${path}"`);
|
|
29
|
-
}
|
|
30
|
-
if (paramNames.has(name2)) {
|
|
31
|
-
throw new Error(`Duplicate route parameter name "${name2}" in path "${path}"`);
|
|
32
|
-
}
|
|
33
|
-
paramNames.add(name2);
|
|
34
|
-
continue;
|
|
35
|
-
}
|
|
36
|
-
if (!segment.startsWith("*")) {
|
|
27
|
+
const marker = segment.at(0);
|
|
28
|
+
if (marker !== ":" && marker !== "*") {
|
|
37
29
|
if (segment.includes("*")) {
|
|
38
30
|
throw new Error(`Wildcard must occupy its own segment in path "${path}"`);
|
|
39
31
|
}
|
|
40
32
|
continue;
|
|
41
33
|
}
|
|
42
34
|
const name = segment.slice(1);
|
|
43
|
-
if (
|
|
35
|
+
if (marker === "*" && name === "") {
|
|
44
36
|
throw new Error(`Trailing wildcard in path "${path}" must be named, for example "/*filePath"`);
|
|
45
37
|
}
|
|
46
|
-
if (
|
|
38
|
+
if (!PARAM_IDENTIFIER.test(name) || isUnsafeKey(name)) {
|
|
39
|
+
const label = marker === "*" ? "wildcard" : "route parameter";
|
|
40
|
+
throw new Error(`Invalid ${label} name "${name}" in path "${path}"`);
|
|
41
|
+
}
|
|
42
|
+
if (names.has(name)) {
|
|
43
|
+
throw new Error(`Duplicate route parameter name "${name}" in path "${path}"`);
|
|
44
|
+
}
|
|
45
|
+
if (marker === "*" && segmentIndex !== segments.length - 1) {
|
|
47
46
|
throw new Error(`Wildcard "*${name}" must be the final segment in path "${path}"`);
|
|
48
47
|
}
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
names.add(name);
|
|
49
|
+
parameters.push({
|
|
50
|
+
name,
|
|
51
|
+
kind: marker === "*" ? "wildcard" : "segment",
|
|
52
|
+
segmentIndex
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return parameters;
|
|
56
|
+
}
|
|
57
|
+
function resolveRouteParamsSchema(path, schema) {
|
|
58
|
+
const parameters = parseRoutePattern(path);
|
|
59
|
+
if (parameters.length === 0)
|
|
60
|
+
return schema;
|
|
61
|
+
if (!schema) {
|
|
62
|
+
const shape = {};
|
|
63
|
+
for (const parameter of parameters)
|
|
64
|
+
shape[parameter.name] = z.string();
|
|
65
|
+
return z.object(shape);
|
|
66
|
+
}
|
|
67
|
+
const jsonSchema = z.toJSONSchema(schema, { io: "input", unrepresentable: "any" });
|
|
68
|
+
const properties = jsonSchema.properties;
|
|
69
|
+
if (!properties) {
|
|
70
|
+
throw new Error(`Route params schema for path "${path}" must be an object schema with a property per path field`);
|
|
71
|
+
}
|
|
72
|
+
for (const parameter of parameters) {
|
|
73
|
+
if (!(parameter.name in properties)) {
|
|
74
|
+
const kind = parameter.kind === "wildcard" ? "wildcard" : "path";
|
|
75
|
+
throw new Error(`Route params schema for path "${path}" is missing ${kind} field "${parameter.name}"`);
|
|
51
76
|
}
|
|
52
|
-
|
|
53
|
-
|
|
77
|
+
}
|
|
78
|
+
const names = new Set(parameters.map((parameter) => parameter.name));
|
|
79
|
+
const required = Array.isArray(jsonSchema.required) ? jsonSchema.required : [];
|
|
80
|
+
for (const field of required) {
|
|
81
|
+
if (typeof field === "string" && !names.has(field)) {
|
|
82
|
+
throw new Error(`Route params schema for path "${path}" requires field "${field}" that the path does not carry`);
|
|
54
83
|
}
|
|
55
|
-
wildcard = { name, segmentIndex };
|
|
56
84
|
}
|
|
57
|
-
return
|
|
85
|
+
return schema;
|
|
86
|
+
}
|
|
87
|
+
function parseTrailingWildcard(path) {
|
|
88
|
+
const wildcard = parseRoutePattern(path).find((parameter) => parameter.kind === "wildcard");
|
|
89
|
+
return wildcard ? { name: wildcard.name, segmentIndex: wildcard.segmentIndex } : null;
|
|
58
90
|
}
|
|
59
91
|
|
|
60
92
|
// src/contract/define.ts
|
|
61
93
|
var ALL_TRANSPORTS = ["HTTP", "MCP", "AGENT", "CLI"];
|
|
62
94
|
function defineContract(meta, endpoints) {
|
|
95
|
+
const materializedEndpoints = {};
|
|
96
|
+
for (const [key, endpoint] of Object.entries(endpoints)) {
|
|
97
|
+
const params = resolveRouteParamsSchema(endpoint.path, endpoint.params);
|
|
98
|
+
materializedEndpoints[key] = params === endpoint.params ? endpoint : { ...endpoint, params };
|
|
99
|
+
}
|
|
63
100
|
const toolTransports = new Map;
|
|
64
|
-
for (const [key, ep] of Object.entries(
|
|
65
|
-
const wildcard = parseTrailingWildcard(ep.path);
|
|
66
|
-
if (wildcard) {
|
|
67
|
-
if (!ep.params) {
|
|
68
|
-
throw new Error(`Contract "${meta.prefix}": endpoint "${key}" wildcard "${wildcard.name}" requires a params schema field`);
|
|
69
|
-
}
|
|
70
|
-
const paramsJson = z.toJSONSchema(ep.params, { io: "input" });
|
|
71
|
-
if (!paramsJson.properties || !(wildcard.name in paramsJson.properties)) {
|
|
72
|
-
throw new Error(`Contract "${meta.prefix}": endpoint "${key}" params schema is missing wildcard field "${wildcard.name}"`);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
101
|
+
for (const [key, ep] of Object.entries(materializedEndpoints)) {
|
|
75
102
|
if (ep.desc.trim() === "") {
|
|
76
103
|
throw new Error(`Contract "${meta.prefix}": endpoint "${key}" has an empty desc`);
|
|
77
104
|
}
|
|
@@ -88,6 +115,8 @@ function defineContract(meta, endpoints) {
|
|
|
88
115
|
assertHeadEndpoint(meta.prefix, key, ep);
|
|
89
116
|
if (ep.rawBody)
|
|
90
117
|
assertRawBodyEndpoint(meta.prefix, key, ep);
|
|
118
|
+
if (ep.safelistedBody)
|
|
119
|
+
assertSafelistedBodyEndpoint(meta.prefix, key, ep);
|
|
91
120
|
if ("responseMeta" in ep)
|
|
92
121
|
assertResponseMetaEndpoint(meta.prefix, key, ep);
|
|
93
122
|
if (!("toolName" in ep) || !ep.toolName)
|
|
@@ -108,7 +137,7 @@ function defineContract(meta, endpoints) {
|
|
|
108
137
|
toolTransports.set(ep.toolName, { key, transports });
|
|
109
138
|
}
|
|
110
139
|
}
|
|
111
|
-
return { meta, endpoints };
|
|
140
|
+
return { meta, endpoints: materializedEndpoints };
|
|
112
141
|
}
|
|
113
142
|
function assertPositiveLimit(where, name, value) {
|
|
114
143
|
if (value !== undefined && (!Number.isSafeInteger(value) || value <= 0)) {
|
|
@@ -238,6 +267,17 @@ function assertRawBodyEndpoint(prefix, key, ep) {
|
|
|
238
267
|
throw new Error(`${where} is HTTP-only — remove ${nonHttp.join(", ")} from expose`);
|
|
239
268
|
}
|
|
240
269
|
}
|
|
270
|
+
function assertSafelistedBodyEndpoint(prefix, key, ep) {
|
|
271
|
+
const where = `Contract "${prefix}": safelistedBody endpoint "${key}"`;
|
|
272
|
+
if (ep.method !== "POST")
|
|
273
|
+
throw new Error(`${where} must use POST`);
|
|
274
|
+
if (!ep.input)
|
|
275
|
+
throw new Error(`${where} must declare an input schema`);
|
|
276
|
+
if (ep.multipart)
|
|
277
|
+
throw new Error(`${where} cannot be multipart`);
|
|
278
|
+
if ("stream" in ep && ep.stream)
|
|
279
|
+
throw new Error(`${where} cannot be a streaming response`);
|
|
280
|
+
}
|
|
241
281
|
function assertResponseMetaEndpoint(prefix, key, ep) {
|
|
242
282
|
const where = `Contract "${prefix}": responseMeta endpoint "${key}"`;
|
|
243
283
|
if (!ep.responseMeta || typeof ep.responseMeta !== "object") {
|
|
@@ -287,7 +327,7 @@ function validateDefinition(code, definition) {
|
|
|
287
327
|
throw new Error(`[stitchkit] Error "${code}" message must be a non-empty string`);
|
|
288
328
|
}
|
|
289
329
|
}
|
|
290
|
-
function defineErrors(source) {
|
|
330
|
+
function defineErrors(source, mapping) {
|
|
291
331
|
const definitions = mapObjectTypeBoundary(source, (code, definition) => {
|
|
292
332
|
const name = String(code);
|
|
293
333
|
validateDefinition(name, definition);
|
|
@@ -312,11 +352,55 @@ function defineErrors(source) {
|
|
|
312
352
|
const codes = mapObjectTypeBoundary(definitions, (code) => String(code));
|
|
313
353
|
Object.freeze(codes);
|
|
314
354
|
const known = new Set(Object.keys(definitions));
|
|
355
|
+
for (const [code, target] of Object.entries(mapping?.map ?? {})) {
|
|
356
|
+
if (!isStitchErrorCode(code)) {
|
|
357
|
+
throw new Error(`[stitchkit] Error vocabulary contains unknown framework code "${code}"`);
|
|
358
|
+
}
|
|
359
|
+
if (typeof target !== "string" || !known.has(target)) {
|
|
360
|
+
throw new Error(`[stitchkit] Error vocabulary maps "${code}" to undeclared code "${String(target)}"`);
|
|
361
|
+
}
|
|
362
|
+
const status = STITCH_ERROR_STATUS[code];
|
|
363
|
+
if (definitions[target]?.status !== status) {
|
|
364
|
+
throw new Error(`[stitchkit] Error vocabulary maps "${code}" (${status}) to "${target}", declared with status ${String(definitions[target]?.status)}`);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
for (const [statusText, target] of Object.entries(mapping?.fallback ?? {})) {
|
|
368
|
+
const status = Number(statusText);
|
|
369
|
+
if (!Number.isInteger(status) || status < 400 || status > 599) {
|
|
370
|
+
throw new Error(`[stitchkit] Error vocabulary fallback status "${statusText}" must be an integer from 400 to 599`);
|
|
371
|
+
}
|
|
372
|
+
if (typeof target !== "string" || !known.has(target)) {
|
|
373
|
+
throw new Error(`[stitchkit] Error vocabulary fallback ${status} names undeclared code "${String(target)}"`);
|
|
374
|
+
}
|
|
375
|
+
if (definitions[target]?.status !== status) {
|
|
376
|
+
throw new Error(`[stitchkit] Error vocabulary fallback ${status} must name a code with status ${status}`);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
if (mapping?.exhaustive === true) {
|
|
380
|
+
for (const code of Object.keys(STITCH_ERROR_STATUS)) {
|
|
381
|
+
if (!Object.hasOwn(mapping.map, code)) {
|
|
382
|
+
throw new Error(`[stitchkit] Exhaustive error vocabulary is missing "${code}"`);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
const codeMap = {};
|
|
387
|
+
for (const [stitchCode, status] of typedEntries(STITCH_ERROR_STATUS)) {
|
|
388
|
+
const code = mapping?.map?.[stitchCode] ?? mapping?.fallback?.[status];
|
|
389
|
+
if (code === undefined)
|
|
390
|
+
continue;
|
|
391
|
+
if (!known.has(code)) {
|
|
392
|
+
throw new Error(`[stitchkit] Error vocabulary maps "${stitchCode}" to undeclared code "${code}"`);
|
|
393
|
+
}
|
|
394
|
+
codeMap[stitchCode] = code;
|
|
395
|
+
}
|
|
396
|
+
Object.freeze(codeMap);
|
|
397
|
+
const vocabularyCodeMap = transportResult(codeMap);
|
|
315
398
|
return Object.freeze({
|
|
316
399
|
errors,
|
|
317
400
|
codes,
|
|
318
401
|
definitions,
|
|
319
|
-
isCode: (code) => known.has(code)
|
|
402
|
+
isCode: (code) => known.has(code),
|
|
403
|
+
codeMap: vocabularyCodeMap
|
|
320
404
|
});
|
|
321
405
|
}
|
|
322
406
|
// src/contract/factory.ts
|
|
@@ -397,4 +481,4 @@ var ContractStreamFrameSchema = z4.discriminatedUnion("type", [
|
|
|
397
481
|
}).strict(),
|
|
398
482
|
z4.object({ type: z4.literal("end") }).strict()
|
|
399
483
|
]);
|
|
400
|
-
export { joinRoutePath, parseTrailingWildcard, ALL_TRANSPORTS, defineContract, mergeMeta, defineErrors, createContractFactory, bytesToBase64Url, base64UrlToBytes, paginatedSchema, encodeCursor, decodeCursor, DEFAULT_CONTRACT_STREAM_FRAME_BYTES, ContractStreamFrameSchema };
|
|
484
|
+
export { joinRoutePath, resolveRouteParamsSchema, parseTrailingWildcard, ALL_TRANSPORTS, defineContract, mergeMeta, defineErrors, createContractFactory, bytesToBase64Url, base64UrlToBytes, paginatedSchema, encodeCursor, decodeCursor, DEFAULT_CONTRACT_STREAM_FRAME_BYTES, ContractStreamFrameSchema };
|
|
@@ -3,15 +3,15 @@ import {
|
|
|
3
3
|
} from "./index-3xnq72rz.js";
|
|
4
4
|
import {
|
|
5
5
|
collectToolSurface
|
|
6
|
-
} from "./index-
|
|
6
|
+
} from "./index-s4ckmrac.js";
|
|
7
7
|
import {
|
|
8
8
|
createToolRunner,
|
|
9
9
|
formatToolError
|
|
10
|
-
} from "./index-
|
|
10
|
+
} from "./index-fwbkp2jn.js";
|
|
11
11
|
import {
|
|
12
12
|
isToolExecutionControlError,
|
|
13
13
|
toolResultFromError
|
|
14
|
-
} from "./index-
|
|
14
|
+
} from "./index-dggwnnjv.js";
|
|
15
15
|
import {
|
|
16
16
|
isRecord
|
|
17
17
|
} from "./index-77fekveh.js";
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createTraceContext,
|
|
3
|
+
formatTraceparent
|
|
4
|
+
} from "./index-zcgf3gqf.js";
|
|
5
|
+
import {
|
|
6
|
+
isRecord,
|
|
7
|
+
transportResult
|
|
8
|
+
} from "./index-77fekveh.js";
|
|
9
|
+
|
|
10
|
+
// src/browser/http.ts
|
|
11
|
+
import ky, {
|
|
12
|
+
isHTTPError,
|
|
13
|
+
isNetworkError,
|
|
14
|
+
isTimeoutError
|
|
15
|
+
} from "ky";
|
|
16
|
+
|
|
17
|
+
// src/browser/cancellation.ts
|
|
18
|
+
class RequestCancellationError extends Error {
|
|
19
|
+
cause;
|
|
20
|
+
constructor(cause) {
|
|
21
|
+
super(cause === "caller" ? "Request was aborted" : "Request timed out");
|
|
22
|
+
this.cause = cause;
|
|
23
|
+
this.name = "RequestCancellationError";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function createRequestCancellation(caller, timeoutMs) {
|
|
27
|
+
if (!caller && timeoutMs === undefined) {
|
|
28
|
+
return { signal: undefined, run: (operation) => operation() };
|
|
29
|
+
}
|
|
30
|
+
const timeoutController = timeoutMs === undefined ? undefined : new AbortController;
|
|
31
|
+
const signal = caller && timeoutController ? AbortSignal.any([caller, timeoutController.signal]) : caller ?? timeoutController?.signal;
|
|
32
|
+
let cause;
|
|
33
|
+
let timer;
|
|
34
|
+
const abortFromCaller = () => {
|
|
35
|
+
if (cause)
|
|
36
|
+
return;
|
|
37
|
+
cause = "caller";
|
|
38
|
+
};
|
|
39
|
+
if (caller?.aborted)
|
|
40
|
+
abortFromCaller();
|
|
41
|
+
else
|
|
42
|
+
caller?.addEventListener("abort", abortFromCaller, { once: true });
|
|
43
|
+
if (timeoutMs !== undefined && timeoutController) {
|
|
44
|
+
timer = setTimeout(() => {
|
|
45
|
+
if (cause)
|
|
46
|
+
return;
|
|
47
|
+
cause = "timeout";
|
|
48
|
+
timeoutController.abort(new DOMException("Request timed out", "TimeoutError"));
|
|
49
|
+
}, timeoutMs);
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
signal,
|
|
53
|
+
async run(operation) {
|
|
54
|
+
try {
|
|
55
|
+
if (cause === "caller")
|
|
56
|
+
throw cancellationError(cause);
|
|
57
|
+
return await operation(signal);
|
|
58
|
+
} catch (error) {
|
|
59
|
+
if (cause)
|
|
60
|
+
throw cancellationError(cause);
|
|
61
|
+
throw error;
|
|
62
|
+
} finally {
|
|
63
|
+
if (timer !== undefined)
|
|
64
|
+
clearTimeout(timer);
|
|
65
|
+
caller?.removeEventListener("abort", abortFromCaller);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function cancellationError(cause) {
|
|
71
|
+
return new RequestCancellationError(cause);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// src/browser/request-id.ts
|
|
75
|
+
var REQUEST_ID_HEADER = "x-request-id";
|
|
76
|
+
function responseTraceId(response) {
|
|
77
|
+
return response?.headers.get(REQUEST_ID_HEADER) ?? undefined;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// src/browser/http.ts
|
|
81
|
+
var API_ERROR_BRAND = Symbol.for("stitchkit.ApiError");
|
|
82
|
+
function messageForCode(code, message) {
|
|
83
|
+
return message !== undefined && message.length > 0 ? message : `${code} (no message supplied)`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
class ApiError extends Error {
|
|
87
|
+
code;
|
|
88
|
+
status;
|
|
89
|
+
details;
|
|
90
|
+
hint;
|
|
91
|
+
traceId;
|
|
92
|
+
constructor(code, status = 0, details, message, hint, traceId, options) {
|
|
93
|
+
super(messageForCode(code, message), options);
|
|
94
|
+
this.code = code;
|
|
95
|
+
this.status = status;
|
|
96
|
+
this.details = details;
|
|
97
|
+
this.hint = hint;
|
|
98
|
+
this.traceId = traceId;
|
|
99
|
+
this.name = "ApiError";
|
|
100
|
+
Object.defineProperty(this, API_ERROR_BRAND, { value: true });
|
|
101
|
+
}
|
|
102
|
+
static is(error) {
|
|
103
|
+
return typeof error === "object" && error !== null && API_ERROR_BRAND in error;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function refuseLocally(path, message) {
|
|
107
|
+
return new ApiError("VALIDATION_ERROR", 0, { issues: [{ path, code: "invalid_type", message }] }, message);
|
|
108
|
+
}
|
|
109
|
+
function isAbortLikeError(error) {
|
|
110
|
+
if (error instanceof RequestCancellationError)
|
|
111
|
+
return true;
|
|
112
|
+
return typeof error === "object" && error !== null && "name" in error && (error.name === "AbortError" || error.name === "TimeoutError");
|
|
113
|
+
}
|
|
114
|
+
function shouldRetryBunNetworkError(error) {
|
|
115
|
+
if (ApiError.is(error) || isHTTPError(error) || isNetworkError(error) || isTimeoutError(error) || isAbortLikeError(error)) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (!(error instanceof Error))
|
|
119
|
+
return;
|
|
120
|
+
const code = Object.getOwnPropertyDescriptor(error, "code");
|
|
121
|
+
return code && "value" in code && code.value === "ConnectionRefused" ? true : undefined;
|
|
122
|
+
}
|
|
123
|
+
function parseApiErrorBody(body) {
|
|
124
|
+
if (!isRecord(body) || !isRecord(body.error))
|
|
125
|
+
return null;
|
|
126
|
+
const error = body.error;
|
|
127
|
+
if (typeof error.code !== "string")
|
|
128
|
+
return null;
|
|
129
|
+
return {
|
|
130
|
+
code: error.code,
|
|
131
|
+
message: typeof error.message === "string" ? error.message : undefined,
|
|
132
|
+
details: error.details,
|
|
133
|
+
hint: typeof error.hint === "string" ? error.hint : undefined
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
function createRetryAwareFetch(transportFetch, unix) {
|
|
137
|
+
const runtimeFetch = transportFetch;
|
|
138
|
+
let attempt = 0;
|
|
139
|
+
return (input, init) => {
|
|
140
|
+
attempt += 1;
|
|
141
|
+
if (unix === undefined && attempt === 1) {
|
|
142
|
+
return runtimeFetch(input, init);
|
|
143
|
+
}
|
|
144
|
+
if (!(input instanceof Request)) {
|
|
145
|
+
if (unix === undefined)
|
|
146
|
+
return runtimeFetch(input, init);
|
|
147
|
+
const unixInit = { ...init, unix };
|
|
148
|
+
return runtimeFetch(input, unixInit);
|
|
149
|
+
}
|
|
150
|
+
const streamedBody = input.body ? { body: input.body, duplex: "half" } : {};
|
|
151
|
+
const materialized = {
|
|
152
|
+
...init,
|
|
153
|
+
...unix !== undefined && { unix },
|
|
154
|
+
method: input.method,
|
|
155
|
+
headers: input.headers,
|
|
156
|
+
...streamedBody,
|
|
157
|
+
cache: input.cache,
|
|
158
|
+
credentials: input.credentials,
|
|
159
|
+
integrity: input.integrity,
|
|
160
|
+
keepalive: input.keepalive,
|
|
161
|
+
mode: input.mode,
|
|
162
|
+
redirect: input.redirect,
|
|
163
|
+
referrer: input.referrer,
|
|
164
|
+
referrerPolicy: input.referrerPolicy,
|
|
165
|
+
signal: input.signal
|
|
166
|
+
};
|
|
167
|
+
return runtimeFetch(input.url, materialized);
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
function createHttpClient(config) {
|
|
171
|
+
if (config.fetch && config.unix) {
|
|
172
|
+
throw new TypeError("HttpClientConfig.fetch and unix are mutually exclusive");
|
|
173
|
+
}
|
|
174
|
+
if (config.unix !== undefined) {
|
|
175
|
+
if (!config.unix.startsWith("/") || config.unix.includes("\x00")) {
|
|
176
|
+
throw new TypeError("HttpClientConfig.unix must be an absolute Unix socket path");
|
|
177
|
+
}
|
|
178
|
+
if (typeof Reflect.get(globalThis, "Bun") !== "object") {
|
|
179
|
+
throw new TypeError("HttpClientConfig.unix requires Bun; on Bun or Node use createUnixClientTransport().fetch for an explicit portable transport");
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
let ssrCookies = null;
|
|
183
|
+
let isLoggedOut = false;
|
|
184
|
+
const listeners = new Set;
|
|
185
|
+
const suppressUnauthorizedFor = config.suppressUnauthorizedFor ?? [];
|
|
186
|
+
const parseError = config.parseError ?? parseApiErrorBody;
|
|
187
|
+
function emit(event) {
|
|
188
|
+
for (const fn of listeners) {
|
|
189
|
+
try {
|
|
190
|
+
fn(event);
|
|
191
|
+
} catch {}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
const client = ky.create({
|
|
195
|
+
prefix: config.baseUrl,
|
|
196
|
+
credentials: config.credentials ?? "include",
|
|
197
|
+
timeout: false,
|
|
198
|
+
retry: {
|
|
199
|
+
limit: config.retry?.limit ?? 2,
|
|
200
|
+
methods: config.retry?.methods ?? ["get"],
|
|
201
|
+
statusCodes: config.retry?.statusCodes ?? [],
|
|
202
|
+
shouldRetry: ({ error }) => shouldRetryBunNetworkError(error)
|
|
203
|
+
},
|
|
204
|
+
hooks: {
|
|
205
|
+
beforeRequest: [
|
|
206
|
+
({ request: request2 }) => {
|
|
207
|
+
if (ssrCookies) {
|
|
208
|
+
request2.headers.set("Cookie", ssrCookies);
|
|
209
|
+
}
|
|
210
|
+
const extra = typeof config.headers === "function" ? config.headers() : config.headers;
|
|
211
|
+
if (extra) {
|
|
212
|
+
for (const [key, value] of Object.entries(extra)) {
|
|
213
|
+
request2.headers.set(key, value);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (config.trace && !request2.headers.has("traceparent")) {
|
|
217
|
+
request2.headers.set("traceparent", formatTraceparent(createTraceContext()));
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
],
|
|
221
|
+
afterResponse: [
|
|
222
|
+
async ({ request: request2, response }) => {
|
|
223
|
+
if (config.release) {
|
|
224
|
+
try {
|
|
225
|
+
config.release.observe(response.headers.get("x-build-id"));
|
|
226
|
+
} catch {}
|
|
227
|
+
}
|
|
228
|
+
if (response.status === 401) {
|
|
229
|
+
const url = new URL(request2.url).pathname;
|
|
230
|
+
if (!isLoggedOut && !suppressUnauthorizedFor.some((matches) => matches(url))) {
|
|
231
|
+
isLoggedOut = true;
|
|
232
|
+
emit({ type: "unauthorized" });
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (!response.ok) {
|
|
236
|
+
const body = await response.clone().json().catch(() => null);
|
|
237
|
+
if (body) {
|
|
238
|
+
const parsed = parseError(body);
|
|
239
|
+
if (parsed) {
|
|
240
|
+
throw new ApiError(parsed.code, response.status, parsed.details, parsed.message, parsed.hint, responseTraceId(response));
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
]
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
async function request(method, url, data, options = {}) {
|
|
249
|
+
const cancellation = createRequestCancellation(options.signal, options.timeout ?? config.timeout ?? 30000);
|
|
250
|
+
const kyOptions = {
|
|
251
|
+
fetch: createRetryAwareFetch(config.fetch ?? globalThis.fetch.bind(globalThis), config.unix),
|
|
252
|
+
timeout: false,
|
|
253
|
+
signal: cancellation.signal
|
|
254
|
+
};
|
|
255
|
+
if (options.params) {
|
|
256
|
+
const searchParams = new URLSearchParams;
|
|
257
|
+
for (const [key, value] of Object.entries(options.params)) {
|
|
258
|
+
if (value === undefined)
|
|
259
|
+
continue;
|
|
260
|
+
if (Array.isArray(value)) {
|
|
261
|
+
for (const item of value)
|
|
262
|
+
searchParams.append(key, String(item));
|
|
263
|
+
} else {
|
|
264
|
+
searchParams.set(key, String(value));
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (searchParams.size > 0) {
|
|
268
|
+
kyOptions.searchParams = searchParams;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
if (data instanceof FormData) {
|
|
272
|
+
kyOptions.body = data;
|
|
273
|
+
} else if (data !== undefined) {
|
|
274
|
+
kyOptions.json = data;
|
|
275
|
+
}
|
|
276
|
+
try {
|
|
277
|
+
return await cancellation.run(async () => {
|
|
278
|
+
if (options.responseType === "blob") {
|
|
279
|
+
return transportResult(await client[method](url, kyOptions).blob());
|
|
280
|
+
}
|
|
281
|
+
if (options.responseType === "response") {
|
|
282
|
+
return transportResult(await client[method](url, kyOptions));
|
|
283
|
+
}
|
|
284
|
+
const response = await client[method](url, kyOptions);
|
|
285
|
+
if (options.responseType === "void") {
|
|
286
|
+
const text = await response.text();
|
|
287
|
+
if (text.length > 0) {
|
|
288
|
+
throw new Error("Server returned data for an endpoint with no output contract");
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
if (options.responseType === "void" || response.status === 204 || response.headers.get("content-length") === "0") {
|
|
292
|
+
return transportResult(undefined);
|
|
293
|
+
}
|
|
294
|
+
return await response.json();
|
|
295
|
+
});
|
|
296
|
+
} catch (error) {
|
|
297
|
+
if (error instanceof RequestCancellationError) {
|
|
298
|
+
throw new ApiError(error.cause === "caller" ? "REQUEST_ABORTED" : "REQUEST_TIMEOUT", 0, undefined, error.message);
|
|
299
|
+
}
|
|
300
|
+
if (ApiError.is(error))
|
|
301
|
+
throw error;
|
|
302
|
+
emit({ type: "network_error" });
|
|
303
|
+
const response = isHTTPError(error) ? error.response : undefined;
|
|
304
|
+
const status = response?.status ?? 0;
|
|
305
|
+
const msg = error instanceof Error ? error.message : undefined;
|
|
306
|
+
throw new ApiError("UNKNOWN_ERROR", status, msg ? { message: msg } : undefined, msg, undefined, responseTraceId(response), { cause: error });
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return {
|
|
310
|
+
baseUrl: config.baseUrl,
|
|
311
|
+
get: (url, options) => request("get", url, undefined, options),
|
|
312
|
+
head: (url, options) => request("head", url, undefined, options),
|
|
313
|
+
post: (url, data, options) => request("post", url, data, options),
|
|
314
|
+
put: (url, data, options) => request("put", url, data, options),
|
|
315
|
+
patch: (url, data, options) => request("patch", url, data, options),
|
|
316
|
+
delete: (url, options) => request("delete", url, undefined, options),
|
|
317
|
+
setServerContext(cookies) {
|
|
318
|
+
ssrCookies = cookies;
|
|
319
|
+
},
|
|
320
|
+
subscribe(listener) {
|
|
321
|
+
listeners.add(listener);
|
|
322
|
+
return () => listeners.delete(listener);
|
|
323
|
+
},
|
|
324
|
+
logout() {
|
|
325
|
+
isLoggedOut = true;
|
|
326
|
+
emit({ type: "logout" });
|
|
327
|
+
},
|
|
328
|
+
resetLogoutState() {
|
|
329
|
+
isLoggedOut = false;
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export { RequestCancellationError, createRequestCancellation, responseTraceId, ApiError, refuseLocally, isAbortLikeError, parseApiErrorBody, createHttpClient };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
collectTools,
|
|
3
3
|
createToolRunner
|
|
4
|
-
} from "./index-
|
|
4
|
+
} from "./index-fwbkp2jn.js";
|
|
5
5
|
import {
|
|
6
6
|
toolErrorFromResult
|
|
7
|
-
} from "./index-
|
|
7
|
+
} from "./index-dggwnnjv.js";
|
|
8
8
|
import {
|
|
9
9
|
assertUniqueToolName
|
|
10
10
|
} from "./index-mgtxfe6b.js";
|