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.
Files changed (177) hide show
  1. package/dist/agent-runtime/provider-failure.d.ts.map +1 -1
  2. package/dist/agent-runtime-coding-tools.js +1 -1
  3. package/dist/agent-runtime-harness.js +7 -5
  4. package/dist/agent-runtime.js +15 -19
  5. package/dist/application/notification-outbox.d.ts +80 -0
  6. package/dist/application/notification-outbox.d.ts.map +1 -0
  7. package/dist/application/process-lifecycle.d.ts +166 -0
  8. package/dist/application/process-lifecycle.d.ts.map +1 -0
  9. package/dist/application/state-store.d.ts +18 -0
  10. package/dist/application/state-store.d.ts.map +1 -0
  11. package/dist/application.d.ts +3 -0
  12. package/dist/application.d.ts.map +1 -1
  13. package/dist/application.js +568 -11
  14. package/dist/browser/http.d.ts +14 -0
  15. package/dist/browser/http.d.ts.map +1 -1
  16. package/dist/browser/resumable.d.ts +6 -0
  17. package/dist/browser/resumable.d.ts.map +1 -1
  18. package/dist/cli.js +9 -7
  19. package/dist/contract/define.d.ts +27 -4
  20. package/dist/contract/define.d.ts.map +1 -1
  21. package/dist/contract/errors-factory.d.ts +22 -3
  22. package/dist/contract/errors-factory.d.ts.map +1 -1
  23. package/dist/contract/index.d.ts +2 -2
  24. package/dist/contract/index.d.ts.map +1 -1
  25. package/dist/contract/index.js +1 -1
  26. package/dist/geo/index.d.ts +4 -0
  27. package/dist/geo/index.d.ts.map +1 -0
  28. package/dist/geo/maxmind.d.ts +4 -0
  29. package/dist/geo/maxmind.d.ts.map +1 -0
  30. package/dist/geo/resolver.d.ts +9 -0
  31. package/dist/geo/resolver.d.ts.map +1 -0
  32. package/dist/geo/types.d.ts +59 -0
  33. package/dist/geo/types.d.ts.map +1 -0
  34. package/dist/geo.d.ts +3 -0
  35. package/dist/geo.d.ts.map +1 -0
  36. package/dist/geo.js +245 -0
  37. package/dist/{index-9s6z8vgf.js → index-1dd82z59.js} +1 -1
  38. package/dist/{index-vwp69871.js → index-27487ay5.js} +4 -4
  39. package/dist/{index-7ag81v2f.js → index-27gm3d6g.js} +10 -321
  40. package/dist/{index-y01f4223.js → index-2bxcmqkb.js} +127 -43
  41. package/dist/{index-dxesg10d.js → index-3ds42w1w.js} +3 -3
  42. package/dist/index-3xwxfj4z.js +334 -0
  43. package/dist/{index-egaabxkz.js → index-5z8tezpx.js} +2 -2
  44. package/dist/{index-fxz0v7vg.js → index-7evdsyez.js} +1 -83
  45. package/dist/{index-xcetq6j3.js → index-90d7h2cr.js} +32 -13
  46. package/dist/{index-t0z5p5w6.js → index-apn8repm.js} +1 -1
  47. package/dist/{index-758zx2ha.js → index-dggwnnjv.js} +2 -2
  48. package/dist/{index-dwf1f2ap.js → index-f9mb610r.js} +1 -62
  49. package/dist/{index-5mtc969c.js → index-fwbkp2jn.js} +1 -1
  50. package/dist/index-p13mwz16.js +16 -0
  51. package/dist/{index-m9kzax52.js → index-p18f59zy.js} +21 -5
  52. package/dist/{index-hb5pf3s4.js → index-qmgs7y68.js} +1 -1
  53. package/dist/{index-2rwwmr7v.js → index-s4ckmrac.js} +1 -1
  54. package/dist/{index-h4pj6fta.js → index-s8cnx02s.js} +8 -5
  55. package/dist/{index-k9ay6v6d.js → index-vchzar7e.js} +74 -23
  56. package/dist/index-vsbzgd7b.js +84 -0
  57. package/dist/{index-dxjjv399.js → index-xbppf54a.js} +95 -15
  58. package/dist/index-y91ry9h2.js +4 -0
  59. package/dist/index-zcgf3gqf.js +83 -0
  60. package/dist/index.js +17 -13
  61. package/dist/internal/error-message.d.ts +3 -0
  62. package/dist/internal/error-message.d.ts.map +1 -0
  63. package/dist/internal/media-type.d.ts +8 -0
  64. package/dist/internal/media-type.d.ts.map +1 -0
  65. package/dist/internal/route-pattern.d.ts +21 -0
  66. package/dist/internal/route-pattern.d.ts.map +1 -1
  67. package/dist/node.js +9 -6
  68. package/dist/observability/audit.d.ts +28 -1
  69. package/dist/observability/audit.d.ts.map +1 -1
  70. package/dist/observability/bounded-logger.d.ts +20 -0
  71. package/dist/observability/bounded-logger.d.ts.map +1 -0
  72. package/dist/observability/context.d.ts +12 -3
  73. package/dist/observability/context.d.ts.map +1 -1
  74. package/dist/observability/index.d.ts +3 -2
  75. package/dist/observability/index.d.ts.map +1 -1
  76. package/dist/observability/index.js +138 -8
  77. package/dist/observability/sanitize.d.ts +17 -0
  78. package/dist/observability/sanitize.d.ts.map +1 -1
  79. package/dist/primitives.js +1 -1
  80. package/dist/react/query-client.d.ts +33 -0
  81. package/dist/react/query-client.d.ts.map +1 -0
  82. package/dist/react.d.ts +1 -0
  83. package/dist/react.d.ts.map +1 -1
  84. package/dist/react.js +83 -1
  85. package/dist/realtime/registry.d.ts +93 -0
  86. package/dist/realtime/registry.d.ts.map +1 -0
  87. package/dist/release/header.d.ts +9 -0
  88. package/dist/release/header.d.ts.map +1 -0
  89. package/dist/release/marker.d.ts +39 -0
  90. package/dist/release/marker.d.ts.map +1 -0
  91. package/dist/release/socket.d.ts +44 -0
  92. package/dist/release/socket.d.ts.map +1 -0
  93. package/dist/release/watcher.d.ts +66 -0
  94. package/dist/release/watcher.d.ts.map +1 -0
  95. package/dist/release.d.ts +15 -0
  96. package/dist/release.d.ts.map +1 -0
  97. package/dist/release.js +196 -0
  98. package/dist/remote.js +12 -7
  99. package/dist/server/context.d.ts +2 -1
  100. package/dist/server/context.d.ts.map +1 -1
  101. package/dist/server/create.d.ts.map +1 -1
  102. package/dist/server/error-hook.d.ts +17 -16
  103. package/dist/server/error-hook.d.ts.map +1 -1
  104. package/dist/server/file-state-store.d.ts +23 -0
  105. package/dist/server/file-state-store.d.ts.map +1 -0
  106. package/dist/server/implement.d.ts.map +1 -1
  107. package/dist/server/index.d.ts +5 -2
  108. package/dist/server/index.d.ts.map +1 -1
  109. package/dist/server/index.js +469 -11
  110. package/dist/server/middleware/cookies.d.ts +33 -1
  111. package/dist/server/middleware/cookies.d.ts.map +1 -1
  112. package/dist/server/middleware/cors.d.ts +16 -1
  113. package/dist/server/middleware/cors.d.ts.map +1 -1
  114. package/dist/server/openapi.d.ts.map +1 -1
  115. package/dist/server/release-signal.d.ts +35 -0
  116. package/dist/server/release-signal.d.ts.map +1 -0
  117. package/dist/server/router.d.ts.map +1 -1
  118. package/dist/server/types.d.ts +26 -2
  119. package/dist/server/types.d.ts.map +1 -1
  120. package/dist/testing/surface-manifest.d.ts.map +1 -1
  121. package/dist/testing.js +7 -4
  122. package/dist/tool-invoker.js +8 -6
  123. package/dist/tools/download-core.d.ts.map +1 -1
  124. package/dist/tools/remote.d.ts.map +1 -1
  125. package/dist/tools/view-file.d.ts.map +1 -1
  126. package/dist/tools.js +20 -16
  127. package/dist/tracking/attribution.d.ts +51 -0
  128. package/dist/tracking/attribution.d.ts.map +1 -0
  129. package/dist/tracking/beacon.d.ts +14 -0
  130. package/dist/tracking/beacon.d.ts.map +1 -0
  131. package/dist/tracking/clicks.d.ts +46 -0
  132. package/dist/tracking/clicks.d.ts.map +1 -0
  133. package/dist/tracking/client.d.ts +121 -0
  134. package/dist/tracking/client.d.ts.map +1 -0
  135. package/dist/tracking/contract.d.ts +46 -0
  136. package/dist/tracking/contract.d.ts.map +1 -0
  137. package/dist/tracking/delivery.d.ts +24 -0
  138. package/dist/tracking/delivery.d.ts.map +1 -0
  139. package/dist/tracking/host.d.ts +43 -0
  140. package/dist/tracking/host.d.ts.map +1 -0
  141. package/dist/tracking/once-per-page.d.ts +17 -0
  142. package/dist/tracking/once-per-page.d.ts.map +1 -0
  143. package/dist/tracking/outbox-storage-indexeddb.d.ts +12 -0
  144. package/dist/tracking/outbox-storage-indexeddb.d.ts.map +1 -0
  145. package/dist/tracking/outbox-storage-memory.d.ts +12 -0
  146. package/dist/tracking/outbox-storage-memory.d.ts.map +1 -0
  147. package/dist/tracking/outbox.d.ts +98 -0
  148. package/dist/tracking/outbox.d.ts.map +1 -0
  149. package/dist/tracking/schemas.d.ts +183 -0
  150. package/dist/tracking/schemas.d.ts.map +1 -0
  151. package/dist/tracking/scroll.d.ts +22 -0
  152. package/dist/tracking/scroll.d.ts.map +1 -0
  153. package/dist/tracking/sequence-reserve.d.ts +34 -0
  154. package/dist/tracking/sequence-reserve.d.ts.map +1 -0
  155. package/dist/tracking/server/active-interval.d.ts +26 -0
  156. package/dist/tracking/server/active-interval.d.ts.map +1 -0
  157. package/dist/tracking/server/bot.d.ts +9 -0
  158. package/dist/tracking/server/bot.d.ts.map +1 -0
  159. package/dist/tracking/server/disposition.d.ts +55 -0
  160. package/dist/tracking/server/disposition.d.ts.map +1 -0
  161. package/dist/tracking/server/hash.d.ts +13 -0
  162. package/dist/tracking/server/hash.d.ts.map +1 -0
  163. package/dist/tracking/server/presence.d.ts +27 -0
  164. package/dist/tracking/server/presence.d.ts.map +1 -0
  165. package/dist/tracking/server/visit-lease.d.ts +92 -0
  166. package/dist/tracking/server/visit-lease.d.ts.map +1 -0
  167. package/dist/tracking/visible-time.d.ts +38 -0
  168. package/dist/tracking/visible-time.d.ts.map +1 -0
  169. package/dist/tracking-server.d.ts +14 -0
  170. package/dist/tracking-server.d.ts.map +1 -0
  171. package/dist/tracking-server.js +173 -0
  172. package/dist/tracking.d.ts +24 -0
  173. package/dist/tracking.d.ts.map +1 -0
  174. package/dist/tracking.js +1090 -0
  175. package/llms-full.txt +938 -29
  176. package/llms.txt +4 -0
  177. 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 parseTrailingWildcard(path) {
22
+ function parseRoutePattern(path) {
21
23
  const segments = path.split("/").filter(Boolean);
22
- const paramNames = new Set;
23
- let wildcard = null;
24
+ const parameters = [];
25
+ const names = new Set;
24
26
  for (const [segmentIndex, segment] of segments.entries()) {
25
- if (segment.startsWith(":")) {
26
- const name2 = segment.slice(1);
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 (!PARAM_IDENTIFIER.test(name)) {
35
+ if (marker === "*" && name === "") {
44
36
  throw new Error(`Trailing wildcard in path "${path}" must be named, for example "/*filePath"`);
45
37
  }
46
- if (segmentIndex !== segments.length - 1) {
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
- if (wildcard) {
50
- throw new Error(`Path "${path}" contains more than one wildcard`);
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
- if (paramNames.has(name)) {
53
- throw new Error(`Duplicate route parameter name "${name}" in path "${path}"`);
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 wildcard;
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(endpoints)) {
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-2rwwmr7v.js";
6
+ } from "./index-s4ckmrac.js";
7
7
  import {
8
8
  createToolRunner,
9
9
  formatToolError
10
- } from "./index-5mtc969c.js";
10
+ } from "./index-fwbkp2jn.js";
11
11
  import {
12
12
  isToolExecutionControlError,
13
13
  toolResultFromError
14
- } from "./index-758zx2ha.js";
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-5mtc969c.js";
4
+ } from "./index-fwbkp2jn.js";
5
5
  import {
6
6
  toolErrorFromResult
7
- } from "./index-758zx2ha.js";
7
+ } from "./index-dggwnnjv.js";
8
8
  import {
9
9
  assertUniqueToolName
10
10
  } from "./index-mgtxfe6b.js";