@sentry/cloudflare 10.50.0 → 10.52.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 (90) hide show
  1. package/build/cjs/durableobject.js +68 -130
  2. package/build/cjs/durableobject.js.map +1 -1
  3. package/build/cjs/index.js +3 -0
  4. package/build/cjs/index.js.map +1 -1
  5. package/build/cjs/instrumentations/instrumentDurableObjectNamespace.js +14 -4
  6. package/build/cjs/instrumentations/instrumentDurableObjectNamespace.js.map +1 -1
  7. package/build/cjs/instrumentations/instrumentWorkerEntrypoint.js +12 -1
  8. package/build/cjs/instrumentations/instrumentWorkerEntrypoint.js.map +1 -1
  9. package/build/cjs/instrumentations/worker/instrumentEnv.js +23 -11
  10. package/build/cjs/instrumentations/worker/instrumentEnv.js.map +1 -1
  11. package/build/cjs/instrumentations/worker/instrumentQueue.js +2 -0
  12. package/build/cjs/instrumentations/worker/instrumentQueue.js.map +1 -1
  13. package/build/cjs/instrumentations/worker/instrumentQueueProducer.js +106 -0
  14. package/build/cjs/instrumentations/worker/instrumentQueueProducer.js.map +1 -0
  15. package/build/cjs/integrations/httpServer.js +68 -0
  16. package/build/cjs/integrations/httpServer.js.map +1 -0
  17. package/build/cjs/request.js +5 -0
  18. package/build/cjs/request.js.map +1 -1
  19. package/build/cjs/sdk.js +2 -0
  20. package/build/cjs/sdk.js.map +1 -1
  21. package/build/cjs/utils/isBinding.js +9 -0
  22. package/build/cjs/utils/isBinding.js.map +1 -1
  23. package/build/cjs/utils/rpcMeta.js +60 -0
  24. package/build/cjs/utils/rpcMeta.js.map +1 -0
  25. package/build/cjs/utils/rpcOptions.js +1 -36
  26. package/build/cjs/utils/rpcOptions.js.map +1 -1
  27. package/build/cjs/wrapMethodWithSentry.js +21 -1
  28. package/build/cjs/wrapMethodWithSentry.js.map +1 -1
  29. package/build/esm/durableobject.js +70 -132
  30. package/build/esm/durableobject.js.map +1 -1
  31. package/build/esm/index.js +2 -1
  32. package/build/esm/index.js.map +1 -1
  33. package/build/esm/instrumentations/instrumentDurableObjectNamespace.js +14 -5
  34. package/build/esm/instrumentations/instrumentDurableObjectNamespace.js.map +1 -1
  35. package/build/esm/instrumentations/instrumentWorkerEntrypoint.js +12 -1
  36. package/build/esm/instrumentations/instrumentWorkerEntrypoint.js.map +1 -1
  37. package/build/esm/instrumentations/worker/instrumentEnv.js +25 -13
  38. package/build/esm/instrumentations/worker/instrumentEnv.js.map +1 -1
  39. package/build/esm/instrumentations/worker/instrumentQueue.js +2 -0
  40. package/build/esm/instrumentations/worker/instrumentQueue.js.map +1 -1
  41. package/build/esm/instrumentations/worker/instrumentQueueProducer.js +104 -0
  42. package/build/esm/instrumentations/worker/instrumentQueueProducer.js.map +1 -0
  43. package/build/esm/integrations/httpServer.js +65 -0
  44. package/build/esm/integrations/httpServer.js.map +1 -0
  45. package/build/esm/package.json +1 -1
  46. package/build/esm/request.js +5 -0
  47. package/build/esm/request.js.map +1 -1
  48. package/build/esm/sdk.js +2 -0
  49. package/build/esm/sdk.js.map +1 -1
  50. package/build/esm/utils/isBinding.js +9 -1
  51. package/build/esm/utils/isBinding.js.map +1 -1
  52. package/build/esm/utils/rpcMeta.js +57 -0
  53. package/build/esm/utils/rpcMeta.js.map +1 -0
  54. package/build/esm/utils/rpcOptions.js +2 -36
  55. package/build/esm/utils/rpcOptions.js.map +1 -1
  56. package/build/esm/wrapMethodWithSentry.js +22 -2
  57. package/build/esm/wrapMethodWithSentry.js.map +1 -1
  58. package/build/types/durableobject.d.ts +2 -2
  59. package/build/types/durableobject.d.ts.map +1 -1
  60. package/build/types/index.d.ts +2 -1
  61. package/build/types/index.d.ts.map +1 -1
  62. package/build/types/instrumentations/instrumentDurableObjectNamespace.d.ts +1 -0
  63. package/build/types/instrumentations/instrumentDurableObjectNamespace.d.ts.map +1 -1
  64. package/build/types/instrumentations/instrumentWorkerEntrypoint.d.ts.map +1 -1
  65. package/build/types/instrumentations/worker/instrumentEnv.d.ts +3 -2
  66. package/build/types/instrumentations/worker/instrumentEnv.d.ts.map +1 -1
  67. package/build/types/instrumentations/worker/instrumentQueue.d.ts.map +1 -1
  68. package/build/types/instrumentations/worker/instrumentQueueProducer.d.ts +10 -0
  69. package/build/types/instrumentations/worker/instrumentQueueProducer.d.ts.map +1 -0
  70. package/build/types/integrations/httpServer.d.ts +61 -0
  71. package/build/types/integrations/httpServer.d.ts.map +1 -0
  72. package/build/types/request.d.ts.map +1 -1
  73. package/build/types/sdk.d.ts.map +1 -1
  74. package/build/types/utils/isBinding.d.ts +6 -1
  75. package/build/types/utils/isBinding.d.ts.map +1 -1
  76. package/build/types/utils/rpcMeta.d.ts +15 -0
  77. package/build/types/utils/rpcMeta.d.ts.map +1 -0
  78. package/build/types/utils/rpcOptions.d.ts +1 -12
  79. package/build/types/utils/rpcOptions.d.ts.map +1 -1
  80. package/build/types/wrapMethodWithSentry.d.ts.map +1 -1
  81. package/build/types-ts3.8/durableobject.d.ts +2 -2
  82. package/build/types-ts3.8/index.d.ts +2 -1
  83. package/build/types-ts3.8/instrumentations/instrumentDurableObjectNamespace.d.ts +1 -0
  84. package/build/types-ts3.8/instrumentations/worker/instrumentEnv.d.ts +3 -2
  85. package/build/types-ts3.8/instrumentations/worker/instrumentQueueProducer.d.ts +10 -0
  86. package/build/types-ts3.8/integrations/httpServer.d.ts +61 -0
  87. package/build/types-ts3.8/utils/isBinding.d.ts +6 -1
  88. package/build/types-ts3.8/utils/rpcMeta.d.ts +15 -0
  89. package/build/types-ts3.8/utils/rpcOptions.d.ts +1 -12
  90. package/package.json +2 -2
