stitchkit 0.48.0 → 0.49.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 (54) hide show
  1. package/README.md +1 -2
  2. package/dist/browser/client.d.ts +3 -0
  3. package/dist/browser/client.d.ts.map +1 -1
  4. package/dist/browser/http.d.ts +7 -4
  5. package/dist/browser/http.d.ts.map +1 -1
  6. package/dist/cli.js +3 -2
  7. package/dist/{index-nrytvb30.js → index-03j2t778.js} +5 -8
  8. package/dist/{index-kp8xamqp.js → index-0hj37z43.js} +4 -2
  9. package/dist/index-48ffdxgk.js +6 -0
  10. package/dist/{index-44xysy8r.js → index-fjfzsq6y.js} +383 -26
  11. package/dist/index-h05ygjqx.js +149 -0
  12. package/dist/{index-ee621cmy.js → index-jewp9r0a.js} +5 -139
  13. package/dist/index-p9d4cxt5.js +436 -0
  14. package/dist/{index-8ekq6res.js → index-v58mwa19.js} +4 -2
  15. package/dist/index.d.ts +1 -1
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +47 -3
  18. package/dist/node.d.ts +4 -3
  19. package/dist/node.d.ts.map +1 -1
  20. package/dist/node.js +109 -14
  21. package/dist/observability/audit.d.ts +4 -1
  22. package/dist/observability/audit.d.ts.map +1 -1
  23. package/dist/observability/index.d.ts +1 -0
  24. package/dist/observability/index.d.ts.map +1 -1
  25. package/dist/observability/index.js +150 -16
  26. package/dist/observability/status.d.ts +105 -0
  27. package/dist/observability/status.d.ts.map +1 -0
  28. package/dist/server/bun.d.ts +7 -8
  29. package/dist/server/bun.d.ts.map +1 -1
  30. package/dist/server/implement.d.ts +16 -6
  31. package/dist/server/implement.d.ts.map +1 -1
  32. package/dist/server/index.d.ts +4 -3
  33. package/dist/server/index.d.ts.map +1 -1
  34. package/dist/server/index.js +132 -11
  35. package/dist/server/node.d.ts +12 -13
  36. package/dist/server/node.d.ts.map +1 -1
  37. package/dist/server/router.d.ts +4 -1
  38. package/dist/server/router.d.ts.map +1 -1
  39. package/dist/server/shutdown.d.ts +76 -0
  40. package/dist/server/shutdown.d.ts.map +1 -0
  41. package/dist/server/socket-io-config.d.ts +5 -1
  42. package/dist/server/socket-io-config.d.ts.map +1 -1
  43. package/dist/server/socket-io-node.d.ts +4 -1
  44. package/dist/server/socket-io-node.d.ts.map +1 -1
  45. package/dist/server/socket-io.d.ts +12 -4
  46. package/dist/server/socket-io.d.ts.map +1 -1
  47. package/dist/testing.d.ts +34 -0
  48. package/dist/testing.d.ts.map +1 -0
  49. package/dist/testing.js +36 -0
  50. package/dist/tools/mcp.d.ts +1 -0
  51. package/dist/tools/mcp.d.ts.map +1 -1
  52. package/dist/tools.js +21 -428
  53. package/llms-full.txt +290 -43
  54. package/package.json +8 -3
package/dist/index.js CHANGED
@@ -295,7 +295,11 @@ function joinClientBaseUrl(baseUrl, relativeUrl) {
295
295
  }
296
296
 
297
297
  // src/browser/http.ts
298
- import ky, { isHTTPError } from "ky";
298
+ import ky, {
299
+ isHTTPError,
300
+ isNetworkError,
301
+ isTimeoutError
302
+ } from "ky";
299
303
 
300
304
  // src/observability/trace.ts
301
305
  var TRACEPARENT_RE = /^([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})(.*)$/i;
@@ -371,6 +375,15 @@ class ApiError extends Error {
371
375
  return error instanceof ApiError;
372
376
  }
373
377
  }
