stitchkit 0.18.0 → 0.19.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 (68) hide show
  1. package/dist/browser/client.d.ts.map +1 -1
  2. package/dist/browser/socket-io.d.ts.map +1 -1
  3. package/dist/cli.js +3 -3
  4. package/dist/contract/errors-factory.d.ts +18 -0
  5. package/dist/contract/errors-factory.d.ts.map +1 -0
  6. package/dist/contract/factory.d.ts +36 -0
  7. package/dist/contract/factory.d.ts.map +1 -0
  8. package/dist/contract/index.d.ts +2 -0
  9. package/dist/contract/index.d.ts.map +1 -1
  10. package/dist/contract/index.js +6 -1
  11. package/dist/contract/pagination.d.ts.map +1 -1
  12. package/dist/index-809wc1tt.js +18 -0
  13. package/dist/{index-tm7dqzxc.js → index-c7nyw0yt.js} +9 -9
  14. package/dist/{index-dz2k8d5h.js → index-dzx781tm.js} +1 -1
  15. package/dist/{index-ktn76xz0.js → index-ng2v2ts4.js} +17 -1
  16. package/dist/index-tje0q6gp.js +44 -0
  17. package/dist/{index-6qv1fxqy.js → index-wpbn133x.js} +13 -50
  18. package/dist/{index-yzac2zbh.js → index-x62gnfsk.js} +50 -9
  19. package/dist/{index-akv8rk4w.js → index-xmncnw8z.js} +2 -2
  20. package/dist/{index-yv93m4yr.js → index-ynnh9x6h.js} +11 -29
  21. package/dist/index.js +82 -62
  22. package/dist/internal/base64url.d.ts +16 -0
  23. package/dist/internal/base64url.d.ts.map +1 -0
  24. package/dist/node.js +5 -4
  25. package/dist/observability/index.js +2 -2
  26. package/dist/react/entity-cache.d.ts +54 -0
  27. package/dist/react/entity-cache.d.ts.map +1 -0
  28. package/dist/react.d.ts +1 -0
  29. package/dist/react.d.ts.map +1 -1
  30. package/dist/react.js +74 -0
  31. package/dist/server/create.d.ts.map +1 -1
  32. package/dist/server/error-hook.d.ts +56 -0
  33. package/dist/server/error-hook.d.ts.map +1 -0
  34. package/dist/server/index.d.ts +2 -1
  35. package/dist/server/index.d.ts.map +1 -1
  36. package/dist/server/index.js +32 -7
  37. package/dist/server/middleware/auth.d.ts.map +1 -1
  38. package/dist/server/middleware/cors.d.ts +13 -0
  39. package/dist/server/middleware/cors.d.ts.map +1 -1
  40. package/dist/server/middleware/pkce.d.ts +0 -6
  41. package/dist/server/middleware/pkce.d.ts.map +1 -1
  42. package/dist/server/router.d.ts +0 -2
  43. package/dist/server/router.d.ts.map +1 -1
  44. package/dist/server/types.d.ts +0 -8
  45. package/dist/server/types.d.ts.map +1 -1
  46. package/dist/tools/agent.d.ts +2 -2
  47. package/dist/tools/agent.d.ts.map +1 -1
  48. package/dist/tools/cli-format.d.ts +2 -2
  49. package/dist/tools/cli-format.d.ts.map +1 -1
  50. package/dist/tools/cli.d.ts +2 -2
  51. package/dist/tools/cli.d.ts.map +1 -1
  52. package/dist/tools/execute.d.ts +7 -0
  53. package/dist/tools/execute.d.ts.map +1 -1
  54. package/dist/tools/mcp.d.ts +3 -3
  55. package/dist/tools/mcp.d.ts.map +1 -1
  56. package/dist/tools/mount.d.ts +3 -3
  57. package/dist/tools/mount.d.ts.map +1 -1
  58. package/dist/tools/oauth-metadata.d.ts.map +1 -1
  59. package/dist/tools/oauth-provider.d.ts.map +1 -1
  60. package/dist/tools/tool-logger.d.ts +31 -0
  61. package/dist/tools/tool-logger.d.ts.map +1 -0
  62. package/dist/tools/transports.d.ts +28 -0
  63. package/dist/tools/transports.d.ts.map +1 -0
  64. package/dist/tools.d.ts +3 -1
  65. package/dist/tools.d.ts.map +1 -1
  66. package/dist/tools.js +71 -11
  67. package/llms-full.txt +235 -4
  68. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5,8 +5,10 @@ import {
5
5
  appError,
6
6
  badRequest,
7
7
  conflict,
8
+ createContractFactory,
8
9
  decodeCursor,
9
10
  defineContract,
11
+ defineErrors,
10
12
  encodeCursor,
11
13
  forbidden,
12
14
  isStitchErrorCode,
@@ -14,30 +16,18 @@ import {
14
16
  paginatedSchema,
15
17
  rateLimited,
16
18
  unauthorized
17
- } from "./index-yzac2zbh.js";
19
+ } from "./index-x62gnfsk.js";
20
+ import {
21
+ isRecord,
22
+ mapObject,
23
+ typedEntries
24
+ } from "./index-809wc1tt.js";
18
25
 
