@sentry/cloudflare 10.55.0 → 10.56.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 (56) hide show
  1. package/build/cjs/durableobject.js +21 -20
  2. package/build/cjs/durableobject.js.map +1 -1
  3. package/build/cjs/index.js +2 -2
  4. package/build/cjs/instrumentations/instrumentDurableObjectStorage.js +1 -1
  5. package/build/cjs/instrumentations/instrumentDurableObjectStorage.js.map +1 -1
  6. package/build/cjs/{d1.js → instrumentations/worker/instrumentD1.js} +1 -1
  7. package/build/cjs/instrumentations/worker/instrumentD1.js.map +1 -0
  8. package/build/cjs/integrations/httpServer.js.map +1 -1
  9. package/build/cjs/request.js +7 -3
  10. package/build/cjs/request.js.map +1 -1
  11. package/build/cjs/sdk.js +2 -3
  12. package/build/cjs/sdk.js.map +1 -1
  13. package/build/cjs/utils/traceLinks.js +6 -5
  14. package/build/cjs/utils/traceLinks.js.map +1 -1
  15. package/build/cjs/workflows.js +9 -1
  16. package/build/cjs/workflows.js.map +1 -1
  17. package/build/cjs/wrapMethodWithSentry.js +43 -66
  18. package/build/cjs/wrapMethodWithSentry.js.map +1 -1
  19. package/build/esm/durableobject.js +21 -20
  20. package/build/esm/durableobject.js.map +1 -1
  21. package/build/esm/index.js +1 -1
  22. package/build/esm/instrumentations/instrumentDurableObjectStorage.js +1 -1
  23. package/build/esm/instrumentations/instrumentDurableObjectStorage.js.map +1 -1
  24. package/build/esm/{d1.js → instrumentations/worker/instrumentD1.js} +1 -1
  25. package/build/esm/instrumentations/worker/instrumentD1.js.map +1 -0
  26. package/build/esm/integrations/httpServer.js.map +1 -1
  27. package/build/esm/package.json +1 -1
  28. package/build/esm/request.js +7 -3
  29. package/build/esm/request.js.map +1 -1
  30. package/build/esm/sdk.js +2 -3
  31. package/build/esm/sdk.js.map +1 -1
  32. package/build/esm/utils/traceLinks.js +7 -6
  33. package/build/esm/utils/traceLinks.js.map +1 -1
  34. package/build/esm/workflows.js +9 -1
  35. package/build/esm/workflows.js.map +1 -1
  36. package/build/esm/wrapMethodWithSentry.js +45 -68
  37. package/build/esm/wrapMethodWithSentry.js.map +1 -1
  38. package/build/types/durableobject.d.ts.map +1 -1
  39. package/build/types/index.d.ts +1 -1
  40. package/build/types/index.d.ts.map +1 -1
  41. package/build/types/{d1.d.ts → instrumentations/worker/instrumentD1.d.ts} +1 -1
  42. package/build/types/instrumentations/worker/instrumentD1.d.ts.map +1 -0
  43. package/build/types/integrations/httpServer.d.ts.map +1 -1
  44. package/build/types/request.d.ts.map +1 -1
  45. package/build/types/sdk.d.ts.map +1 -1
  46. package/build/types/utils/traceLinks.d.ts +3 -2
  47. package/build/types/utils/traceLinks.d.ts.map +1 -1
  48. package/build/types/workflows.d.ts.map +1 -1
  49. package/build/types/wrapMethodWithSentry.d.ts.map +1 -1
  50. package/build/types-ts3.8/index.d.ts +1 -1
  51. package/build/types-ts3.8/{d1.d.ts → instrumentations/worker/instrumentD1.d.ts} +1 -1
  52. package/build/types-ts3.8/utils/traceLinks.d.ts +3 -2
  53. package/package.json +2 -2
  54. package/build/cjs/d1.js.map +0 -1
  55. package/build/esm/d1.js.map +0 -1
  56. package/build/types/d1.d.ts.map +0 -1
@@ -7,6 +7,18 @@ const sdk = require('./sdk.js');
7
7
  const rpcMeta = require('./utils/rpcMeta.js');
8
8
  const traceLinks = require('./utils/traceLinks.js');
9
9
 