@@ -0,0 +1,104 @@
1
+ import { startSpan, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_OP } from '@sentry/core';
2
+
3
+ const ORIGIN = 'auto.faas.cloudflare.queue';
4
+
5
+ function startPublishSpan(
6
+ options
7
+
8
+ ,
9
+ callback,
10
+ ) {
11
+ const { bindingName, bodySize, messageCount } = options;
12
+
13
+ return startSpan(
14
+ {
15
+ op: 'queue.publish',
16
+ name: `send ${bindingName}`,
17
+ attributes: {
18
+ 'messaging.system': 'cloudflare',
19
+ 'messaging.destination.name': bindingName,
20
+ 'messaging.operation.type': 'send',
21
+ 'messaging.operation.name': 'send',
22
+ ...(messageCount !== undefined && { 'messaging.batch.message_count': messageCount }),
23
+ 'messaging.message.body.size': bodySize,
24
+ [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'queue.publish',
25
+ [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,
26
+ },
27
+ },
28
+ callback,
29
+ );
30
+ }
31
+
32
+ function getBodySize(body) {
33
+ if (body == null) {
34
+ return undefined;
35
+ }
36
+
37
+ if (typeof body === 'string') {
38
+ return new TextEncoder().encode(body).byteLength;
39
+ }
40
+
41
+ if (body instanceof ArrayBuffer) {
42
+ return body.byteLength;
43
+ }
44
+
45
+ if (ArrayBuffer.isView(body)) {
46
+ return body.byteLength;
47
+ }
48
+
49
+ try {
50
+ return new TextEncoder().encode(JSON.stringify(body)).byteLength;
51
+ } catch {
52
+ return undefined;
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Wraps a Queue producer binding to create `queue.publish` spans on
58
+ * `send` and `sendBatch` calls.
59
+ *
60
+ * The queue's own name is not available on the binding object, so we use
61
+ * the env binding key (e.g. `MY_QUEUE`) as `messaging.destination.name`.
62
+ */
63
+ function instrumentQueueProducer(queue, bindingName) {
64
+ return new Proxy(queue, {
65
+ get(target, prop, receiver) {
66
+ if (prop === 'send') {
67
+ const original = Reflect.get(target, prop, receiver) ;
68
+
69
+ return function ( message, options) {
70
+ return startPublishSpan({ bindingName, bodySize: getBodySize(message) }, () =>
71
+ Reflect.apply(original, target, [message, options]),
72
+ );
73
+ };
74
+ }
75
+
76
+ if (prop === 'sendBatch') {
77
+ const original = Reflect.get(target, prop, receiver) ;
78
+ return function (
79
+
80
+ messages,
81
+ options,
82
+ ) {
83
+ const messageArray = Array.from(messages);
84
+ const totalBodySize = messageArray.reduce((acc, m) => {
85
+ const size = getBodySize(m.body);
86
+ if (size === undefined) {
87
+ return acc;
88
+ }
89
+ return (acc ?? 0) + size;
90
+ }, undefined);
91
+
92
+ return startPublishSpan({ bindingName, bodySize: totalBodySize, messageCount: messageArray.length }, () =>
93
+ Reflect.apply(original, target, [messageArray, options]),
94
+ );
95
+ };
96
+ }
97
+
98
+ return Reflect.get(target, prop, receiver);
99
+ },
100
+ });
101
+ }
102
+
103
+ export { instrumentQueueProducer };
104
+ //# sourceMappingURL=instrumentQueueProducer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instrumentQueueProducer.js","sources":["../../../../src/instrumentations/worker/instrumentQueueProducer.ts"],"sourcesContent":["import type { MessageSendRequest, Queue, QueueSendBatchOptions, QueueSendOptions } from '@cloudflare/workers-types';\nimport { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startSpan } from '@sentry/core';\n\nconst ORIGIN = 'auto.faas.cloudflare.queue';\n\nfunction startPublishSpan<T>(\n options: {\n bindingName: string;\n bodySize: number | undefined;\n messageCount?: number;\n },\n callback: () => T,\n): T {\n const { bindingName, bodySize, messageCount } = options;\n\n return startSpan(\n {\n op: 'queue.publish',\n name: `send ${bindingName}`,\n attributes: {\n 'messaging.system': 'cloudflare',\n 'messaging.destination.name': bindingName,\n 'messaging.operation.type': 'send',\n 'messaging.operation.name': 'send',\n ...(messageCount !== undefined && { 'messaging.batch.message_count': messageCount }),\n 'messaging.message.body.size': bodySize,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'queue.publish',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n },\n },\n callback,\n );\n}\n\nfunction getBodySize(body: unknown): number | undefined {\n if (body == null) {\n return undefined;\n }\n\n if (typeof body === 'string') {\n return new TextEncoder().encode(body).byteLength;\n }\n\n if (body instanceof ArrayBuffer) {\n return body.byteLength;\n }\n\n if (ArrayBuffer.isView(body)) {\n return body.byteLength;\n }\n\n try {\n return new TextEncoder().encode(JSON.stringify(body)).byteLength;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Wraps a Queue producer binding to create `queue.publish` spans on\n * `send` and `sendBatch` calls.\n *\n * The queue's own name is not available on the binding object, so we use\n * the env binding key (e.g. `MY_QUEUE`) as `messaging.destination.name`.\n */\nexport function instrumentQueueProducer<T extends Queue>(queue: T, bindingName: string): T {\n return new Proxy(queue, {\n get(target, prop, receiver) {\n if (prop === 'send') {\n const original = Reflect.get(target, prop, receiver) as Queue['send'];\n\n return function (this: unknown, message: unknown, options?: QueueSendOptions): Promise<void> {\n return startPublishSpan({ bindingName, bodySize: getBodySize(message) }, () =>\n Reflect.apply(original, target, [message, options]),\n );\n };\n }\n\n if (prop === 'sendBatch') {\n const original = Reflect.get(target, prop, receiver) as Queue['sendBatch'];\n return function (\n this: unknown,\n messages: Iterable<MessageSendRequest>,\n options?: QueueSendBatchOptions,\n ): Promise<void> {\n const messageArray = Array.from(messages);\n const totalBodySize = messageArray.reduce<number | undefined>((acc, m) => {\n const size = getBodySize(m.body);\n if (size === undefined) {\n return acc;\n }\n return (acc ?? 0) + size;\n }, undefined);\n\n return startPublishSpan({ bindingName, bodySize: totalBodySize, messageCount: messageArray.length }, () =>\n Reflect.apply(original, target, [messageArray, options]),\n );\n };\n }\n\n return Reflect.get(target, prop, receiver);\n },\n });\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ import { defineIntegration, getIsolationScope, captureBodyFromWinterCGRequest } from '@sentry/core';
2
+
3
+ const INTEGRATION_NAME = 'HttpServer';
4
+
5
+ const _httpServerIntegration = ((options = {}) => {
6
+ return {
7
+ name: INTEGRATION_NAME,
8
+ maxRequestBodySize: options.maxRequestBodySize ?? 'medium',
9
+ ignoreRequestBody: options.ignoreRequestBody,
10
+ };
11
+ }) ;
12
+
13
+ /**
14
+ * Configures incoming HTTP request handling for Cloudflare Workers.
15
+ *
16
+ * This integration controls how incoming HTTP request data is captured,
17
+ * matching the API of `httpServerIntegration` in Node.js.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * Sentry.init({
22
+ * integrations: [
23
+ * Sentry.httpServerIntegration({
24
+ * maxRequestBodySize: 'medium',
25
+ * ignoreRequestBody: (url) => url.includes('/health'),
26
+ * }),
27
+ * ],
28
+ * });
29
+ * ```
30
+ */
31
+ const httpServerIntegration = defineIntegration(_httpServerIntegration);
32
+
33
+ /**
34
+ * Capture the request body based on the HttpServer integration config.
35
+ * Called internally by `wrapRequestHandler`.
36
+ */
37
+ async function captureIncomingRequestBody(client, request) {
38
+ const integration = client.getIntegrationByName(INTEGRATION_NAME);
39
+
40
+ if (!integration) {
41
+ return;
42
+ }
43
+
44
+ const maxRequestBodySize = integration.maxRequestBodySize;
45
+
46
+ if (maxRequestBodySize === 'none') {
47
+ return;
48
+ }
49
+
50
+ // Skip GET and HEAD requests - they don't have bodies
51
+ // Also skip OPTIONS, even if they may have a body, they might not give a lot of extra value
52
+ if (request.method === 'GET' || request.method === 'HEAD' || request.method === 'OPTIONS') {
53
+ return;
54
+ }
55
+
56
+ if (integration.ignoreRequestBody?.(request.url, request)) {
57
+ return;
58
+ }
59
+
60
+ const isolationScope = getIsolationScope();
61
+ await captureBodyFromWinterCGRequest(request, isolationScope, maxRequestBodySize);
62
+ }
63
+
64
+ export { captureIncomingRequestBody, httpServerIntegration };
65
+ //# sourceMappingURL=httpServer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"httpServer.js","sources":["../../../src/integrations/httpServer.ts"],"sourcesContent":["import type { Client, IntegrationFn, MaxRequestBodySize } from '@sentry/core';\nimport { captureBodyFromWinterCGRequest, defineIntegration, getIsolationScope } from '@sentry/core';\n\nconst INTEGRATION_NAME = 'HttpServer';\n\nexport interface HttpServerIntegrationOptions {\n /**\n * Controls the maximum size of incoming request bodies attached to events.\n *\n * Only applies to requests with textual content types (text/*, application/json,\n * application/x-www-form-urlencoded, application/xml, application/graphql).\n * Binary data is not captured.\n *\n * Available options:\n * - `'none'`: No request bodies will be attached\n * - `'small'`: Request bodies up to 1,000 bytes will be attached\n * - `'medium'`: Request bodies up to 10,000 bytes will be attached (default)\n * - `'always'`: Request bodies will always be attached (up to 1MB limit)\n *\n * @default 'medium'\n */\n maxRequestBodySize?: MaxRequestBodySize;\n\n /**\n * Do not capture the request body for incoming HTTP requests to URLs where the given callback returns `true`.\n * This can be useful for long running requests where the body is not needed, health check endpoints,\n * or requests containing sensitive data that should not be captured.\n *\n * @param url The full URL of the incoming request, including query string, protocol, host, etc.\n * @param request The incoming Request object.\n * @returns `true` to skip body capture for this request, `false` to capture normally.\n *\n * @example\n * ```ts\n * Sentry.httpServerIntegration({\n * ignoreRequestBody: (url) => url.includes('/health') || url.includes('/upload'),\n * })\n * ```\n */\n ignoreRequestBody?: (url: string, request: Request) => boolean;\n}\n\ninterface HttpServerIntegrationInstance {\n name: string;\n maxRequestBodySize: MaxRequestBodySize;\n ignoreRequestBody?: (url: string, request: Request) => boolean;\n}\n\nconst _httpServerIntegration = ((options: HttpServerIntegrationOptions = {}): HttpServerIntegrationInstance => {\n return {\n name: INTEGRATION_NAME,\n maxRequestBodySize: options.maxRequestBodySize ?? 'medium',\n ignoreRequestBody: options.ignoreRequestBody,\n };\n}) satisfies IntegrationFn;\n\n/**\n * Configures incoming HTTP request handling for Cloudflare Workers.\n *\n * This integration controls how incoming HTTP request data is captured,\n * matching the API of `httpServerIntegration` in Node.js.\n *\n * @example\n * ```ts\n * Sentry.init({\n * integrations: [\n * Sentry.httpServerIntegration({\n * maxRequestBodySize: 'medium',\n * ignoreRequestBody: (url) => url.includes('/health'),\n * }),\n * ],\n * });\n * ```\n */\nexport const httpServerIntegration = defineIntegration(_httpServerIntegration);\n\n/**\n * Capture the request body based on the HttpServer integration config.\n * Called internally by `wrapRequestHandler`.\n */\nexport async function captureIncomingRequestBody(client: Client, request: Request): Promise<void> {\n const integration = client.getIntegrationByName<HttpServerIntegrationInstance>(INTEGRATION_NAME);\n\n if (!integration) {\n return;\n }\n\n const maxRequestBodySize = integration.maxRequestBodySize;\n\n if (maxRequestBodySize === 'none') {\n return;\n }\n\n // Skip GET and HEAD requests - they don't have bodies\n // Also skip OPTIONS, even if they may have a body, they might not give a lot of extra value\n if (request.method === 'GET' || request.method === 'HEAD' || request.method === 'OPTIONS') {\n return;\n }\n\n if (integration.ignoreRequestBody?.(request.url, request)) {\n return;\n }\n\n const isolationScope = getIsolationScope();\n await captureBodyFromWinterCGRequest(request, isolationScope, maxRequestBodySize);\n}\n"],"names":[],"mappings":";;AAGA,MAAM,gBAAA,GAAmB,YAAY;;AA6CrC,MAAM,sBAAA,IAA0B,CAAC,OAAO,GAAiC,EAAE,KAAoC;AAC/G,EAAE,OAAO;AACT,IAAI,IAAI,EAAE,gBAAgB;AAC1B,IAAI,kBAAkB,EAAE,OAAO,CAAC,kBAAA,IAAsB,QAAQ;AAC9D,IAAI,iBAAiB,EAAE,OAAO,CAAC,iBAAiB;AAChD,GAAG;AACH,CAAC,CAAA;;AAED;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;MACa,qBAAA,GAAwB,iBAAiB,CAAC,sBAAsB;;AAE7E;AACA;AACA;AACA;AACO,eAAe,0BAA0B,CAAC,MAAM,EAAU,OAAO,EAA0B;AAClG,EAAE,MAAM,cAAc,MAAM,CAAC,oBAAoB,CAAgC,gBAAgB,CAAC;;AAElG,EAAE,IAAI,CAAC,WAAW,EAAE;AACpB,IAAI;AACJ,EAAE;;AAEF,EAAE,MAAM,kBAAA,GAAqB,WAAW,CAAC,kBAAkB;;AAE3D,EAAE,IAAI,kBAAA,KAAuB,MAAM,EAAE;AACrC,IAAI;AACJ,EAAE;;AAEF;AACA;AACA,EAAE,IAAI,OAAO,CAAC,MAAA,KAAW,SAAS,OAAO,CAAC,MAAA,KAAW,UAAU,OAAO,CAAC,MAAA,KAAW,SAAS,EAAE;AAC7F,IAAI;AACJ,EAAE;;AAEF,EAAE,IAAI,WAAW,CAAC,iBAAiB,GAAG,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,EAAE;AAC7D,IAAI;AACJ,EAAE;;AAEF,EAAE,MAAM,cAAA,GAAiB,iBAAiB,EAAE;AAC5C,EAAE,MAAM,8BAA8B,CAAC,OAAO,EAAE,cAAc,EAAE,kBAAkB,CAAC;AACnF;;;;"}
@@ -1 +1 @@
1
- {"type":"module","version":"10.50.0","sideEffects":false}
1
+ {"type":"module","version":"10.52.0","sideEffects":false}
@@ -1,4 +1,5 @@
1
1
  import { withIsolationScope, parseStringToURLObject, getHttpSpanDetailsFromUrlObject, httpHeadersToSpanAttributes, winterCGHeadersToDict, getClient, SEMANTIC_ATTRIBUTE_SENTRY_OP, captureException, continueTrace, startSpanManual, setHttpStatus } from '@sentry/core';
2
+ import { captureIncomingRequestBody } from './integrations/httpServer.js';
2
3
  import { flushAndDispose } from './flush.js';
3
4
  import { addCloudResourceContext, addRequest, addCultureContext } from './scope-utils.js';
4
5
  import { init } from './sdk.js';
@@ -67,6 +68,10 @@ function wrapRequestHandler(
67
68
  }
68
69
  }
69
70
 
71
+ if (client) {
72
+ await captureIncomingRequestBody(client, request);
73
+ }
74
+
70
75
  return continueTrace(
71
76
  { sentryTrace: request.headers.get('sentry-trace') || '', baggage: request.headers.get('baggage') },
72
77
  () => {
@@ -1 +1 @@
1
- {"version":3,"file":"request.js","sources":["../../src/request.ts"],"sourcesContent":["import type { CfProperties, ExecutionContext, IncomingRequestCfProperties } from '@cloudflare/workers-types';\nimport {\n captureException,\n continueTrace,\n getClient,\n getHttpSpanDetailsFromUrlObject,\n httpHeadersToSpanAttributes,\n parseStringToURLObject,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n setHttpStatus,\n startSpanManual,\n winterCGHeadersToDict,\n withIsolationScope,\n} from '@sentry/core';\nimport type { CloudflareOptions } from './client';\nimport { flushAndDispose } from './flush';\nimport { addCloudResourceContext, addCultureContext, addRequest } from './scope-utils';\nimport { init } from './sdk';\nimport { classifyResponseStreaming } from './utils/streaming';\n\ninterface RequestHandlerWrapperOptions {\n options: CloudflareOptions;\n request: Request<unknown, IncomingRequestCfProperties<unknown> | CfProperties<unknown>>;\n context: ExecutionContext | undefined;\n /**\n * If true, errors will be captured, rethrown and sent to Sentry.\n * Otherwise, errors are rethrown but not captured.\n *\n * You most likely don't want to set this to `false`, if you use `wrapRequestHandler` directly.\n * This is primarily meant as an escape hatch for higher-level SDKs relying on additional error\n * capturing mechanisms where this wrapper captures errors too early or too generally.\n *\n * @default true\n */\n captureErrors?: boolean;\n}\n\n/**\n * Wraps a cloudflare request handler in Sentry instrumentation\n */\nexport function wrapRequestHandler(\n wrapperOptions: RequestHandlerWrapperOptions,\n handler: (...args: unknown[]) => Response | Promise<Response>,\n): Promise<Response> {\n return withIsolationScope(async isolationScope => {\n const { options, request, captureErrors = true } = wrapperOptions;\n const context = wrapperOptions.context;\n\n const waitUntil = context?.waitUntil?.bind?.(context);\n\n const client = init({ ...options, ctx: context });\n isolationScope.setClient(client);\n\n const urlObject = parseStringToURLObject(request.url);\n const [name, attributes] = getHttpSpanDetailsFromUrlObject(urlObject, 'server', 'auto.http.cloudflare', request);\n\n const contentLength = request.headers.get('content-length');\n if (contentLength) {\n attributes['http.request.body.size'] = parseInt(contentLength, 10);\n }\n\n const userAgentHeader = request.headers.get('user-agent');\n if (userAgentHeader) {\n attributes['user_agent.original'] = userAgentHeader;\n }\n\n Object.assign(\n attributes,\n httpHeadersToSpanAttributes(\n winterCGHeadersToDict(request.headers),\n getClient()?.getOptions().sendDefaultPii ?? false,\n ),\n );\n\n attributes[SEMANTIC_ATTRIBUTE_SENTRY_OP] = 'http.server';\n\n addCloudResourceContext(isolationScope);\n addRequest(isolationScope, request);\n if (request.cf) {\n addCultureContext(isolationScope, request.cf);\n\n if (typeof request.cf.httpProtocol === 'string') {\n attributes['network.protocol.name'] = request.cf.httpProtocol;\n }\n }\n\n // Do not capture spans for OPTIONS and HEAD requests\n if (request.method === 'OPTIONS' || request.method === 'HEAD') {\n try {\n return await handler();\n } catch (e) {\n if (captureErrors) {\n captureException(e, { mechanism: { handled: false, type: 'auto.http.cloudflare' } });\n }\n throw e;\n } finally {\n waitUntil?.(flushAndDispose(client));\n }\n }\n\n return continueTrace(\n { sentryTrace: request.headers.get('sentry-trace') || '', baggage: request.headers.get('baggage') },\n () => {\n // Note: This span will not have a duration unless I/O happens in the handler. This is\n // because of how the cloudflare workers runtime works.\n // See: https://developers.cloudflare.com/workers/runtime-apis/performance/\n\n // Use startSpanManual to control when span ends (needed for streaming responses)\n return startSpanManual({ name, attributes }, async span => {\n let res: Response;\n\n try {\n res = await handler();\n setHttpStatus(span, res.status);\n\n // After the handler runs, the span name might have been updated by nested instrumentation\n // (e.g., Remix parameterizing routes). The span should already have the correct name\n // from that instrumentation, so we don't need to do anything here.\n } catch (e) {\n span.end();\n if (captureErrors) {\n captureException(e, { mechanism: { handled: false, type: 'auto.http.cloudflare' } });\n }\n waitUntil?.(flushAndDispose(client));\n throw e;\n }\n\n // Classify response to detect actual streaming\n const classification = classifyResponseStreaming(res);\n\n if (classification.isStreaming && res.body) {\n try {\n let ended = false;\n\n const endSpanOnce = (): void => {\n if (ended) return;\n\n ended = true;\n span.end();\n waitUntil?.(flushAndDispose(client));\n };\n\n const transform = new TransformStream({\n flush() {\n // Source stream completed normally.\n endSpanOnce();\n },\n cancel() {\n // Client disconnected (or downstream cancelled). The `cancel`\n // is being called while the response is still considered\n // active, so this is a safe place to end the span.\n endSpanOnce();\n },\n });\n\n return new Response(res.body.pipeThrough(transform), {\n status: res.status,\n statusText: res.statusText,\n headers: res.headers,\n });\n } catch {\n span.end();\n waitUntil?.(flushAndDispose(client));\n return res;\n }\n }\n\n // Non-streaming response - end span immediately and return original\n span.end();\n\n // Don't dispose for protocol upgrades (101 Switching Protocols) - the connection stays alive.\n // This includes WebSocket upgrades where webSocketMessage/webSocketClose handlers\n // will still be called and may need the client to capture errors.\n if (res.status === 101) {\n waitUntil?.(client?.flush(2000));\n } else {\n waitUntil?.(flushAndDispose(client));\n }\n return res;\n });\n },\n );\n });\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"request.js","sources":["../../src/request.ts"],"sourcesContent":["import type { CfProperties, ExecutionContext, IncomingRequestCfProperties } from '@cloudflare/workers-types';\nimport {\n captureException,\n continueTrace,\n getClient,\n getHttpSpanDetailsFromUrlObject,\n httpHeadersToSpanAttributes,\n parseStringToURLObject,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n setHttpStatus,\n startSpanManual,\n winterCGHeadersToDict,\n withIsolationScope,\n} from '@sentry/core';\nimport { captureIncomingRequestBody } from './integrations/httpServer';\nimport type { CloudflareOptions } from './client';\nimport { flushAndDispose } from './flush';\nimport { addCloudResourceContext, addCultureContext, addRequest } from './scope-utils';\nimport { init } from './sdk';\nimport { classifyResponseStreaming } from './utils/streaming';\n\ninterface RequestHandlerWrapperOptions {\n options: CloudflareOptions;\n request: Request<unknown, IncomingRequestCfProperties<unknown> | CfProperties<unknown>>;\n context: ExecutionContext | undefined;\n /**\n * If true, errors will be captured, rethrown and sent to Sentry.\n * Otherwise, errors are rethrown but not captured.\n *\n * You most likely don't want to set this to `false`, if you use `wrapRequestHandler` directly.\n * This is primarily meant as an escape hatch for higher-level SDKs relying on additional error\n * capturing mechanisms where this wrapper captures errors too early or too generally.\n *\n * @default true\n */\n captureErrors?: boolean;\n}\n\n/**\n * Wraps a cloudflare request handler in Sentry instrumentation\n */\nexport function wrapRequestHandler(\n wrapperOptions: RequestHandlerWrapperOptions,\n handler: (...args: unknown[]) => Response | Promise<Response>,\n): Promise<Response> {\n return withIsolationScope(async isolationScope => {\n const { options, request, captureErrors = true } = wrapperOptions;\n const context = wrapperOptions.context;\n\n const waitUntil = context?.waitUntil?.bind?.(context);\n\n const client = init({ ...options, ctx: context });\n isolationScope.setClient(client);\n\n const urlObject = parseStringToURLObject(request.url);\n const [name, attributes] = getHttpSpanDetailsFromUrlObject(urlObject, 'server', 'auto.http.cloudflare', request);\n\n const contentLength = request.headers.get('content-length');\n if (contentLength) {\n attributes['http.request.body.size'] = parseInt(contentLength, 10);\n }\n\n const userAgentHeader = request.headers.get('user-agent');\n if (userAgentHeader) {\n attributes['user_agent.original'] = userAgentHeader;\n }\n\n Object.assign(\n attributes,\n httpHeadersToSpanAttributes(\n winterCGHeadersToDict(request.headers),\n getClient()?.getOptions().sendDefaultPii ?? false,\n ),\n );\n\n attributes[SEMANTIC_ATTRIBUTE_SENTRY_OP] = 'http.server';\n\n addCloudResourceContext(isolationScope);\n addRequest(isolationScope, request);\n if (request.cf) {\n addCultureContext(isolationScope, request.cf);\n\n if (typeof request.cf.httpProtocol === 'string') {\n attributes['network.protocol.name'] = request.cf.httpProtocol;\n }\n }\n\n // Do not capture spans for OPTIONS and HEAD requests\n if (request.method === 'OPTIONS' || request.method === 'HEAD') {\n try {\n return await handler();\n } catch (e) {\n if (captureErrors) {\n captureException(e, { mechanism: { handled: false, type: 'auto.http.cloudflare' } });\n }\n throw e;\n } finally {\n waitUntil?.(flushAndDispose(client));\n }\n }\n\n if (client) {\n await captureIncomingRequestBody(client, request);\n }\n\n return continueTrace(\n { sentryTrace: request.headers.get('sentry-trace') || '', baggage: request.headers.get('baggage') },\n () => {\n // Note: This span will not have a duration unless I/O happens in the handler. This is\n // because of how the cloudflare workers runtime works.\n // See: https://developers.cloudflare.com/workers/runtime-apis/performance/\n\n // Use startSpanManual to control when span ends (needed for streaming responses)\n return startSpanManual({ name, attributes }, async span => {\n let res: Response;\n\n try {\n res = await handler();\n setHttpStatus(span, res.status);\n\n // After the handler runs, the span name might have been updated by nested instrumentation\n // (e.g., Remix parameterizing routes). The span should already have the correct name\n // from that instrumentation, so we don't need to do anything here.\n } catch (e) {\n span.end();\n if (captureErrors) {\n captureException(e, { mechanism: { handled: false, type: 'auto.http.cloudflare' } });\n }\n waitUntil?.(flushAndDispose(client));\n throw e;\n }\n\n // Classify response to detect actual streaming\n const classification = classifyResponseStreaming(res);\n\n if (classification.isStreaming && res.body) {\n try {\n let ended = false;\n\n const endSpanOnce = (): void => {\n if (ended) return;\n\n ended = true;\n span.end();\n waitUntil?.(flushAndDispose(client));\n };\n\n const transform = new TransformStream({\n flush() {\n // Source stream completed normally.\n endSpanOnce();\n },\n cancel() {\n // Client disconnected (or downstream cancelled). The `cancel`\n // is being called while the response is still considered\n // active, so this is a safe place to end the span.\n endSpanOnce();\n },\n });\n\n return new Response(res.body.pipeThrough(transform), {\n status: res.status,\n statusText: res.statusText,\n headers: res.headers,\n });\n } catch {\n span.end();\n waitUntil?.(flushAndDispose(client));\n return res;\n }\n }\n\n // Non-streaming response - end span immediately and return original\n span.end();\n\n // Don't dispose for protocol upgrades (101 Switching Protocols) - the connection stays alive.\n // This includes WebSocket upgrades where webSocketMessage/webSocketClose handlers\n // will still be called and may need the client to capture errors.\n if (res.status === 101) {\n waitUntil?.(client?.flush(2000));\n } else {\n waitUntil?.(flushAndDispose(client));\n }\n return res;\n });\n },\n );\n });\n}\n"],"names":[],"mappings":"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"}
package/build/esm/sdk.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { dedupeIntegration, inboundFiltersIntegration, functionToStringIntegration, conversationIdIntegration, linkedErrorsIntegration, requestDataIntegration, consoleIntegration, getIntegrationsToSetup, spanStreamingIntegration, stackParserFromStackParserOptions, initAndBind } from '@sentry/core';
2
2
  import { CloudflareClient } from './client.js';
3
3
  import { makeFlushLock } from './flush.js';
4
+ import { httpServerIntegration } from './integrations/httpServer.js';
4
5
  import { fetchIntegration } from './integrations/fetch.js';
5
6
  import { honoIntegration } from './integrations/hono.js';
6
7
  import { setupOpenTelemetryTracer } from './opentelemetry/tracer.js';
@@ -22,6 +23,7 @@ function getDefaultIntegrations(options) {
22
23
  linkedErrorsIntegration(),
23
24
  fetchIntegration(),
24
25
  honoIntegration(),
26
+ httpServerIntegration(),
25
27
  // TODO(v11): the `include` object should be defined directly in the integration based on `sendDefaultPii`
26
28
  requestDataIntegration(sendDefaultPii ? undefined : { include: { cookies: false } }),
27
29
  consoleIntegration(),
@@ -1 +1 @@
1
- {"version":3,"file":"sdk.js","sources":["../../src/sdk.ts"],"sourcesContent":["import type { Integration } from '@sentry/core';\nimport {\n consoleIntegration,\n conversationIdIntegration,\n dedupeIntegration,\n functionToStringIntegration,\n getIntegrationsToSetup,\n inboundFiltersIntegration,\n initAndBind,\n linkedErrorsIntegration,\n requestDataIntegration,\n spanStreamingIntegration,\n stackParserFromStackParserOptions,\n} from '@sentry/core';\nimport type { CloudflareClientOptions, CloudflareOptions } from './client';\nimport { CloudflareClient } from './client';\nimport { makeFlushLock } from './flush';\nimport { fetchIntegration } from './integrations/fetch';\nimport { honoIntegration } from './integrations/hono';\nimport { setupOpenTelemetryTracer } from './opentelemetry/tracer';\nimport { makeCloudflareTransport } from './transport';\nimport { defaultStackParser } from './vendor/stacktrace';\n\n/** Get the default integrations for the Cloudflare SDK. */\nexport function getDefaultIntegrations(options: CloudflareOptions): Integration[] {\n const sendDefaultPii = options.sendDefaultPii ?? false;\n return [\n // The Dedupe integration should not be used in workflows because we want to\n // capture all step failures, even if they are the same error.\n ...(options.enableDedupe === false ? [] : [dedupeIntegration()]),\n // TODO(v11): Replace with `eventFiltersIntegration` once we remove the deprecated `inboundFiltersIntegration`\n // eslint-disable-next-line deprecation/deprecation\n inboundFiltersIntegration(),\n functionToStringIntegration(),\n conversationIdIntegration(),\n linkedErrorsIntegration(),\n fetchIntegration(),\n honoIntegration(),\n // TODO(v11): the `include` object should be defined directly in the integration based on `sendDefaultPii`\n requestDataIntegration(sendDefaultPii ? undefined : { include: { cookies: false } }),\n consoleIntegration(),\n ];\n}\n\n/**\n * Initializes the cloudflare SDK.\n */\nexport function init(options: CloudflareOptions): CloudflareClient | undefined {\n if (options.defaultIntegrations === undefined) {\n options.defaultIntegrations = getDefaultIntegrations(options);\n }\n\n const flushLock = options.ctx ? makeFlushLock(options.ctx) : undefined;\n delete options.ctx;\n\n const resolvedIntegrations = getIntegrationsToSetup(options);\n if (options.traceLifecycle === 'stream' && !resolvedIntegrations.some(i => i.name === 'SpanStreaming')) {\n resolvedIntegrations.push(spanStreamingIntegration());\n }\n\n const clientOptions: CloudflareClientOptions = {\n ...options,\n stackParser: stackParserFromStackParserOptions(options.stackParser || defaultStackParser),\n integrations: resolvedIntegrations,\n transport: options.transport || makeCloudflareTransport,\n flushLock,\n };\n\n /**\n * The Cloudflare SDK is not OpenTelemetry native, however, we set up some OpenTelemetry compatibility\n * via a custom trace provider.\n * This ensures that any spans emitted via `@opentelemetry/api` will be captured by Sentry.\n * HOWEVER, big caveat: This does not handle custom context handling, it will always work off the current scope.\n * This should be good enough for many, but not all integrations.\n */\n if (!options.skipOpenTelemetrySetup) {\n setupOpenTelemetryTracer();\n }\n\n return initAndBind(CloudflareClient, clientOptions) as CloudflareClient;\n}\n"],"names":[],"mappings":";;;;;;;;;AAuBA;AACO,SAAS,sBAAsB,CAAC,OAAO,EAAoC;AAClF,EAAE,MAAM,cAAA,GAAiB,OAAO,CAAC,cAAA,IAAkB,KAAK;AACxD,EAAE,OAAO;AACT;AACA;AACA,IAAI,IAAI,OAAO,CAAC,YAAA,KAAiB,KAAA,GAAQ,EAAC,GAAI,CAAC,iBAAiB,EAAE,CAAC,CAAC;AACpE;AACA;AACA,IAAI,yBAAyB,EAAE;AAC/B,IAAI,2BAA2B,EAAE;AACjC,IAAI,yBAAyB,EAAE;AAC/B,IAAI,uBAAuB,EAAE;AAC7B,IAAI,gBAAgB,EAAE;AACtB,IAAI,eAAe,EAAE;AACrB;AACA,IAAI,sBAAsB,CAAC,cAAA,GAAiB,YAAY,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,KAAA,EAAM,EAAG,CAAC;AACxF,IAAI,kBAAkB,EAAE;AACxB,GAAG;AACH;;AAEA;AACA;AACA;AACO,SAAS,IAAI,CAAC,OAAO,EAAmD;AAC/E,EAAE,IAAI,OAAO,CAAC,mBAAA,KAAwB,SAAS,EAAE;AACjD,IAAI,OAAO,CAAC,mBAAA,GAAsB,sBAAsB,CAAC,OAAO,CAAC;AACjE,EAAE;;AAEF,EAAE,MAAM,SAAA,GAAY,OAAO,CAAC,GAAA,GAAM,aAAa,CAAC,OAAO,CAAC,GAAG,CAAA,GAAI,SAAS;AACxE,EAAE,OAAO,OAAO,CAAC,GAAG;;AAEpB,EAAE,MAAM,oBAAA,GAAuB,sBAAsB,CAAC,OAAO,CAAC;AAC9D,EAAE,IAAI,OAAO,CAAC,mBAAmB,QAAA,IAAY,CAAC,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAA,KAAS,eAAe,CAAC,EAAE;AAC1G,IAAI,oBAAoB,CAAC,IAAI,CAAC,wBAAwB,EAAE,CAAC;AACzD,EAAE;;AAEF,EAAE,MAAM,aAAa,GAA4B;AACjD,IAAI,GAAG,OAAO;AACd,IAAI,WAAW,EAAE,iCAAiC,CAAC,OAAO,CAAC,WAAA,IAAe,kBAAkB,CAAC;AAC7F,IAAI,YAAY,EAAE,oBAAoB;AACtC,IAAI,SAAS,EAAE,OAAO,CAAC,SAAA,IAAa,uBAAuB;AAC3D,IAAI,SAAS;AACb,GAAG;;AAEH;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAE,IAAI,CAAC,OAAO,CAAC,sBAAsB,EAAE;AACvC,IAAI,wBAAwB,EAAE;AAC9B,EAAE;;AAEF,EAAE,OAAO,WAAW,CAAC,gBAAgB,EAAE,aAAa,CAAA;AACpD;;;;"}
1
+ {"version":3,"file":"sdk.js","sources":["../../src/sdk.ts"],"sourcesContent":["import type { Integration } from '@sentry/core';\nimport {\n consoleIntegration,\n conversationIdIntegration,\n dedupeIntegration,\n functionToStringIntegration,\n getIntegrationsToSetup,\n inboundFiltersIntegration,\n initAndBind,\n linkedErrorsIntegration,\n requestDataIntegration,\n spanStreamingIntegration,\n stackParserFromStackParserOptions,\n} from '@sentry/core';\nimport type { CloudflareClientOptions, CloudflareOptions } from './client';\nimport { CloudflareClient } from './client';\nimport { makeFlushLock } from './flush';\nimport { httpServerIntegration } from './integrations/httpServer';\nimport { fetchIntegration } from './integrations/fetch';\nimport { honoIntegration } from './integrations/hono';\nimport { setupOpenTelemetryTracer } from './opentelemetry/tracer';\nimport { makeCloudflareTransport } from './transport';\nimport { defaultStackParser } from './vendor/stacktrace';\n\n/** Get the default integrations for the Cloudflare SDK. */\nexport function getDefaultIntegrations(options: CloudflareOptions): Integration[] {\n const sendDefaultPii = options.sendDefaultPii ?? false;\n return [\n // The Dedupe integration should not be used in workflows because we want to\n // capture all step failures, even if they are the same error.\n ...(options.enableDedupe === false ? [] : [dedupeIntegration()]),\n // TODO(v11): Replace with `eventFiltersIntegration` once we remove the deprecated `inboundFiltersIntegration`\n // eslint-disable-next-line deprecation/deprecation\n inboundFiltersIntegration(),\n functionToStringIntegration(),\n conversationIdIntegration(),\n linkedErrorsIntegration(),\n fetchIntegration(),\n honoIntegration(),\n httpServerIntegration(),\n // TODO(v11): the `include` object should be defined directly in the integration based on `sendDefaultPii`\n requestDataIntegration(sendDefaultPii ? undefined : { include: { cookies: false } }),\n consoleIntegration(),\n ];\n}\n\n/**\n * Initializes the cloudflare SDK.\n */\nexport function init(options: CloudflareOptions): CloudflareClient | undefined {\n if (options.defaultIntegrations === undefined) {\n options.defaultIntegrations = getDefaultIntegrations(options);\n }\n\n const flushLock = options.ctx ? makeFlushLock(options.ctx) : undefined;\n delete options.ctx;\n\n const resolvedIntegrations = getIntegrationsToSetup(options);\n if (options.traceLifecycle === 'stream' && !resolvedIntegrations.some(i => i.name === 'SpanStreaming')) {\n resolvedIntegrations.push(spanStreamingIntegration());\n }\n\n const clientOptions: CloudflareClientOptions = {\n ...options,\n stackParser: stackParserFromStackParserOptions(options.stackParser || defaultStackParser),\n integrations: resolvedIntegrations,\n transport: options.transport || makeCloudflareTransport,\n flushLock,\n };\n\n /**\n * The Cloudflare SDK is not OpenTelemetry native, however, we set up some OpenTelemetry compatibility\n * via a custom trace provider.\n * This ensures that any spans emitted via `@opentelemetry/api` will be captured by Sentry.\n * HOWEVER, big caveat: This does not handle custom context handling, it will always work off the current scope.\n * This should be good enough for many, but not all integrations.\n */\n if (!options.skipOpenTelemetrySetup) {\n setupOpenTelemetryTracer();\n }\n\n return initAndBind(CloudflareClient, clientOptions) as CloudflareClient;\n}\n"],"names":[],"mappings":";;;;;;;;;;AAwBA;AACO,SAAS,sBAAsB,CAAC,OAAO,EAAoC;AAClF,EAAE,MAAM,cAAA,GAAiB,OAAO,CAAC,cAAA,IAAkB,KAAK;AACxD,EAAE,OAAO;AACT;AACA;AACA,IAAI,IAAI,OAAO,CAAC,YAAA,KAAiB,KAAA,GAAQ,EAAC,GAAI,CAAC,iBAAiB,EAAE,CAAC,CAAC;AACpE;AACA;AACA,IAAI,yBAAyB,EAAE;AAC/B,IAAI,2BAA2B,EAAE;AACjC,IAAI,yBAAyB,EAAE;AAC/B,IAAI,uBAAuB,EAAE;AAC7B,IAAI,gBAAgB,EAAE;AACtB,IAAI,eAAe,EAAE;AACrB,IAAI,qBAAqB,EAAE;AAC3B;AACA,IAAI,sBAAsB,CAAC,cAAA,GAAiB,YAAY,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,KAAA,EAAM,EAAG,CAAC;AACxF,IAAI,kBAAkB,EAAE;AACxB,GAAG;AACH;;AAEA;AACA;AACA;AACO,SAAS,IAAI,CAAC,OAAO,EAAmD;AAC/E,EAAE,IAAI,OAAO,CAAC,mBAAA,KAAwB,SAAS,EAAE;AACjD,IAAI,OAAO,CAAC,mBAAA,GAAsB,sBAAsB,CAAC,OAAO,CAAC;AACjE,EAAE;;AAEF,EAAE,MAAM,SAAA,GAAY,OAAO,CAAC,GAAA,GAAM,aAAa,CAAC,OAAO,CAAC,GAAG,CAAA,GAAI,SAAS;AACxE,EAAE,OAAO,OAAO,CAAC,GAAG;;AAEpB,EAAE,MAAM,oBAAA,GAAuB,sBAAsB,CAAC,OAAO,CAAC;AAC9D,EAAE,IAAI,OAAO,CAAC,mBAAmB,QAAA,IAAY,CAAC,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAA,KAAS,eAAe,CAAC,EAAE;AAC1G,IAAI,oBAAoB,CAAC,IAAI,CAAC,wBAAwB,EAAE,CAAC;AACzD,EAAE;;AAEF,EAAE,MAAM,aAAa,GAA4B;AACjD,IAAI,GAAG,OAAO;AACd,IAAI,WAAW,EAAE,iCAAiC,CAAC,OAAO,CAAC,WAAA,IAAe,kBAAkB,CAAC;AAC7F,IAAI,YAAY,EAAE,oBAAoB;AACtC,IAAI,SAAS,EAAE,OAAO,CAAC,SAAA,IAAa,uBAAuB;AAC3D,IAAI,SAAS;AACb,GAAG;;AAEH;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAE,IAAI,CAAC,OAAO,CAAC,sBAAsB,EAAE;AACvC,IAAI,wBAAwB,EAAE;AAC9B,EAAE;;AAEF,EAAE,OAAO,WAAW,CAAC,gBAAgB,EAAE,aAAa,CAAA;AACpD;;;;"}
@@ -25,5 +25,13 @@ function isDurableObjectNamespace(item) {
25
25
  return item != null && isNotJSRPC(item) && typeof item.idFromName === 'function';
26
26
  }
27
27
 
28
- export { isDurableObjectNamespace, isJSRPC };
28
+ /**
29
+ * Duck-type check for Queue producer bindings.
30
+ * Queue has `send` and `sendBatch` async methods.
31
+ */
32
+ function isQueue(item) {
33
+ return item != null && isNotJSRPC(item) && typeof item.send === 'function' && typeof item.sendBatch === 'function';
34
+ }
35
+
36
+ export { isDurableObjectNamespace, isJSRPC, isQueue };
29
37
  //# sourceMappingURL=isBinding.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"isBinding.js","sources":["../../../src/utils/isBinding.ts"],"sourcesContent":["/**\n * Adapted from https://github.com/evanderkoogh/otel-cf-workers/blob/effeb549f0a4ed1c55ea0c4f0d8e8e37e5494fb3/src/instrumentation/env.ts\n *\n * BSD 3-Clause License\n *\n * Copyright (c) 2023, Erwin van der Koogh\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\nimport type { DurableObjectNamespace } from '@cloudflare/workers-types';\n\n/**\n * Checks if a value is a JSRPC proxy (service binding).\n *\n * JSRPC proxies return a truthy value for ANY property access, including\n * properties that don't exist. This makes other duck-type checks unreliable\n * unless we exclude JSRPC first.\n *\n * Must be checked before other binding type checks.\n */\nexport function isJSRPC(item: unknown): item is Service {\n try {\n return !!(item as Record<string, unknown>)[`__some_property_that_will_never_exist__${Math.random()}`];\n } catch {\n return false;\n }\n}\n\nconst isNotJSRPC = (item: unknown): item is Record<string, unknown> => !isJSRPC(item);\n\n/**\n * Duck-type check for DurableObjectNamespace bindings.\n * DurableObjectNamespace has `idFromName`, `idFromString`, `get`, `newUniqueId`.\n */\nexport function isDurableObjectNamespace(item: unknown): item is DurableObjectNamespace {\n return item != null && isNotJSRPC(item) && typeof item.idFromName === 'function';\n}\n"],"names":[],"mappings":"AAmCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,OAAO,CAAC,IAAI,EAA4B;AACxD,EAAE,IAAI;AACN,IAAI,OAAO,CAAC,CAAC,CAAC,IAAA,GAAiC,CAAC,uCAAuC,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAA,CAAA;AACA,EAAA,CAAA,CAAA,MAAA;AACA,IAAA,OAAA,KAAA;AACA,EAAA;AACA;;AAEA,MAAA,UAAA,GAAA,CAAA,IAAA,KAAA,CAAA,OAAA,CAAA,IAAA,CAAA;;AAEA;AACA;AACA;AACA;AACA,SAAA,wBAAA,CAAA,IAAA,EAAA;AACA,EAAA,OAAA,IAAA,IAAA,IAAA,IAAA,UAAA,CAAA,IAAA,CAAA,IAAA,OAAA,IAAA,CAAA,UAAA,KAAA,UAAA;AACA;;;;"}
1
+ {"version":3,"file":"isBinding.js","sources":["../../../src/utils/isBinding.ts"],"sourcesContent":["/**\n * Adapted from https://github.com/evanderkoogh/otel-cf-workers/blob/effeb549f0a4ed1c55ea0c4f0d8e8e37e5494fb3/src/instrumentation/env.ts\n *\n * BSD 3-Clause License\n *\n * Copyright (c) 2023, Erwin van der Koogh\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\nimport type { DurableObjectNamespace, Queue } from '@cloudflare/workers-types';\n\n/**\n * Checks if a value is a JSRPC proxy (service binding).\n *\n * JSRPC proxies return a truthy value for ANY property access, including\n * properties that don't exist. This makes other duck-type checks unreliable\n * unless we exclude JSRPC first.\n *\n * Must be checked before other binding type checks.\n */\nexport function isJSRPC(item: unknown): item is Service {\n try {\n return !!(item as Record<string, unknown>)[`__some_property_that_will_never_exist__${Math.random()}`];\n } catch {\n return false;\n }\n}\n\nconst isNotJSRPC = (item: unknown): item is Record<string, unknown> => !isJSRPC(item);\n\n/**\n * Duck-type check for DurableObjectNamespace bindings.\n * DurableObjectNamespace has `idFromName`, `idFromString`, `get`, `newUniqueId`.\n */\nexport function isDurableObjectNamespace(item: unknown): item is DurableObjectNamespace {\n return item != null && isNotJSRPC(item) && typeof item.idFromName === 'function';\n}\n\n/**\n * Duck-type check for Queue producer bindings.\n * Queue has `send` and `sendBatch` async methods.\n */\nexport function isQueue(item: unknown): item is Queue {\n return item != null && isNotJSRPC(item) && typeof item.send === 'function' && typeof item.sendBatch === 'function';\n}\n"],"names":[],"mappings":"AAmCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,OAAO,CAAC,IAAI,EAA4B;AACxD,EAAE,IAAI;AACN,IAAI,OAAO,CAAC,CAAC,CAAC,IAAA,GAAiC,CAAC,uCAAuC,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAA,CAAA;AACA,EAAA,CAAA,CAAA,MAAA;AACA,IAAA,OAAA,KAAA;AACA,EAAA;AACA;;AAEA,MAAA,UAAA,GAAA,CAAA,IAAA,KAAA,CAAA,OAAA,CAAA,IAAA,CAAA;;AAEA;AACA;AACA;AACA;AACA,SAAA,wBAAA,CAAA,IAAA,EAAA;AACA,EAAA,OAAA,IAAA,IAAA,IAAA,IAAA,UAAA,CAAA,IAAA,CAAA,IAAA,OAAA,IAAA,CAAA,UAAA,KAAA,UAAA;AACA;;AAEA;AACA;AACA;AACA;AACA,SAAA,OAAA,CAAA,IAAA,EAAA;AACA,EAAA,OAAA,IAAA,IAAA,IAAA,IAAA,UAAA,CAAA,IAAA,CAAA,IAAA,OAAA,IAAA,CAAA,IAAA,KAAA,UAAA,IAAA,OAAA,IAAA,CAAA,SAAA,KAAA,UAAA;AACA;;;;"}
@@ -0,0 +1,57 @@
1
+ import { getTraceData } from '@sentry/core';
2
+
3
+ /**
4
+ * Key used to identify Sentry RPC metadata in a trailing argument.
5
+ * This enables transparent trace propagation across Cloudflare Workers RPC
6
+ * calls (Cap'n Proto), which have no native header/metadata support.
7
+ */
8
+ const SENTRY_RPC_META_KEY = '__sentry_rpc_meta__';
9
+
10
+ function isSentryRpcMeta(value) {
11
+ if (typeof value !== 'object' || value === null || !(SENTRY_RPC_META_KEY in value)) {
12
+ return false;
13
+ }
14
+ const sentry = (value ).__sentry_rpc_meta__;
15
+ return typeof sentry === 'object' && sentry !== null;
16
+ }
17
+
18
+ /**
19
+ * Appends Sentry RPC metadata to an args array for trace propagation.
20
+ * If no active trace exists, returns the original args unchanged.
21
+ */
22
+ function appendRpcMeta(args) {
23
+ const traceData = getTraceData();
24
+
25
+ if (!traceData['sentry-trace']) {
26
+ return args;
27
+ }
28
+
29
+ return [...args, { [SENTRY_RPC_META_KEY]: traceData }];
30
+ }
31
+
32
+ /**
33
+ * Extracts Sentry RPC metadata from the trailing argument of an args array.
34
+ * Returns cleaned args (without meta) and the extracted trace data if found.
35
+ */
36
+ function extractRpcMeta(
37
+ args,
38
+ )
39
+
40
+ {
41
+ if (args.length === 0) {
42
+ return { args };
43
+ }
44
+
45
+ const last = args[args.length - 1];
46
+ if (isSentryRpcMeta(last)) {
47
+ return {
48
+ args: args.slice(0, -1) ,
49
+ rpcMeta: last.__sentry_rpc_meta__,
50
+ };
51
+ }
52
+
53
+ return { args };
54
+ }
55
+
56
+ export { appendRpcMeta, extractRpcMeta };
57
+ //# sourceMappingURL=rpcMeta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rpcMeta.js","sources":["../../../src/utils/rpcMeta.ts"],"sourcesContent":["import { getTraceData, type SerializedTraceData } from '@sentry/core';\n\n/**\n * Key used to identify Sentry RPC metadata in a trailing argument.\n * This enables transparent trace propagation across Cloudflare Workers RPC\n * calls (Cap'n Proto), which have no native header/metadata support.\n */\nconst SENTRY_RPC_META_KEY = '__sentry_rpc_meta__';\n\ninterface SentryRpcMeta {\n __sentry_rpc_meta__: SerializedTraceData;\n}\n\nfunction isSentryRpcMeta(value: unknown): value is SentryRpcMeta {\n if (typeof value !== 'object' || value === null || !(SENTRY_RPC_META_KEY in value)) {\n return false;\n }\n const sentry = (value as SentryRpcMeta).__sentry_rpc_meta__;\n return typeof sentry === 'object' && sentry !== null;\n}\n\n/**\n * Appends Sentry RPC metadata to an args array for trace propagation.\n * If no active trace exists, returns the original args unchanged.\n */\nexport function appendRpcMeta(args: unknown[]): unknown[] {\n const traceData = getTraceData();\n\n if (!traceData['sentry-trace']) {\n return args;\n }\n\n return [...args, { [SENTRY_RPC_META_KEY]: traceData }];\n}\n\n/**\n * Extracts Sentry RPC metadata from the trailing argument of an args array.\n * Returns cleaned args (without meta) and the extracted trace data if found.\n */\nexport function extractRpcMeta<T extends unknown[]>(\n args: T,\n): {\n args: T;\n rpcMeta?: SerializedTraceData;\n} {\n if (args.length === 0) {\n return { args };\n }\n\n const last = args[args.length - 1];\n if (isSentryRpcMeta(last)) {\n return {\n args: args.slice(0, -1) as T,\n rpcMeta: last.__sentry_rpc_meta__,\n };\n }\n\n return { args };\n}\n"],"names":[],"mappings":";;AAEA;AACA;AACA;AACA;AACA;AACA,MAAM,mBAAA,GAAsB,qBAAqB;;AAMjD,SAAS,eAAe,CAAC,KAAK,EAAmC;AACjE,EAAE,IAAI,OAAO,KAAA,KAAU,YAAY,KAAA,KAAU,IAAA,IAAQ,EAAE,uBAAuB,KAAK,CAAC,EAAE;AACtF,IAAI,OAAO,KAAK;AAChB,EAAE;AACF,EAAE,MAAM,MAAA,GAAS,CAAC,KAAA,GAAwB,mBAAmB;AAC7D,EAAE,OAAO,OAAO,MAAA,KAAW,YAAY,MAAA,KAAW,IAAI;AACtD;;AAEA;AACA;AACA;AACA;AACO,SAAS,aAAa,CAAC,IAAI,EAAwB;AAC1D,EAAE,MAAM,SAAA,GAAY,YAAY,EAAE;;AAElC,EAAE,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,EAAE;AAClC,IAAI,OAAO,IAAI;AACf,EAAE;;AAEF,EAAE,OAAO,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,mBAAmB,GAAG,SAAA,EAAW,CAAC;AACxD;;AAEA;AACA;AACA;AACA;AACO,SAAS,cAAc;AAC9B,EAAE,IAAI;AACN;;AAGA,CAAE;AACF,EAAE,IAAI,IAAI,CAAC,MAAA,KAAW,CAAC,EAAE;AACzB,IAAI,OAAO,EAAE,IAAA,EAAM;AACnB,EAAE;;AAEF,EAAE,MAAM,IAAA,GAAO,IAAI,CAAC,IAAI,CAAC,MAAA,GAAS,CAAC,CAAC;AACpC,EAAE,IAAI,eAAe,CAAC,IAAI,CAAC,EAAE;AAC7B,IAAI,OAAO;AACX,MAAM,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAA;AAC5B,MAAM,OAAO,EAAE,IAAI,CAAC,mBAAmB;AACvC,KAAK;AACL,EAAE;;AAEF,EAAE,OAAO,EAAE,IAAA,EAAM;AACjB;;;;"}
@@ -6,7 +6,7 @@ import { DEBUG_BUILD } from '../debug-build.js';
6
6
  *
7
7
  * Priority:
8
8
  * 1. If `enableRpcTracePropagation` is set, use it (ignore `instrumentPrototypeMethods`)
9
- * 2. If only `instrumentPrototypeMethods` is set, use it with deprecation warning (converted to boolean)
9
+ * 2. If only `instrumentPrototypeMethods` is set, use it with deprecation warning
10
10
  * 3. If neither is set, return `false`
11
11
  *
12
12
  * @returns The effective setting for RPC trace propagation
@@ -43,39 +43,5 @@ function getEffectiveRpcPropagation(options) {
43
43
  return false;
44
44
  }
45
45
 
46
- /**
47
- * Gets the method filter for prototype method instrumentation.
48
- *
49
- * Returns:
50
- * - `null` if no instrumentation should occur
51
- * - `true` if all methods should be instrumented
52
- * - `string[]` if only specific methods should be instrumented (deprecated behavior)
53
- *
54
- * @returns The method filter or null if no instrumentation
55
- */
56
- function getPrototypeMethodFilter(options) {
57
- const { enableRpcTracePropagation, instrumentPrototypeMethods } = options;
58
-
59
- // If the new option is explicitly set, use it (boolean only, no filtering)
60
- if (enableRpcTracePropagation !== undefined) {
61
- return !!enableRpcTracePropagation;
62
- }
63
-
64
- // Fall back to deprecated option - preserve array filtering behavior
65
- if (instrumentPrototypeMethods !== undefined) {
66
- if (instrumentPrototypeMethods === true) {
67
- return true;
68
- }
69
-
70
- if (Array.isArray(instrumentPrototypeMethods) && instrumentPrototypeMethods.length > 0) {
71
- return instrumentPrototypeMethods;
72
- }
73
-
74
- return false;
75
- }
76
-
77
- return false;
78
- }
79
-
80
- export { getEffectiveRpcPropagation, getPrototypeMethodFilter };
46
+ export { getEffectiveRpcPropagation };
81
47
  //# sourceMappingURL=rpcOptions.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"rpcOptions.js","sources":["../../../src/utils/rpcOptions.ts"],"sourcesContent":["import { debug } from '@sentry/core';\nimport type { CloudflareOptions } from '../client';\nimport { DEBUG_BUILD } from '../debug-build';\n\n/**\n * Gets the effective RPC propagation setting, handling deprecation of `instrumentPrototypeMethods`.\n *\n * Priority:\n * 1. If `enableRpcTracePropagation` is set, use it (ignore `instrumentPrototypeMethods`)\n * 2. If only `instrumentPrototypeMethods` is set, use it with deprecation warning (converted to boolean)\n * 3. If neither is set, return `false`\n *\n * @returns The effective setting for RPC trace propagation\n */\nexport function getEffectiveRpcPropagation(options: CloudflareOptions): boolean {\n const { enableRpcTracePropagation, instrumentPrototypeMethods } = options;\n\n // If the new option is explicitly set, use it\n if (enableRpcTracePropagation !== undefined) {\n if (instrumentPrototypeMethods !== undefined) {\n DEBUG_BUILD &&\n debug.warn(\n '[Sentry] Both `enableRpcTracePropagation` and `instrumentPrototypeMethods` are set. ' +\n 'Using `enableRpcTracePropagation` and ignoring `instrumentPrototypeMethods`.',\n );\n }\n return enableRpcTracePropagation;\n }\n\n // Fall back to deprecated option with warning\n if (instrumentPrototypeMethods !== undefined) {\n DEBUG_BUILD &&\n debug.warn(\n '[Sentry] `instrumentPrototypeMethods` is deprecated and will be removed in a future major version. ' +\n 'Please use `enableRpcTracePropagation` instead.',\n );\n // instrumentPrototypeMethods can be boolean or string[], convert to boolean\n return (\n instrumentPrototypeMethods === true ||\n (Array.isArray(instrumentPrototypeMethods) && instrumentPrototypeMethods.length > 0)\n );\n }\n\n return false;\n}\n\n/**\n * Gets the method filter for prototype method instrumentation.\n *\n * Returns:\n * - `null` if no instrumentation should occur\n * - `true` if all methods should be instrumented\n * - `string[]` if only specific methods should be instrumented (deprecated behavior)\n *\n * @returns The method filter or null if no instrumentation\n */\nexport function getPrototypeMethodFilter(options: CloudflareOptions): boolean | string[] {\n const { enableRpcTracePropagation, instrumentPrototypeMethods } = options;\n\n // If the new option is explicitly set, use it (boolean only, no filtering)\n if (enableRpcTracePropagation !== undefined) {\n return !!enableRpcTracePropagation;\n }\n\n // Fall back to deprecated option - preserve array filtering behavior\n if (instrumentPrototypeMethods !== undefined) {\n if (instrumentPrototypeMethods === true) {\n return true;\n }\n\n if (Array.isArray(instrumentPrototypeMethods) && instrumentPrototypeMethods.length > 0) {\n return instrumentPrototypeMethods;\n }\n\n return false;\n }\n\n return false;\n}\n"],"names":[],"mappings":";;;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,0BAA0B,CAAC,OAAO,EAA8B;AAChF,EAAE,MAAM,EAAE,yBAAyB,EAAE,0BAAA,EAA2B,GAAI,OAAO;;AAE3E;AACA,EAAE,IAAI,yBAAA,KAA8B,SAAS,EAAE;AAC/C,IAAI,IAAI,0BAAA,KAA+B,SAAS,EAAE;AAClD,MAAM,WAAA;AACN,QAAQ,KAAK,CAAC,IAAI;AAClB,UAAU,sFAAA;AACV,YAAY,8EAA8E;AAC1F,SAAS;AACT,IAAI;AACJ,IAAI,OAAO,yBAAyB;AACpC,EAAE;;AAEF;AACA,EAAE,IAAI,0BAAA,KAA+B,SAAS,EAAE;AAChD,IAAI,WAAA;AACJ,MAAM,KAAK,CAAC,IAAI;AAChB,QAAQ,qGAAA;AACR,UAAU,iDAAiD;AAC3D,OAAO;AACP;AACA,IAAI;AACJ,MAAM,0BAAA,KAA+B,IAAA;AACrC,OAAO,KAAK,CAAC,OAAO,CAAC,0BAA0B,CAAA,IAAK,0BAA0B,CAAC,MAAA,GAAS,CAAC;AACzF;AACA,EAAE;;AAEF,EAAE,OAAO,KAAK;AACd;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,wBAAwB,CAAC,OAAO,EAAyC;AACzF,EAAE,MAAM,EAAE,yBAAyB,EAAE,0BAAA,EAA2B,GAAI,OAAO;;AAE3E;AACA,EAAE,IAAI,yBAAA,KAA8B,SAAS,EAAE;AAC/C,IAAI,OAAO,CAAC,CAAC,yBAAyB;AACtC,EAAE;;AAEF;AACA,EAAE,IAAI,0BAAA,KAA+B,SAAS,EAAE;AAChD,IAAI,IAAI,0BAAA,KAA+B,IAAI,EAAE;AAC7C,MAAM,OAAO,IAAI;AACjB,IAAI;;AAEJ,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,0BAA0B,CAAA,IAAK,0BAA0B,CAAC,MAAA,GAAS,CAAC,EAAE;AAC5F,MAAM,OAAO,0BAA0B;AACvC,IAAI;;AAEJ,IAAI,OAAO,KAAK;AAChB,EAAE;;AAEF,EAAE,OAAO,KAAK;AACd;;;;"}
1
+ {"version":3,"file":"rpcOptions.js","sources":["../../../src/utils/rpcOptions.ts"],"sourcesContent":["import { debug } from '@sentry/core';\nimport type { CloudflareOptions } from '../client';\nimport { DEBUG_BUILD } from '../debug-build';\n\n/**\n * Gets the effective RPC propagation setting, handling deprecation of `instrumentPrototypeMethods`.\n *\n * Priority:\n * 1. If `enableRpcTracePropagation` is set, use it (ignore `instrumentPrototypeMethods`)\n * 2. If only `instrumentPrototypeMethods` is set, use it with deprecation warning\n * 3. If neither is set, return `false`\n *\n * @returns The effective setting for RPC trace propagation\n */\nexport function getEffectiveRpcPropagation(options: CloudflareOptions): boolean {\n const { enableRpcTracePropagation, instrumentPrototypeMethods } = options;\n\n // If the new option is explicitly set, use it\n if (enableRpcTracePropagation !== undefined) {\n if (instrumentPrototypeMethods !== undefined) {\n DEBUG_BUILD &&\n debug.warn(\n '[Sentry] Both `enableRpcTracePropagation` and `instrumentPrototypeMethods` are set. ' +\n 'Using `enableRpcTracePropagation` and ignoring `instrumentPrototypeMethods`.',\n );\n }\n return enableRpcTracePropagation;\n }\n\n // Fall back to deprecated option with warning\n if (instrumentPrototypeMethods !== undefined) {\n DEBUG_BUILD &&\n debug.warn(\n '[Sentry] `instrumentPrototypeMethods` is deprecated and will be removed in a future major version. ' +\n 'Please use `enableRpcTracePropagation` instead.',\n );\n // instrumentPrototypeMethods can be boolean or string[], convert to boolean\n return (\n instrumentPrototypeMethods === true ||\n (Array.isArray(instrumentPrototypeMethods) && instrumentPrototypeMethods.length > 0)\n );\n }\n\n return false;\n}\n"],"names":[],"mappings":";;;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,0BAA0B,CAAC,OAAO,EAA8B;AAChF,EAAE,MAAM,EAAE,yBAAyB,EAAE,0BAAA,EAA2B,GAAI,OAAO;;AAE3E;AACA,EAAE,IAAI,yBAAA,KAA8B,SAAS,EAAE;AAC/C,IAAI,IAAI,0BAAA,KAA+B,SAAS,EAAE;AAClD,MAAM,WAAA;AACN,QAAQ,KAAK,CAAC,IAAI;AAClB,UAAU,sFAAA;AACV,YAAY,8EAA8E;AAC1F,SAAS;AACT,IAAI;AACJ,IAAI,OAAO,yBAAyB;AACpC,EAAE;;AAEF;AACA,EAAE,IAAI,0BAAA,KAA+B,SAAS,EAAE;AAChD,IAAI,WAAA;AACJ,MAAM,KAAK,CAAC,IAAI;AAChB,QAAQ,qGAAA;AACR,UAAU,iDAAiD;AAC3D,OAAO;AACP;AACA,IAAI;AACJ,MAAM,0BAAA,KAA+B,IAAA;AACrC,OAAO,KAAK,CAAC,OAAO,CAAC,0BAA0B,CAAA,IAAK,0BAA0B,CAAC,MAAA,GAAS,CAAC;AACzF;AACA,EAAE;;AAEF,EAAE,OAAO,KAAK;AACd;;;;"}
@@ -1,7 +1,8 @@
1
- import { getClient, withIsolationScope, withScope, isThenable, captureException, SEMANTIC_ATTRIBUTE_SENTRY_OP, startNewTrace, startSpan, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core';
1
+ import { getClient, withIsolationScope, withScope, isThenable, captureException, SEMANTIC_ATTRIBUTE_SENTRY_OP, continueTrace, startNewTrace, startSpan, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core';
2
2
  import { flushAndDispose } from './flush.js';
3
3
  import { ensureInstrumented } from './instrument.js';
4
4
  import { init } from './sdk.js';
5
+ import { extractRpcMeta } from './utils/rpcMeta.js';
5
6
  import { storeSpanContext, getStoredSpanContext, buildSpanLinks } from './utils/traceLinks.js';
6
7
 
7
8
  /** Extended DurableObjectState with originalStorage exposed by instrumentContext */
@@ -26,9 +27,21 @@ function wrapMethodWithSentry(
26
27
  handler,
27
28
  original =>
28
29
  new Proxy(original, {
29
- apply(target, thisArg, args) {
30
+ apply(target, thisArg, rawArgs) {
30
31
  const { startNewTrace: startNewTrace$1 } = wrapperOptions;
31
32
 
33
+ // For RPC methods, extract Sentry trace context from the trailing argument.
34
+ // The caller side (instrumentDurableObjectStub / JSRPC proxy) appends it;
35
+ // we strip it here so the user's method never sees it.
36
+ let args = rawArgs;
37
+ let rpcMeta;
38
+
39
+ if (wrapperOptions.spanOp === 'rpc') {
40
+ const extracted = extractRpcMeta(rawArgs);
41
+ args = extracted.args;
42
+ rpcMeta = extracted.rpcMeta;
43
+ }
44
+
32
45
  // For startNewTrace, always use withIsolationScope to ensure a fresh scope
33
46
  // Otherwise, use existing client's scope or isolation scope
34
47
  const currentClient = getClient();
@@ -175,6 +188,13 @@ function wrapMethodWithSentry(
175
188
  });
176
189
  };
177
190
 
191
+ if (rpcMeta) {
192
+ return continueTrace(
193
+ { sentryTrace: rpcMeta['sentry-trace'] || '', baggage: rpcMeta.baggage || '' },
194
+ executeSpan,
195
+ );
196
+ }
197
+
178
198
  if (startNewTrace$1) {
179
199
  return startNewTrace(() => executeSpan());
180
200
  }
@@ -1 +1 @@
1
- {"version":3,"file":"wrapMethodWithSentry.js","sources":["../../src/wrapMethodWithSentry.ts"],"sourcesContent":["import type { DurableObjectStorage } from '@cloudflare/workers-types';\nimport {\n captureException,\n getClient,\n isThenable,\n type Scope,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n startNewTrace as startNewTraceCore,\n startSpan,\n withIsolationScope,\n withScope,\n} from '@sentry/core';\nimport type { CloudflareOptions } from './client';\nimport { flushAndDispose } from './flush';\nimport { ensureInstrumented } from './instrument';\nimport { init } from './sdk';\nimport { buildSpanLinks, getStoredSpanContext, storeSpanContext } from './utils/traceLinks';\n\n/** Extended DurableObjectState with originalStorage exposed by instrumentContext */\ninterface InstrumentedDurableObjectState extends DurableObjectState {\n originalStorage?: DurableObjectStorage;\n}\n\ntype MethodWrapperOptions = {\n spanName?: string;\n spanOp?: string;\n options: CloudflareOptions;\n context: ExecutionContext | InstrumentedDurableObjectState;\n /**\n * If true, starts a fresh trace instead of inheriting from a parent trace.\n * Useful for scheduled/independent invocations like alarms.\n *\n * If true, it also stores the current span context and links to the previous invocation's span.\n * Uses Durable Object storage to persist the link. The link is set asynchronously via `span.addLinks()`\n * in a `waitUntil` to avoid blocking.\n *\n * @default false\n */\n startNewTrace?: boolean;\n};\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type UncheckedMethod = (...args: any[]) => any;\ntype OriginalMethod = UncheckedMethod;\n\n/**\n * Wraps a method with Sentry error tracking and optional tracing.\n * Supports starting new traces and linking to previous invocations via Durable Object storage.\n *\n * @param wrapperOptions - The options for the wrapper.\n * @param handler - The method to wrap.\n * @param callback - The callback to call.\n * @param noMark - Whether to mark the method as instrumented.\n * @returns The wrapped method.\n */\nexport function wrapMethodWithSentry<T extends OriginalMethod>(\n wrapperOptions: MethodWrapperOptions,\n handler: T,\n callback?: (...args: Parameters<T>) => void,\n noMark?: true,\n): T {\n return ensureInstrumented(\n handler,\n original =>\n new Proxy(original, {\n apply(target, thisArg, args: Parameters<T>) {\n const { startNewTrace } = wrapperOptions;\n\n // For startNewTrace, always use withIsolationScope to ensure a fresh scope\n // Otherwise, use existing client's scope or isolation scope\n const currentClient = getClient();\n const sentryWithScope = startNewTrace ? withIsolationScope : currentClient ? withScope : withIsolationScope;\n\n const wrappedFunction = (scope: Scope): unknown | Promise<unknown> => {\n // In certain situations, the passed context can become undefined.\n // For example, for Astro while prerendering pages at build time.\n // see: https://github.com/getsentry/sentry-javascript/issues/13217\n const context: typeof wrapperOptions.context | undefined = wrapperOptions.context;\n\n const waitUntil = context?.waitUntil?.bind?.(context);\n const storage = context && 'originalStorage' in context ? context.originalStorage : undefined;\n\n let scopeClient = scope.getClient();\n // Check if client exists AND is still usable (transport not disposed)\n // This handles the case where a previous handler disposed the client\n // but the scope still holds a reference to it (e.g., alarm handlers in Durable Objects)\n // For startNewTrace, always create a fresh client\n if (startNewTrace || !scopeClient?.getTransport()) {\n const client = init({\n ...wrapperOptions.options,\n ctx: context as unknown as ExecutionContext | undefined,\n });\n scope.setClient(client);\n scopeClient = client;\n }\n\n const clientToDispose = scopeClient;\n const methodName = wrapperOptions.spanName || 'unknown';\n\n const teardown = async (): Promise<void> => {\n if (startNewTrace && storage) {\n await storeSpanContext(storage, methodName);\n }\n await flushAndDispose(clientToDispose);\n };\n\n if (!wrapperOptions.spanName) {\n try {\n if (callback) {\n callback(...args);\n }\n\n const result = Reflect.apply(target, thisArg, args);\n\n if (isThenable(result)) {\n return result.then(\n (res: unknown) => {\n waitUntil?.(teardown());\n return res;\n },\n (e: unknown) => {\n captureException(e, {\n mechanism: {\n type: 'auto.faas.cloudflare.durable_object',\n handled: false,\n },\n });\n waitUntil?.(teardown());\n throw e;\n },\n );\n } else {\n waitUntil?.(teardown());\n return result;\n }\n } catch (e) {\n captureException(e, {\n mechanism: {\n type: 'auto.faas.cloudflare.durable_object',\n handled: false,\n },\n });\n waitUntil?.(teardown());\n throw e;\n }\n }\n\n const attributes = wrapperOptions.spanOp\n ? {\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: wrapperOptions.spanOp,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.faas.cloudflare.durable_object',\n }\n : {};\n\n const executeSpan = (): unknown => {\n return startSpan({ name: methodName, attributes }, span => {\n // When linking to previous trace, fetch the stored context and add links asynchronously\n // This avoids blocking the response while fetching from storage\n if (startNewTrace && storage) {\n waitUntil?.(\n getStoredSpanContext(storage, methodName).then(storedContext => {\n if (storedContext) {\n span.addLinks(buildSpanLinks(storedContext));\n // TODO: Remove this once EAP can store span links. We currently only set this attribute so that we\n // can obtain the previous trace information from the EAP store. Long-term, EAP will handle\n // span links and then we should remove this again. Also throwing in a TODO(v11), to remind us\n // to check this at v11 time :)\n const sampledFlag = storedContext.sampled ? '1' : '0';\n span.setAttribute(\n 'sentry.previous_trace',\n `${storedContext.traceId}-${storedContext.spanId}-${sampledFlag}`,\n );\n }\n }),\n );\n }\n\n try {\n const result = Reflect.apply(target, thisArg, args);\n\n if (isThenable(result)) {\n return result.then(\n (res: unknown) => {\n waitUntil?.(teardown());\n return res;\n },\n (e: unknown) => {\n captureException(e, {\n mechanism: {\n type: 'auto.faas.cloudflare.durable_object',\n handled: false,\n },\n });\n waitUntil?.(teardown());\n throw e;\n },\n );\n } else {\n waitUntil?.(teardown());\n return result;\n }\n } catch (e) {\n captureException(e, {\n mechanism: {\n type: 'auto.faas.cloudflare.durable_object',\n handled: false,\n },\n });\n waitUntil?.(teardown());\n throw e;\n }\n });\n };\n\n if (startNewTrace) {\n return startNewTraceCore(() => executeSpan());\n }\n\n return executeSpan();\n };\n\n return sentryWithScope(wrappedFunction);\n },\n }),\n noMark,\n );\n}\n"],"names":["startNewTrace","startNewTraceCore"],"mappings":"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"}
1
+ {"version":3,"file":"wrapMethodWithSentry.js","sources":["../../src/wrapMethodWithSentry.ts"],"sourcesContent":["import type { DurableObjectStorage } from '@cloudflare/workers-types';\nimport type { SerializedTraceData } from '@sentry/core';\nimport {\n captureException,\n continueTrace,\n getClient,\n isThenable,\n type Scope,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n startNewTrace as startNewTraceCore,\n startSpan,\n withIsolationScope,\n withScope,\n} from '@sentry/core';\nimport type { CloudflareOptions } from './client';\nimport { flushAndDispose } from './flush';\nimport { ensureInstrumented } from './instrument';\nimport { init } from './sdk';\nimport { extractRpcMeta } from './utils/rpcMeta';\nimport { buildSpanLinks, getStoredSpanContext, storeSpanContext } from './utils/traceLinks';\n\n/** Extended DurableObjectState with originalStorage exposed by instrumentContext */\ninterface InstrumentedDurableObjectState extends DurableObjectState {\n originalStorage?: DurableObjectStorage;\n}\n\ntype MethodWrapperOptions = {\n spanName?: string;\n spanOp?: string;\n options: CloudflareOptions;\n context: ExecutionContext | InstrumentedDurableObjectState;\n /**\n * If true, starts a fresh trace instead of inheriting from a parent trace.\n * Useful for scheduled/independent invocations like alarms.\n *\n * If true, it also stores the current span context and links to the previous invocation's span.\n * Uses Durable Object storage to persist the link. The link is set asynchronously via `span.addLinks()`\n * in a `waitUntil` to avoid blocking.\n *\n * @default false\n */\n startNewTrace?: boolean;\n};\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type UncheckedMethod = (...args: any[]) => any;\ntype OriginalMethod = UncheckedMethod;\n\n/**\n * Wraps a method with Sentry error tracking and optional tracing.\n * Supports starting new traces and linking to previous invocations via Durable Object storage.\n *\n * @param wrapperOptions - The options for the wrapper.\n * @param handler - The method to wrap.\n * @param callback - The callback to call.\n * @param noMark - Whether to mark the method as instrumented.\n * @returns The wrapped method.\n */\nexport function wrapMethodWithSentry<T extends OriginalMethod>(\n wrapperOptions: MethodWrapperOptions,\n handler: T,\n callback?: (...args: Parameters<T>) => void,\n noMark?: true,\n): T {\n return ensureInstrumented(\n handler,\n original =>\n new Proxy(original, {\n apply(target, thisArg, rawArgs: Parameters<T>) {\n const { startNewTrace } = wrapperOptions;\n\n // For RPC methods, extract Sentry trace context from the trailing argument.\n // The caller side (instrumentDurableObjectStub / JSRPC proxy) appends it;\n // we strip it here so the user's method never sees it.\n let args = rawArgs;\n let rpcMeta: SerializedTraceData | undefined;\n\n if (wrapperOptions.spanOp === 'rpc') {\n const extracted = extractRpcMeta(rawArgs);\n args = extracted.args;\n rpcMeta = extracted.rpcMeta;\n }\n\n // For startNewTrace, always use withIsolationScope to ensure a fresh scope\n // Otherwise, use existing client's scope or isolation scope\n const currentClient = getClient();\n const sentryWithScope = startNewTrace ? withIsolationScope : currentClient ? withScope : withIsolationScope;\n\n const wrappedFunction = (scope: Scope): unknown | Promise<unknown> => {\n // In certain situations, the passed context can become undefined.\n // For example, for Astro while prerendering pages at build time.\n // see: https://github.com/getsentry/sentry-javascript/issues/13217\n const context: typeof wrapperOptions.context | undefined = wrapperOptions.context;\n\n const waitUntil = context?.waitUntil?.bind?.(context);\n const storage = context && 'originalStorage' in context ? context.originalStorage : undefined;\n\n let scopeClient = scope.getClient();\n // Check if client exists AND is still usable (transport not disposed)\n // This handles the case where a previous handler disposed the client\n // but the scope still holds a reference to it (e.g., alarm handlers in Durable Objects)\n // For startNewTrace, always create a fresh client\n if (startNewTrace || !scopeClient?.getTransport()) {\n const client = init({\n ...wrapperOptions.options,\n ctx: context as unknown as ExecutionContext | undefined,\n });\n scope.setClient(client);\n scopeClient = client;\n }\n\n const clientToDispose = scopeClient;\n const methodName = wrapperOptions.spanName || 'unknown';\n\n const teardown = async (): Promise<void> => {\n if (startNewTrace && storage) {\n await storeSpanContext(storage, methodName);\n }\n await flushAndDispose(clientToDispose);\n };\n\n if (!wrapperOptions.spanName) {\n try {\n if (callback) {\n callback(...args);\n }\n\n const result = Reflect.apply(target, thisArg, args);\n\n if (isThenable(result)) {\n return result.then(\n (res: unknown) => {\n waitUntil?.(teardown());\n return res;\n },\n (e: unknown) => {\n captureException(e, {\n mechanism: {\n type: 'auto.faas.cloudflare.durable_object',\n handled: false,\n },\n });\n waitUntil?.(teardown());\n throw e;\n },\n );\n } else {\n waitUntil?.(teardown());\n return result;\n }\n } catch (e) {\n captureException(e, {\n mechanism: {\n type: 'auto.faas.cloudflare.durable_object',\n handled: false,\n },\n });\n waitUntil?.(teardown());\n throw e;\n }\n }\n\n const attributes = wrapperOptions.spanOp\n ? {\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: wrapperOptions.spanOp,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.faas.cloudflare.durable_object',\n }\n : {};\n\n const executeSpan = (): unknown => {\n return startSpan({ name: methodName, attributes }, span => {\n // When linking to previous trace, fetch the stored context and add links asynchronously\n // This avoids blocking the response while fetching from storage\n if (startNewTrace && storage) {\n waitUntil?.(\n getStoredSpanContext(storage, methodName).then(storedContext => {\n if (storedContext) {\n span.addLinks(buildSpanLinks(storedContext));\n // TODO: Remove this once EAP can store span links. We currently only set this attribute so that we\n // can obtain the previous trace information from the EAP store. Long-term, EAP will handle\n // span links and then we should remove this again. Also throwing in a TODO(v11), to remind us\n // to check this at v11 time :)\n const sampledFlag = storedContext.sampled ? '1' : '0';\n span.setAttribute(\n 'sentry.previous_trace',\n `${storedContext.traceId}-${storedContext.spanId}-${sampledFlag}`,\n );\n }\n }),\n );\n }\n\n try {\n const result = Reflect.apply(target, thisArg, args);\n\n if (isThenable(result)) {\n return result.then(\n (res: unknown) => {\n waitUntil?.(teardown());\n return res;\n },\n (e: unknown) => {\n captureException(e, {\n mechanism: {\n type: 'auto.faas.cloudflare.durable_object',\n handled: false,\n },\n });\n waitUntil?.(teardown());\n throw e;\n },\n );\n } else {\n waitUntil?.(teardown());\n return result;\n }\n } catch (e) {\n captureException(e, {\n mechanism: {\n type: 'auto.faas.cloudflare.durable_object',\n handled: false,\n },\n });\n waitUntil?.(teardown());\n throw e;\n }\n });\n };\n\n if (rpcMeta) {\n return continueTrace(\n { sentryTrace: rpcMeta['sentry-trace'] || '', baggage: rpcMeta.baggage || '' },\n executeSpan,\n );\n }\n\n if (startNewTrace) {\n return startNewTraceCore(() => executeSpan());\n }\n\n return executeSpan();\n };\n\n return sentryWithScope(wrappedFunction);\n },\n }),\n noMark,\n );\n}\n"],"names":["startNewTrace","startNewTraceCore"],"mappings":"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"}
@@ -3,14 +3,14 @@ import type { CloudflareOptions } from './client';
3
3
  /**
4
4
  * Instruments a Durable Object class to capture errors and performance data.
5
5
  *
6
- * Instruments the following methods:
6
+ * Instruments the following methods by default:
7
7
  * - fetch
8
8
  * - alarm
9
9
  * - webSocketMessage
10
10
  * - webSocketClose
11
11
  * - webSocketError
12
12
  *
13
- * as well as any other public RPC methods on the Durable Object instance.
13
+ * To instrument RPC methods (prototype methods), enable the `enableRpcTracePropagation` option.
14
14
  *
15
15
  * @param optionsCallback Function that returns the options for the SDK initialization.
16
16
  * @param DurableObjectClass The Durable Object class to instrument.
@@ -1 +1 @@
1
- {"version":3,"file":"durableobject.d.ts","sourceRoot":"","sources":["../../src/durableobject.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAUlD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,iCAAiC,CAC/C,CAAC,EACD,CAAC,SAAS,aAAa,CAAC,CAAC,CAAC,EAC1B,CAAC,SAAS,KAAK,KAAK,EAAE,kBAAkB,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,EACtD,eAAe,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,iBAAiB,EAAE,kBAAkB,EAAE,CAAC,GAAG,CAAC,CAiH1E"}
1
+ {"version":3,"file":"durableobject.d.ts","sourceRoot":"","sources":["../../src/durableobject.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAkBlD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,iCAAiC,CAC/C,CAAC,EACD,CAAC,SAAS,aAAa,CAAC,CAAC,CAAC,EAC1B,CAAC,SAAS,KAAK,KAAK,EAAE,kBAAkB,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,EACtD,eAAe,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,iBAAiB,EAAE,kBAAkB,EAAE,CAAC,GAAG,CAAC,CAmI1E"}