19
26
  // src/internal/http-input.ts
20
27
  function inputIsQuery(method) {
21
28
  return method === "GET" || method === "DELETE";
22
29
  }
23
30
 
24
- // src/internal/typed.ts
25
- function typedEntries(value) {
26
- return Object.entries(value);
27
- }
28
- function isRecord(value) {
29
- return typeof value === "object" && value !== null && !Array.isArray(value);
30
- }
31
- function mapObject(source, mapper) {
32
- const result = {};
33
- for (const [key, value] of typedEntries(source)) {
34
- const mapped = mapper(key, value);
35
- if (mapped !== undefined)
36
- result[key] = mapped;
37
- }
38
- return result;
39
- }
40
-
41
31
  // src/browser/http.ts
42
32
  import ky, { isHTTPError } from "ky";
43
33
 
@@ -244,6 +234,12 @@ function withTimeout(options, timeout) {
244
234
  return options;
245
235
  return { ...options, timeout };
246
236
  }
237
+ function withOutput(endpoint, result) {
238
+ const schema = endpoint.output;
239
+ if (!schema)
240
+ return result;
241
+ return result.then((value) => value === undefined ? undefined : schema.parse(value));
242
+ }
247
243
  function isParamArray(value) {
248
244
  return Array.isArray(value) && value.every((v) => typeof v === "string" || typeof v === "number");
249
245
  }
@@ -310,18 +306,21 @@ function createHttpMethod(endpoint, prefix, client, config) {
310
306
  if (!isMultipartFile(file)) {
311
307
  throw new Error(`Missing multipart file field: ${endpoint.multipart}`);
312
308
  }
309
+ if (httpMethod === "get" || httpMethod === "delete") {
310
+ throw new Error(`Multipart endpoint ${endpoint.method} ${endpoint.path} must be POST / PUT / PATCH`);
311
+ }
313
312
  const formData = new FormData;
314
313
  appendMultipartFile(formData, endpoint.multipart, file);
315
314
  appendFormFields(formData, firstArg, new Set([...prefixKeys, endpoint.multipart]));
316
- return client.post(url, formData, withTimeout(undefined, endpoint.timeout));
315
+ return withOutput(endpoint, client[httpMethod](url, formData, withTimeout(undefined, endpoint.timeout)));
317
316
  }
318
317
  if (isGet) {
319
318
  const params = collectQueryParams(firstArg, prefixKeys, endpoint);
320
- return client.get(url, withTimeout(params ? { params } : undefined, endpoint.timeout));
319
+ return withOutput(endpoint, client.get(url, withTimeout(params ? { params } : undefined, endpoint.timeout)));
321
320
  }
322
321
  if (httpMethod === "delete") {
323
322
  const params = collectQueryParams(firstArg, prefixKeys, endpoint);
324
- return client.delete(url, withTimeout(params ? { params } : undefined, endpoint.timeout));
323
+ return withOutput(endpoint, client.delete(url, withTimeout(params ? { params } : undefined, endpoint.timeout)));
325
324
  }
326
325
  const payload = {};
327
326
  for (const [key, value] of Object.entries(firstArg)) {
@@ -329,7 +328,7 @@ function createHttpMethod(endpoint, prefix, client, config) {
329
328
  payload[key] = value;
330
329
  }
331
330
  }
332
- return client[httpMethod](url, Object.keys(payload).length > 0 ? payload : undefined, withTimeout(undefined, endpoint.timeout));
331
+ return withOutput(endpoint, client[httpMethod](url, Object.keys(payload).length > 0 ? payload : undefined, withTimeout(undefined, endpoint.timeout)));
333
332
  };
334
333
  }