10
+ function resolveOriginalStorage(context, thisArg) {
11
+ if (thisArg && typeof thisArg === "object" && "ctx" in thisArg) {
12
+ const doCtx = thisArg.ctx;
13
+ if (doCtx?.originalStorage) {
14
+ return doCtx.originalStorage;
15
+ }
16
+ }
17
+ if (context && "originalStorage" in context && context.originalStorage) {
18
+ return context.originalStorage;
19
+ }
20
+ return void 0;
21
+ }
10
22
  function wrapMethodWithSentry(wrapperOptions, handler, callback, noMark) {
11
23
  return instrument.ensureInstrumented(
12
24
  handler,
@@ -25,7 +37,7 @@ function wrapMethodWithSentry(wrapperOptions, handler, callback, noMark) {
25
37
  const wrappedFunction = (scope) => {
26
38
  const context = wrapperOptions.context;
27
39
  const waitUntil = context?.waitUntil?.bind?.(context);
28
- const storage = context && "originalStorage" in context ? context.originalStorage : void 0;
40
+ const storage = resolveOriginalStorage(context, thisArg);
29
41
  let scopeClient = scope.getClient();
30
42
  if (startNewTrace || !scopeClient?.getTransport()) {
31
43
  const client = sdk.init({
@@ -39,10 +51,24 @@ function wrapMethodWithSentry(wrapperOptions, handler, callback, noMark) {
39
51
  const methodName = wrapperOptions.spanName || "unknown";
40
52
  const teardown = async () => {
41
53
  if (startNewTrace && storage) {
42
- await traceLinks.storeSpanContext(storage, methodName);
54
+ traceLinks.storeSpanContext(storage, methodName);
43
55
  }
44
56
  await flush.flushAndDispose(clientToDispose);
45
57
  };
58
+ const onFulfilled = (res) => {
59
+ waitUntil?.(teardown());
60
+ return res;
61
+ };
62
+ const onRejected = (e) => {
63
+ core.captureException(e, {
64
+ mechanism: {
65
+ type: "auto.faas.cloudflare.durable_object",
66
+ handled: false
67
+ }
68
+ });
69
+ waitUntil?.(teardown());
70
+ throw e;
71
+ };
46
72
  if (!wrapperOptions.spanName) {
47
73
  try {
48
74
  if (callback) {
@@ -50,35 +76,12 @@ function wrapMethodWithSentry(wrapperOptions, handler, callback, noMark) {
50
76
  }
51
77
  const result = Reflect.apply(target, thisArg, args);
52
78
  if (core.isThenable(result)) {
53
- return result.then(
54
- (res) => {
55
- waitUntil?.(teardown());
56
- return res;
57
- },
58
- (e) => {
59
- core.captureException(e, {
60
- mechanism: {
61
- type: "auto.faas.cloudflare.durable_object",
62
- handled: false
63
- }
64
- });
65
- waitUntil?.(teardown());
66
- throw e;
67
- }
68
- );
79
+ return result.then(onFulfilled, onRejected);
69
80
  } else {
70
- waitUntil?.(teardown());
71
- return result;
81
+ return onFulfilled(result);
72
82
  }
73
83
  } catch (e) {
74
- core.captureException(e, {
75
- mechanism: {
76
- type: "auto.faas.cloudflare.durable_object",
77
- handled: false
78
- }
79
- });
80
- waitUntil?.(teardown());
81
- throw e;
84
+ return onRejected(e);
82
85
  }
83
86
  }
84
87
  const attributes = wrapperOptions.spanOp ? {
@@ -88,51 +91,25 @@ function wrapMethodWithSentry(wrapperOptions, handler, callback, noMark) {
88
91
  const executeSpan = () => {
89
92
  return core.startSpan({ name: methodName, attributes }, (span) => {
90
93
  if (startNewTrace && storage) {
91
- waitUntil?.(
92
- traceLinks.getStoredSpanContext(storage, methodName).then((storedContext) => {
93
- if (storedContext) {
94
- span.addLinks(traceLinks.buildSpanLinks(storedContext));
95
- const sampledFlag = storedContext.sampled ? "1" : "0";
96
- span.setAttribute(
97
- "sentry.previous_trace",
98
- `${storedContext.traceId}-${storedContext.spanId}-${sampledFlag}`
99
- );
100
- }
101
- })
102
- );
94
+ const storedContext = traceLinks.getStoredSpanContext(storage, methodName);
95
+ if (storedContext) {
96
+ span.addLinks(traceLinks.buildSpanLinks(storedContext));
97
+ const sampledFlag = storedContext.sampled ? "1" : "0";
98
+ span.setAttribute(
99
+ "sentry.previous_trace",
100
+ `${storedContext.traceId}-${storedContext.spanId}-${sampledFlag}`
101
+ );
102
+ }
103
103
  }
104
104
  try {
105
105
  const result = Reflect.apply(target, thisArg, args);
106
106
  if (core.isThenable(result)) {
107
- return result.then(
108
- (res) => {
109
- waitUntil?.(teardown());
110
- return res;
111
- },
112
- (e) => {
113
- core.captureException(e, {
114
- mechanism: {
115
- type: "auto.faas.cloudflare.durable_object",
116
- handled: false
117
- }
118
- });
119
- waitUntil?.(teardown());
120
- throw e;
121
- }
122
- );
107
+ return result.then(onFulfilled, onRejected);
123
108
  } else {
124
- waitUntil?.(teardown());
125
- return result;
109
+ return onFulfilled(result);
126
110
  }
127
111
  } catch (e) {
128
- core.captureException(e, {
129
- mechanism: {
130
- type: "auto.faas.cloudflare.durable_object",
131
- handled: false
132
- }
133
- });
134
- waitUntil?.(teardown());
135
- throw e;
112
+ return onRejected(e);
136
113
  }
137
114
  });
138
115
  };
@@ -1 +1 @@
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":["ensureInstrumented","rpcMeta","extractRpcMeta","getClient","withIsolationScope","withScope","init","storeSpanContext","flushAndDispose","isThenable","captureException","SEMANTIC_ATTRIBUTE_SENTRY_OP","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","startSpan","getStoredSpanContext","buildSpanLinks","continueTrace","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\n/**\n * Resolves uninstrumented DO storage for the current invocation.\n * Prefer `thisArg.ctx` (the live Durable Object instance) over the context captured at\n * construction time to avoid cross-DO I/O errors in the same isolate.\n */\nfunction resolveOriginalStorage(\n context: ExecutionContext | InstrumentedDurableObjectState | undefined,\n thisArg: unknown,\n): DurableObjectStorage | undefined {\n if (thisArg && typeof thisArg === 'object' && 'ctx' in thisArg) {\n const doCtx = (thisArg as { ctx: InstrumentedDurableObjectState }).ctx;\n if (doCtx?.originalStorage) {\n return doCtx.originalStorage;\n }\n }\n\n if (context && 'originalStorage' in context && context.originalStorage) {\n return context.originalStorage;\n }\n\n return undefined;\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 = resolveOriginalStorage(context, thisArg);\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 storeSpanContext(storage, methodName);\n }\n await flushAndDispose(clientToDispose);\n };\n\n const onFulfilled = (res: unknown) => {\n waitUntil?.(teardown());\n return res;\n };\n\n const onRejected = (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 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(onFulfilled, onRejected);\n } else {\n return onFulfilled(result);\n }\n } catch (e) {\n return onRejected(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 if (startNewTrace && storage) {\n const storedContext = getStoredSpanContext(storage, methodName);\n\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 try {\n const result = Reflect.apply(target, thisArg, args);\n\n if (isThenable(result)) {\n return result.then(onFulfilled, onRejected);\n } else {\n return onFulfilled(result);\n }\n } catch (e) {\n return onRejected(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":["ensureInstrumented","rpcMeta","extractRpcMeta","getClient","withIsolationScope","withScope","init","storeSpanContext","flushAndDispose","captureException","isThenable","SEMANTIC_ATTRIBUTE_SENTRY_OP","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","startSpan","getStoredSpanContext","buildSpanLinks","continueTrace","startNewTraceCore"],"mappings":"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"}
@@ -5,17 +5,10 @@ import { instrumentEnv } from './instrumentations/worker/instrumentEnv.js';
5
5
  import { getFinalOptions } from './options.js';
6
6
  import { wrapRequestHandler } from './request.js';
7
7
  import { instrumentContext } from './utils/instrumentContext.js';
8
+ import { extractRpcMeta } from './utils/rpcMeta.js';
8
9
  import { getEffectiveRpcPropagation } from './utils/rpcOptions.js';
9
10
  import { wrapMethodWithSentry } from './wrapMethodWithSentry.js';
10
11
 
11
- const BUILT_IN_DO_METHODS = /* @__PURE__ */ new Set([
12
- "constructor",
13
- "fetch",
14
- "alarm",
15
- "webSocketError",
16
- "webSocketClose",
17
- "webSocketMessage"
18
- ]);
19
12
  function instrumentDurableObjectWithSentry(optionsCallback, DurableObjectClass) {
20
13
  return new Proxy(DurableObjectClass, {
21
14
  construct(target, [ctx, env]) {
@@ -69,31 +62,39 @@ function instrumentDurableObjectWithSentry(optionsCallback, DurableObjectClass)
69
62
  }
70
63
  const instrumentPrototypeMethods = Array.isArray(options.instrumentPrototypeMethods) ? options.instrumentPrototypeMethods : void 0;
71
64
  const allowSet = instrumentPrototypeMethods ? new Set(instrumentPrototypeMethods) : null;
72
- const rpcMethodCache = /* @__PURE__ */ new Map();
65
+ const alwaysTrace = options.enableRpcTracePropagation === void 0;
66
+ const methodCache = /* @__PURE__ */ new Map();
73
67
  return new Proxy(obj, {
74
68
  get(proxyTarget, prop, receiver) {
75
69
  const value = Reflect.get(proxyTarget, prop, receiver);
76
- if (typeof prop !== "string" || BUILT_IN_DO_METHODS.has(prop)) {
70
+ if (typeof prop !== "string" || typeof value !== "function" || prop === "constructor") {
77
71
  return value;
78
72
  }
79
- const cached = rpcMethodCache.get(prop);
73
+ const cached = methodCache.get(prop);
80
74
  if (cached) {
81
75
  return cached;
82
76
  }
83
- if (typeof value !== "function" || Object.prototype.hasOwnProperty.call(proxyTarget, prop) || allowSet && !allowSet.has(prop) || // Exclude inherited Object.prototype methods (toString, valueOf, etc.)
84
- // These are not RPC methods and should not create spans
85
- prop in Object.prototype) {
86
- return value;
77
+ const boundMethod = value.bind(proxyTarget);
78
+ if (prop in Object.prototype || Object.prototype.hasOwnProperty.call(proxyTarget, prop) || allowSet && !allowSet.has(prop)) {
79
+ methodCache.set(prop, boundMethod);
80
+ return boundMethod;
87
81
  }
88
- const boundValue = value.bind(proxyTarget);
89
- const wrapped = wrapMethodWithSentry(
82
+ const tracedMethod = wrapMethodWithSentry(
90
83
  { options, context, spanName: prop, spanOp: "rpc" },
91
- boundValue,
84
+ boundMethod,
92
85
  void 0,
93
86
  true
94
87
  );
95
- rpcMethodCache.set(prop, wrapped);
96
- return wrapped;
88
+ if (alwaysTrace) {
89
+ methodCache.set(prop, tracedMethod);
90
+ return tracedMethod;
91
+ }
92
+ const wrappedMethod = ((...args) => {
93
+ const { rpcMeta } = extractRpcMeta(args);
94
+ return rpcMeta ? tracedMethod(...args) : boundMethod(...args);
95
+ });
96
+ methodCache.set(prop, wrappedMethod);
97
+ return wrappedMethod;
97
98
  }
98
99
  });
99
100
  }
@@ -1 +1 @@
1
- {"version":3,"file":"durableobject.js","sources":["../../src/durableobject.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/unbound-method */\nimport { captureException } from '@sentry/core';\nimport type { DurableObject } from 'cloudflare:workers';\nimport { setAsyncLocalStorageAsyncContextStrategy } from './async';\nimport type { CloudflareOptions } from './client';\nimport { ensureInstrumented } from './instrument';\nimport { instrumentEnv } from './instrumentations/worker/instrumentEnv';\nimport { getFinalOptions } from './options';\nimport { wrapRequestHandler } from './request';\nimport { instrumentContext } from './utils/instrumentContext';\nimport { getEffectiveRpcPropagation } from './utils/rpcOptions';\nimport { type UncheckedMethod, wrapMethodWithSentry } from './wrapMethodWithSentry';\n\nconst BUILT_IN_DO_METHODS = new Set([\n 'constructor',\n 'fetch',\n 'alarm',\n 'webSocketError',\n 'webSocketClose',\n 'webSocketMessage',\n]);\n\n/**\n * Instruments a Durable Object class to capture errors and performance data.\n *\n * Instruments the following methods by default:\n * - fetch\n * - alarm\n * - webSocketMessage\n * - webSocketClose\n * - webSocketError\n *\n * To instrument RPC methods (prototype methods), enable the `enableRpcTracePropagation` option.\n *\n * @param optionsCallback Function that returns the options for the SDK initialization.\n * @param DurableObjectClass The Durable Object class to instrument.\n * @returns The instrumented Durable Object class.\n *\n * @example\n * ```ts\n * class MyDurableObjectBase extends DurableObject {\n * constructor(ctx: DurableObjectState, env: Env) {\n * super(ctx, env);\n * }\n * }\n *\n * export const MyDurableObject = instrumentDurableObjectWithSentry(\n * env => ({\n * dsn: env.SENTRY_DSN,\n * tracesSampleRate: 1.0,\n * }),\n * MyDurableObjectBase,\n * );\n * ```\n */\nexport function instrumentDurableObjectWithSentry<\n E,\n T extends DurableObject<E>,\n C extends new (state: DurableObjectState, env: E) => T,\n>(optionsCallback: (env: E) => CloudflareOptions, DurableObjectClass: C): C {\n return new Proxy(DurableObjectClass, {\n construct(target, [ctx, env]) {\n setAsyncLocalStorageAsyncContextStrategy();\n const context = instrumentContext(ctx);\n const options = getFinalOptions(optionsCallback(env), env);\n const instrumentedEnv = instrumentEnv(env, options);\n\n const obj = new target(context, instrumentedEnv);\n\n // These are the methods that are available on a Durable Object\n // ref: https://developers.cloudflare.com/durable-objects/api/base/\n // obj.alarm\n // obj.fetch\n // obj.webSocketError\n // obj.webSocketClose\n // obj.webSocketMessage\n\n // Any other public methods on the Durable Object instance are RPC calls.\n\n if (obj.fetch && typeof obj.fetch === 'function') {\n obj.fetch = ensureInstrumented(\n obj.fetch,\n original =>\n new Proxy(original, {\n apply(target, thisArg, args) {\n return wrapRequestHandler({ options, request: args[0], context }, () => {\n return Reflect.apply(target, thisArg, args);\n });\n },\n }),\n );\n }\n\n if (obj.alarm && typeof obj.alarm === 'function') {\n // Alarms are independent invocations, so we start a new trace and link to the previous alarm\n obj.alarm = wrapMethodWithSentry(\n { options, context, spanName: 'alarm', spanOp: 'function', startNewTrace: true },\n obj.alarm,\n );\n }\n\n if (obj.webSocketMessage && typeof obj.webSocketMessage === 'function') {\n obj.webSocketMessage = wrapMethodWithSentry(\n { options, context, spanName: 'webSocketMessage' },\n obj.webSocketMessage,\n );\n }\n\n if (obj.webSocketClose && typeof obj.webSocketClose === 'function') {\n obj.webSocketClose = wrapMethodWithSentry({ options, context, spanName: 'webSocketClose' }, obj.webSocketClose);\n }\n\n if (obj.webSocketError && typeof obj.webSocketError === 'function') {\n obj.webSocketError = wrapMethodWithSentry(\n { options, context, spanName: 'webSocketError' },\n obj.webSocketError,\n (_, error) =>\n captureException(error, {\n mechanism: {\n type: 'auto.faas.cloudflare.durable_object_websocket',\n handled: false,\n },\n }),\n );\n }\n\n // Get effective RPC propagation setting (handles deprecation of instrumentPrototypeMethods)\n const rpcPropagation = getEffectiveRpcPropagation(options);\n\n // Skip RPC instrumentation if not enabled\n if (!rpcPropagation) {\n return obj;\n }\n\n // If `instrumentPrototypeMethods` was passed as an array (deprecated),\n // only the listed method names should be instrumented.\n const instrumentPrototypeMethods = Array.isArray(options.instrumentPrototypeMethods)\n ? options.instrumentPrototypeMethods\n : undefined;\n const allowSet = instrumentPrototypeMethods ? new Set(instrumentPrototypeMethods) : null;\n\n // Return a Proxy that lazily wraps prototype methods on access.\n // This avoids iterating the prototype chain at construction time —\n // we only check if a property is an RPC method when it's accessed.\n const rpcMethodCache = new Map<string, UncheckedMethod>();\n\n return new Proxy(obj, {\n get(proxyTarget, prop, receiver) {\n const value = Reflect.get(proxyTarget, prop, receiver);\n\n if (typeof prop !== 'string' || BUILT_IN_DO_METHODS.has(prop)) {\n return value;\n }\n\n const cached = rpcMethodCache.get(prop);\n\n if (cached) {\n return cached;\n }\n\n if (\n typeof value !== 'function' ||\n Object.prototype.hasOwnProperty.call(proxyTarget, prop) ||\n (allowSet && !allowSet.has(prop)) ||\n // Exclude inherited Object.prototype methods (toString, valueOf, etc.)\n // These are not RPC methods and should not create spans\n prop in Object.prototype\n ) {\n return value;\n }\n\n // Bind the method to the original object to ensure private fields work correctly.\n // When called via the Proxy, `this` would be the Proxy, but private fields require\n // the original object. Bound functions ignore the thisArg passed via Reflect.apply.\n const boundValue = (value as UncheckedMethod).bind(proxyTarget);\n\n const wrapped = wrapMethodWithSentry(\n { options, context, spanName: prop, spanOp: 'rpc' },\n boundValue,\n undefined,\n true,\n );\n\n rpcMethodCache.set(prop, wrapped);\n\n return wrapped;\n },\n });\n },\n });\n}\n"],"names":["target"],"mappings":"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"}
1
+ {"version":3,"file":"durableobject.js","sources":["../../src/durableobject.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/unbound-method */\nimport { captureException } from '@sentry/core';\nimport type { DurableObject } from 'cloudflare:workers';\nimport { setAsyncLocalStorageAsyncContextStrategy } from './async';\nimport type { CloudflareOptions } from './client';\nimport { ensureInstrumented } from './instrument';\nimport { instrumentEnv } from './instrumentations/worker/instrumentEnv';\nimport { getFinalOptions } from './options';\nimport { wrapRequestHandler } from './request';\nimport { instrumentContext } from './utils/instrumentContext';\nimport { extractRpcMeta } from './utils/rpcMeta';\nimport { getEffectiveRpcPropagation } from './utils/rpcOptions';\nimport { type UncheckedMethod, wrapMethodWithSentry } from './wrapMethodWithSentry';\n\n/**\n * Instruments a Durable Object class to capture errors and performance data.\n *\n * Instruments the following methods by default:\n * - fetch\n * - alarm\n * - webSocketMessage\n * - webSocketClose\n * - webSocketError\n *\n * To instrument RPC methods (prototype methods), enable the `enableRpcTracePropagation` option.\n *\n * @param optionsCallback Function that returns the options for the SDK initialization.\n * @param DurableObjectClass The Durable Object class to instrument.\n * @returns The instrumented Durable Object class.\n *\n * @example\n * ```ts\n * class MyDurableObjectBase extends DurableObject {\n * constructor(ctx: DurableObjectState, env: Env) {\n * super(ctx, env);\n * }\n * }\n *\n * export const MyDurableObject = instrumentDurableObjectWithSentry(\n * env => ({\n * dsn: env.SENTRY_DSN,\n * tracesSampleRate: 1.0,\n * }),\n * MyDurableObjectBase,\n * );\n * ```\n */\nexport function instrumentDurableObjectWithSentry<\n E,\n T extends DurableObject<E>,\n C extends new (state: DurableObjectState, env: E) => T,\n>(optionsCallback: (env: E) => CloudflareOptions, DurableObjectClass: C): C {\n return new Proxy(DurableObjectClass, {\n construct(target, [ctx, env]) {\n setAsyncLocalStorageAsyncContextStrategy();\n const context = instrumentContext(ctx);\n const options = getFinalOptions(optionsCallback(env), env);\n const instrumentedEnv = instrumentEnv(env, options);\n\n const obj = new target(context, instrumentedEnv);\n\n // These are the methods that are available on a Durable Object\n // ref: https://developers.cloudflare.com/durable-objects/api/base/\n // obj.alarm\n // obj.fetch\n // obj.webSocketError\n // obj.webSocketClose\n // obj.webSocketMessage\n\n // Any other public methods on the Durable Object instance are RPC calls.\n\n if (obj.fetch && typeof obj.fetch === 'function') {\n obj.fetch = ensureInstrumented(\n obj.fetch,\n original =>\n new Proxy(original, {\n apply(target, thisArg, args) {\n return wrapRequestHandler({ options, request: args[0], context }, () => {\n return Reflect.apply(target, thisArg, args);\n });\n },\n }),\n );\n }\n\n if (obj.alarm && typeof obj.alarm === 'function') {\n // Alarms are independent invocations, so we start a new trace and link to the previous alarm\n obj.alarm = wrapMethodWithSentry(\n { options, context, spanName: 'alarm', spanOp: 'function', startNewTrace: true },\n obj.alarm,\n );\n }\n\n if (obj.webSocketMessage && typeof obj.webSocketMessage === 'function') {\n obj.webSocketMessage = wrapMethodWithSentry(\n { options, context, spanName: 'webSocketMessage' },\n obj.webSocketMessage,\n );\n }\n\n if (obj.webSocketClose && typeof obj.webSocketClose === 'function') {\n obj.webSocketClose = wrapMethodWithSentry({ options, context, spanName: 'webSocketClose' }, obj.webSocketClose);\n }\n\n if (obj.webSocketError && typeof obj.webSocketError === 'function') {\n obj.webSocketError = wrapMethodWithSentry(\n { options, context, spanName: 'webSocketError' },\n obj.webSocketError,\n (_, error) =>\n captureException(error, {\n mechanism: {\n type: 'auto.faas.cloudflare.durable_object_websocket',\n handled: false,\n },\n }),\n );\n }\n\n // Get effective RPC propagation setting (handles deprecation of instrumentPrototypeMethods)\n const rpcPropagation = getEffectiveRpcPropagation(options);\n\n // Skip RPC instrumentation if not enabled\n if (!rpcPropagation) {\n return obj;\n }\n\n // If `instrumentPrototypeMethods` was passed as an array (deprecated),\n // only the listed method names should be instrumented.\n const instrumentPrototypeMethods = Array.isArray(options.instrumentPrototypeMethods)\n ? options.instrumentPrototypeMethods\n : undefined;\n const allowSet = instrumentPrototypeMethods ? new Set(instrumentPrototypeMethods) : null;\n\n // When using the deprecated `instrumentPrototypeMethods` option, always create spans.\n // When using the new `enableRpcTracePropagation`, only create spans when RPC metadata is present.\n const alwaysTrace = options.enableRpcTracePropagation === undefined;\n\n // Return a Proxy that binds all methods to the original object and creates spans\n // for RPC calls that have Sentry trace context propagated.\n // Binding is required because frameworks may use private fields (babel WeakMap pattern),\n // which fail if `this` is the Proxy instead of the original object.\n const methodCache = new Map<string, UncheckedMethod>();\n\n return new Proxy(obj, {\n get(proxyTarget, prop, receiver) {\n const value = Reflect.get(proxyTarget, prop, receiver);\n\n if (typeof prop !== 'string' || typeof value !== 'function' || prop === 'constructor') {\n return value;\n }\n\n const cached = methodCache.get(prop);\n\n if (cached) {\n return cached;\n }\n\n const boundMethod = (value as UncheckedMethod).bind(proxyTarget);\n\n if (\n prop in Object.prototype ||\n Object.prototype.hasOwnProperty.call(proxyTarget, prop) ||\n (allowSet && !allowSet.has(prop))\n ) {\n methodCache.set(prop, boundMethod);\n\n return boundMethod;\n }\n\n // Pre-create the traced version\n const tracedMethod = wrapMethodWithSentry(\n { options, context, spanName: prop, spanOp: 'rpc' },\n boundMethod,\n undefined,\n true,\n );\n\n // For deprecated `instrumentPrototypeMethods`, always trace.\n // For new `enableRpcTracePropagation`, only trace when RPC metadata is present.\n if (alwaysTrace) {\n methodCache.set(prop, tracedMethod);\n\n return tracedMethod;\n }\n\n // Wrapper that checks for Sentry RPC metadata at call time\n const wrappedMethod = ((...args: unknown[]) => {\n const { rpcMeta } = extractRpcMeta(args);\n\n // If Sentry RPC metadata is present, use the traced version (creates span)\n // Otherwise, call the bound method directly (no span)\n return rpcMeta ? tracedMethod(...args) : boundMethod(...args);\n }) as UncheckedMethod;\n\n methodCache.set(prop, wrappedMethod);\n\n return wrappedMethod;\n },\n });\n },\n });\n}\n"],"names":["target"],"mappings":"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"}
@@ -9,7 +9,7 @@ export { httpServerIntegration } from './integrations/httpServer.js';
9
9
  export { fetchIntegration } from './integrations/fetch.js';
10
10
  export { vercelAIIntegration } from './integrations/tracing/vercelai.js';
11
11
  export { honoIntegration } from './integrations/hono.js';
12
- export { instrumentD1WithSentry } from './d1.js';
12
+ export { instrumentD1WithSentry } from './instrumentations/worker/instrumentD1.js';
13
13
  export { instrumentWorkflowWithSentry } from './workflows.js';
14
14
  export { setAsyncLocalStorageAsyncContextStrategy } from './async.js';
15
15
  //# sourceMappingURL=index.js.map
@@ -28,7 +28,7 @@ function instrumentDurableObjectStorage(storage, waitUntil) {
28
28
  () => {
29
29
  const teardown = async () => {
30
30
  if (methodName === "setAlarm") {
31
- await storeSpanContext(target, "alarm");
31
+ storeSpanContext(target, "alarm");
32
32
  }
33
33
  };
34
34
  const result = original.apply(target, args);
@@ -1 +1 @@
1
- {"version":3,"file":"instrumentDurableObjectStorage.js","sources":["../../../src/instrumentations/instrumentDurableObjectStorage.ts"],"sourcesContent":["import type { DurableObjectStorage } from '@cloudflare/workers-types';\nimport { isThenable, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startSpan } from '@sentry/core';\nimport { storeSpanContext } from '../utils/traceLinks';\n\nconst STORAGE_METHODS_TO_INSTRUMENT = ['get', 'put', 'delete', 'list', 'setAlarm', 'getAlarm', 'deleteAlarm'] as const;\n\ntype StorageMethod = (typeof STORAGE_METHODS_TO_INSTRUMENT)[number];\n\ntype WaitUntil = (promise: Promise<unknown>) => void;\n\n/**\n * Instruments DurableObjectStorage methods with Sentry spans.\n *\n * Wraps the following async methods:\n * - get, put, delete, list (KV API)\n * - setAlarm, getAlarm, deleteAlarm (Alarm API)\n *\n * When setAlarm is called, it also stores the current span context so that when\n * the alarm fires later, it can link back to the trace that called setAlarm.\n *\n * @param storage - The DurableObjectStorage instance to instrument\n * @param waitUntil - Optional waitUntil function to defer span context storage\n * @returns An instrumented DurableObjectStorage instance\n */\nexport function instrumentDurableObjectStorage(\n storage: DurableObjectStorage,\n waitUntil?: WaitUntil,\n): DurableObjectStorage {\n return new Proxy(storage, {\n get(target, prop, _receiver) {\n // Use `target` as the receiver instead of the proxy (`_receiver`).\n // Native workerd getters (e.g., `storage.sql`) validate `this` via\n // internal slots. Passing the proxy as receiver breaks that check,\n // causing \"Illegal invocation: function called with incorrect `this`\n // reference\" errors.\n const original = Reflect.get(target, prop, target);\n\n if (typeof original !== 'function') {\n return original;\n }\n\n const methodName = prop as string;\n if (!STORAGE_METHODS_TO_INSTRUMENT.includes(methodName as StorageMethod)) {\n return (original as (...args: unknown[]) => unknown).bind(target);\n }\n\n return function (this: unknown, ...args: unknown[]) {\n return startSpan(\n {\n // Use underscore naming to match Cloudflare's native instrumentation (e.g., \"durable_object_storage_get\")\n name: `durable_object_storage_${methodName}`,\n op: 'db',\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.db.cloudflare.durable_object',\n 'db.system.name': 'cloudflare.durable_object.storage',\n 'db.operation.name': methodName,\n },\n },\n () => {\n const teardown = async (): Promise<void> => {\n // When setAlarm is called, store the current span context so that when the alarm\n // fires later, it can link back to the trace that called setAlarm.\n // We use the original (uninstrumented) storage (target) to avoid creating a span\n // for this internal operation. The storage is deferred via waitUntil to not block.\n if (methodName === 'setAlarm') {\n await storeSpanContext(target, 'alarm');\n }\n };\n\n const result = (original as (...args: unknown[]) => unknown).apply(target, args);\n\n if (!isThenable(result)) {\n waitUntil?.(teardown());\n\n return result;\n }\n\n return result.then(\n res => {\n waitUntil?.(teardown());\n return res;\n },\n e => {\n throw e;\n },\n );\n },\n );\n };\n },\n });\n}\n"],"names":[],"mappings":";;;AAIA,MAAM,6BAAA,GAAgC,CAAC,KAAA,EAAO,KAAA,EAAO,UAAU,MAAA,EAAQ,UAAA,EAAY,YAAY,aAAa,CAAA;AAoBrG,SAAS,8BAAA,CACd,SACA,SAAA,EACsB;AACtB,EAAA,OAAO,IAAI,MAAM,OAAA,EAAS;AAAA,IACxB,GAAA,CAAI,MAAA,EAAQ,IAAA,EAAM,SAAA,EAAW;AAM3B,MAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,GAAA,CAAI,MAAA,EAAQ,MAAM,MAAM,CAAA;AAEjD,MAAA,IAAI,OAAO,aAAa,UAAA,EAAY;AAClC,QAAA,OAAO,QAAA;AAAA,MACT;AAEA,MAAA,MAAM,UAAA,GAAa,IAAA;AACnB,MAAA,IAAI,CAAC,6BAAA,CAA8B,QAAA,CAAS,UAA2B,CAAA,EAAG;AACxE,QAAA,OAAQ,QAAA,CAA6C,KAAK,MAAM,CAAA;AAAA,MAClE;AAEA,MAAA,OAAO,YAA4B,IAAA,EAAiB;AAClD,QAAA,OAAO,SAAA;AAAA,UACL;AAAA;AAAA,YAEE,IAAA,EAAM,0BAA0B,UAAU,CAAA,CAAA;AAAA,YAC1C,EAAA,EAAI,IAAA;AAAA,YACJ,UAAA,EAAY;AAAA,cACV,CAAC,gCAAgC,GAAG,mCAAA;AAAA,cACpC,gBAAA,EAAkB,mCAAA;AAAA,cAClB,mBAAA,EAAqB;AAAA;AACvB,WACF;AAAA,UACA,MAAM;AACJ,YAAA,MAAM,WAAW,YAA2B;AAK1C,cAAA,IAAI,eAAe,UAAA,EAAY;AAC7B,gBAAA,MAAM,gBAAA,CAAiB,QAAQ,OAAO,CAAA;AAAA,cACxC;AAAA,YACF,CAAA;AAEA,YAAA,MAAM,MAAA,GAAU,QAAA,CAA6C,KAAA,CAAM,MAAA,EAAQ,IAAI,CAAA;AAE/E,YAAA,IAAI,CAAC,UAAA,CAAW,MAAM,CAAA,EAAG;AACvB,cAAA,SAAA,GAAY,UAAU,CAAA;AAEtB,cAAA,OAAO,MAAA;AAAA,YACT;AAEA,YAAA,OAAO,MAAA,CAAO,IAAA;AAAA,cACZ,CAAA,GAAA,KAAO;AACL,gBAAA,SAAA,GAAY,UAAU,CAAA;AACtB,gBAAA,OAAO,GAAA;AAAA,cACT,CAAA;AAAA,cACA,CAAA,CAAA,KAAK;AACH,gBAAA,MAAM,CAAA;AAAA,cACR;AAAA,aACF;AAAA,UACF;AAAA,SACF;AAAA,MACF,CAAA;AAAA,IACF;AAAA,GACD,CAAA;AACH;;;;"}
1
+ {"version":3,"file":"instrumentDurableObjectStorage.js","sources":["../../../src/instrumentations/instrumentDurableObjectStorage.ts"],"sourcesContent":["import type { DurableObjectStorage } from '@cloudflare/workers-types';\nimport { isThenable, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startSpan } from '@sentry/core';\nimport { storeSpanContext } from '../utils/traceLinks';\n\nconst STORAGE_METHODS_TO_INSTRUMENT = ['get', 'put', 'delete', 'list', 'setAlarm', 'getAlarm', 'deleteAlarm'] as const;\n\ntype StorageMethod = (typeof STORAGE_METHODS_TO_INSTRUMENT)[number];\n\ntype WaitUntil = (promise: Promise<unknown>) => void;\n\n/**\n * Instruments DurableObjectStorage methods with Sentry spans.\n *\n * Wraps the following async methods:\n * - get, put, delete, list (KV API)\n * - setAlarm, getAlarm, deleteAlarm (Alarm API)\n *\n * When setAlarm is called, it also stores the current span context so that when\n * the alarm fires later, it can link back to the trace that called setAlarm.\n *\n * @param storage - The DurableObjectStorage instance to instrument\n * @param waitUntil - Optional waitUntil function to defer span context storage\n * @returns An instrumented DurableObjectStorage instance\n */\nexport function instrumentDurableObjectStorage(\n storage: DurableObjectStorage,\n waitUntil?: WaitUntil,\n): DurableObjectStorage {\n return new Proxy(storage, {\n get(target, prop, _receiver) {\n // Use `target` as the receiver instead of the proxy (`_receiver`).\n // Native workerd getters (e.g., `storage.sql`) validate `this` via\n // internal slots. Passing the proxy as receiver breaks that check,\n // causing \"Illegal invocation: function called with incorrect `this`\n // reference\" errors.\n const original = Reflect.get(target, prop, target);\n\n if (typeof original !== 'function') {\n return original;\n }\n\n const methodName = prop as string;\n if (!STORAGE_METHODS_TO_INSTRUMENT.includes(methodName as StorageMethod)) {\n return (original as (...args: unknown[]) => unknown).bind(target);\n }\n\n return function (this: unknown, ...args: unknown[]) {\n return startSpan(\n {\n // Use underscore naming to match Cloudflare's native instrumentation (e.g., \"durable_object_storage_get\")\n name: `durable_object_storage_${methodName}`,\n op: 'db',\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.db.cloudflare.durable_object',\n 'db.system.name': 'cloudflare.durable_object.storage',\n 'db.operation.name': methodName,\n },\n },\n () => {\n const teardown = async (): Promise<void> => {\n // When setAlarm is called, store the current span context so that when the alarm\n // fires later, it can link back to the trace that called setAlarm.\n // We use the original (uninstrumented) storage (target) to avoid creating a span\n // for this internal operation. The storage is deferred via waitUntil to not block.\n if (methodName === 'setAlarm') {\n storeSpanContext(target, 'alarm');\n }\n };\n\n const result = (original as (...args: unknown[]) => unknown).apply(target, args);\n\n if (!isThenable(result)) {\n waitUntil?.(teardown());\n\n return result;\n }\n\n return result.then(\n res => {\n waitUntil?.(teardown());\n return res;\n },\n e => {\n throw e;\n },\n );\n },\n );\n };\n },\n });\n}\n"],"names":[],"mappings":";;;AAIA,MAAM,6BAAA,GAAgC,CAAC,KAAA,EAAO,KAAA,EAAO,UAAU,MAAA,EAAQ,UAAA,EAAY,YAAY,aAAa,CAAA;AAoBrG,SAAS,8BAAA,CACd,SACA,SAAA,EACsB;AACtB,EAAA,OAAO,IAAI,MAAM,OAAA,EAAS;AAAA,IACxB,GAAA,CAAI,MAAA,EAAQ,IAAA,EAAM,SAAA,EAAW;AAM3B,MAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,GAAA,CAAI,MAAA,EAAQ,MAAM,MAAM,CAAA;AAEjD,MAAA,IAAI,OAAO,aAAa,UAAA,EAAY;AAClC,QAAA,OAAO,QAAA;AAAA,MACT;AAEA,MAAA,MAAM,UAAA,GAAa,IAAA;AACnB,MAAA,IAAI,CAAC,6BAAA,CAA8B,QAAA,CAAS,UAA2B,CAAA,EAAG;AACxE,QAAA,OAAQ,QAAA,CAA6C,KAAK,MAAM,CAAA;AAAA,MAClE;AAEA,MAAA,OAAO,YAA4B,IAAA,EAAiB;AAClD,QAAA,OAAO,SAAA;AAAA,UACL;AAAA;AAAA,YAEE,IAAA,EAAM,0BAA0B,UAAU,CAAA,CAAA;AAAA,YAC1C,EAAA,EAAI,IAAA;AAAA,YACJ,UAAA,EAAY;AAAA,cACV,CAAC,gCAAgC,GAAG,mCAAA;AAAA,cACpC,gBAAA,EAAkB,mCAAA;AAAA,cAClB,mBAAA,EAAqB;AAAA;AACvB,WACF;AAAA,UACA,MAAM;AACJ,YAAA,MAAM,WAAW,YAA2B;AAK1C,cAAA,IAAI,eAAe,UAAA,EAAY;AAC7B,gBAAA,gBAAA,CAAiB,QAAQ,OAAO,CAAA;AAAA,cAClC;AAAA,YACF,CAAA;AAEA,YAAA,MAAM,MAAA,GAAU,QAAA,CAA6C,KAAA,CAAM,MAAA,EAAQ,IAAI,CAAA;AAE/E,YAAA,IAAI,CAAC,UAAA,CAAW,MAAM,CAAA,EAAG;AACvB,cAAA,SAAA,GAAY,UAAU,CAAA;AAEtB,cAAA,OAAO,MAAA;AAAA,YACT;AAEA,YAAA,OAAO,MAAA,CAAO,IAAA;AAAA,cACZ,CAAA,GAAA,KAAO;AACL,gBAAA,SAAA,GAAY,UAAU,CAAA;AACtB,gBAAA,OAAO,GAAA;AAAA,cACT,CAAA;AAAA,cACA,CAAA,CAAA,KAAK;AACH,gBAAA,MAAM,CAAA;AAAA,cACR;AAAA,aACF;AAAA,UACF;AAAA,SACF;AAAA,MACF,CAAA;AAAA,IACF;AAAA,GACD,CAAA;AACH;;;;"}
@@ -98,4 +98,4 @@ function instrumentD1WithSentry(db) {
98
98
  }
99
99
 
100
100
  export { instrumentD1WithSentry };
101
- //# sourceMappingURL=d1.js.map
101
+ //# sourceMappingURL=instrumentD1.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instrumentD1.js","sources":["../../../../src/instrumentations/worker/instrumentD1.ts"],"sourcesContent":["import type { D1Database, D1PreparedStatement, D1Response } from '@cloudflare/workers-types';\nimport type { Span, SpanAttributes, StartSpanOptions } from '@sentry/core';\nimport { addBreadcrumb, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SPAN_STATUS_ERROR, startSpan } from '@sentry/core';\n\n// Patching is based on internal Cloudflare D1 API\n// https://github.com/cloudflare/workerd/blob/cd5279e7b305003f1d9c851e73efa9d67e4b68b2/src/cloudflare/internal/d1-api.ts\n\nconst patchedStatement = new WeakSet<D1PreparedStatement>();\n\n/**\n * Patches the query methods of a Cloudflare D1 prepared statement with Sentry.\n */\nfunction instrumentD1PreparedStatementQueries(statement: D1PreparedStatement, query: string): D1PreparedStatement {\n if (patchedStatement.has(statement)) {\n return statement;\n }\n\n // eslint-disable-next-line @typescript-eslint/unbound-method\n statement.first = new Proxy(statement.first, {\n apply(target, thisArg, args: Parameters<typeof statement.first>) {\n return startSpan(createStartSpanOptions(query, 'first'), async () => {\n const res = await Reflect.apply(target, thisArg, args);\n createD1Breadcrumb(query, 'first');\n return res;\n });\n },\n });\n\n // eslint-disable-next-line @typescript-eslint/unbound-method\n statement.run = new Proxy(statement.run, {\n apply(target, thisArg, args: Parameters<typeof statement.run>) {\n return startSpan(createStartSpanOptions(query, 'run'), async span => {\n const d1Response = await Reflect.apply(target, thisArg, args);\n applyD1ReturnObjectToSpan(span, d1Response);\n createD1Breadcrumb(query, 'run', d1Response);\n return d1Response;\n });\n },\n });\n\n // eslint-disable-next-line @typescript-eslint/unbound-method\n statement.all = new Proxy(statement.all, {\n apply(target, thisArg, args: Parameters<typeof statement.all>) {\n return startSpan(createStartSpanOptions(query, 'all'), async span => {\n const d1Result = await Reflect.apply(target, thisArg, args);\n applyD1ReturnObjectToSpan(span, d1Result);\n createD1Breadcrumb(query, 'all', d1Result);\n return d1Result;\n });\n },\n });\n\n // eslint-disable-next-line @typescript-eslint/unbound-method\n statement.raw = new Proxy(statement.raw, {\n apply(target, thisArg, args: Parameters<typeof statement.raw>) {\n return startSpan(createStartSpanOptions(query, 'raw'), async () => {\n const res = await Reflect.apply(target, thisArg, args);\n createD1Breadcrumb(query, 'raw');\n return res;\n });\n },\n });\n\n patchedStatement.add(statement);\n\n return statement;\n}\n\n/**\n * Instruments a Cloudflare D1 prepared statement with Sentry.\n *\n * This is meant to be used as a top-level call, under the hood it calls `instrumentD1PreparedStatementQueries`\n * to patch the query methods. The reason for this abstraction is to ensure that the `bind` method is also patched.\n */\nfunction instrumentD1PreparedStatement(statement: D1PreparedStatement, query: string): D1PreparedStatement {\n // statement.bind() returns a new instance of D1PreparedStatement, so we have to patch it as well.\n // eslint-disable-next-line @typescript-eslint/unbound-method\n statement.bind = new Proxy(statement.bind, {\n apply(target, thisArg, args: Parameters<typeof statement.bind>) {\n return instrumentD1PreparedStatementQueries(Reflect.apply(target, thisArg, args), query);\n },\n });\n\n return instrumentD1PreparedStatementQueries(statement, query);\n}\n\n/**\n * Add D1Response meta information to a span.\n *\n * See: https://developers.cloudflare.com/d1/build-with-d1/d1-client-api/#return-object\n */\nfunction applyD1ReturnObjectToSpan(span: Span, d1Result: D1Response): void {\n if (!d1Result.success) {\n span.setStatus({ code: SPAN_STATUS_ERROR });\n }\n\n span.setAttributes(getAttributesFromD1Response(d1Result));\n}\n\nfunction getAttributesFromD1Response(d1Result: D1Response): SpanAttributes {\n return {\n 'cloudflare.d1.duration': d1Result.meta.duration,\n 'cloudflare.d1.rows_read': d1Result.meta.rows_read,\n 'cloudflare.d1.rows_written': d1Result.meta.rows_written,\n };\n}\n\nfunction createD1Breadcrumb(query: string, type: 'first' | 'run' | 'all' | 'raw', d1Result?: D1Response): void {\n addBreadcrumb({\n category: 'query',\n message: query,\n data: {\n ...(d1Result ? getAttributesFromD1Response(d1Result) : {}),\n 'cloudflare.d1.query_type': type,\n },\n });\n}\n\nfunction createStartSpanOptions(query: string, type: 'first' | 'run' | 'all' | 'raw'): StartSpanOptions {\n return {\n op: 'db.query',\n name: query,\n attributes: {\n 'cloudflare.d1.query_type': type,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.db.cloudflare.d1',\n },\n };\n}\n\n/**\n * Instruments Cloudflare D1 bindings with Sentry.\n *\n * Currently, only prepared statements are instrumented. `db.exec` and `db.batch` are not instrumented.\n *\n * @example\n *\n * ```js\n * // env.DB is the D1 DB binding configured in your `wrangler.toml`\n * const db = instrumentD1WithSentry(env.DB);\n * // Now you can use the database as usual\n * await db.prepare('SELECT * FROM table WHERE id = ?').bind(1).run();\n * ```\n */\nexport function instrumentD1WithSentry(db: D1Database): D1Database {\n // eslint-disable-next-line @typescript-eslint/unbound-method\n db.prepare = new Proxy(db.prepare, {\n apply(target, thisArg, args: Parameters<typeof db.prepare>) {\n const [query] = args;\n return instrumentD1PreparedStatement(Reflect.apply(target, thisArg, args), query);\n },\n });\n\n return db;\n}\n"],"names":[],"mappings":"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"}
@@ -1 +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,YAAA;AA6CzB,MAAM,sBAAA,IAA0B,CAAC,OAAA,GAAwC,EAAC,KAAqC;AAC7G,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,kBAAA,EAAoB,QAAQ,kBAAA,IAAsB,QAAA;AAAA,IAClD,mBAAmB,OAAA,CAAQ;AAAA,GAC7B;AACF,CAAA,CAAA;AAoBO,MAAM,qBAAA,GAAwB,kBAAkB,sBAAsB;AAM7E,eAAsB,0BAAA,CAA2B,QAAgB,OAAA,EAAiC;AAChG,EAAA,MAAM,WAAA,GAAc,MAAA,CAAO,oBAAA,CAAoD,gBAAgB,CAAA;AAE/F,EAAA,IAAI,CAAC,WAAA,EAAa;AAChB,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,qBAAqB,WAAA,CAAY,kBAAA;AAEvC,EAAA,IAAI,uBAAuB,MAAA,EAAQ;AACjC,IAAA;AAAA,EACF;AAIA,EAAA,IAAI,OAAA,CAAQ,WAAW,KAAA,IAAS,OAAA,CAAQ,WAAW,MAAA,IAAU,OAAA,CAAQ,WAAW,SAAA,EAAW;AACzF,IAAA;AAAA,EACF;AAEA,EAAA,IAAI,WAAA,CAAY,iBAAA,GAAoB,OAAA,CAAQ,GAAA,EAAK,OAAO,CAAA,EAAG;AACzD,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,iBAAiB,iBAAA,EAAkB;AACzC,EAAA,MAAM,8BAAA,CAA+B,OAAA,EAAS,cAAA,EAAgB,kBAAkB,CAAA;AAClF;;;;"}
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 // TODO(v11): Gate incoming request body capture on `dataCollection.httpBodies` (capture only when\n // `'incomingRequest'` is listed) instead of defaulting to `'medium'`.\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,YAAA;AA6CzB,MAAM,sBAAA,IAA0B,CAAC,OAAA,GAAwC,EAAC,KAAqC;AAC7G,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,kBAAA,EAAoB,QAAQ,kBAAA,IAAsB,QAAA;AAAA,IAClD,mBAAmB,OAAA,CAAQ;AAAA,GAC7B;AACF,CAAA,CAAA;AAoBO,MAAM,qBAAA,GAAwB,kBAAkB,sBAAsB;AAM7E,eAAsB,0BAAA,CAA2B,QAAgB,OAAA,EAAiC;AAChG,EAAA,MAAM,WAAA,GAAc,MAAA,CAAO,oBAAA,CAAoD,gBAAgB,CAAA;AAE/F,EAAA,IAAI,CAAC,WAAA,EAAa;AAChB,IAAA;AAAA,EACF;AAIA,EAAA,MAAM,qBAAqB,WAAA,CAAY,kBAAA;AAEvC,EAAA,IAAI,uBAAuB,MAAA,EAAQ;AACjC,IAAA;AAAA,EACF;AAIA,EAAA,IAAI,OAAA,CAAQ,WAAW,KAAA,IAAS,OAAA,CAAQ,WAAW,MAAA,IAAU,OAAA,CAAQ,WAAW,SAAA,EAAW;AACzF,IAAA;AAAA,EACF;AAEA,EAAA,IAAI,WAAA,CAAY,iBAAA,GAAoB,OAAA,CAAQ,GAAA,EAAK,OAAO,CAAA,EAAG;AACzD,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,iBAAiB,iBAAA,EAAkB;AACzC,EAAA,MAAM,8BAAA,CAA+B,OAAA,EAAS,cAAA,EAAgB,kBAAkB,CAAA;AAClF;;;;"}
@@ -1 +1 @@
1
- {"type":"module","version":"10.55.0","sideEffects":false}
1
+ {"type":"module","version":"10.56.0","sideEffects":false}
@@ -5,11 +5,15 @@ import { addCloudResourceContext, addRequest, addCultureContext } from './scope-
5
5
  import { init } from './sdk.js';
6
6
  import { classifyResponseStreaming } from './utils/streaming.js';
7
7
 
8
+ function getRequestErrorMechanismType(context) {
9
+ return context && "storage" in context ? "auto.faas.cloudflare.durable_object" : "auto.http.cloudflare";
10
+ }
8
11
  function wrapRequestHandler(wrapperOptions, handler) {
9
12
  return withIsolationScope(async (isolationScope) => {
10
13
  const { options, request, captureErrors = true } = wrapperOptions;
11
14
  const context = wrapperOptions.context;
12
15
  const waitUntil = context ? getOriginalWaitUntil(context)?.bind(context) : void 0;
16
+ const errorMechanismType = getRequestErrorMechanismType(context);
13
17
  const client = init({ ...options, ctx: context });
14
18
  isolationScope.setClient(client);
15
19
  const urlObject = parseStringToURLObject(request.url);
@@ -26,7 +30,7 @@ function wrapRequestHandler(wrapperOptions, handler) {
26
30
  attributes,
27
31
  httpHeadersToSpanAttributes(
28
32
  winterCGHeadersToDict(request.headers),
29
- getClient()?.getOptions().sendDefaultPii ?? false
33
+ getClient()?.getDataCollectionOptions() ?? false
30
34
  )
31
35
  );
32
36
  attributes[SEMANTIC_ATTRIBUTE_SENTRY_OP] = "http.server";
@@ -43,7 +47,7 @@ function wrapRequestHandler(wrapperOptions, handler) {
43
47
  return await handler();
44
48
  } catch (e) {
45
49
  if (captureErrors) {
46
- captureException(e, { mechanism: { handled: false, type: "auto.http.cloudflare" } });
50
+ captureException(e, { mechanism: { handled: false, type: errorMechanismType } });
47
51
  }
48
52
  throw e;
49
53
  } finally {
@@ -64,7 +68,7 @@ function wrapRequestHandler(wrapperOptions, handler) {
64
68
  } catch (e) {
65
69
  span.end();
66
70
  if (captureErrors) {
67
- captureException(e, { mechanism: { handled: false, type: "auto.http.cloudflare" } });
71
+ captureException(e, { mechanism: { handled: false, type: errorMechanismType } });
68
72
  }
69
73
  waitUntil?.(flushAndDispose(client));
70
74
  throw e;
@@ -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 { captureIncomingRequestBody } from './integrations/httpServer';\nimport type { CloudflareOptions } from './client';\nimport { flushAndDispose, getOriginalWaitUntil } 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 // Use getOriginalWaitUntil to get the un-instrumented waitUntil function.\n // This is crucial to avoid deadlock: the flush lock mechanism wraps waitUntil\n // to track pending tasks. If we use the instrumented version for flushAndDispose,\n // it acquires the lock, then flushAndDispose tries to wait for the same lock,\n // creating a deadlock.\n const waitUntil = context ? getOriginalWaitUntil(context)?.bind(context) : undefined;\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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"}
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, getOriginalWaitUntil } from './flush';\nimport { addCloudResourceContext, addCultureContext, addRequest } from './scope-utils';\nimport { init } from './sdk';\nimport { classifyResponseStreaming } from './utils/streaming';\n\nfunction getRequestErrorMechanismType(context: ExecutionContext | undefined): string {\n // Durable Object fetch handlers use DO state as context (see instrumentDurableObjectWithSentry)\n return context && 'storage' in context ? 'auto.faas.cloudflare.durable_object' : 'auto.http.cloudflare';\n}\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 // Use getOriginalWaitUntil to get the un-instrumented waitUntil function.\n // This is crucial to avoid deadlock: the flush lock mechanism wraps waitUntil\n // to track pending tasks. If we use the instrumented version for flushAndDispose,\n // it acquires the lock, then flushAndDispose tries to wait for the same lock,\n // creating a deadlock.\n const waitUntil = context ? getOriginalWaitUntil(context)?.bind(context) : undefined;\n const errorMechanismType = getRequestErrorMechanismType(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()?.getDataCollectionOptions() ?? 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: errorMechanismType } });\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: errorMechanismType } });\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":";;;;;;;AAqBA,SAAS,6BAA6B,OAAA,EAA+C;AAEnF,EAAA,OAAO,OAAA,IAAW,SAAA,IAAa,OAAA,GAAU,qCAAA,GAAwC,sBAAA;AACnF;AAsBO,SAAS,kBAAA,CACd,gBACA,OAAA,EACmB;AACnB,EAAA,OAAO,kBAAA,CAAmB,OAAM,cAAA,KAAkB;AAChD,IAAA,MAAM,EAAE,OAAA,EAAS,OAAA,EAAS,aAAA,GAAgB,MAAK,GAAI,cAAA;AACnD,IAAA,MAAM,UAAU,cAAA,CAAe,OAAA;AAO/B,IAAA,MAAM,YAAY,OAAA,GAAU,oBAAA,CAAqB,OAAO,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,GAAI,MAAA;AAC3E,IAAA,MAAM,kBAAA,GAAqB,6BAA6B,OAAO,CAAA;AAE/D,IAAA,MAAM,SAAS,IAAA,CAAK,EAAE,GAAG,OAAA,EAAS,GAAA,EAAK,SAAS,CAAA;AAChD,IAAA,cAAA,CAAe,UAAU,MAAM,CAAA;AAE/B,IAAA,MAAM,SAAA,GAAY,sBAAA,CAAuB,OAAA,CAAQ,GAAG,CAAA;AACpD,IAAA,MAAM,CAAC,MAAM,UAAU,CAAA,GAAI,gCAAgC,SAAA,EAAW,QAAA,EAAU,wBAAwB,OAAO,CAAA;AAE/G,IAAA,MAAM,aAAA,GAAgB,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,gBAAgB,CAAA;AAC1D,IAAA,IAAI,aAAA,EAAe;AACjB,MAAA,UAAA,CAAW,wBAAwB,CAAA,GAAI,QAAA,CAAS,aAAA,EAAe,EAAE,CAAA;AAAA,IACnE;AAEA,IAAA,MAAM,eAAA,GAAkB,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,YAAY,CAAA;AACxD,IAAA,IAAI,eAAA,EAAiB;AACnB,MAAA,UAAA,CAAW,qBAAqB,CAAA,GAAI,eAAA;AAAA,IACtC;AAEA,IAAA,MAAA,CAAO,MAAA;AAAA,MACL,UAAA;AAAA,MACA,2BAAA;AAAA,QACE,qBAAA,CAAsB,QAAQ,OAAO,CAAA;AAAA,QACrC,SAAA,EAAU,EAAG,wBAAA,EAAyB,IAAK;AAAA;AAC7C,KACF;AAEA,IAAA,UAAA,CAAW,4BAA4B,CAAA,GAAI,aAAA;AAE3C,IAAA,uBAAA,CAAwB,cAAc,CAAA;AACtC,IAAA,UAAA,CAAW,gBAAgB,OAAO,CAAA;AAClC,IAAA,IAAI,QAAQ,EAAA,EAAI;AACd,MAAA,iBAAA,CAAkB,cAAA,EAAgB,QAAQ,EAAE,CAAA;AAE5C,MAAA,IAAI,OAAO,OAAA,CAAQ,EAAA,CAAG,YAAA,KAAiB,QAAA,EAAU;AAC/C,QAAA,UAAA,CAAW,uBAAuB,CAAA,GAAI,OAAA,CAAQ,EAAA,CAAG,YAAA;AAAA,MACnD;AAAA,IACF;AAGA,IAAA,IAAI,OAAA,CAAQ,MAAA,KAAW,SAAA,IAAa,OAAA,CAAQ,WAAW,MAAA,EAAQ;AAC7D,MAAA,IAAI;AACF,QAAA,OAAO,MAAM,OAAA,EAAQ;AAAA,MACvB,SAAS,CAAA,EAAG;AACV,QAAA,IAAI,aAAA,EAAe;AACjB,UAAA,gBAAA,CAAiB,CAAA,EAAG,EAAE,SAAA,EAAW,EAAE,SAAS,KAAA,EAAO,IAAA,EAAM,kBAAA,EAAmB,EAAG,CAAA;AAAA,QACjF;AACA,QAAA,MAAM,CAAA;AAAA,MACR,CAAA,SAAE;AACA,QAAA,SAAA,GAAY,eAAA,CAAgB,MAAM,CAAC,CAAA;AAAA,MACrC;AAAA,IACF;AAEA,IAAA,IAAI,MAAA,EAAQ;AACV,MAAA,MAAM,0BAAA,CAA2B,QAAQ,OAAO,CAAA;AAAA,IAClD;AAEA,IAAA,OAAO,aAAA;AAAA,MACL,EAAE,WAAA,EAAa,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,cAAc,CAAA,IAAK,EAAA,EAAI,OAAA,EAAS,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,SAAS,CAAA,EAAE;AAAA,MAClG,MAAM;AAMJ,QAAA,OAAO,gBAAgB,EAAE,IAAA,EAAM,UAAA,EAAW,EAAG,OAAM,IAAA,KAAQ;AACzD,UAAA,IAAI,GAAA;AAEJ,UAAA,IAAI;AACF,YAAA,GAAA,GAAM,MAAM,OAAA,EAAQ;AACpB,YAAA,aAAA,CAAc,IAAA,EAAM,IAAI,MAAM,CAAA;AAAA,UAKhC,SAAS,CAAA,EAAG;AACV,YAAA,IAAA,CAAK,GAAA,EAAI;AACT,YAAA,IAAI,aAAA,EAAe;AACjB,cAAA,gBAAA,CAAiB,CAAA,EAAG,EAAE,SAAA,EAAW,EAAE,SAAS,KAAA,EAAO,IAAA,EAAM,kBAAA,EAAmB,EAAG,CAAA;AAAA,YACjF;AACA,YAAA,SAAA,GAAY,eAAA,CAAgB,MAAM,CAAC,CAAA;AACnC,YAAA,MAAM,CAAA;AAAA,UACR;AAGA,UAAA,MAAM,cAAA,GAAiB,0BAA0B,GAAG,CAAA;AAEpD,UAAA,IAAI,cAAA,CAAe,WAAA,IAAe,GAAA,CAAI,IAAA,EAAM;AAC1C,YAAA,IAAI;AACF,cAAA,IAAI,KAAA,GAAQ,KAAA;AAEZ,cAAA,MAAM,cAAc,MAAY;AAC9B,gBAAA,IAAI,KAAA,EAAO;AAEX,gBAAA,KAAA,GAAQ,IAAA;AACR,gBAAA,IAAA,CAAK,GAAA,EAAI;AACT,gBAAA,SAAA,GAAY,eAAA,CAAgB,MAAM,CAAC,CAAA;AAAA,cACrC,CAAA;AAEA,cAAA,MAAM,SAAA,GAAY,IAAI,eAAA,CAAgB;AAAA,gBACpC,KAAA,GAAQ;AAEN,kBAAA,WAAA,EAAY;AAAA,gBACd,CAAA;AAAA,gBACA,MAAA,GAAS;AAIP,kBAAA,WAAA,EAAY;AAAA,gBACd;AAAA,eACD,CAAA;AAED,cAAA,OAAO,IAAI,QAAA,CAAS,GAAA,CAAI,IAAA,CAAK,WAAA,CAAY,SAAS,CAAA,EAAG;AAAA,gBACnD,QAAQ,GAAA,CAAI,MAAA;AAAA,gBACZ,YAAY,GAAA,CAAI,UAAA;AAAA,gBAChB,SAAS,GAAA,CAAI;AAAA,eACd,CAAA;AAAA,YACH,CAAA,CAAA,MAAQ;AACN,cAAA,IAAA,CAAK,GAAA,EAAI;AACT,cAAA,SAAA,GAAY,eAAA,CAAgB,MAAM,CAAC,CAAA;AACnC,cAAA,OAAO,GAAA;AAAA,YACT;AAAA,UACF;AAGA,UAAA,IAAA,CAAK,GAAA,EAAI;AAKT,UAAA,IAAI,GAAA,CAAI,WAAW,GAAA,EAAK;AACtB,YAAA,SAAA,GAAY,MAAA,EAAQ,KAAA,CAAM,GAAI,CAAC,CAAA;AAAA,UACjC,CAAA,MAAO;AACL,YAAA,SAAA,GAAY,eAAA,CAAgB,MAAM,CAAC,CAAA;AAAA,UACrC;AACA,UAAA,OAAO,GAAA;AAAA,QACT,CAAC,CAAA;AAAA,MACH;AAAA,KACF;AAAA,EACF,CAAC,CAAA;AACH;;;;"}
package/build/esm/sdk.js CHANGED
@@ -9,7 +9,7 @@ import { makeCloudflareTransport } from './transport.js';
9
9
  import { defaultStackParser } from './vendor/stacktrace.js';
10
10
 
11
11
  function getDefaultIntegrations(options) {
12
- const sendDefaultPii = options.sendDefaultPii ?? false;
12
+ const cookiesEnabled = options.sendDefaultPii || options.dataCollection?.cookies != null;
13
13
  return [
14
14
  // The Dedupe integration should not be used in workflows because we want to
15
15
  // capture all step failures, even if they are the same error.
@@ -23,8 +23,7 @@ function getDefaultIntegrations(options) {
23
23
  fetchIntegration(),
24
24
  honoIntegration(),
25
25
  httpServerIntegration(),
26
- // TODO(v11): the `include` object should be defined directly in the integration based on `sendDefaultPii`
27
- requestDataIntegration(sendDefaultPii ? void 0 : { include: { cookies: false } }),
26
+ requestDataIntegration(cookiesEnabled ? void 0 : { include: { cookies: false } }),
28
27
  consoleIntegration()
29
28
  ];
30
29
  }
@@ -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 { 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":";;;;;;;;;;AAyBO,SAAS,uBAAuB,OAAA,EAA2C;AAChF,EAAA,MAAM,cAAA,GAAiB,QAAQ,cAAA,IAAkB,KAAA;AACjD,EAAA,OAAO;AAAA;AAAA;AAAA,IAGL,GAAI,QAAQ,YAAA,KAAiB,KAAA,GAAQ,EAAC,GAAI,CAAC,mBAAmB,CAAA;AAAA;AAAA;AAAA,IAG9D,yBAAA,EAA0B;AAAA,IAC1B,2BAAA,EAA4B;AAAA,IAC5B,yBAAA,EAA0B;AAAA,IAC1B,uBAAA,EAAwB;AAAA,IACxB,gBAAA,EAAiB;AAAA,IACjB,eAAA,EAAgB;AAAA,IAChB,qBAAA,EAAsB;AAAA;AAAA,IAEtB,sBAAA,CAAuB,iBAAiB,MAAA,GAAY,EAAE,SAAS,EAAE,OAAA,EAAS,KAAA,EAAM,EAAG,CAAA;AAAA,IACnF,kBAAA;AAAmB,GACrB;AACF;AAKO,SAAS,KAAK,OAAA,EAA0D;AAC7E,EAAA,IAAI,OAAA,CAAQ,wBAAwB,MAAA,EAAW;AAC7C,IAAA,OAAA,CAAQ,mBAAA,GAAsB,uBAAuB,OAAO,CAAA;AAAA,EAC9D;AAEA,EAAA,MAAM,YAAY,OAAA,CAAQ,GAAA,GAAM,aAAA,CAAc,OAAA,CAAQ,GAAG,CAAA,GAAI,MAAA;AAC7D,EAAA,OAAO,OAAA,CAAQ,GAAA;AAEf,EAAA,MAAM,oBAAA,GAAuB,uBAAuB,OAAO,CAAA;AAC3D,EAAA,IAAI,OAAA,CAAQ,cAAA,KAAmB,QAAA,IAAY,CAAC,oBAAA,CAAqB,KAAK,CAAA,CAAA,KAAK,CAAA,CAAE,IAAA,KAAS,eAAe,CAAA,EAAG;AACtG,IAAA,oBAAA,CAAqB,IAAA,CAAK,0BAA0B,CAAA;AAAA,EACtD;AAEA,EAAA,MAAM,aAAA,GAAyC;AAAA,IAC7C,GAAG,OAAA;AAAA,IACH,WAAA,EAAa,iCAAA,CAAkC,OAAA,CAAQ,WAAA,IAAe,kBAAkB,CAAA;AAAA,IACxF,YAAA,EAAc,oBAAA;AAAA,IACd,SAAA,EAAW,QAAQ,SAAA,IAAa,uBAAA;AAAA,IAChC;AAAA,GACF;AASA,EAAA,IAAI,CAAC,QAAQ,sBAAA,EAAwB;AACnC,IAAA,wBAAA,EAAyB;AAAA,EAC3B;AAEA,EAAA,OAAO,WAAA,CAAY,kBAAkB,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 // TODO(v11): Drop this transitional gating and let `requestDataIntegration` rely on the resolved\n // `dataCollection` defaults directly. Until then, preserve the historical Cloudflare behavior of not\n // attaching cookies unless the user explicitly opts in via `sendDefaultPii` or `dataCollection.cookies`.\n const cookiesEnabled = options.sendDefaultPii || options.dataCollection?.cookies != null;\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 requestDataIntegration(cookiesEnabled ? 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":";;;;;;;;;;AAyBO,SAAS,uBAAuB,OAAA,EAA2C;AAIhF,EAAA,MAAM,cAAA,GAAiB,OAAA,CAAQ,cAAA,IAAkB,OAAA,CAAQ,gBAAgB,OAAA,IAAW,IAAA;AACpF,EAAA,OAAO;AAAA;AAAA;AAAA,IAGL,GAAI,QAAQ,YAAA,KAAiB,KAAA,GAAQ,EAAC,GAAI,CAAC,mBAAmB,CAAA;AAAA;AAAA;AAAA,IAG9D,yBAAA,EAA0B;AAAA,IAC1B,2BAAA,EAA4B;AAAA,IAC5B,yBAAA,EAA0B;AAAA,IAC1B,uBAAA,EAAwB;AAAA,IACxB,gBAAA,EAAiB;AAAA,IACjB,eAAA,EAAgB;AAAA,IAChB,qBAAA,EAAsB;AAAA,IACtB,sBAAA,CAAuB,iBAAiB,MAAA,GAAY,EAAE,SAAS,EAAE,OAAA,EAAS,KAAA,EAAM,EAAG,CAAA;AAAA,IACnF,kBAAA;AAAmB,GACrB;AACF;AAKO,SAAS,KAAK,OAAA,EAA0D;AAC7E,EAAA,IAAI,OAAA,CAAQ,wBAAwB,MAAA,EAAW;AAC7C,IAAA,OAAA,CAAQ,mBAAA,GAAsB,uBAAuB,OAAO,CAAA;AAAA,EAC9D;AAEA,EAAA,MAAM,YAAY,OAAA,CAAQ,GAAA,GAAM,aAAA,CAAc,OAAA,CAAQ,GAAG,CAAA,GAAI,MAAA;AAC7D,EAAA,OAAO,OAAA,CAAQ,GAAA;AAEf,EAAA,MAAM,oBAAA,GAAuB,uBAAuB,OAAO,CAAA;AAC3D,EAAA,IAAI,OAAA,CAAQ,cAAA,KAAmB,QAAA,IAAY,CAAC,oBAAA,CAAqB,KAAK,CAAA,CAAA,KAAK,CAAA,CAAE,IAAA,KAAS,eAAe,CAAA,EAAG;AACtG,IAAA,oBAAA,CAAqB,IAAA,CAAK,0BAA0B,CAAA;AAAA,EACtD;AAEA,EAAA,MAAM,aAAA,GAAyC;AAAA,IAC7C,GAAG,OAAA;AAAA,IACH,WAAA,EAAa,iCAAA,CAAkC,OAAA,CAAQ,WAAA,IAAe,kBAAkB,CAAA;AAAA,IACxF,YAAA,EAAc,oBAAA;AAAA,IACd,SAAA,EAAW,QAAQ,SAAA,IAAa,uBAAA;AAAA,IAChC;AAAA,GACF;AASA,EAAA,IAAI,CAAC,QAAQ,sBAAA,EAAwB;AACnC,IAAA,wBAAA,EAAyB;AAAA,EAC3B;AAEA,EAAA,OAAO,WAAA,CAAY,kBAAkB,aAAa,CAAA;AACpD;;;;"}