378
+ function shouldRetryBunNetworkError(error) {
379
+ if (ApiError.is(error) || isHTTPError(error) || isNetworkError(error) || isTimeoutError(error) || error instanceof RequestCancellationError || error instanceof DOMException && (error.name === "AbortError" || error.name === "TimeoutError")) {
380
+ return;
381
+ }
382
+ if (!(error instanceof Error))
383
+ return;
384
+ const code = Object.getOwnPropertyDescriptor(error, "code");
385
+ return code && "value" in code && code.value === "ConnectionRefused" ? true : undefined;
386
+ }
374
387
  function parseApiErrorBody(body) {
375
388
  if (!isRecord(body) || !isRecord(body.error))
376
389
  return null;
@@ -384,6 +397,34 @@ function parseApiErrorBody(body) {
384
397
  hint: typeof error.hint === "string" ? error.hint : undefined
385
398
  };
386
399
  }
400
+ function createRetryAwareFetch() {
401
+ const runtimeFetch = globalThis.fetch.bind(globalThis);
402
+ let attempt = 0;
403
+ return (input, init) => {
404
+ attempt += 1;
405
+ if (attempt === 1) {
406
+ return runtimeFetch(input, init);
407
+ }
408
+ if (!(input instanceof Request))
409
+ return runtimeFetch(input, init);
410
+ const streamedBody = input.body ? { body: input.body, duplex: "half" } : {};
411
+ return runtimeFetch(input.url, {
412
+ ...init,
413
+ method: input.method,
414
+ headers: input.headers,
415
+ ...streamedBody,
416
+ cache: input.cache,
417
+ credentials: input.credentials,
418
+ integrity: input.integrity,
419
+ keepalive: input.keepalive,
420
+ mode: input.mode,
421
+ redirect: input.redirect,
422
+ referrer: input.referrer,
423
+ referrerPolicy: input.referrerPolicy,
424
+ signal: input.signal
425
+ });
426
+ };
427
+ }
387
428
  function createHttpClient(config) {
388
429
  let ssrCookies = null;
389
430
  let isLoggedOut = false;
@@ -404,7 +445,8 @@ function createHttpClient(config) {
404
445
  retry: {
405
446
  limit: config.retry?.limit ?? 2,
406
447
  methods: config.retry?.methods ?? ["get"],
407
- statusCodes: config.retry?.statusCodes ?? []
448
+ statusCodes: config.retry?.statusCodes ?? [],
449
+ shouldRetry: ({ error }) => shouldRetryBunNetworkError(error)
408
450
  },
409
451
  hooks: {
410
452
  beforeRequest: [
@@ -448,6 +490,7 @@ function createHttpClient(config) {
448
490
  async function request(method, url, data, options = {}) {
449
491
  const cancellation = createRequestCancellation(options.signal, options.timeout ?? config.timeout ?? 30000);
450
492
  const kyOptions = {
493
+ fetch: createRetryAwareFetch(),
451
494
  timeout: false,
452
495
  signal: cancellation.signal
453
496
  };
@@ -657,6 +700,7 @@ function createHttpExecutor(endpoint, prefix, client, config) {
657
700
  };
658
701
  }
659
702
  function createFetchExecutor(endpoint, prefix, config, contractConfig) {
703
+ const executeFetch = config.fetch ?? globalThis.fetch;
660
704
  return async (requestArgs, options) => {
661
705
  const plan = planClientRequest(endpoint, prefix, requestArgs, contractConfig);
662
706
  const url = joinClientBaseUrl(config.baseUrl, plan.relativeUrl);
@@ -671,7 +715,7 @@ function createFetchExecutor(endpoint, prefix, config, contractConfig) {
671
715
  try {
672
716
  return await cancellation.run(async (signal) => {
673
717
  const body = endpoint.multipart ? buildMultipartForm(endpoint.multipart, plan.remainingArgs) : hasBody ? JSON.stringify(plan.remainingArgs) : undefined;
674
- const res = await fetch(url, {
718
+ const res = await executeFetch(url, {
675
719
  method: endpoint.method,
676
720
  headers,
677
721
  credentials: config.credentials,
package/dist/node.d.ts CHANGED
@@ -6,10 +6,11 @@
6
6
  */
7
7
  export { AppError, appError, badRequest, conflict, forbidden, notFound, rateLimited, unauthorized, } from './contract';
8
8
  export { createHandler } from './server/create';
9
- export { createImplement, implement } from './server/implement';
9
+ export { createImplement, createImplementRegistry, type ExactRegistryHandlers, type ImplementationRegistry, implement, implementRegistry, type RegistryHandlers, } from './server/implement';
10
10
  export type { LogFormat } from './server/logger';
11
- export { type NodeServerConfig, type NodeServerHandle, serveNode } from './server/node';
11
+ export { type NodeRuntimeServer, type NodeServerConfig, type NodeServerHandle, type NodeSocketLifecycle, serveNode, } from './server/node';
12
12
  export { bindRealtimeServer, type RealtimeServer, type RealtimeServerConnection, type RealtimeServerHandle, } from './server/realtime';
13
- export { createNodeSocketIOServer as createSocketIOServer, type NodeSocketIOServerHandle as SocketIOServerHandle, type SocketIOServerConfig, } from './server/socket-io-node';
13
+ export { type ManagedServerHandle, type ShutdownOptions, ShutdownOptionsSchema, type ShutdownResult, ShutdownResultSchema, type ShutdownState, ShutdownStateSchema, type ShutdownStatus, ShutdownStatusSchema, } from './server/shutdown';
14
+ export { createNodeSocketIOServer as createSocketIOServer, type NodeSocketIOServerHandle as SocketIOServerHandle, type SocketIORequestPolicy, type SocketIOServerConfig, } from './server/socket-io-node';
14
15
  export type { FetchComposition, FetchHandler, HandlerConfig, LoggingConfig, LogOutcome, RawRoute, RawRouteContext, ServiceDef, } from './server/types';
15
16
  //# sourceMappingURL=node.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,WAAW,EACX,YAAY,GACb,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAChE,YAAY,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,KAAK,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AACxF,OAAO,EACL,kBAAkB,EAClB,KAAK,cAAc,EACnB,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,GAC1B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,wBAAwB,IAAI,oBAAoB,EAChD,KAAK,wBAAwB,IAAI,oBAAoB,EACrD,KAAK,oBAAoB,GAC1B,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,aAAa,EACb,UAAU,EACV,QAAQ,EACR,eAAe,EACf,UAAU,GACX,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,WAAW,EACX,YAAY,GACb,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EACL,eAAe,EACf,uBAAuB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,SAAS,EACT,iBAAiB,EACjB,KAAK,gBAAgB,GACtB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,SAAS,GACV,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,kBAAkB,EAClB,KAAK,cAAc,EACnB,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,GAC1B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,qBAAqB,EACrB,KAAK,cAAc,EACnB,oBAAoB,EACpB,KAAK,aAAa,EAClB,mBAAmB,EACnB,KAAK,cAAc,EACnB,oBAAoB,GACrB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,wBAAwB,IAAI,oBAAoB,EAChD,KAAK,wBAAwB,IAAI,oBAAoB,EACrD,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,GAC1B,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,aAAa,EACb,UAAU,EACV,QAAQ,EACR,eAAe,EACf,UAAU,GACX,MAAM,gBAAgB,CAAC"}
package/dist/node.js CHANGED
@@ -1,10 +1,17 @@
1
1
  import {
2
+ ShutdownOptionsSchema,
3
+ ShutdownResultSchema,
4
+ ShutdownStateSchema,
5
+ ShutdownStatusSchema,
2
6
  bindRealtimeServer,
3
7
  createHandler,
4
8
  createImplement,
9
+ createImplementRegistry,
10
+ createServerLifecycle,
5
11
  createSocketIOServer,
6
- implement
7
- } from "./index-44xysy8r.js";
12
+ implement,
13
+ implementRegistry
14
+ } from "./index-fjfzsq6y.js";
8
15
  import"./index-r1qp4rve.js";
9
16
  import {
10
17
  AppError,
@@ -15,29 +22,111 @@ import {
15
22
  notFound,
16
23
  rateLimited,
17
24
  unauthorized
18
- } from "./index-ee621cmy.js";
25
+ } from "./index-jewp9r0a.js";
26
+ import"./index-h05ygjqx.js";
19
27
  // src/server/node.ts
20
- import { serve } from "srvx";
28
+ import { serve } from "srvx/node";
21
29
  async function serveNode(config) {
22
30
  const { port = 3000, hostname, socket, wrapFetch, ...handlerConfig } = config;
23
31
  const handler = createHandler(handlerConfig);
24
- const fetch = wrapFetch ? wrapFetch(handler) : handler;
25
- const server = serve({ port, hostname, fetch });
26
- await server.ready();
27
- if (socket) {
28
- const nodeServer = server.node?.server;
29
- if (!nodeServer || !("maxRequestsPerSocket" in nodeServer)) {
30
- throw new Error("[stitchkit] serveNode: expected a node:http.Server for Socket.IO (none / http2).");
32
+ let runtime;
33
+ let nodeServer;
34
+ const sockets = new Set;
35
+ const upgradedSockets = new Set;
36
+ const responses = new Set;
37
+ const socketDrainWaiters = new Set;
38
+ const resolveSocketDrain = () => {
39
+ if (sockets.size !== 0)
40
+ return;
41
+ for (const resolve of socketDrainWaiters)
42
+ resolve();
43
+ socketDrainWaiters.clear();
44
+ };
45
+ const waitForSocketDrain = () => {
46
+ if (sockets.size === 0)
47
+ return Promise.resolve();
48
+ return new Promise((resolve) => socketDrainWaiters.add(resolve));
49
+ };
50
+ const requireRuntime = () => {
51
+ if (!runtime)
52
+ throw new Error("[stitchkit] Node server lifecycle started before srvx");
53
+ return runtime;
54
+ };
55
+ const requireNodeServer = () => {
56
+ if (!nodeServer)
57
+ throw new Error("[stitchkit] Node HTTP server is unavailable");
58
+ return nodeServer;
59
+ };
60
+ const adapter = {
61
+ beginShutdown: () => socket?.beginShutdown(),
62
+ pendingRequests: () => responses.size,
63
+ pendingWebSockets: () => upgradedSockets.size,
64
+ closeRealtime: () => socket?.close() ?? Promise.resolve(),
65
+ async stopGracefully() {
66
+ const runtimeClose = socket ? Promise.resolve() : requireRuntime().close(false);
67
+ requireNodeServer().closeIdleConnections?.();
68
+ await runtimeClose;
69
+ await waitForSocketDrain();
70
+ },
71
+ async forceStop() {
72
+ const activeServer = requireNodeServer();
73
+ const logicalClose = socket?.close();
74
+ const physicalClosures = [...sockets].map((activeSocket) => new Promise((resolve) => {
75
+ if (activeSocket.closed)
76
+ resolve();
77
+ else
78
+ activeSocket.once("close", () => resolve());
79
+ }));
80
+ activeServer.closeAllConnections?.();
81
+ for (const activeSocket of sockets)
82
+ activeSocket.destroy();
83
+ if (logicalClose)
84
+ await logicalClose;
85
+ else
86
+ await requireRuntime().close(true);
87
+ await Promise.all(physicalClosures);
88
+ responses.clear();
31
89
  }
32
- socket.attach(nodeServer);
90
+ };
91
+ const lifecycle = createServerLifecycle(() => adapter);
92
+ const consumerFetch = wrapFetch ? wrapFetch(handler) : handler;
93
+ const fetch = lifecycle.wrapFetch(consumerFetch);
94
+ runtime = serve({ port, hostname, fetch, gracefulShutdown: false });
95
+ await runtime.ready();
96
+ const candidate = runtime.node?.server;
97
+ if (!candidate || !("maxRequestsPerSocket" in candidate)) {
98
+ await runtime.close(true);
99
+ throw new Error("[stitchkit] serveNode: expected a node:http.Server for the managed lifecycle.");
33
100
  }
34
- const listenUrl = server.url ?? `http://${hostname ?? "localhost"}:${port}`;
101
+ nodeServer = candidate;
102
+ nodeServer.on("connection", (activeSocket) => {
103
+ sockets.add(activeSocket);
104
+ activeSocket.once("close", () => {
105
+ sockets.delete(activeSocket);
106
+ upgradedSockets.delete(activeSocket);
107
+ resolveSocketDrain();
108
+ });
109
+ });
110
+ nodeServer.on("upgrade", (request) => upgradedSockets.add(request.socket));
111
+ nodeServer.prependListener("request", (_request, response) => {
112
+ responses.add(response);
113
+ const complete = () => responses.delete(response);
114
+ response.once("finish", complete);
115
+ response.once("close", complete);
116
+ });
117
+ socket?.attach(nodeServer);
118
+ const listenUrl = runtime.url ?? `http://${hostname ?? "localhost"}:${port}`;
35
119
  const resolvedPort = Number(new URL(listenUrl).port) || port;
36
120
  const resolvedHost = hostname ?? "localhost";
121
+ const server = runtime;
37
122
  return {
38
123
  url: `http://${resolvedHost}:${resolvedPort}`,
39
124
  port: resolvedPort,
40
- close: (closeActive) => server.close(closeActive)
125
+ runtime: server,
126
+ get status() {
127
+ return lifecycle.status;
128
+ },
129
+ shutdown: lifecycle.shutdown
41
130
  };
42
131
  }
43
132
  // src/server/socket-io-node.ts
@@ -49,14 +138,20 @@ export {
49
138
  serveNode,
50
139
  rateLimited,
51
140
  notFound,
141
+ implementRegistry,
52
142
  implement,
53
143
  forbidden,
54
144
  createNodeSocketIOServer as createSocketIOServer,
145
+ createImplementRegistry,
55
146
  createImplement,
56
147
  createHandler,
57
148
  conflict,
58
149
  bindRealtimeServer,
59
150
  badRequest,
60
151
  appError,
152
+ ShutdownStatusSchema,
153
+ ShutdownStateSchema,
154
+ ShutdownResultSchema,
155
+ ShutdownOptionsSchema,
61
156
  AppError
62
157
  };
@@ -2,6 +2,7 @@ import type { ToolCallHooks } from '../tools/execute';
2
2
  import { type RequestContext } from './context';
3
3
  import type { RequestEvent } from './event';
4
4
  import { type SanitizeOptions } from './sanitize';
5
+ import { type ObservabilityDrainReport, type ObservabilityStatus } from './status';
5
6
  /** One isolated RequestEvent sink and its sanitisation policy. */
6
7
  export interface RequestEventSinkConfig {
7
8
  /**
@@ -56,8 +57,10 @@ export interface Observability {
56
57
  toolCall: ToolCallHooks;
57
58
  /** Wait for events admitted before this call. */
58
59
  flush(): Promise<void>;
60
+ /** Read an immutable snapshot of each enabled sink and their aggregate. */
61
+ getStatus(): ObservabilityStatus;
59
62
  /** Stop admission and drain every previously accepted event. Idempotent. */
60
- close(): Promise<void>;
63
+ close(): Promise<ObservabilityDrainReport>;
61
64
  }
62
65
  export declare function createObservability(config: ObservabilityConfig): Observability;
63
66
  //# sourceMappingURL=audit.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/observability/audit.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,aAAa,EAAc,MAAM,kBAAkB,CAAC;AAClE,OAAO,EAAqB,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAe,KAAK,eAAe,EAAmB,MAAM,YAAY,CAAC;AAGhF,kEAAkE;AAClE,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,KAAK,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,iFAAiF;IACjF,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC;IAC1C,yEAAyE;IACzE,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2DAA2D;IAC3D,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,SAAS,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,8DAA8D;IAC9D,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnD;AAED,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,QAAQ,CAAC;AAEnD,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,cAAc,CAAC;IACvB,KAAK,EAAE,YAAY,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,0BAA2B,SAAQ,sBAAsB;IACxE,2DAA2D;IAC3D,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,0BAA0B,CAAC;IACrC,KAAK,CAAC,EAAE,sBAAsB,CAAC;CAChC;AAED,gEAAgE;AAChE,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,cAAc,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;CAC5B;AAED,0EAA0E;AAC1E,MAAM,WAAW,mBAAmB;IAClC,cAAc,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,qBAAqB,GAAG,IAAI,CAAC;CACnD;AAED,iFAAiF;AACjF,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,QAAQ,EAAE,aAAa,CAAC;IACxB,iDAAiD;IACjD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,4EAA4E;IAC5E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAuKD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,mBAAmB,GAAG,aAAa,CAkI9E"}
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/observability/audit.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,aAAa,EAAc,MAAM,kBAAkB,CAAC;AAClE,OAAO,EAAqB,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAe,KAAK,eAAe,EAAmB,MAAM,YAAY,CAAC;AAChF,OAAO,EAEL,KAAK,wBAAwB,EAI7B,KAAK,mBAAmB,EAEzB,MAAM,UAAU,CAAC;AAGlB,kEAAkE;AAClE,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,KAAK,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,iFAAiF;IACjF,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC;IAC1C,yEAAyE;IACzE,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2DAA2D;IAC3D,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,SAAS,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,8DAA8D;IAC9D,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnD;AAED,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,QAAQ,CAAC;AAEnD,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,cAAc,CAAC;IACvB,KAAK,EAAE,YAAY,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,0BAA2B,SAAQ,sBAAsB;IACxE,2DAA2D;IAC3D,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,0BAA0B,CAAC;IACrC,KAAK,CAAC,EAAE,sBAAsB,CAAC;CAChC;AAED,gEAAgE;AAChE,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,cAAc,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;CAC5B;AAED,0EAA0E;AAC1E,MAAM,WAAW,mBAAmB;IAClC,cAAc,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,qBAAqB,GAAG,IAAI,CAAC;CACnD;AAED,iFAAiF;AACjF,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,QAAQ,EAAE,aAAa,CAAC;IACxB,iDAAiD;IACjD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,2EAA2E;IAC3E,SAAS,IAAI,mBAAmB,CAAC;IACjC,4EAA4E;IAC5E,KAAK,IAAI,OAAO,CAAC,wBAAwB,CAAC,CAAC;CAC5C;AAkOD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,mBAAmB,GAAG,aAAa,CAyJ9E"}
@@ -10,5 +10,6 @@ export { createObservability, type HttpRequestCompletion, type HttpRequestObserv
10
10
  export { getRequestContext, getTraceId, getUserId, type RequestContext, runWithRequestContext, setRequestDimensions, setRequestEndpoint, setRequestError, setRequestUser, type WrapRequestContextOptions, wrapInRequestContext, } from './context';
11
11
  export type { RequestEvent } from './event';
12
12
  export { type JsonValue, measureSize, redact, type SanitizeOptions, type SizeMeasure, sanitizePayload, truncatePreview, } from './sanitize';
13
+ export { type ObservabilityDrainReport, ObservabilityDrainReportSchema, type ObservabilitySinkStatus, ObservabilitySinkStatusSchema, type ObservabilityStatus, ObservabilityStatusSchema, } from './status';
13
14
  export { childSpan, createTraceContext, formatTraceparent, parseTraceparent, resolvePropagationContext, resolveTraceContext, type TraceContext, } from './trace';
14
15
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/observability/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EACL,mBAAmB,EACnB,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,0BAA0B,EAC/B,KAAK,QAAQ,EACb,KAAK,cAAc,EACnB,KAAK,SAAS,GACf,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,iBAAiB,EACjB,UAAU,EACV,SAAS,EACT,KAAK,cAAc,EACnB,qBAAqB,EACrB,oBAAoB,EACpB,kBAAkB,EAClB,eAAe,EACf,cAAc,EACd,KAAK,yBAAyB,EAC9B,oBAAoB,GACrB,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EACL,KAAK,SAAS,EACd,WAAW,EACX,MAAM,EACN,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,eAAe,EACf,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,SAAS,EACT,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,yBAAyB,EACzB,mBAAmB,EACnB,KAAK,YAAY,GAClB,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/observability/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EACL,mBAAmB,EACnB,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,0BAA0B,EAC/B,KAAK,QAAQ,EACb,KAAK,cAAc,EACnB,KAAK,SAAS,GACf,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,iBAAiB,EACjB,UAAU,EACV,SAAS,EACT,KAAK,cAAc,EACnB,qBAAqB,EACrB,oBAAoB,EACpB,kBAAkB,EAClB,eAAe,EACf,cAAc,EACd,KAAK,yBAAyB,EAC9B,oBAAoB,GACrB,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EACL,KAAK,SAAS,EACd,WAAW,EACX,MAAM,EACN,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,eAAe,EACf,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,KAAK,wBAAwB,EAC7B,8BAA8B,EAC9B,KAAK,uBAAuB,EAC5B,6BAA6B,EAC7B,KAAK,mBAAmB,EACxB,yBAAyB,GAC1B,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,SAAS,EACT,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,yBAAyB,EACzB,mBAAmB,EACnB,KAAK,YAAY,GAClB,MAAM,SAAS,CAAC"}
@@ -3,26 +3,85 @@ import {
3
3
  redact,
4
4
  sanitizePayload,
5
5
  truncatePreview
6
- } from "../index-8ekq6res.js";
6
+ } from "../index-v58mwa19.js";
7
7
  import {
8
- childSpan,
9
- createTraceContext,
10
- formatTraceparent,
11
8
  getRequestContext,
12
9
  getTraceId,
13
10
  getUserId,
14
- isRecord,
15
- parseTraceparent,
16
11
  recordedErrorMessage,
17
- resolvePropagationContext,
18
- resolveTraceContext,
19
12
  runWithRequestContext,
20
13
  setRequestDimensions,
21
14
  setRequestEndpoint,
22
15
  setRequestError,
23
16
  setRequestUser,
24
17
  wrapInRequestContext
25
- } from "../index-ee621cmy.js";
18
+ } from "../index-jewp9r0a.js";
19
+ import {
20
+ childSpan,
21
+ createTraceContext,
22
+ formatTraceparent,
23
+ isRecord,
24
+ parseTraceparent,
25
+ resolvePropagationContext,
26
+ resolveTraceContext
27
+ } from "../index-h05ygjqx.js";
28
+
29
+ // src/observability/status.ts
30
+ import { z } from "zod";
31
+ var NonNegativeIntegerSchema = z.number().int().nonnegative();
32
+ var ObservabilitySinkStatusSchema = z.object({
33
+ capacity: NonNegativeIntegerSchema,
34
+ received: NonNegativeIntegerSchema,
35
+ accepted: NonNegativeIntegerSchema,
36
+ filtered: NonNegativeIntegerSchema,
37
+ completed: NonNegativeIntegerSchema,
38
+ dropped: NonNegativeIntegerSchema,
39
+ failed: NonNegativeIntegerSchema,
40
+ preparationFailed: NonNegativeIntegerSchema,
41
+ preparing: NonNegativeIntegerSchema,
42
+ pending: NonNegativeIntegerSchema,
43
+ closed: z.boolean()
44
+ }).readonly();
45
+ var ObservabilityStatusObjectSchema = z.object({
46
+ request: ObservabilitySinkStatusSchema.optional(),
47
+ tools: ObservabilitySinkStatusSchema.optional(),
48
+ total: ObservabilitySinkStatusSchema
49
+ });
50
+ var ObservabilityStatusSchema = ObservabilityStatusObjectSchema.readonly();
51
+ var ObservabilityDrainReportSchema = ObservabilityStatusObjectSchema.extend({
52
+ durationMs: z.number().nonnegative()
53
+ }).readonly();
54
+ var SUMMED_STATUS_KEYS = [
55
+ "capacity",
56
+ "received",
57
+ "accepted",
58
+ "filtered",
59
+ "completed",
60
+ "dropped",
61
+ "failed",
62
+ "preparationFailed",
63
+ "preparing",
64
+ "pending"
65
+ ];
66
+ function aggregateObservabilityStatus(surfaces, closed) {
67
+ const totals = {
68
+ capacity: 0,
69
+ received: 0,
70
+ accepted: 0,
71
+ filtered: 0,
72
+ completed: 0,
73
+ dropped: 0,
74
+ failed: 0,
75
+ preparationFailed: 0,
76
+ preparing: 0,
77
+ pending: 0
78
+ };
79
+ for (const surface of surfaces) {
80
+ for (const key of SUMMED_STATUS_KEYS)
81
+ totals[key] += surface[key];
82
+ }
83
+ return ObservabilitySinkStatusSchema.parse({ ...totals, closed });
84
+ }
26
85
 
27
86
  // src/observability/audit.ts
28
87
  function toolErrorMessage(result) {
@@ -76,6 +135,13 @@ function createSinkManager(config) {
76
135
  }
77
136
  let sequence = 0;
78
137
  let closed = false;
138
+ let received = 0;
139
+ let accepted = 0;
140
+ let filtered = 0;
141
+ let completed = 0;
142
+ let dropped = 0;
143
+ let failed = 0;
144
+ let preparationFailed = 0;
79
145
  let closePromise;
80
146
  const preparing = new Map;
81
147
  const writes = new Map;
@@ -87,17 +153,27 @@ function createSinkManager(config) {
87
153
  };
88
154
  const admit = (id, event) => {
89
155
  try {
90
- if (config.filter && !config.filter(event))
156
+ if (config.filter && !config.filter(event)) {
157
+ filtered += 1;
91
158
  return;
159
+ }
92
160
  } catch (error) {
161
+ preparationFailed += 1;
93
162
  reportError(error, event);
94
163
  return;
95
164
  }
96
165
  if (writes.size >= maxPending) {
166
+ dropped += 1;
97
167
  reportDrop("capacity", event);
98
168
  return;
99
169
  }
100
- const write = Promise.resolve().then(() => config.write(event)).catch((error) => reportError(error, event)).finally(() => writes.delete(id));
170
+ accepted += 1;
171
+ const write = Promise.resolve().then(() => config.write(event)).then(() => {
172
+ completed += 1;
173
+ }).catch((error) => {
174
+ failed += 1;
175
+ reportError(error, event);
176
+ }).finally(() => writes.delete(id));
101
177
  writes.set(id, write);
102
178
  };
103
179
  const awaitGeneration = async (boundary) => {
@@ -107,22 +183,59 @@ function createSinkManager(config) {
107
183
  };
108
184
  return {
109
185
  submit(produce) {
186
+ received += 1;
110
187
  const id = ++sequence;
111
188
  if (closed) {
112
- Promise.resolve().then(produce).then((event) => reportDrop("closed", event)).catch((error) => reportError(error));
189
+ Promise.resolve().then(produce).then((event) => {
190
+ dropped += 1;
191
+ reportDrop("closed", event);
192
+ }).catch((error) => {
193
+ preparationFailed += 1;
194
+ reportError(error);
195
+ });
113
196
  return;
114
197
  }
115
- const preparation = Promise.resolve().then(produce).then((event) => admit(id, event)).catch((error) => reportError(error)).finally(() => preparing.delete(id));
198
+ const preparation = Promise.resolve().then(produce).then((event) => admit(id, event)).catch((error) => {
199
+ preparationFailed += 1;
200
+ reportError(error);
201
+ }).finally(() => preparing.delete(id));
116
202
  preparing.set(id, preparation);
117
203
  },
118
204
  flush() {
119
205
  return awaitGeneration(sequence);
120
206
  },
207
+ getStatus() {
208
+ return ObservabilitySinkStatusSchema.parse({
209
+ capacity: maxPending,
210
+ received,
211
+ accepted,
212
+ filtered,
213
+ completed,
214
+ dropped,
215
+ failed,
216
+ preparationFailed,
217
+ preparing: preparing.size,
218
+ pending: writes.size,
219
+ closed
220
+ });
221
+ },
121
222
  close() {
122
223
  if (closePromise)
123
224
  return closePromise;
124
225
  closed = true;
125
- closePromise = awaitGeneration(sequence);
226
+ closePromise = awaitGeneration(sequence).then(() => ObservabilitySinkStatusSchema.parse({
227
+ capacity: maxPending,
228
+ received,
229
+ accepted,
230
+ filtered,
231
+ completed,
232
+ dropped,
233
+ failed,
234
+ preparationFailed,
235
+ preparing: preparing.size,
236
+ pending: writes.size,
237
+ closed
238
+ }));
126
239
  return closePromise;
127
240
  }
128
241
  };
@@ -130,6 +243,7 @@ function createSinkManager(config) {
130
243
  function createObservability(config) {
131
244
  const requestManager = config.request ? createSinkManager(config.request) : undefined;
132
245
  const toolManager = config.tools ? createSinkManager(config.tools) : undefined;
246
+ let closed = false;
133
247
  let closePromise;
134
248
  const request = config.request ? {
135
249
  includePayload: config.request.includePayload ?? false,
@@ -228,16 +342,33 @@ function createObservability(config) {
228
342
  } catch {}
229
343
  }
230
344
  } : undefined;
345
+ const getStatus = () => {
346
+ const requestStatus = requestManager?.getStatus();
347
+ const toolsStatus = toolManager?.getStatus();
348
+ const surfaces = [requestStatus, toolsStatus].filter((status) => status !== undefined);
349
+ return ObservabilityStatusSchema.parse({
350
+ ...requestStatus && { request: requestStatus },
351
+ ...toolsStatus && { tools: toolsStatus },
352
+ total: aggregateObservabilityStatus(surfaces, closed)
353
+ });
354
+ };
231
355
  return {
232
356
  ...request && { request },
233
357
  toolCall: toolCall ?? {},
234
358
  async flush() {
235
359
  await Promise.all([requestManager?.flush(), toolManager?.flush()]);
236
360
  },
361
+ getStatus,
237
362
  close() {
238
363
  if (!closePromise) {
364
+ closed = true;
365
+ const startedAt = performance.now();
239
366
  closePromise = Promise.all([requestManager?.close(), toolManager?.close()]).then(() => {
240
- return;
367
+ const status = getStatus();
368
+ return ObservabilityDrainReportSchema.parse({
369
+ ...status,
370
+ durationMs: performance.now() - startedAt
371
+ });
241
372
  });
242
373
  }
243
374
  return closePromise;
@@ -264,5 +395,8 @@ export {
264
395
  formatTraceparent,
265
396
  createTraceContext,
266
397
  createObservability,
267
- childSpan
398
+ childSpan,
399
+ ObservabilityStatusSchema,
400
+ ObservabilitySinkStatusSchema,
401
+ ObservabilityDrainReportSchema
268
402
  };