335
334
  function createFetchMethod(endpoint, prefix, config, contractConfig) {
@@ -346,6 +345,7 @@ function createFetchMethod(endpoint, prefix, config, contractConfig) {
346
345
  Accept: "application/json",
347
346
  ...typeof config.headers === "function" ? config.headers() : config.headers
348
347
  };
348
+ const signal = endpoint.timeout !== undefined ? AbortSignal.timeout(endpoint.timeout) : undefined;
349
349
  const isQuery = inputIsQuery(endpoint.method);
350
350
  const hasBody = !isQuery && !endpoint.multipart && endpoint.input && args;
351
351
  if (isQuery && args) {
@@ -378,7 +378,8 @@ function createFetchMethod(endpoint, prefix, config, contractConfig) {
378
378
  method: endpoint.method,
379
379
  headers,
380
380
  credentials: config.credentials,
381
- body: formData
381
+ body: formData,
382
+ signal
382
383
  });
383
384
  if (!res2.ok) {
384
385
  await throwForErrorResponse(res2, config, null);
@@ -392,6 +393,7 @@ function createFetchMethod(endpoint, prefix, config, contractConfig) {
392
393
  method: endpoint.method,
393
394
  headers,
394
395
  credentials: config.credentials,
396
+ signal,
395
397
  ...hasBody && {
396
398
  body: JSON.stringify(stripParams(hasBody, endpoint.path, prefixKeys))
397
399
  }
@@ -461,9 +463,6 @@ function stripParams(args, path, extra) {
461
463
  }
462
464
  return result;
463
465
  }
464
- // src/browser/socket-io.ts
465
- import { io } from "socket.io-client";
466
-
467
466
  // src/retained.ts
468
467
  function createRetainedTopics() {
469
468
  const last = {};
@@ -491,6 +490,17 @@ function createRetainedTopics() {
491
490
  }
492
491
 
493
492
  // src/browser/socket-io.ts
493
+ var SOCKET_IO_CLIENT = "socket.io-client";
494
+ var ioLoader = null;
495
+ function loadIo() {
496
+ if (!ioLoader) {
497
+ ioLoader = import(SOCKET_IO_CLIENT).then((mod) => mod.io, (cause) => {
498
+ ioLoader = null;
499
+ throw new Error('stitchkit: createSocketIOClient needs the "socket.io-client" peer — install it (e.g. `bun add socket.io-client`).', { cause });
500
+ });
501
+ }
502
+ return ioLoader;
503
+ }
494
504
  function toIoAuth(auth) {
495
505
  if (typeof auth !== "function")
496
506
  return auth;
@@ -500,6 +510,7 @@ function toIoAuth(auth) {
500
510
  }
501
511
  function createSocketIOClient(config) {
502
512
  let socket = null;
513
+ let desiredConnected = false;
503
514
  let reconnectTimer = null;
504
515
  const serverDisconnectDelay = config.reconnectOnServerDisconnect ?? 1000;
505
516
  const connectionListeners = new Set;
@@ -517,50 +528,57 @@ function createSocketIOClient(config) {
517
528
  reconnectTimer = null;
518
529
  }
519
530
  }
531
+ function openSocket(io) {
532
+ if (!desiredConnected || socket)
533
+ return;
534
+ socket = io(config.url, {
535
+ path: config.path ?? "/socket.io/",
536
+ withCredentials: config.withCredentials ?? true,
537
+ auth: toIoAuth(config.auth),
538
+ ...config.query && { query: config.query },
539
+ ...config.extraHeaders && { extraHeaders: config.extraHeaders },
540
+ transports: config.transports ?? ["websocket", "polling"],
541
+ autoConnect: false,
542
+ reconnection: true,
543
+ reconnectionAttempts: config.reconnectionAttempts ?? Infinity,
544
+ reconnectionDelay: config.reconnectionDelay ?? 1000,
545
+ reconnectionDelayMax: config.reconnectionDelayMax ?? 5000,
546
+ timeout: config.timeout ?? 20000
547
+ });
548
+ socket.on("connect", () => notifyConnection(true));
549
+ socket.on("disconnect", (reason) => {
550
+ notifyConnection(false, reason);
551
+ if (reason === "io server disconnect" && serverDisconnectDelay !== false) {
552
+ const current = socket;
553
+ clearReconnectTimer();
554
+ reconnectTimer = setTimeout(() => {
555
+ reconnectTimer = null;
556
+ if (socket === current && current && !current.connected)
557
+ current.connect();
558
+ }, serverDisconnectDelay);
559
+ }
560
+ });
561
+ if (retained) {
562
+ for (const name of retainNames) {
563
+ socket.on(name, (payload) => retained.record(name, payload));
564
+ }
565
+ }
566
+ for (const attach of subscriptions)
567
+ attach(socket);
568
+ socket.connect();
569
+ }
520
570
  return {
521
571
  get connected() {
522
572
  return socket?.connected ?? false;
523
573
  },
524
574
  connect() {
525
- if (socket)
575
+ if (socket || desiredConnected)
526
576
  return;
527
- socket = io(config.url, {
528
- path: config.path ?? "/socket.io/",
529
- withCredentials: config.withCredentials ?? true,
530
- auth: toIoAuth(config.auth),
531
- ...config.query && { query: config.query },
532
- ...config.extraHeaders && { extraHeaders: config.extraHeaders },
533
- transports: config.transports ?? ["websocket", "polling"],
534
- autoConnect: false,
535
- reconnection: true,
536
- reconnectionAttempts: config.reconnectionAttempts ?? Infinity,
537
- reconnectionDelay: config.reconnectionDelay ?? 1000,
538
- reconnectionDelayMax: config.reconnectionDelayMax ?? 5000,
539
- timeout: config.timeout ?? 20000
540
- });
541
- socket.on("connect", () => notifyConnection(true));
542
- socket.on("disconnect", (reason) => {
543
- notifyConnection(false, reason);
544
- if (reason === "io server disconnect" && serverDisconnectDelay !== false) {
545
- const current = socket;
546
- clearReconnectTimer();
547
- reconnectTimer = setTimeout(() => {
548
- reconnectTimer = null;
549
- if (socket === current && current && !current.connected)
550
- current.connect();
551
- }, serverDisconnectDelay);
552
- }
553
- });
554
- if (retained) {
555
- for (const name of retainNames) {
556
- socket.on(name, (payload) => retained.record(name, payload));
557
- }
558
- }
559
- for (const attach of subscriptions)
560
- attach(socket);
561
- socket.connect();
577
+ desiredConnected = true;
578
+ loadIo().then(openSocket);
562
579
  },
563
580
  disconnect() {
581
+ desiredConnected = false;
564
582
  clearReconnectTimer();
565
583
  if (!socket)
566
584
  return;
@@ -648,12 +666,14 @@ export {
648
666
  formatTraceparent,
649
667
  forbidden,
650
668
  encodeCursor,
669
+ defineErrors,
651
670
  defineContract,
652
671
  decodeCursor,
653
672
  createTraceContext,
654
673
  createSocketIOClient,
655
674
  createRetainedTopics,
656
675
  createHttpClient,
676
+ createContractFactory,
657
677
  createClients,
658
678
  createClient,
659
679
  conflict,
@@ -0,0 +1,16 @@
1
+ /**
2
+ * base64url over raw bytes — the one codec shared by cursor pagination, JWT
3
+ * segments, OAuth-token minting and PKCE. `btoa`/`atob` based (not Node
4
+ * `Buffer`), so it works identically in the server, the browser and the typed
5
+ * client. Kept byte-level: string callers compose with `TextEncoder` /
6
+ * `TextDecoder` themselves (pagination round-trips UTF-8; JWT/PKCE are binary).
7
+ */
8
+ /** Encode raw bytes as an unpadded base64url string. */
9
+ export declare function bytesToBase64Url(bytes: Uint8Array): string;
10
+ /**
11
+ * Decode a base64url string to bytes. A character outside the alphabet, or a
12
+ * length that no base64 string can have (`% 4 === 1`), is malformed — reject it
13
+ * before `atob` rather than emit silent garbage.
14
+ */
15
+ export declare function base64UrlToBytes(segment: string): Uint8Array<ArrayBuffer>;
16
+ //# sourceMappingURL=base64url.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base64url.d.ts","sourceRoot":"","sources":["../../src/internal/base64url.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,wDAAwD;AACxD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAI1D;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,CAAC,WAAW,CAAC,CAOzE"}
package/dist/node.js CHANGED
@@ -3,7 +3,8 @@ import {
3
3
  createImplement,
4
4
  createSocketIOServer,
5
5
  implement
6
- } from "./index-6qv1fxqy.js";
6
+ } from "./index-wpbn133x.js";
7
+ import"./index-tje0q6gp.js";
7
8
  import {
8
9
  AppError,
9
10
  appError,
@@ -13,10 +14,10 @@ import {
13
14
  notFound,
14
15
  rateLimited,
15
16
  unauthorized
16
- } from "./index-ktn76xz0.js";
17
- import"./index-dz2k8d5h.js";
17
+ } from "./index-ng2v2ts4.js";
18
+ import"./index-dzx781tm.js";
18
19
  import"./index-khwedj16.js";
19
- import"./index-tm7dqzxc.js";
20
+ import"./index-c7nyw0yt.js";
20
21
  // src/server/node.ts
21
22
  import { serve } from "srvx";
22
23
  async function serveNode(config) {
@@ -8,7 +8,7 @@ import {
8
8
  setRequestError,
9
9
  setRequestUser,
10
10
  wrapInRequestContext
11
- } from "../index-dz2k8d5h.js";
11
+ } from "../index-dzx781tm.js";
12
12
  import {
13
13
  childSpan,
14
14
  createTraceContext,
@@ -19,7 +19,7 @@ import {
19
19
  import {
20
20
  isRecord,
21
21
  isUnsafeKey
22
- } from "../index-tm7dqzxc.js";
22
+ } from "../index-c7nyw0yt.js";
23
23
 
24
24
  // src/observability/sanitize.ts
25
25
  var DEFAULT_SENSITIVE_KEYS = /(password|passwd|pwd|secret|token|apikey|api[-_ ]?key|auth|authorization|bearer|session|cookie|init[-_ ]?data|credential|private[-_ ]?key)/i;
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Declarative CRUD cache handlers for `createCacheBridge` — the created /
3
+ * updated / deleted socket events of one entity, patched into its list and
4
+ * detail queries. Every project hand-rolls this same updater; this builds it
5
+ * from a small config.
6
+ *
7
+ * It patches stitchkit's own `Paginated<T>` list envelope (plain or a TanStack
8
+ * `InfiniteData` of it) and the entity's detail query. It does **not** flatten
9
+ * pages or expose a `useAllX` surface — flattening stays in the component (a
10
+ * deliberate boundary): this only keeps the cache correct.
11
+ *
12
+ * ```ts
13
+ * const handlers = createEntityCacheHandlers<Widget>({
14
+ * getId: (w) => w.id,
15
+ * listKey: ['widgets'],
16
+ * detailKey: (id) => ['widgets', id],
17
+ * });
18
+ * createCacheBridge({ socket, queryClient, handlers: {
19
+ * widgetCreated: handlers.created,
20
+ * widgetUpdated: handlers.updated,
21
+ * widgetDeleted: handlers.deleted,
22
+ * }});
23
+ * ```
24
+ */
25
+ import type { QueryKey } from '@tanstack/react-query';
26
+ import type { CacheBridgeHandler } from './cache-bridge';
27
+ /** The `deleted` event may carry the whole entity or just its id. */
28
+ export type DeletedPayload<T> = T | {
29
+ id: string;
30
+ };
31
+ /** Config for `createEntityCacheHandlers`. */
32
+ export interface EntityCacheConfig<T> {
33
+ /** Read the entity's id — the identity used to match, replace and remove. */
34
+ getId: (entity: T) => string;
35
+ /** Read the id from a `deleted` payload (entity or `{ id }`). Default `getId` / `.id`. */
36
+ getDeletedId?: (payload: DeletedPayload<T>) => string;
37
+ /** Query key (or prefix) of the list(s) to patch — matched by partial equality. */
38
+ listKey: QueryKey;
39
+ /** Build the detail query key for an id. Omit to skip detail-cache updates. */
40
+ detailKey?: (id: string) => QueryKey;
41
+ }
42
+ /** The three handlers to wire onto a `createCacheBridge` `handlers` map. */
43
+ export interface EntityCacheHandlers<T> {
44
+ created: CacheBridgeHandler<T>;
45
+ updated: CacheBridgeHandler<T>;
46
+ deleted: CacheBridgeHandler<DeletedPayload<T>>;
47
+ }
48
+ /**
49
+ * Build created / updated / deleted cache handlers for one entity. Each skips a
50
+ * stale socket echo of a change the client just made (`ctx.isFresh`), keyed on
51
+ * the entity's detail key when available, else the list key.
52
+ */
53
+ export declare function createEntityCacheHandlers<T>(config: EntityCacheConfig<T>): EntityCacheHandlers<T>;
54
+ //# sourceMappingURL=entity-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entity-cache.d.ts","sourceRoot":"","sources":["../../src/react/entity-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,KAAK,EAAgB,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGpE,OAAO,KAAK,EAAsB,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAK7E,qEAAqE;AACrE,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI,CAAC,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnD,8CAA8C;AAC9C,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAClC,6EAA6E;IAC7E,KAAK,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,MAAM,CAAC;IAC7B,0FAA0F;IAC1F,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC;IACtD,mFAAmF;IACnF,OAAO,EAAE,QAAQ,CAAC;IAClB,+EAA+E;IAC/E,SAAS,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,QAAQ,CAAC;CACtC;AAED,4EAA4E;AAC5E,MAAM,WAAW,mBAAmB,CAAC,CAAC;IACpC,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC;IAC/B,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC;IAC/B,OAAO,EAAE,kBAAkB,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;CAChD;AAyBD;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,CAAC,EACzC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAC3B,mBAAmB,CAAC,CAAC,CAAC,CAuCxB"}
package/dist/react.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export { type CacheBridge, type CacheBridgeConfig, type CacheBridgeContext, type CacheBridgeHandler, type CacheBridgeHandlers, type CacheBridgeSocket, createCacheBridge, } from './react/cache-bridge';
2
2
  export { type CursorQueryConfig, createCursorQuery } from './react/cursor-query';
3
+ export { createEntityCacheHandlers, type DeletedPayload, type EntityCacheConfig, type EntityCacheHandlers, } from './react/entity-cache';
3
4
  //# sourceMappingURL=react.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,iBAAiB,GAClB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,KAAK,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,iBAAiB,GAClB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,KAAK,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACjF,OAAO,EACL,yBAAyB,EACzB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,GACzB,MAAM,sBAAsB,CAAC"}
package/dist/react.js CHANGED
@@ -1,3 +1,7 @@
1
+ import {
2
+ isRecord
3
+ } from "./index-809wc1tt.js";
4
+
1
5
  // src/react/cache-bridge.ts
2
6
  function createCacheBridge(config) {
3
7
  const freshWindow = config.freshWindow ?? 500;
@@ -46,7 +50,77 @@ function createCursorQuery(config) {
46
50
  getNextPageParam: (lastPage) => lastPage.nextCursor
47
51
  });
48
52
  }
53
+ // src/react/entity-cache.ts
54
+ function patchItems(data, fn) {
55
+ if (!data)
56
+ return data;
57
+ if ("pages" in data) {
58
+ return { ...data, pages: data.pages.map((page) => ({ ...page, items: fn(page.items) })) };
59
+ }
60
+ return { ...data, items: fn(data.items) };
61
+ }
62
+ function prepend(data, entity) {
63
+ if (!data)
64
+ return data;
65
+ if ("pages" in data) {
66
+ const [first, ...rest] = data.pages;
67
+ if (!first)
68
+ return data;
69
+ return { ...data, pages: [{ ...first, items: [entity, ...first.items] }, ...rest] };
70
+ }
71
+ return { ...data, items: [entity, ...data.items] };
72
+ }
73
+ function createEntityCacheHandlers(config) {
74
+ const { getId, listKey, detailKey } = config;
75
+ const deletedId = config.getDeletedId ?? ((payload) => idOf(payload, getId));
76
+ const freshKey = (id) => detailKey ? detailKey(id) : listKey;
77
+ const patchList = (ctx, fn) => {
78
+ ctx.queryClient.setQueriesData({ queryKey: listKey }, (old) => patchItems(old, fn));
79
+ };
80
+ return {
81
+ created(entity, ctx) {
82
+ const id = getId(entity);
83
+ if (ctx.isFresh(freshKey(id)))
84
+ return;
85
+ ctx.queryClient.setQueriesData({ queryKey: listKey }, (old) => {
86
+ const present = hasId(old, getId, id);
87
+ return present ? old : prepend(old, entity);
88
+ });
89
+ if (detailKey)
90
+ ctx.queryClient.setQueryData(detailKey(id), entity);
91
+ },
92
+ updated(entity, ctx) {
93
+ const id = getId(entity);
94
+ if (ctx.isFresh(freshKey(id)))
95
+ return;
96
+ patchList(ctx, (items) => items.map((item) => getId(item) === id ? entity : item));
97
+ if (detailKey)
98
+ ctx.queryClient.setQueryData(detailKey(id), entity);
99
+ },
100
+ deleted(payload, ctx) {
101
+ const id = deletedId(payload);
102
+ if (ctx.isFresh(freshKey(id)))
103
+ return;
104
+ patchList(ctx, (items) => items.filter((item) => getId(item) !== id));
105
+ if (detailKey)
106
+ ctx.queryClient.removeQueries({ queryKey: detailKey(id) });
107
+ }
108
+ };
109
+ }
110
+ function hasStringId(value) {
111
+ return isRecord(value) && typeof value.id === "string";
112
+ }
113
+ function idOf(payload, getId) {
114
+ return hasStringId(payload) ? payload.id : getId(payload);
115
+ }
116
+ function hasId(data, getId, id) {
117
+ if (!data)
118
+ return false;
119
+ const pages = "pages" in data ? data.pages : [data];
120
+ return pages.some((page) => page.items.some((item) => getId(item) === id));
121
+ }
49
122
  export {
123
+ createEntityCacheHandlers,
50
124
  createCursorQuery,
51
125
  createCacheBridge
52
126
  };
@@ -1 +1 @@
1
- {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/server/create.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAEV,eAAe,EACf,aAAa,EAGd,MAAM,SAAS,CAAC;AAEjB,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAyNxF;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,eAAe,uBAsBnD"}
1
+ {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/server/create.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAEV,eAAe,EACf,aAAa,EAGd,MAAM,SAAS,CAAC;AAEjB,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CA8NxF;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,eAAe,uBAsBnD"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * A framework for the `onError` hook — the project supplies a code map and an
3
+ * envelope shape, `createErrorHook` does the normalisation.
4
+ *
5
+ * Every project writes the same `onError`: turn a thrown value into one wire
6
+ * shape, mapping stitchkit's own error codes to the app's public codes. The
7
+ * exhaustive `codeMap` (`satisfies Record<StitchErrorCode, …>`) is the point —
8
+ * a new `StitchErrorCode` in an upgrade breaks the map at compile time instead
9
+ * of leaking stitchkit's code to the wire. The envelope stays app-owned via
10
+ * `render`, so the core prescribes no domain shape (ADR 0002).
11
+ *
12
+ * ```ts
13
+ * const onError = createErrorHook({
14
+ * codeMap: {
15
+ * BAD_REQUEST: 'bad_request', VALIDATION_ERROR: 'bad_request',
16
+ * UNAUTHORIZED: 'unauthenticated', FORBIDDEN: 'forbidden',
17
+ * NOT_FOUND: 'not_found', METHOD_NOT_ALLOWED: 'not_found',
18
+ * CONFLICT: 'conflict', RATE_LIMITED: 'rate_limited',
19
+ * INTERNAL_SERVER_ERROR: 'internal',
20
+ * } satisfies Record<StitchErrorCode, string>,
21
+ * render: (info) => ({ ok: false, error: { code: info.code, message: info.message } }),
22
+ * });
23
+ * createServer({ services, hooks: { onError } });
24
+ * ```
25
+ */
26
+ import { type StitchErrorCode } from '../contract';
27
+ import type { LifecycleHooks } from './types';
28
+ /** The normalised error handed to `render` — code already remapped. */
29
+ export interface ResolvedError {
30
+ /** Wire code — the app code from `codeMap` for a stitch error, else the thrown code. */
31
+ code: string;
32
+ /** HTTP status. */
33
+ status: number;
34
+ /** Safe message — the `AppError` message, or a generic string for a raw throw. */
35
+ message: string;
36
+ /** Structured details, when the thrown `AppError` carried them. */
37
+ details?: Record<string, unknown>;
38
+ /** Recovery hint, when the thrown `AppError` carried one. */
39
+ hint?: string;
40
+ }
41
+ /** Config for `createErrorHook`. */
42
+ export interface ErrorHookConfig<TWireCode extends string = string> {
43
+ /**
44
+ * Remap stitchkit's own error codes to your public wire codes. Exhaustive —
45
+ * `satisfies Record<StitchErrorCode, …>` makes a new framework code a compile
46
+ * error here. Codes you threw yourself (not stitchkit's) pass through as-is.
47
+ */
48
+ codeMap?: Record<StitchErrorCode, TWireCode>;
49
+ /** Build the response body from the resolved error. */
50
+ render: (info: ResolvedError) => unknown;
51
+ /** Observe the raw thrown value before rendering — logging / metrics. */
52
+ onError?: (error: unknown, info: ResolvedError) => void;
53
+ }
54
+ /** Build an `onError` hook from a code map + envelope renderer. */
55
+ export declare function createErrorHook<TWireCode extends string = string>(config: ErrorHookConfig<TWireCode>): NonNullable<LifecycleHooks['onError']>;
56
+ //# sourceMappingURL=error-hook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-hook.d.ts","sourceRoot":"","sources":["../../src/server/error-hook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,EAA+B,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAChF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,uEAAuE;AACvE,MAAM,WAAW,aAAa;IAC5B,wFAAwF;IACxF,IAAI,EAAE,MAAM,CAAC;IACb,mBAAmB;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,kFAAkF;IAClF,OAAO,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,oCAAoC;AACpC,MAAM,WAAW,eAAe,CAAC,SAAS,SAAS,MAAM,GAAG,MAAM;IAChE;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,eAAe,EAAE,SAAS,CAAC,CAAC;IAC7C,uDAAuD;IACvD,MAAM,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,OAAO,CAAC;IACzC,yEAAyE;IACzE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;CACzD;AAED,mEAAmE;AACnE,wBAAgB,eAAe,CAAC,SAAS,SAAS,MAAM,GAAG,MAAM,EAC/D,MAAM,EAAE,eAAe,CAAC,SAAS,CAAC,GACjC,WAAW,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,CAoBxC"}
@@ -1,12 +1,13 @@
1
1
  export { AppError, appError, badRequest, conflict, forbidden, isStitchErrorCode, notFound, rateLimited, STITCH_ERROR_STATUS, type StitchErrorCode, unauthorized, } from '../contract';
2
2
  export { cacheHeaders, createCache } from './cache';
3
3
  export { createHandler, createServer } from './create';
4
+ export { createErrorHook, type ErrorHookConfig, type ResolvedError, } from './error-hook';
4
5
  export { createEventBus, type EventBus } from './event-bus';
5
6
  export { type ByteRange, parseByteRange, type ServeFileOptions, serveFile, weakETag, } from './file';
6
7
  export { createImplement, implement } from './implement';
7
8
  export { type AuthHook, type AuthHookConfig, type AuthRule, type BearerResolverConfig, createAuthHook, createBearerResolver, extractToken, type JwtPayload, type SignJwtOptions, signJwt, verifyJwt, } from './middleware/auth';
8
9
  export { type CookieDef, type CookieOptions, defineCookie, parseCookies, serializeCookie, } from './middleware/cookies';
9
- export { type CorsConfig, corsHeaders, corsPreflightResponse, } from './middleware/cors';
10
+ export { type CorsConfig, corsHeaders, corsPreflightResponse, DEFAULT_CORS_ALLOW_HEADERS, } from './middleware/cors';
10
11
  export { deriveCodeChallenge, type PkceMethod, verifyPkce } from './middleware/pkce';
11
12
  export { parseMultipart } from './multipart';
12
13
  export { generateOpenApiDocument, type OpenApiConfig, type OpenApiDocument, type OpenApiInfo, type OpenApiServer, openApiRoute, } from './openapi';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,SAAS,EACT,iBAAiB,EACjB,QAAQ,EACR,WAAW,EACX,mBAAmB,EACnB,KAAK,eAAe,EACpB,YAAY,GACb,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EACL,KAAK,SAAS,EACd,cAAc,EACd,KAAK,gBAAgB,EACrB,SAAS,EACT,QAAQ,GACT,MAAM,QAAQ,CAAC;AAChB,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,KAAK,oBAAoB,EACzB,cAAc,EACd,oBAAoB,EACpB,YAAY,EACZ,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,OAAO,EACP,SAAS,GACV,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,YAAY,EACZ,YAAY,EACZ,eAAe,GAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,KAAK,UAAU,EACf,WAAW,EACX,qBAAqB,GACtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,mBAAmB,EAAE,KAAK,UAAU,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACrF,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EACL,uBAAuB,EACvB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,YAAY,GACb,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,iBAAiB,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAC9D,OAAO,EACL,KAAK,eAAe,EACpB,SAAS,EACT,eAAe,EACf,aAAa,EACb,eAAe,EACf,cAAc,GACf,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,YAAY,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,KAAK,eAAe,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrE,YAAY,EACV,SAAS,EACT,eAAe,EACf,aAAa,EACb,QAAQ,EACR,cAAc,EACd,SAAS,EACT,QAAQ,EACR,eAAe,EACf,UAAU,EACV,iBAAiB,EACjB,UAAU,EACV,YAAY,GACb,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,KAAK,YAAY,EACjB,wBAAwB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,aAAa,EAClB,aAAa,GACd,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,SAAS,EACT,iBAAiB,EACjB,QAAQ,EACR,WAAW,EACX,mBAAmB,EACnB,KAAK,eAAe,EACpB,YAAY,GACb,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACvD,OAAO,EACL,eAAe,EACf,KAAK,eAAe,EACpB,KAAK,aAAa,GACnB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,cAAc,EAAE,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EACL,KAAK,SAAS,EACd,cAAc,EACd,KAAK,gBAAgB,EACrB,SAAS,EACT,QAAQ,GACT,MAAM,QAAQ,CAAC;AAChB,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,KAAK,oBAAoB,EACzB,cAAc,EACd,oBAAoB,EACpB,YAAY,EACZ,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,OAAO,EACP,SAAS,GACV,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,YAAY,EACZ,YAAY,EACZ,eAAe,GAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,KAAK,UAAU,EACf,WAAW,EACX,qBAAqB,EACrB,0BAA0B,GAC3B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,mBAAmB,EAAE,KAAK,UAAU,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACrF,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EACL,uBAAuB,EACvB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,YAAY,GACb,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,iBAAiB,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAC9D,OAAO,EACL,KAAK,eAAe,EACpB,SAAS,EACT,eAAe,EACf,aAAa,EACb,eAAe,EACf,cAAc,GACf,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,YAAY,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,KAAK,eAAe,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrE,YAAY,EACV,SAAS,EACT,eAAe,EACf,aAAa,EACb,QAAQ,EACR,cAAc,EACd,SAAS,EACT,QAAQ,EACR,eAAe,EACf,UAAU,EACV,iBAAiB,EACjB,UAAU,EACV,YAAY,GACb,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,KAAK,YAAY,EACjB,wBAAwB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,aAAa,EAClB,aAAa,GACd,MAAM,aAAa,CAAC"}
@@ -1,7 +1,5 @@
1
1
  import {
2
2
  composeWebSocketHandlers,
3
- corsHeaders,
4
- corsPreflightResponse,
5
3
  createHandler,
6
4
  createImplement,
7
5
  createServer,
@@ -12,7 +10,7 @@ import {
12
10
  socketIoLane,
13
11
  staticRoute,
14
12
  webSocketLane
15
- } from "../index-6qv1fxqy.js";
13
+ } from "../index-wpbn133x.js";
16
14
  import {
17
15
  createAuthHook,
18
16
  createBearerResolver,
@@ -25,7 +23,12 @@ import {
25
23
  signJwt,
26
24
  verifyJwt,
27
25
  verifyPkce
28
- } from "../index-yv93m4yr.js";
26
+ } from "../index-ynnh9x6h.js";
27
+ import {
28
+ DEFAULT_CORS_ALLOW_HEADERS,
29
+ corsHeaders,
30
+ corsPreflightResponse
31
+ } from "../index-tje0q6gp.js";
29
32
  import {
30
33
  jsonSchemaFields,
31
34
  toJsonSchema
@@ -42,7 +45,7 @@ import {
42
45
  notFound,
43
46
  rateLimited,
44
47
  unauthorized
45
- } from "../index-ktn76xz0.js";
48
+ } from "../index-ng2v2ts4.js";
46
49
  import {
47
50
  extractIp,
48
51
  generateTraceId,
@@ -50,11 +53,11 @@ import {
50
53
  getTraceId,
51
54
  resolveSocketIp,
52
55
  resolveTraceId
53
- } from "../index-dz2k8d5h.js";
56
+ } from "../index-dzx781tm.js";
54
57
  import"../index-khwedj16.js";
55
58
  import {
56
59
  isRecord
57
- } from "../index-tm7dqzxc.js";
60
+ } from "../index-c7nyw0yt.js";
58
61
  // src/server/swept-map.ts
59
62
  function createSweptMap(options) {
60
63
  const store = new Map;
@@ -112,6 +115,26 @@ function cacheHeaders(maxAge, scope = "public") {
112
115
  "Cache-Control": `${scope}, max-age=${maxAge}`
113
116
  };
114
117
  }
118
+ // src/server/error-hook.ts
119
+ function createErrorHook(config) {
120
+ return (_ctx, error) => {
121
+ const isApp = AppError.is(error);
122
+ const rawCode = isApp ? error.code : "INTERNAL_SERVER_ERROR";
123
+ const code = config.codeMap && isStitchErrorCode(rawCode) ? config.codeMap[rawCode] : rawCode;
124
+ const info = {
125
+ code,
126
+ status: isApp ? error.status : 500,
127
+ message: isApp ? error.message : "Internal server error",
128
+ details: isApp ? error.details : undefined,
129
+ hint: isApp ? error.hint : undefined
130
+ };
131
+ config.onError?.(error, info);
132
+ return new Response(JSON.stringify(config.render(info)), {
133
+ status: info.status,
134
+ headers: { "content-type": "application/json" }
135
+ });
136
+ };
137
+ }
115
138
  // src/server/event-bus.ts
116
139
  function createEventBus(options = {}) {
117
140
  const subscriptions = new Map;
@@ -633,6 +656,7 @@ export {
633
656
  createImplement,
634
657
  createHandler,
635
658
  createEventBus,
659
+ createErrorHook,
636
660
  createCache,
637
661
  createBearerResolver,
638
662
  createAuthHook,
@@ -644,5 +668,6 @@ export {
644
668
  badRequest,
645
669
  appError,
646
670
  STITCH_ERROR_STATUS,
671
+ DEFAULT_CORS_ALLOW_HEADERS,
647
672
  AppError
648
673
  };