@sentry/cloudflare 11.0.0-rc.1 → 11.1.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 (115) hide show
  1. package/README.md +8 -200
  2. package/build/cjs/dev/client.js +11 -4
  3. package/build/cjs/dev/client.js.map +1 -1
  4. package/build/cjs/dev/index.js +1 -0
  5. package/build/cjs/dev/index.js.map +1 -1
  6. package/build/cjs/dev/instrumentations/instrumentDurableObjectNamespace.js +3 -0
  7. package/build/cjs/dev/instrumentations/instrumentDurableObjectNamespace.js.map +1 -1
  8. package/build/cjs/dev/instrumentations/worker/instrumentEnv.js +3 -0
  9. package/build/cjs/dev/instrumentations/worker/instrumentEnv.js.map +1 -1
  10. package/build/cjs/dev/instrumentations/worker/instrumentQueueProducer.js +2 -1
  11. package/build/cjs/dev/instrumentations/worker/instrumentQueueProducer.js.map +1 -1
  12. package/build/cjs/dev/request.js +2 -0
  13. package/build/cjs/dev/request.js.map +1 -1
  14. package/build/cjs/dev/transport.js +18 -4
  15. package/build/cjs/dev/transport.js.map +1 -1
  16. package/build/cjs/dev/vite/flueRuntime.js +6 -35
  17. package/build/cjs/dev/vite/flueRuntime.js.map +1 -1
  18. package/build/cjs/dev/vite/mastraObservability.js +6 -30
  19. package/build/cjs/dev/vite/mastraObservability.js.map +1 -1
  20. package/build/cjs/dev/vite/providedModulePlugin.js +53 -0
  21. package/build/cjs/dev/vite/providedModulePlugin.js.map +1 -0
  22. package/build/cjs/dev/vite/transform.js +26 -29
  23. package/build/cjs/dev/vite/transform.js.map +1 -1
  24. package/build/cjs/dev/workflows.js +7 -2
  25. package/build/cjs/dev/workflows.js.map +1 -1
  26. package/build/cjs/prod/client.js +11 -4
  27. package/build/cjs/prod/client.js.map +1 -1
  28. package/build/cjs/prod/index.js +1 -0
  29. package/build/cjs/prod/index.js.map +1 -1
  30. package/build/cjs/prod/instrumentations/instrumentDurableObjectNamespace.js +3 -0
  31. package/build/cjs/prod/instrumentations/instrumentDurableObjectNamespace.js.map +1 -1
  32. package/build/cjs/prod/instrumentations/worker/instrumentEnv.js +3 -0
  33. package/build/cjs/prod/instrumentations/worker/instrumentEnv.js.map +1 -1
  34. package/build/cjs/prod/instrumentations/worker/instrumentQueueProducer.js +2 -1
  35. package/build/cjs/prod/instrumentations/worker/instrumentQueueProducer.js.map +1 -1
  36. package/build/cjs/prod/request.js +2 -0
  37. package/build/cjs/prod/request.js.map +1 -1
  38. package/build/cjs/prod/transport.js +18 -4
  39. package/build/cjs/prod/transport.js.map +1 -1
  40. package/build/cjs/prod/vite/flueRuntime.js +6 -35
  41. package/build/cjs/prod/vite/flueRuntime.js.map +1 -1
  42. package/build/cjs/prod/vite/mastraObservability.js +6 -30
  43. package/build/cjs/prod/vite/mastraObservability.js.map +1 -1
  44. package/build/cjs/prod/vite/providedModulePlugin.js +53 -0
  45. package/build/cjs/prod/vite/providedModulePlugin.js.map +1 -0
  46. package/build/cjs/prod/vite/transform.js +26 -29
  47. package/build/cjs/prod/vite/transform.js.map +1 -1
  48. package/build/cjs/prod/workflows.js +7 -2
  49. package/build/cjs/prod/workflows.js.map +1 -1
  50. package/build/esm/dev/client.js +11 -4
  51. package/build/esm/dev/client.js.map +1 -1
  52. package/build/esm/dev/index.js +1 -1
  53. package/build/esm/dev/instrumentations/instrumentDurableObjectNamespace.js +3 -0
  54. package/build/esm/dev/instrumentations/instrumentDurableObjectNamespace.js.map +1 -1
  55. package/build/esm/dev/instrumentations/worker/instrumentEnv.js +3 -0
  56. package/build/esm/dev/instrumentations/worker/instrumentEnv.js.map +1 -1
  57. package/build/esm/dev/instrumentations/worker/instrumentQueueProducer.js +2 -1
  58. package/build/esm/dev/instrumentations/worker/instrumentQueueProducer.js.map +1 -1
  59. package/build/esm/dev/package.json +1 -1
  60. package/build/esm/dev/request.js +1 -0
  61. package/build/esm/dev/request.js.map +1 -1
  62. package/build/esm/dev/transport.js +19 -5
  63. package/build/esm/dev/transport.js.map +1 -1
  64. package/build/esm/dev/vite/flueRuntime.js +7 -35
  65. package/build/esm/dev/vite/flueRuntime.js.map +1 -1
  66. package/build/esm/dev/vite/mastraObservability.js +7 -30
  67. package/build/esm/dev/vite/mastraObservability.js.map +1 -1
  68. package/build/esm/dev/vite/providedModulePlugin.js +50 -0
  69. package/build/esm/dev/vite/providedModulePlugin.js.map +1 -0
  70. package/build/esm/dev/vite/transform.js +26 -29
  71. package/build/esm/dev/vite/transform.js.map +1 -1
  72. package/build/esm/dev/workflows.js +7 -2
  73. package/build/esm/dev/workflows.js.map +1 -1
  74. package/build/esm/prod/client.js +11 -4
  75. package/build/esm/prod/client.js.map +1 -1
  76. package/build/esm/prod/index.js +1 -1
  77. package/build/esm/prod/instrumentations/instrumentDurableObjectNamespace.js +3 -0
  78. package/build/esm/prod/instrumentations/instrumentDurableObjectNamespace.js.map +1 -1
  79. package/build/esm/prod/instrumentations/worker/instrumentEnv.js +3 -0
  80. package/build/esm/prod/instrumentations/worker/instrumentEnv.js.map +1 -1
  81. package/build/esm/prod/instrumentations/worker/instrumentQueueProducer.js +2 -1
  82. package/build/esm/prod/instrumentations/worker/instrumentQueueProducer.js.map +1 -1
  83. package/build/esm/prod/package.json +1 -1
  84. package/build/esm/prod/request.js +1 -0
  85. package/build/esm/prod/request.js.map +1 -1
  86. package/build/esm/prod/transport.js +19 -5
  87. package/build/esm/prod/transport.js.map +1 -1
  88. package/build/esm/prod/vite/flueRuntime.js +7 -35
  89. package/build/esm/prod/vite/flueRuntime.js.map +1 -1
  90. package/build/esm/prod/vite/mastraObservability.js +7 -30
  91. package/build/esm/prod/vite/mastraObservability.js.map +1 -1
  92. package/build/esm/prod/vite/providedModulePlugin.js +50 -0
  93. package/build/esm/prod/vite/providedModulePlugin.js.map +1 -0
  94. package/build/esm/prod/vite/transform.js +26 -29
  95. package/build/esm/prod/vite/transform.js.map +1 -1
  96. package/build/esm/prod/workflows.js +7 -2
  97. package/build/esm/prod/workflows.js.map +1 -1
  98. package/build/types/client.d.ts +5 -3
  99. package/build/types/client.d.ts.map +1 -1
  100. package/build/types/index.d.ts +1 -1
  101. package/build/types/index.d.ts.map +1 -1
  102. package/build/types/instrumentations/worker/instrumentEnv.d.ts.map +1 -1
  103. package/build/types/instrumentations/worker/instrumentQueueProducer.d.ts.map +1 -1
  104. package/build/types/request.d.ts +1 -0
  105. package/build/types/request.d.ts.map +1 -1
  106. package/build/types/transport.d.ts +5 -0
  107. package/build/types/transport.d.ts.map +1 -1
  108. package/build/types/vite/flueRuntime.d.ts +2 -13
  109. package/build/types/vite/flueRuntime.d.ts.map +1 -1
  110. package/build/types/vite/mastraObservability.d.ts +8 -23
  111. package/build/types/vite/mastraObservability.d.ts.map +1 -1
  112. package/build/types/vite/providedModulePlugin.d.ts +62 -0
  113. package/build/types/vite/providedModulePlugin.d.ts.map +1 -0
  114. package/build/types/workflows.d.ts.map +1 -1
  115. package/package.json +5 -5
package/README.md CHANGED
@@ -10,207 +10,15 @@
10
10
  [![npm dm](https://img.shields.io/npm/dm/@sentry/cloudflare.svg)](https://www.npmjs.com/package/@sentry/cloudflare)
11
11
  [![npm dt](https://img.shields.io/npm/dt/@sentry/cloudflare.svg)](https://www.npmjs.com/package/@sentry/cloudflare)
12
12
 
13
- ## Links
13
+ The official Sentry SDK for monitoring Cloudflare applications.
14
14
 
15
- - [Official SDK Docs](https://docs.sentry.io/quickstart/)
15
+ ## Documentation
16
16
 
17
- ## Install
17
+ - [Getting started](https://docs.sentry.io/platforms/javascript/guides/cloudflare/)
18
+ - [Configuration](https://docs.sentry.io/platforms/javascript/guides/cloudflare/configuration/)
19
+ - [Cloudflare-specific features](https://docs.sentry.io/platforms/javascript/guides/cloudflare/features/)
18
20
 
19
- To get started, first install the `@sentry/cloudflare` package:
21
+ ## Support
20
22
 
21
- ```bash
22
- npm install @sentry/cloudflare
23
- ```
24
-
25
- Then set the `nodejs_compat` compatibility flag in your `wrangler.jsonc`/`wrangler.toml` config. This is because the SDK needs access to Node.js compatibility APIs to work correctly.
26
-
27
- ```jsonc {tabTitle:JSON} {filename:wrangler.jsonc}
28
- {
29
- "compatibility_flags": ["nodejs_compat"],
30
- }
31
- ```
32
-
33
- ```toml {tabTitle:Toml} {filename:wrangler.toml}
34
- compatibility_flags = ["nodejs_compat"]
35
- ```
36
-
37
- ## Setup (Cloudflare Pages)
38
-
39
- To use this SDK, add the `sentryPagesPlugin` as
40
- [middleware to your Cloudflare Pages application](https://developers.cloudflare.com/pages/functions/middleware/).
41
-
42
- We recommend adding a `functions/_middleware.js` for the middleware setup so that Sentry is initialized for your entire
43
- app.
44
-
45
- ```javascript
46
- // functions/_middleware.js
47
- import * as Sentry from '@sentry/cloudflare';
48
-
49
- export const onRequest = Sentry.sentryPagesPlugin({
50
- dsn: process.env.SENTRY_DSN,
51
- // Set tracesSampleRate to 1.0 to capture 100% of spans for tracing.
52
- tracesSampleRate: 1.0,
53
- });
54
- ```
55
-
56
- If you need to to chain multiple middlewares, you can do so by exporting an array of middlewares. Make sure the Sentry
57
- middleware is the first one in the array.
58
-
59
- ```javascript
60
- import * as Sentry from '@sentry/cloudflare';
61
-
62
- export const onRequest = [
63
- // Make sure Sentry is the first middleware
64
- Sentry.sentryPagesPlugin({
65
- dsn: process.env.SENTRY_DSN,
66
- tracesSampleRate: 1.0,
67
- }),
68
- // Add more middlewares here
69
- ];
70
- ```
71
-
72
- If you need to access the `context` object (for example to grab environmental variables), you can pass a function to
73
- `sentryPagesPlugin` that takes the `context` object as an argument and returns `init` options:
74
-
75
- ```javascript
76
- export const onRequest = Sentry.sentryPagesPlugin(context => ({
77
- dsn: context.env.SENTRY_DSN,
78
- tracesSampleRate: 1.0,
79
- }));
80
- ```
81
-
82
- If you do not have access to the `onRequest` middleware API, you can use the `wrapRequestHandler` API instead.
83
-
84
- Here is an example with SvelteKit:
85
-
86
- ```javascript
87
- // hooks.server.js
88
- import * as Sentry from '@sentry/cloudflare';
89
-
90
- export const handle = ({ event, resolve }) => {
91
- const requestHandlerOptions = {
92
- options: {
93
- dsn: event.platform.env.SENTRY_DSN,
94
- tracesSampleRate: 1.0,
95
- },
96
- request: event.request,
97
- context: event.platform.ctx,
98
- };
99
- return Sentry.wrapRequestHandler(requestHandlerOptions, () => resolve(event));
100
- };
101
- ```
102
-
103
- ## Setup (Cloudflare Workers)
104
-
105
- To use this SDK, wrap your handler with the `withSentry` function. This will initialize the SDK and hook into the
106
- environment. Note that you can turn off almost all side effects using the respective options.
107
-
108
- Currently only ESM handlers are supported.
109
-
110
- ```javascript
111
- import * as Sentry from '@sentry/cloudflare';
112
-
113
- export default withSentry(
114
- env => ({
115
- dsn: env.SENTRY_DSN,
116
- // Set tracesSampleRate to 1.0 to capture 100% of spans for tracing.
117
- tracesSampleRate: 1.0,
118
- }),
119
- {
120
- async fetch(request, env, ctx) {
121
- return new Response('Hello World!');
122
- },
123
- } satisfies ExportedHandler<Env>,
124
- );
125
- ```
126
-
127
- ### Sourcemaps
128
-
129
- Configure uploading sourcemaps via the Sentry Wizard:
130
-
131
- ```bash
132
- npx @sentry/wizard@latest -i sourcemaps
133
- ```
134
-
135
- See more details in our [docs](https://docs.sentry.io/platforms/javascript/sourcemaps/).
136
-
137
- ## Usage
138
-
139
- To set context information or send manual events, use the exported functions of `@sentry/cloudflare`. Note that these
140
- functions will require the usage of the Sentry helpers, either `withSentry` function for Cloudflare Workers or the
141
- `sentryPagesPlugin` middleware for Cloudflare Pages.
142
-
143
- ```javascript
144
- import * as Sentry from '@sentry/cloudflare';
145
-
146
- // Set user information, as well as tags and further extras
147
- Sentry.setExtra('battery', 0.7);
148
- Sentry.setTag('user_mode', 'admin');
149
- Sentry.setUser({ id: '4711' });
150
-
151
- // Add a breadcrumb for future events
152
- Sentry.addBreadcrumb({
153
- message: 'My Breadcrumb',
154
- // ...
155
- });
156
-
157
- // Capture exceptions, messages or manual events
158
- Sentry.captureMessage('Hello, world!');
159
- Sentry.captureException(new Error('Good bye'));
160
- Sentry.captureEvent({
161
- message: 'Manual',
162
- stacktrace: [
163
- // ...
164
- ],
165
- });
166
- ```
167
-
168
- ## Cloudflare D1 Instrumentation
169
-
170
- `withSentry()` automatically instruments all [Cloudflare D1](https://developers.cloudflare.com/d1/) bindings on `env`,
171
- Cloudflare's serverless SQL database. Just use the binding as usual:
172
-
173
- ```javascript
174
- // env.DB is the D1 DB binding configured in your `wrangler.toml`
175
- await env.DB.prepare('SELECT * FROM table WHERE id = ?').bind(1).run();
176
- ```
177
-
178
- ## Cron Monitoring (Cloudflare Workers)
179
-
180
- [Sentry Crons](https://docs.sentry.io/product/crons/) allows you to monitor the uptime and performance of any scheduled,
181
- recurring job in your application.
182
-
183
- To instrument your cron triggers, use the `Sentry.withMonitor` API in your
184
- [`Scheduled` handler](https://developers.cloudflare.com/workers/runtime-apis/handlers/scheduled/).
185
-
186
- ```js
187
- export default {
188
- async scheduled(event, env, ctx) {
189
- ctx.waitUntil(
190
- Sentry.withMonitor('your-cron-name', () => {
191
- return doSomeTaskOnASchedule();
192
- }),
193
- );
194
- },
195
- };
196
- ```
197
-
198
- You can also use supply a monitor config to upsert cron monitors with additional metadata:
199
-
200
- ```js
201
- const monitorConfig = {
202
- schedule: {
203
- type: 'crontab',
204
- value: '* * * * *',
205
- },
206
- checkinMargin: 2, // In minutes. Optional.
207
- maxRuntime: 10, // In minutes. Optional.
208
- timezone: 'America/Los_Angeles', // Optional.
209
- };
210
-
211
- export default {
212
- async scheduled(event, env, ctx) {
213
- Sentry.withMonitor('your-cron-name', () => doSomeTaskOnASchedule(), monitorConfig);
214
- },
215
- };
216
- ```
23
+ - [Report a bug](https://github.com/getsentry/sentry-javascript/issues/new/choose)
24
+ - [Contributing](https://github.com/getsentry/sentry-javascript/blob/develop/CONTRIBUTING.md)
@@ -61,16 +61,23 @@ class CloudflareClient extends server.ServerRuntimeClient {
61
61
  }
62
62
  /**
63
63
  * Flushes pending operations and ensures all data is processed.
64
- * If a timeout is provided, the operation will be completed within the specified time limit.
65
64
  *
66
- * It will wait for all pending spans to complete before flushing.
65
+ * Each phase waits at most `timeout`: the flush lock of a per-invocation client, pending spans, event
66
+ * processing and the transport drain. So a flush can take a small multiple of `timeout`, which stays well
67
+ * below Cloudflare's 30 second `waitUntil` limit for the timeouts the SDK uses. Sends still pending when
68
+ * the drain times out are aborted.
67
69
  *
68
- * @param {number} [timeout] - Optional timeout in milliseconds to force the completion of the flush operation.
70
+ * @param {number} [timeout] - Maximum time in milliseconds for each phase of the flush.
69
71
  * @return {Promise<boolean>} A promise that resolves to a boolean indicating whether the flush operation was successful.
70
72
  */
71
73
  async flush(timeout) {
72
74
  if (this._flushLock) {
73
- await this._flushLock.finalize();
75
+ let timer;
76
+ await Promise.race([
77
+ this._flushLock.finalize(),
78
+ ...timeout ? [new Promise((resolve) => timer = setTimeout(resolve, timeout))] : []
79
+ ]);
80
+ clearTimeout(timer);
74
81
  }
75
82
  if (this._pendingSpans.size > 0 && this._spanCompletionPromise) {
76
83
  debugBuild.DEBUG_BUILD && core.debug.log("[CloudflareClient] Waiting for", this._pendingSpans.size, "pending spans to complete...");
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sources":["../../../src/client.ts"],"sourcesContent":["import type { ClientOptions, Options, TracePropagationTargets } from '@sentry/core';\nimport {\n _INTERNAL_clearAiProviderSkips,\n _INTERNAL_flushLogsBuffer,\n _INTERNAL_flushMetricsBuffer,\n applySdkMetadata,\n debug,\n spanIsSampled,\n} from '@sentry/core';\nimport type { ServerRuntimeClientOptions } from '@sentry/core/server';\nimport { ServerRuntimeClient } from '@sentry/core/server';\nimport { DEBUG_BUILD } from './debug-build';\nimport type { ExecutionContextCompat } from './executionContext';\nimport type { makeFlushLock } from './flush';\nimport type { CloudflareTransportOptions } from './transport';\nimport { getInvocationState, getInvocationWaitUntil } from './utils/invocationContext';\n\n/**\n * The Sentry Cloudflare SDK Client.\n *\n * @see CloudflareClientOptions for documentation on configuration options.\n * @see ServerRuntimeClient for usage documentation.\n */\nexport class CloudflareClient extends ServerRuntimeClient {\n private readonly _flushLock: ReturnType<typeof makeFlushLock> | void;\n private _pendingSpans: Set<string> = new Set();\n private _spanCompletionPromise: Promise<void> | null = null;\n private _resolveSpanCompletion: (() => void) | null = null;\n\n private _unsubscribeSpanStart: (() => void) | null = null;\n private _unsubscribeSpanEnd: (() => void) | null = null;\n\n // True while a boundary `flush()` is draining; envelopes created by that drain ride it\n // and must not each start their own eager transport flush.\n private _inBoundaryFlush = false;\n\n /**\n * Whether this client is a cached, cross-invocation client (`cacheClient`).\n * Cached clients are never disposed at an invocation boundary, so their spans/events\n * are delivered eagerly instead of waiting for a per-invocation flush.\n */\n public readonly isCachedClient: boolean;\n\n /**\n * Creates a new Cloudflare SDK instance.\n * @param options Configuration options for this SDK.\n */\n public constructor(options: CloudflareClientOptions) {\n applySdkMetadata(options, 'cloudflare');\n options._metadata = options._metadata || {};\n const { flushLock, ...serverOptions } = options;\n\n const clientOptions: ServerRuntimeClientOptions = {\n ...serverOptions,\n platform: 'javascript',\n // TODO: Grab version information\n runtime: { name: 'cloudflare' },\n // TODO: Add server name\n _flushInterval: 0,\n };\n\n super(clientOptions);\n this._flushLock = flushLock;\n this.isCachedClient = options.cacheClient === true;\n\n // Track span lifecycle to know when to flush. Skipped for cached clients\n // (`cacheClient`): they are never disposed, so spans that end after\n // a flush are still delivered. Per-invocation clients are disposed right after\n // the boundary flush, so the flush must wait for open spans to end otherwise\n // their transaction never gets emitted.\n if (!this.isCachedClient) {\n this._unsubscribeSpanStart = this.on('spanStart', span => {\n const spanId = span.spanContext().spanId;\n DEBUG_BUILD && debug.log('[CloudflareClient] Span started:', spanId);\n\n // Negatively sampled spans never emit spanEnd,\n // so tracking them would cause _pendingSpans to grow unboundedly.\n // We should fix the inconsistent behavior for NonRecordingSpans in the future but\n // for now, we just ignore them.\n if (!spanIsSampled(span)) {\n return;\n }\n\n this._pendingSpans.add(spanId);\n\n if (!this._spanCompletionPromise) {\n this._spanCompletionPromise = new Promise(resolve => {\n this._resolveSpanCompletion = resolve;\n });\n }\n });\n\n this._unsubscribeSpanEnd = this.on('spanEnd', span => {\n const spanId = span.spanContext().spanId;\n DEBUG_BUILD && debug.log('[CloudflareClient] Span ended:', spanId);\n this._pendingSpans.delete(spanId);\n\n // If no more pending spans, resolve the completion promise\n if (this._pendingSpans.size === 0 && this._resolveSpanCompletion) {\n DEBUG_BUILD && debug.log('[CloudflareClient] All spans completed, preparing to flush');\n this._resolveSpanCompletion();\n this._resetSpanCompletionPromise();\n }\n });\n }\n }\n\n /**\n * Flushes pending operations and ensures all data is processed.\n * If a timeout is provided, the operation will be completed within the specified time limit.\n *\n * It will wait for all pending spans to complete before flushing.\n *\n * @param {number} [timeout] - Optional timeout in milliseconds to force the completion of the flush operation.\n * @return {Promise<boolean>} A promise that resolves to a boolean indicating whether the flush operation was successful.\n */\n public async flush(timeout?: number): Promise<boolean> {\n // Wait for user waitUntil-registered work to settle before draining, so events\n // captured in that work are still in the buffer. Without this the final flush\n // can drain (and the client be disposed) before background captures land.\n if (this._flushLock) {\n await this._flushLock.finalize();\n }\n\n if (this._pendingSpans.size > 0 && this._spanCompletionPromise) {\n DEBUG_BUILD &&\n debug.log('[CloudflareClient] Waiting for', this._pendingSpans.size, 'pending spans to complete...');\n\n const timeoutMs = timeout ?? 5000;\n const spanCompletionRace = Promise.race([\n this._spanCompletionPromise,\n new Promise(resolve =>\n setTimeout(() => {\n DEBUG_BUILD &&\n debug.log('[CloudflareClient] Span completion timeout after', timeoutMs, 'ms, flushing anyway');\n resolve(undefined);\n }, timeoutMs),\n ),\n ]);\n\n await spanCompletionRace;\n }\n\n // Envelopes created while this flush drains (log/metric/span buffers turning into\n // envelopes on the `flush` emit) ride its own transport drain; without the flag each\n // of them would also start an eager drain and a `waitUntil` registration.\n this._inBoundaryFlush = true;\n try {\n return await super.flush(timeout);\n } finally {\n this._inBoundaryFlush = false;\n }\n }\n\n /**\n * Disposes of the client and releases all resources.\n *\n * This method clears all Cloudflare-specific state in addition to the base client cleanup.\n * It unsubscribes from span lifecycle events and clears pending span tracking.\n *\n * Call this method after flushing to allow the client to be garbage collected.\n * After calling dispose(), the client should not be used anymore.\n */\n public override dispose(): void {\n DEBUG_BUILD && debug.log('[CloudflareClient] Disposing client...');\n\n super.dispose();\n\n if (this._unsubscribeSpanStart) {\n this._unsubscribeSpanStart();\n this._unsubscribeSpanStart = null;\n }\n if (this._unsubscribeSpanEnd) {\n this._unsubscribeSpanEnd();\n this._unsubscribeSpanEnd = null;\n }\n\n this._resetSpanCompletionPromise();\n (this as unknown as { _flushLock: ReturnType<typeof makeFlushLock> | void })._flushLock = undefined;\n }\n\n /** @inheritDoc */\n protected override _setupIntegrations(): void {\n // Clear AI provider skip registrations before setting up integrations.\n // The registry is module-global and Cloudflare calls `init()` per request, so without this a\n // single `ai` SDK call would suppress direct `env.AI.run` spans for the rest of the isolate's\n // life. Mirrors the same reset in the Node client.\n _INTERNAL_clearAiProviderSkips();\n super._setupIntegrations();\n\n // Registered after the integrations on purpose: the flush-point marker must run\n // after core and the span buffer have turned their buffers into envelopes on the\n // same `flush` emit (so those envelopes ride the boundary drain instead of starting\n // eager ones), and the eager span handler relies on `spanStreamingIntegration`\n // having already buffered the span that triggers it.\n if (this.isCachedClient) {\n // Marks the invocation's natural flush point: anything captured from now on\n // (post-response `waitUntil` work, detached continuations) has no later flush\n // to ride and is delivered eagerly by the hooks below.\n this.on('flush', () => {\n const invocationState = getInvocationState();\n if (invocationState) {\n invocationState.flushPointReached = true;\n }\n });\n this._setupEagerEnvelopeDelivery();\n this._setupEagerBufferDelivery();\n }\n }\n\n /**\n * Resets the span completion promise and resolve function.\n */\n private _resetSpanCompletionPromise(): void {\n this._pendingSpans.clear();\n this._spanCompletionPromise = null;\n this._resolveSpanCompletion = null;\n }\n\n /**\n * Drains the transport after an envelope has been accepted past the invocation's\n * flush point. The transport queues request producers until `flush()`; before the\n * flush point the boundary `flush()` drains everything, but envelopes created after\n * it (post-response `waitUntil` work, detached continuations) have no later flush to\n * ride. The drain is registered with the invocation's `waitUntil` to keep the fetch\n * alive; outside any instrumented invocation it always runs.\n */\n private _setupEagerEnvelopeDelivery(): void {\n this.on('afterEnvelope', () => {\n const transport = this.getTransport();\n const invocationState = getInvocationState();\n\n if (!transport || this._inBoundaryFlush || (invocationState && !invocationState.flushPointReached)) {\n return;\n }\n\n this._registerWithInvocationWaitUntil(transport.flush(2000));\n });\n }\n\n /**\n * Turns spans, logs and metrics captured past the invocation's flush point into\n * envelopes. Before the flush point the boundary `flush()` drains their buffers; after\n * it (or outside any instrumented invocation) nothing else would: the log/metric idle\n * timer is disabled for this runtime (`_flushInterval: 0`) and the span buffer's own 5s\n * timer fires outside any invocation, where the runtime suspends the send. Each capture\n * drains only its own bucket (the ended span's trace, the log buffer, the metric\n * buffer), so a fan-out of concurrent traces stays one envelope per trace. RPC method\n * spans never reach this path: `wrapMethodWithSentry` runs the boundary flush after the\n * method span has ended, so only work past that (e.g. detached continuations) lands here.\n */\n private _setupEagerBufferDelivery(): void {\n this.on('afterSpanEnd', span => {\n // The trace id must come from the span, not the current scope: `continueTrace`\n // writes the propagation context to the *current* scope, so the forked\n // isolation scope's propagation context carries a different trace id and\n // flushing by it silently no-ops.\n const traceId = span.spanContext().traceId;\n this._eagerDrain(() => this.emit('flushTraceSpans', traceId));\n });\n this.on('afterCaptureLog', () => this._eagerDrain(() => _INTERNAL_flushLogsBuffer(this)));\n this.on('afterCaptureMetric', () => this._eagerDrain(() => _INTERNAL_flushMetricsBuffer(this)));\n }\n\n /**\n * Runs `drain` unless the owning invocation has not reached its flush point yet\n * (before it the boundary `flush()` delivers the buffers).\n */\n private _eagerDrain(drain: () => void): void {\n const invocationState = getInvocationState();\n\n if (invocationState && !invocationState.flushPointReached) {\n return;\n }\n\n drain();\n }\n\n /**\n * Attaches a promise to the `waitUntil` of the invocation that owns the current\n * async context, so the runtime keeps the send alive after the response is returned.\n * Outside any instrumented invocation (or where the context has no `waitUntil`,\n * e.g. Astro prerendering) the send is fire-and-forget.\n */\n private _registerWithInvocationWaitUntil(promise: PromiseLike<unknown>): void {\n const invocationState = getInvocationState();\n const waitUntil = invocationState && getInvocationWaitUntil(invocationState);\n if (!waitUntil) {\n return;\n }\n\n try {\n waitUntil(\n Promise.resolve(promise).then(\n () => undefined,\n () => undefined,\n ),\n );\n } catch {\n // The owning invocation already ended; the send races isolate teardown either way.\n }\n }\n}\n\ninterface BaseCloudflareOptions {\n /**\n * @ignore Used internally to disable the deDupeIntegration for workflows.\n * @hidden Used internally to disable the deDupeIntegration for workflows.\n * @default true\n */\n enableDedupe?: boolean;\n\n /**\n * The Cloudflare SDK is not OpenTelemetry native. By default (`false`) it does not set up a tracer\n * provider; spans are emitted via the SDK's own instrumentation and scopes are isolated with\n * AsyncLocalStorage.\n *\n * Set this to `true` to opt into the OpenTelemetry compatibility tracer, which captures spans\n * emitted via `@opentelemetry/api`. Big caveat: it does not handle custom context, always working\n * off the current scope. This is good enough for many, but not all, integrations.\n *\n * @default false\n */\n enableOpenTelemetrySetup?: boolean;\n\n /**\n * The bindings on `env` that outgoing RPC calls propagate trace context to.\n *\n * Strings match a binding name exactly, regular expressions match by pattern. An empty array\n * (the default) propagates to nothing.\n *\n * RPC has no headers to carry trace context, so the SDK appends it as a trailing argument to\n * every RPC method call on a matching binding. Only a Sentry-instrumented receiver strips that\n * argument again. Anywhere else it arrives as a real argument and changes what the method was\n * called with, so list only the bindings whose receiver you know runs Sentry.\n *\n * Propagation over `stub.fetch()` and service binding `fetch()` uses HTTP headers and is not\n * affected by this option.\n *\n * Strings match a binding name exactly, regular expressions match by pattern. Both match\n * case-insensitively.\n *\n * When you build with the Sentry Cloudflare Vite plugin, bindings that resolve to *this* worker\n * (its own Durable Objects, its self service bindings) are added for you, because the plugin\n * instruments those receivers itself. Whatever you list here is added on top of them.\n *\n * @default []\n * @example\n * ```ts\n * // Propagate to `env.ORDERS` and every `env.SVC_*` binding\n * export default Sentry.withSentry(\n * env => ({\n * dsn: env.SENTRY_DSN,\n * rpcTracePropagationBindings: ['ORDERS', /^SVC_/],\n * }),\n * handler,\n * );\n * ```\n */\n rpcTracePropagationBindings?: TracePropagationTargets;\n\n /**\n * Table names that should stay instrumented even though they match the reserved `cf_` prefix used\n * by Durable Object frameworks (`agents`, `partyserver`, ...) for their internal SQLite tables.\n *\n * By default, `exec` queries against `cf_`-prefixed tables are treated as framework noise and no\n * `db.query` span is created for them. If one of your own tables happens to use this prefix, add it\n * here to opt it back into instrumentation. Entries are matched against each table name in the\n * query summary — strings must match exactly, while regular expressions give you prefix/pattern\n * matching.\n *\n * @default []\n * @example\n * ```ts\n * export default Sentry.withSentry(\n * (env) => ({\n * dsn: env.SENTRY_DSN,\n * durableObjectSqlSpanAllowlist: ['cf_my_table', /^cf_reports_/],\n * }),\n * handler,\n * );\n * ```\n */\n durableObjectSqlSpanAllowlist?: Array<string | RegExp>;\n\n /**\n * KV keys that should stay instrumented even though they match a reserved prefix used by Durable\n * Object frameworks (`agents`, `partyserver`, ...) for their internal storage entries.\n *\n * By default, KV reads/writes (`get`, `put`, `delete`, `list`) of `cf_`- or `__ps_`-prefixed keys\n * are treated as framework noise and no `durable_object_storage_*` span is created for them,\n * mirroring how `cf_`-prefixed SQL tables are handled (see {@link durableObjectSqlSpanAllowlist}).\n * If one of your own keys happens to use such a prefix, add it here to opt it back into\n * instrumentation. Strings must match exactly, while regular expressions give you prefix/pattern\n * matching.\n *\n * @default []\n * @example\n * ```ts\n * export default Sentry.withSentry(\n * (env) => ({\n * dsn: env.SENTRY_DSN,\n * durableObjectStorageSpanAllowlist: ['cf_my_key', /^cf_reports_/],\n * }),\n * handler,\n * );\n * ```\n */\n durableObjectStorageSpanAllowlist?: Array<string | RegExp>;\n\n /**\n * Sets an optional server name (device name).\n *\n * This is useful for identifying which server or instance is sending events.\n */\n serverName?: string;\n\n /**\n * If you use Spotlight by Sentry during development, use\n * this option to forward captured Sentry events to Spotlight.\n *\n * Either set it to true, or provide a specific Spotlight Sidecar URL.\n *\n * More details: https://spotlightjs.com/\n *\n * IMPORTANT: Only set this option to `true` while developing, not in production!\n */\n spotlight?: boolean | string;\n\n /**\n * Cache the client and reuse it across invocations within the same isolate.\n *\n * The SDK creates one client per isolate and reuses it for all requests/DO\n * handlers in that isolate. This avoids the per-invocation cost of\n * constructing a new client.\n *\n * Since a cached client outlives any single invocation, data captured after an\n * invocation's flush point (post-response `waitUntil` work, detached background\n * work) is delivered eagerly instead of waiting for a flush that never comes.\n *\n * The first `init()` in an isolate decides the cached client's options; later\n * `init()` calls with different options (e.g. another DSN) reuse the cached client\n * unchanged (a debug warning is logged for a DSN change). Use `cacheClient: false`\n * when options must differ per invocation.\n *\n * When disabled, a new client is created per invocation and disposed after the\n * handler completes.\n *\n * @default true\n */\n cacheClient?: boolean;\n}\n\n/**\n * Configuration options for the Sentry Cloudflare SDK\n *\n * @see @sentry/core Options for more information.\n */\nexport interface CloudflareOptions extends Options<CloudflareTransportOptions>, BaseCloudflareOptions {\n ctx?: ExecutionContextCompat;\n}\n\n/**\n * Configuration options for the Sentry Cloudflare SDK Client class\n *\n * @see CloudflareClient for more information.\n */\nexport interface CloudflareClientOptions extends ClientOptions<CloudflareTransportOptions>, BaseCloudflareOptions {\n flushLock?: ReturnType<typeof makeFlushLock>;\n}\n"],"names":["ServerRuntimeClient","applySdkMetadata","DEBUG_BUILD","debug","spanIsSampled","_INTERNAL_clearAiProviderSkips","getInvocationState","_INTERNAL_flushLogsBuffer","_INTERNAL_flushMetricsBuffer","getInvocationWaitUntil"],"mappings":"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"}
1
+ {"version":3,"file":"client.js","sources":["../../../src/client.ts"],"sourcesContent":["import type { ClientOptions, Options, TracePropagationTargets } from '@sentry/core';\nimport {\n _INTERNAL_clearAiProviderSkips,\n _INTERNAL_flushLogsBuffer,\n _INTERNAL_flushMetricsBuffer,\n applySdkMetadata,\n debug,\n spanIsSampled,\n} from '@sentry/core';\nimport type { ServerRuntimeClientOptions } from '@sentry/core/server';\nimport { ServerRuntimeClient } from '@sentry/core/server';\nimport { DEBUG_BUILD } from './debug-build';\nimport type { ExecutionContextCompat } from './executionContext';\nimport type { makeFlushLock } from './flush';\nimport type { CloudflareTransportOptions } from './transport';\nimport { getInvocationState, getInvocationWaitUntil } from './utils/invocationContext';\n\n/**\n * The Sentry Cloudflare SDK Client.\n *\n * @see CloudflareClientOptions for documentation on configuration options.\n * @see ServerRuntimeClient for usage documentation.\n */\nexport class CloudflareClient extends ServerRuntimeClient {\n private readonly _flushLock: ReturnType<typeof makeFlushLock> | void;\n private _pendingSpans: Set<string> = new Set();\n private _spanCompletionPromise: Promise<void> | null = null;\n private _resolveSpanCompletion: (() => void) | null = null;\n\n private _unsubscribeSpanStart: (() => void) | null = null;\n private _unsubscribeSpanEnd: (() => void) | null = null;\n\n // True while a boundary `flush()` is draining; envelopes created by that drain ride it\n // and must not each start their own eager transport flush.\n private _inBoundaryFlush = false;\n\n /**\n * Whether this client is a cached, cross-invocation client (`cacheClient`).\n * Cached clients are never disposed at an invocation boundary, so their spans/events\n * are delivered eagerly instead of waiting for a per-invocation flush.\n */\n public readonly isCachedClient: boolean;\n\n /**\n * Creates a new Cloudflare SDK instance.\n * @param options Configuration options for this SDK.\n */\n public constructor(options: CloudflareClientOptions) {\n applySdkMetadata(options, 'cloudflare');\n options._metadata = options._metadata || {};\n const { flushLock, ...serverOptions } = options;\n\n const clientOptions: ServerRuntimeClientOptions = {\n ...serverOptions,\n platform: 'javascript',\n // TODO: Grab version information\n runtime: { name: 'cloudflare' },\n // TODO: Add server name\n _flushInterval: 0,\n };\n\n super(clientOptions);\n this._flushLock = flushLock;\n this.isCachedClient = options.cacheClient === true;\n\n // Track span lifecycle to know when to flush. Skipped for cached clients\n // (`cacheClient`): they are never disposed, so spans that end after\n // a flush are still delivered. Per-invocation clients are disposed right after\n // the boundary flush, so the flush must wait for open spans to end otherwise\n // their transaction never gets emitted.\n if (!this.isCachedClient) {\n this._unsubscribeSpanStart = this.on('spanStart', span => {\n const spanId = span.spanContext().spanId;\n DEBUG_BUILD && debug.log('[CloudflareClient] Span started:', spanId);\n\n // Negatively sampled spans never emit spanEnd,\n // so tracking them would cause _pendingSpans to grow unboundedly.\n // We should fix the inconsistent behavior for NonRecordingSpans in the future but\n // for now, we just ignore them.\n if (!spanIsSampled(span)) {\n return;\n }\n\n this._pendingSpans.add(spanId);\n\n if (!this._spanCompletionPromise) {\n this._spanCompletionPromise = new Promise(resolve => {\n this._resolveSpanCompletion = resolve;\n });\n }\n });\n\n this._unsubscribeSpanEnd = this.on('spanEnd', span => {\n const spanId = span.spanContext().spanId;\n DEBUG_BUILD && debug.log('[CloudflareClient] Span ended:', spanId);\n this._pendingSpans.delete(spanId);\n\n // If no more pending spans, resolve the completion promise\n if (this._pendingSpans.size === 0 && this._resolveSpanCompletion) {\n DEBUG_BUILD && debug.log('[CloudflareClient] All spans completed, preparing to flush');\n this._resolveSpanCompletion();\n this._resetSpanCompletionPromise();\n }\n });\n }\n }\n\n /**\n * Flushes pending operations and ensures all data is processed.\n *\n * Each phase waits at most `timeout`: the flush lock of a per-invocation client, pending spans, event\n * processing and the transport drain. So a flush can take a small multiple of `timeout`, which stays well\n * below Cloudflare's 30 second `waitUntil` limit for the timeouts the SDK uses. Sends still pending when\n * the drain times out are aborted.\n *\n * @param {number} [timeout] - Maximum time in milliseconds for each phase of the flush.\n * @return {Promise<boolean>} A promise that resolves to a boolean indicating whether the flush operation was successful.\n */\n public async flush(timeout?: number): Promise<boolean> {\n // Wait for user waitUntil-registered work to settle before draining, so events\n // captured in that work are still in the buffer. Without this the final flush\n // can drain (and the client be disposed) before background captures land.\n //\n // Only per-invocation clients (`cacheClient: false`) have a flush lock; remove this with them in v12.\n // The wait is bounded by `timeout` because a user `waitUntil` task that outlives the invocation\n // would otherwise keep the flush from draining until the runtime cancels it.\n if (this._flushLock) {\n let timer: ReturnType<typeof setTimeout> | undefined;\n await Promise.race([\n this._flushLock.finalize(),\n ...(timeout ? [new Promise<void>(resolve => (timer = setTimeout(resolve, timeout)))] : []),\n ]);\n clearTimeout(timer);\n }\n\n if (this._pendingSpans.size > 0 && this._spanCompletionPromise) {\n DEBUG_BUILD &&\n debug.log('[CloudflareClient] Waiting for', this._pendingSpans.size, 'pending spans to complete...');\n\n const timeoutMs = timeout ?? 5000;\n const spanCompletionRace = Promise.race([\n this._spanCompletionPromise,\n new Promise(resolve =>\n setTimeout(() => {\n DEBUG_BUILD &&\n debug.log('[CloudflareClient] Span completion timeout after', timeoutMs, 'ms, flushing anyway');\n resolve(undefined);\n }, timeoutMs),\n ),\n ]);\n\n await spanCompletionRace;\n }\n\n // Envelopes created while this flush drains (log/metric/span buffers turning into\n // envelopes on the `flush` emit) ride its own transport drain; without the flag each\n // of them would also start an eager drain and a `waitUntil` registration.\n this._inBoundaryFlush = true;\n try {\n return await super.flush(timeout);\n } finally {\n this._inBoundaryFlush = false;\n }\n }\n\n /**\n * Disposes of the client and releases all resources.\n *\n * This method clears all Cloudflare-specific state in addition to the base client cleanup.\n * It unsubscribes from span lifecycle events and clears pending span tracking.\n *\n * Call this method after flushing to allow the client to be garbage collected.\n * After calling dispose(), the client should not be used anymore.\n */\n public override dispose(): void {\n DEBUG_BUILD && debug.log('[CloudflareClient] Disposing client...');\n\n super.dispose();\n\n if (this._unsubscribeSpanStart) {\n this._unsubscribeSpanStart();\n this._unsubscribeSpanStart = null;\n }\n if (this._unsubscribeSpanEnd) {\n this._unsubscribeSpanEnd();\n this._unsubscribeSpanEnd = null;\n }\n\n this._resetSpanCompletionPromise();\n (this as unknown as { _flushLock: ReturnType<typeof makeFlushLock> | void })._flushLock = undefined;\n }\n\n /** @inheritDoc */\n protected override _setupIntegrations(): void {\n // Clear AI provider skip registrations before setting up integrations.\n // The registry is module-global and Cloudflare calls `init()` per request, so without this a\n // single `ai` SDK call would suppress direct `env.AI.run` spans for the rest of the isolate's\n // life. Mirrors the same reset in the Node client.\n _INTERNAL_clearAiProviderSkips();\n super._setupIntegrations();\n\n // Registered after the integrations on purpose: the flush-point marker must run\n // after core and the span buffer have turned their buffers into envelopes on the\n // same `flush` emit (so those envelopes ride the boundary drain instead of starting\n // eager ones), and the eager span handler relies on `spanStreamingIntegration`\n // having already buffered the span that triggers it.\n if (this.isCachedClient) {\n // Marks the invocation's natural flush point: anything captured from now on\n // (post-response `waitUntil` work, detached continuations) has no later flush\n // to ride and is delivered eagerly by the hooks below.\n this.on('flush', () => {\n const invocationState = getInvocationState();\n if (invocationState) {\n invocationState.flushPointReached = true;\n }\n });\n this._setupEagerEnvelopeDelivery();\n this._setupEagerBufferDelivery();\n }\n }\n\n /**\n * Resets the span completion promise and resolve function.\n */\n private _resetSpanCompletionPromise(): void {\n this._pendingSpans.clear();\n this._spanCompletionPromise = null;\n this._resolveSpanCompletion = null;\n }\n\n /**\n * Drains the transport after an envelope has been accepted past the invocation's\n * flush point. The transport queues request producers until `flush()`; before the\n * flush point the boundary `flush()` drains everything, but envelopes created after\n * it (post-response `waitUntil` work, detached continuations) have no later flush to\n * ride. The drain is registered with the invocation's `waitUntil` to keep the fetch\n * alive; outside any instrumented invocation it always runs.\n */\n private _setupEagerEnvelopeDelivery(): void {\n this.on('afterEnvelope', () => {\n const transport = this.getTransport();\n const invocationState = getInvocationState();\n\n if (!transport || this._inBoundaryFlush || (invocationState && !invocationState.flushPointReached)) {\n return;\n }\n\n this._registerWithInvocationWaitUntil(transport.flush(2000));\n });\n }\n\n /**\n * Turns spans, logs and metrics captured past the invocation's flush point into\n * envelopes. Before the flush point the boundary `flush()` drains their buffers; after\n * it (or outside any instrumented invocation) nothing else would: the log/metric idle\n * timer is disabled for this runtime (`_flushInterval: 0`) and the span buffer's own 5s\n * timer fires outside any invocation, where the runtime suspends the send. Each capture\n * drains only its own bucket (the ended span's trace, the log buffer, the metric\n * buffer), so a fan-out of concurrent traces stays one envelope per trace. RPC method\n * spans never reach this path: `wrapMethodWithSentry` runs the boundary flush after the\n * method span has ended, so only work past that (e.g. detached continuations) lands here.\n */\n private _setupEagerBufferDelivery(): void {\n this.on('afterSpanEnd', span => {\n // The trace id must come from the span, not the current scope: `continueTrace`\n // writes the propagation context to the *current* scope, so the forked\n // isolation scope's propagation context carries a different trace id and\n // flushing by it silently no-ops.\n const traceId = span.spanContext().traceId;\n this._eagerDrain(() => this.emit('flushTraceSpans', traceId));\n });\n this.on('afterCaptureLog', () => this._eagerDrain(() => _INTERNAL_flushLogsBuffer(this)));\n this.on('afterCaptureMetric', () => this._eagerDrain(() => _INTERNAL_flushMetricsBuffer(this)));\n }\n\n /**\n * Runs `drain` unless the owning invocation has not reached its flush point yet\n * (before it the boundary `flush()` delivers the buffers).\n */\n private _eagerDrain(drain: () => void): void {\n const invocationState = getInvocationState();\n\n if (invocationState && !invocationState.flushPointReached) {\n return;\n }\n\n drain();\n }\n\n /**\n * Attaches a promise to the `waitUntil` of the invocation that owns the current\n * async context, so the runtime keeps the send alive after the response is returned.\n * Outside any instrumented invocation (or where the context has no `waitUntil`,\n * e.g. Astro prerendering) the send is fire-and-forget.\n */\n private _registerWithInvocationWaitUntil(promise: PromiseLike<unknown>): void {\n const invocationState = getInvocationState();\n const waitUntil = invocationState && getInvocationWaitUntil(invocationState);\n if (!waitUntil) {\n return;\n }\n\n try {\n waitUntil(\n Promise.resolve(promise).then(\n () => undefined,\n () => undefined,\n ),\n );\n } catch {\n // The owning invocation already ended; the send races isolate teardown either way.\n }\n }\n}\n\ninterface BaseCloudflareOptions {\n /**\n * @ignore Used internally to disable the deDupeIntegration for workflows.\n * @hidden Used internally to disable the deDupeIntegration for workflows.\n * @default true\n */\n enableDedupe?: boolean;\n\n /**\n * The Cloudflare SDK is not OpenTelemetry native. By default (`false`) it does not set up a tracer\n * provider; spans are emitted via the SDK's own instrumentation and scopes are isolated with\n * AsyncLocalStorage.\n *\n * Set this to `true` to opt into the OpenTelemetry compatibility tracer, which captures spans\n * emitted via `@opentelemetry/api`. Big caveat: it does not handle custom context, always working\n * off the current scope. This is good enough for many, but not all, integrations.\n *\n * @default false\n */\n enableOpenTelemetrySetup?: boolean;\n\n /**\n * The bindings on `env` that outgoing RPC calls propagate trace context to.\n *\n * Strings match a binding name exactly, regular expressions match by pattern. An empty array\n * (the default) propagates to nothing.\n *\n * RPC has no headers to carry trace context, so the SDK appends it as a trailing argument to\n * every RPC method call on a matching binding. Only a Sentry-instrumented receiver strips that\n * argument again. Anywhere else it arrives as a real argument and changes what the method was\n * called with, so list only the bindings whose receiver you know runs Sentry.\n *\n * Propagation over `stub.fetch()` and service binding `fetch()` uses HTTP headers and is not\n * affected by this option.\n *\n * Strings match a binding name exactly, regular expressions match by pattern. Both match\n * case-insensitively.\n *\n * When you build with the Sentry Cloudflare Vite plugin, bindings that resolve to *this* worker\n * (its own Durable Objects, its self service bindings) are added for you, because the plugin\n * instruments those receivers itself. Whatever you list here is added on top of them.\n *\n * @default []\n * @example\n * ```ts\n * // Propagate to `env.ORDERS` and every `env.SVC_*` binding\n * export default Sentry.withSentry(\n * env => ({\n * dsn: env.SENTRY_DSN,\n * rpcTracePropagationBindings: ['ORDERS', /^SVC_/],\n * }),\n * handler,\n * );\n * ```\n */\n rpcTracePropagationBindings?: TracePropagationTargets;\n\n /**\n * Table names that should stay instrumented even though they match the reserved `cf_` prefix used\n * by Durable Object frameworks (`agents`, `partyserver`, ...) for their internal SQLite tables.\n *\n * By default, `exec` queries against `cf_`-prefixed tables are treated as framework noise and no\n * `db.query` span is created for them. If one of your own tables happens to use this prefix, add it\n * here to opt it back into instrumentation. Entries are matched against each table name in the\n * query summary — strings must match exactly, while regular expressions give you prefix/pattern\n * matching.\n *\n * @default []\n * @example\n * ```ts\n * export default Sentry.withSentry(\n * (env) => ({\n * dsn: env.SENTRY_DSN,\n * durableObjectSqlSpanAllowlist: ['cf_my_table', /^cf_reports_/],\n * }),\n * handler,\n * );\n * ```\n */\n durableObjectSqlSpanAllowlist?: Array<string | RegExp>;\n\n /**\n * KV keys that should stay instrumented even though they match a reserved prefix used by Durable\n * Object frameworks (`agents`, `partyserver`, ...) for their internal storage entries.\n *\n * By default, KV reads/writes (`get`, `put`, `delete`, `list`) of `cf_`- or `__ps_`-prefixed keys\n * are treated as framework noise and no `durable_object_storage_*` span is created for them,\n * mirroring how `cf_`-prefixed SQL tables are handled (see {@link durableObjectSqlSpanAllowlist}).\n * If one of your own keys happens to use such a prefix, add it here to opt it back into\n * instrumentation. Strings must match exactly, while regular expressions give you prefix/pattern\n * matching.\n *\n * @default []\n * @example\n * ```ts\n * export default Sentry.withSentry(\n * (env) => ({\n * dsn: env.SENTRY_DSN,\n * durableObjectStorageSpanAllowlist: ['cf_my_key', /^cf_reports_/],\n * }),\n * handler,\n * );\n * ```\n */\n durableObjectStorageSpanAllowlist?: Array<string | RegExp>;\n\n /**\n * Sets an optional server name (device name).\n *\n * This is useful for identifying which server or instance is sending events.\n */\n serverName?: string;\n\n /**\n * If you use Spotlight by Sentry during development, use\n * this option to forward captured Sentry events to Spotlight.\n *\n * Either set it to true, or provide a specific Spotlight Sidecar URL.\n *\n * More details: https://spotlightjs.com/\n *\n * IMPORTANT: Only set this option to `true` while developing, not in production!\n */\n spotlight?: boolean | string;\n\n /**\n * Cache the client and reuse it across invocations within the same isolate.\n *\n * The SDK creates one client per isolate and reuses it for all requests/DO\n * handlers in that isolate. This avoids the per-invocation cost of\n * constructing a new client.\n *\n * Since a cached client outlives any single invocation, data captured after an\n * invocation's flush point (post-response `waitUntil` work, detached background\n * work) is delivered eagerly instead of waiting for a flush that never comes.\n *\n * The first `init()` in an isolate decides the cached client's options; later\n * `init()` calls with different options (e.g. another DSN) reuse the cached client\n * unchanged (a debug warning is logged for a DSN change). Use `cacheClient: false`\n * when options must differ per invocation.\n *\n * When disabled, a new client is created per invocation and disposed after the\n * handler completes.\n *\n * @default true\n */\n cacheClient?: boolean;\n}\n\n/**\n * Configuration options for the Sentry Cloudflare SDK\n *\n * @see @sentry/core Options for more information.\n */\nexport interface CloudflareOptions extends Options<CloudflareTransportOptions>, BaseCloudflareOptions {\n ctx?: ExecutionContextCompat;\n}\n\n/**\n * Configuration options for the Sentry Cloudflare SDK Client class\n *\n * @see CloudflareClient for more information.\n */\nexport interface CloudflareClientOptions extends ClientOptions<CloudflareTransportOptions>, BaseCloudflareOptions {\n flushLock?: ReturnType<typeof makeFlushLock>;\n}\n"],"names":["ServerRuntimeClient","applySdkMetadata","DEBUG_BUILD","debug","spanIsSampled","_INTERNAL_clearAiProviderSkips","getInvocationState","_INTERNAL_flushLogsBuffer","_INTERNAL_flushMetricsBuffer","getInvocationWaitUntil"],"mappings":"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"}
@@ -113,6 +113,7 @@ exports.instrumentMistralAiClient = serverUtils.instrumentMistralAiClient;
113
113
  exports.instrumentOpenAiClient = serverUtils.instrumentOpenAiClient;
114
114
  exports.instrumentPostgresJsSql = serverUtils.instrumentPostgresJsSql;
115
115
  exports.instrumentStateGraph = serverUtils.instrumentStateGraph;
116
+ exports.instrumentTypeSafeClient = serverUtils.instrumentTypeSafeClient;
116
117
  exports.instrumentWorkersAiClient = serverUtils.instrumentWorkersAiClient;
117
118
  exports.openTelemetryIntegration = serverUtils.openTelemetryIntegration;
118
119
  exports.prismaIntegration = serverUtils.prismaIntegration;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -34,6 +34,9 @@ function instrumentDurableObjectStub(stub, propagateRpcTrace) {
34
34
  if (prop === "fetch" && typeof value === "function") {
35
35
  return instrumentFetcher.instrumentFetcher((...args) => Reflect.apply(value, target, args));
36
36
  }
37
+ if (prop === "connect" && typeof value === "function") {
38
+ return (...args) => Reflect.apply(value, target, args);
39
+ }
37
40
  if (propagateRpcTrace && typeof value === "function" && typeof prop === "string" && !STUB_NON_RPC_METHODS.has(prop)) {
38
41
  return (...args) => Reflect.apply(value, target, rpcMeta.appendRpcMeta(args));
39
42
  }
@@ -1 +1 @@
1
- {"version":3,"file":"instrumentDurableObjectNamespace.js","sources":["../../../../src/instrumentations/instrumentDurableObjectNamespace.ts"],"sourcesContent":["import type { DurableObjectNamespace, DurableObjectStub } from '@cloudflare/workers-types';\nimport { appendRpcMeta } from '../utils/rpcMeta';\nimport { instrumentFetcher } from './worker/instrumentFetcher';\n\n// Built-in DurableObjectStub methods that are not RPC calls.\nexport const STUB_NON_RPC_METHODS = new Set(['fetch', 'connect', 'dup']);\n\n/**\n * Instruments a DurableObjectNamespace binding to create spans for DO interactions.\n *\n * Wraps:\n * - `namespace.get(id)` / `namespace.getByName(name)` with a span + instruments returned stub\n * - `namespace.jurisdiction(name)` by instrumenting the returned namespace the same way\n * - `namespace.idFromName(name)` / `namespace.idFromString(id)` / `namespace.newUniqueId()` with breadcrumbs\n *\n * @param namespace - The DurableObjectNamespace to instrument\n * @param propagateRpcTrace - Whether RPC method calls on the returned stubs carry trace context\n */\nexport function instrumentDurableObjectNamespace(\n namespace: DurableObjectNamespace,\n propagateRpcTrace = false,\n): DurableObjectNamespace {\n return new Proxy(namespace, {\n get(target, prop, _receiver) {\n const value = Reflect.get(target, prop) as unknown;\n\n if (typeof value !== 'function') {\n return value;\n }\n\n if (prop === 'get' || prop === 'getByName') {\n return function (this: unknown, ...args: unknown[]) {\n const stub = Reflect.apply(value, target, args);\n\n return instrumentDurableObjectStub(stub, propagateRpcTrace);\n };\n }\n\n if (prop === 'jurisdiction') {\n return function (this: unknown, ...args: unknown[]) {\n const subnamespace = Reflect.apply(value, target, args);\n\n return instrumentDurableObjectNamespace(subnamespace, propagateRpcTrace);\n };\n }\n\n return value.bind(target);\n },\n });\n}\n\n/**\n * Instruments a DurableObjectStub to create spans for outgoing fetch calls\n * and propagate trace context across RPC calls.\n *\n * @param stub - The DurableObjectStub to instrument\n * @param propagateRpcTrace - Whether RPC method calls carry trace context\n */\nfunction instrumentDurableObjectStub(stub: DurableObjectStub, propagateRpcTrace: boolean): DurableObjectStub {\n return new Proxy(stub, {\n get(target, prop) {\n const value = Reflect.get(target, prop);\n\n if (prop === 'fetch' && typeof value === 'function') {\n return instrumentFetcher((...args) => Reflect.apply(value, target, args));\n }\n\n if (\n propagateRpcTrace &&\n typeof value === 'function' &&\n typeof prop === 'string' &&\n !STUB_NON_RPC_METHODS.has(prop)\n ) {\n return (...args: unknown[]) => Reflect.apply(value, target, appendRpcMeta(args));\n }\n\n return value;\n },\n });\n}\n"],"names":["instrumentFetcher","appendRpcMeta"],"mappings":";;;;;AAKO,MAAM,uCAAuB,IAAI,GAAA,CAAI,CAAC,OAAA,EAAS,SAAA,EAAW,KAAK,CAAC;AAahE,SAAS,gCAAA,CACd,SAAA,EACA,iBAAA,GAAoB,KAAA,EACI;AACxB,EAAA,OAAO,IAAI,MAAM,SAAA,EAAW;AAAA,IAC1B,GAAA,CAAI,MAAA,EAAQ,IAAA,EAAM,SAAA,EAAW;AAC3B,MAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,GAAA,CAAI,MAAA,EAAQ,IAAI,CAAA;AAEtC,MAAA,IAAI,OAAO,UAAU,UAAA,EAAY;AAC/B,QAAA,OAAO,KAAA;AAAA,MACT;AAEA,MAAA,IAAI,IAAA,KAAS,KAAA,IAAS,IAAA,KAAS,WAAA,EAAa;AAC1C,QAAA,OAAO,YAA4B,IAAA,EAAiB;AAClD,UAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,KAAA,CAAM,KAAA,EAAO,QAAQ,IAAI,CAAA;AAE9C,UAAA,OAAO,2BAAA,CAA4B,MAAM,iBAAiB,CAAA;AAAA,QAC5D,CAAA;AAAA,MACF;AAEA,MAAA,IAAI,SAAS,cAAA,EAAgB;AAC3B,QAAA,OAAO,YAA4B,IAAA,EAAiB;AAClD,UAAA,MAAM,YAAA,GAAe,OAAA,CAAQ,KAAA,CAAM,KAAA,EAAO,QAAQ,IAAI,CAAA;AAEtD,UAAA,OAAO,gCAAA,CAAiC,cAAc,iBAAiB,CAAA;AAAA,QACzE,CAAA;AAAA,MACF;AAEA,MAAA,OAAO,KAAA,CAAM,KAAK,MAAM,CAAA;AAAA,IAC1B;AAAA,GACD,CAAA;AACH;AASA,SAAS,2BAAA,CAA4B,MAAyB,iBAAA,EAA+C;AAC3G,EAAA,OAAO,IAAI,MAAM,IAAA,EAAM;AAAA,IACrB,GAAA,CAAI,QAAQ,IAAA,EAAM;AAChB,MAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,GAAA,CAAI,MAAA,EAAQ,IAAI,CAAA;AAEtC,MAAA,IAAI,IAAA,KAAS,OAAA,IAAW,OAAO,KAAA,KAAU,UAAA,EAAY;AACnD,QAAA,OAAOA,mCAAA,CAAkB,IAAI,IAAA,KAAS,OAAA,CAAQ,MAAM,KAAA,EAAO,MAAA,EAAQ,IAAI,CAAC,CAAA;AAAA,MAC1E;AAEA,MAAA,IACE,iBAAA,IACA,OAAO,KAAA,KAAU,UAAA,IACjB,OAAO,IAAA,KAAS,QAAA,IAChB,CAAC,oBAAA,CAAqB,GAAA,CAAI,IAAI,CAAA,EAC9B;AACA,QAAA,OAAO,CAAA,GAAI,SAAoB,OAAA,CAAQ,KAAA,CAAM,OAAO,MAAA,EAAQC,qBAAA,CAAc,IAAI,CAAC,CAAA;AAAA,MACjF;AAEA,MAAA,OAAO,KAAA;AAAA,IACT;AAAA,GACD,CAAA;AACH;;;;;"}
1
+ {"version":3,"file":"instrumentDurableObjectNamespace.js","sources":["../../../../src/instrumentations/instrumentDurableObjectNamespace.ts"],"sourcesContent":["import type { DurableObjectNamespace, DurableObjectStub } from '@cloudflare/workers-types';\nimport { appendRpcMeta } from '../utils/rpcMeta';\nimport { instrumentFetcher } from './worker/instrumentFetcher';\n\n// Built-in DurableObjectStub methods that are not RPC calls.\nexport const STUB_NON_RPC_METHODS = new Set(['fetch', 'connect', 'dup']);\n\n/**\n * Instruments a DurableObjectNamespace binding to create spans for DO interactions.\n *\n * Wraps:\n * - `namespace.get(id)` / `namespace.getByName(name)` with a span + instruments returned stub\n * - `namespace.jurisdiction(name)` by instrumenting the returned namespace the same way\n * - `namespace.idFromName(name)` / `namespace.idFromString(id)` / `namespace.newUniqueId()` with breadcrumbs\n *\n * @param namespace - The DurableObjectNamespace to instrument\n * @param propagateRpcTrace - Whether RPC method calls on the returned stubs carry trace context\n */\nexport function instrumentDurableObjectNamespace(\n namespace: DurableObjectNamespace,\n propagateRpcTrace = false,\n): DurableObjectNamespace {\n return new Proxy(namespace, {\n get(target, prop, _receiver) {\n const value = Reflect.get(target, prop) as unknown;\n\n if (typeof value !== 'function') {\n return value;\n }\n\n if (prop === 'get' || prop === 'getByName') {\n return function (this: unknown, ...args: unknown[]) {\n const stub = Reflect.apply(value, target, args);\n\n return instrumentDurableObjectStub(stub, propagateRpcTrace);\n };\n }\n\n if (prop === 'jurisdiction') {\n return function (this: unknown, ...args: unknown[]) {\n const subnamespace = Reflect.apply(value, target, args);\n\n return instrumentDurableObjectNamespace(subnamespace, propagateRpcTrace);\n };\n }\n\n return value.bind(target);\n },\n });\n}\n\n/**\n * Instruments a DurableObjectStub to create spans for outgoing fetch calls\n * and propagate trace context across RPC calls.\n *\n * @param stub - The DurableObjectStub to instrument\n * @param propagateRpcTrace - Whether RPC method calls carry trace context\n */\nfunction instrumentDurableObjectStub(stub: DurableObjectStub, propagateRpcTrace: boolean): DurableObjectStub {\n return new Proxy(stub, {\n get(target, prop) {\n const value = Reflect.get(target, prop);\n\n if (prop === 'fetch' && typeof value === 'function') {\n return instrumentFetcher((...args) => Reflect.apply(value, target, args));\n }\n\n if (prop === 'connect' && typeof value === 'function') {\n return (...args: unknown[]) => Reflect.apply(value, target, args);\n }\n\n if (\n propagateRpcTrace &&\n typeof value === 'function' &&\n typeof prop === 'string' &&\n !STUB_NON_RPC_METHODS.has(prop)\n ) {\n return (...args: unknown[]) => Reflect.apply(value, target, appendRpcMeta(args));\n }\n\n return value;\n },\n });\n}\n"],"names":["instrumentFetcher","appendRpcMeta"],"mappings":";;;;;AAKO,MAAM,uCAAuB,IAAI,GAAA,CAAI,CAAC,OAAA,EAAS,SAAA,EAAW,KAAK,CAAC;AAahE,SAAS,gCAAA,CACd,SAAA,EACA,iBAAA,GAAoB,KAAA,EACI;AACxB,EAAA,OAAO,IAAI,MAAM,SAAA,EAAW;AAAA,IAC1B,GAAA,CAAI,MAAA,EAAQ,IAAA,EAAM,SAAA,EAAW;AAC3B,MAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,GAAA,CAAI,MAAA,EAAQ,IAAI,CAAA;AAEtC,MAAA,IAAI,OAAO,UAAU,UAAA,EAAY;AAC/B,QAAA,OAAO,KAAA;AAAA,MACT;AAEA,MAAA,IAAI,IAAA,KAAS,KAAA,IAAS,IAAA,KAAS,WAAA,EAAa;AAC1C,QAAA,OAAO,YAA4B,IAAA,EAAiB;AAClD,UAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,KAAA,CAAM,KAAA,EAAO,QAAQ,IAAI,CAAA;AAE9C,UAAA,OAAO,2BAAA,CAA4B,MAAM,iBAAiB,CAAA;AAAA,QAC5D,CAAA;AAAA,MACF;AAEA,MAAA,IAAI,SAAS,cAAA,EAAgB;AAC3B,QAAA,OAAO,YAA4B,IAAA,EAAiB;AAClD,UAAA,MAAM,YAAA,GAAe,OAAA,CAAQ,KAAA,CAAM,KAAA,EAAO,QAAQ,IAAI,CAAA;AAEtD,UAAA,OAAO,gCAAA,CAAiC,cAAc,iBAAiB,CAAA;AAAA,QACzE,CAAA;AAAA,MACF;AAEA,MAAA,OAAO,KAAA,CAAM,KAAK,MAAM,CAAA;AAAA,IAC1B;AAAA,GACD,CAAA;AACH;AASA,SAAS,2BAAA,CAA4B,MAAyB,iBAAA,EAA+C;AAC3G,EAAA,OAAO,IAAI,MAAM,IAAA,EAAM;AAAA,IACrB,GAAA,CAAI,QAAQ,IAAA,EAAM;AAChB,MAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,GAAA,CAAI,MAAA,EAAQ,IAAI,CAAA;AAEtC,MAAA,IAAI,IAAA,KAAS,OAAA,IAAW,OAAO,KAAA,KAAU,UAAA,EAAY;AACnD,QAAA,OAAOA,mCAAA,CAAkB,IAAI,IAAA,KAAS,OAAA,CAAQ,MAAM,KAAA,EAAO,MAAA,EAAQ,IAAI,CAAC,CAAA;AAAA,MAC1E;AAEA,MAAA,IAAI,IAAA,KAAS,SAAA,IAAa,OAAO,KAAA,KAAU,UAAA,EAAY;AACrD,QAAA,OAAO,IAAI,IAAA,KAAoB,OAAA,CAAQ,KAAA,CAAM,KAAA,EAAO,QAAQ,IAAI,CAAA;AAAA,MAClE;AAEA,MAAA,IACE,iBAAA,IACA,OAAO,KAAA,KAAU,UAAA,IACjB,OAAO,IAAA,KAAS,QAAA,IAChB,CAAC,oBAAA,CAAqB,GAAA,CAAI,IAAI,CAAA,EAC9B;AACA,QAAA,OAAO,CAAA,GAAI,SAAoB,OAAA,CAAQ,KAAA,CAAM,OAAO,MAAA,EAAQC,qBAAA,CAAc,IAAI,CAAC,CAAA;AAAA,MACjF;AAEA,MAAA,OAAO,KAAA;AAAA,IACT;AAAA,GACD,CAAA;AACH;;;;;"}
@@ -65,6 +65,9 @@ function instrumentEnv(env, options) {
65
65
  if (p === "fetch" && typeof value === "function") {
66
66
  return instrumentFetcher.instrumentFetcher((...args) => Reflect.apply(value, target2, args));
67
67
  }
68
+ if (p === "connect" && typeof value === "function") {
69
+ return (...args) => Reflect.apply(value, target2, args);
70
+ }
68
71
  if (propagateRpcTrace && typeof value === "function" && typeof p === "string" && !instrumentDurableObjectNamespace.STUB_NON_RPC_METHODS.has(p)) {
69
72
  return (...args) => Reflect.apply(value, target2, rpcMeta.appendRpcMeta(args));
70
73
  }
@@ -1 +1 @@
1
- {"version":3,"file":"instrumentEnv.js","sources":["../../../../../src/instrumentations/worker/instrumentEnv.ts"],"sourcesContent":["import { isObjectLike } from '@sentry/core';\nimport { instrumentWorkersAiClient } from '@sentry/server-utils';\nimport type { CloudflareOptions } from '../../client';\nimport {\n isAiBinding,\n isD1Database,\n isDurableObjectNamespace,\n isJSRPC,\n isQueue,\n isR2Bucket,\n} from '../../utils/isBinding';\nimport { instrumentD1 } from './instrumentD1';\nimport { appendRpcMeta } from '../../utils/rpcMeta';\nimport { createRpcPropagationResolver } from '../../utils/rpcPropagation';\nimport { instrumentDurableObjectNamespace, STUB_NON_RPC_METHODS } from '../instrumentDurableObjectNamespace';\nimport { instrumentFetcher } from './instrumentFetcher';\nimport { instrumentQueueProducer } from './instrumentQueueProducer';\nimport { instrumentR2Bucket } from './instrumentR2';\n\nfunction isProxyable(item: unknown): item is object {\n return isObjectLike(item) || typeof item === 'function';\n}\n\nconst instrumentedBindings = new WeakMap<object, unknown>();\n\n/**\n * Wraps the Cloudflare `env` object in a Proxy that detects binding types\n * on property access and returns instrumented versions.\n *\n * Currently detects:\n * - DurableObjectNamespace (via `idFromName` duck-typing)\n * - Service bindings / JSRPC proxies\n * - Queue producers (via `send` + `sendBatch` duck-typing)\n * - R2 Buckets (via `head` + `put` + `createMultipartUpload` duck-typing)\n * - Workers AI (via `run` + `gateway` + `toMarkdown` duck-typing)\n *\n * @param env - The Cloudflare env object to instrument\n * @param options - Optional CloudflareOptions to control RPC trace propagation\n */\nexport function instrumentEnv<Env extends Record<string, unknown>>(env: Env, options?: CloudflareOptions): Env {\n if (!env || typeof env !== 'object') {\n return env;\n }\n\n const shouldPropagateRpcTrace = createRpcPropagationResolver(options);\n\n return new Proxy(env, {\n get(target, prop, receiver) {\n const item = Reflect.get(target, prop, receiver);\n\n if (!isProxyable(item)) {\n return item;\n }\n\n const cached = instrumentedBindings.get(item);\n\n if (cached) {\n return cached;\n }\n\n if (isD1Database(item)) {\n const instrumented = instrumentD1(item);\n instrumentedBindings.set(item, instrumented);\n return instrumented;\n }\n\n if (isQueue(item)) {\n const bindingName = typeof prop === 'string' ? prop : String(prop);\n const instrumented = instrumentQueueProducer(item, bindingName);\n instrumentedBindings.set(item, instrumented);\n return instrumented;\n }\n\n if (isR2Bucket(item)) {\n const bindingName = typeof prop === 'string' ? prop : String(prop);\n const instrumented = instrumentR2Bucket(item, bindingName);\n instrumentedBindings.set(item, instrumented);\n return instrumented;\n }\n\n if (isAiBinding(item)) {\n const instrumented = instrumentWorkersAiClient(item);\n instrumentedBindings.set(item, instrumented);\n return instrumented;\n }\n\n const propagateRpcTrace = shouldPropagateRpcTrace(String(prop));\n\n if (isDurableObjectNamespace(item)) {\n const instrumented = instrumentDurableObjectNamespace(item, propagateRpcTrace);\n instrumentedBindings.set(item, instrumented);\n return instrumented;\n }\n\n if (isJSRPC(item)) {\n const instrumented = new Proxy(item, {\n get(target, p) {\n const value = Reflect.get(target, p);\n\n if (p === 'fetch' && typeof value === 'function') {\n return instrumentFetcher((...args) => Reflect.apply(value, target, args));\n }\n\n if (\n propagateRpcTrace &&\n typeof value === 'function' &&\n typeof p === 'string' &&\n !STUB_NON_RPC_METHODS.has(p)\n ) {\n return (...args: unknown[]) => Reflect.apply(value, target, appendRpcMeta(args));\n }\n\n return value;\n },\n });\n\n instrumentedBindings.set(item, instrumented);\n return instrumented;\n }\n\n return item;\n },\n });\n}\n"],"names":["isObjectLike","createRpcPropagationResolver","isD1Database","instrumentD1","isQueue","instrumentQueueProducer","isR2Bucket","instrumentR2Bucket","isAiBinding","instrumentWorkersAiClient","isDurableObjectNamespace","instrumentDurableObjectNamespace","isJSRPC","target","instrumentFetcher","STUB_NON_RPC_METHODS","appendRpcMeta"],"mappings":"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"}
1
+ {"version":3,"file":"instrumentEnv.js","sources":["../../../../../src/instrumentations/worker/instrumentEnv.ts"],"sourcesContent":["import { isObjectLike } from '@sentry/core';\nimport { instrumentWorkersAiClient } from '@sentry/server-utils';\nimport type { CloudflareOptions } from '../../client';\nimport {\n isAiBinding,\n isD1Database,\n isDurableObjectNamespace,\n isJSRPC,\n isQueue,\n isR2Bucket,\n} from '../../utils/isBinding';\nimport { instrumentD1 } from './instrumentD1';\nimport { appendRpcMeta } from '../../utils/rpcMeta';\nimport { createRpcPropagationResolver } from '../../utils/rpcPropagation';\nimport { instrumentDurableObjectNamespace, STUB_NON_RPC_METHODS } from '../instrumentDurableObjectNamespace';\nimport { instrumentFetcher } from './instrumentFetcher';\nimport { instrumentQueueProducer } from './instrumentQueueProducer';\nimport { instrumentR2Bucket } from './instrumentR2';\n\nfunction isProxyable(item: unknown): item is object {\n return isObjectLike(item) || typeof item === 'function';\n}\n\nconst instrumentedBindings = new WeakMap<object, unknown>();\n\n/**\n * Wraps the Cloudflare `env` object in a Proxy that detects binding types\n * on property access and returns instrumented versions.\n *\n * Currently detects:\n * - DurableObjectNamespace (via `idFromName` duck-typing)\n * - Service bindings / JSRPC proxies\n * - Queue producers (via `send` + `sendBatch` duck-typing)\n * - R2 Buckets (via `head` + `put` + `createMultipartUpload` duck-typing)\n * - Workers AI (via `run` + `gateway` + `toMarkdown` duck-typing)\n *\n * @param env - The Cloudflare env object to instrument\n * @param options - Optional CloudflareOptions to control RPC trace propagation\n */\nexport function instrumentEnv<Env extends Record<string, unknown>>(env: Env, options?: CloudflareOptions): Env {\n if (!env || typeof env !== 'object') {\n return env;\n }\n\n const shouldPropagateRpcTrace = createRpcPropagationResolver(options);\n\n return new Proxy(env, {\n get(target, prop, receiver) {\n const item = Reflect.get(target, prop, receiver);\n\n if (!isProxyable(item)) {\n return item;\n }\n\n const cached = instrumentedBindings.get(item);\n\n if (cached) {\n return cached;\n }\n\n if (isD1Database(item)) {\n const instrumented = instrumentD1(item);\n instrumentedBindings.set(item, instrumented);\n return instrumented;\n }\n\n if (isQueue(item)) {\n const bindingName = typeof prop === 'string' ? prop : String(prop);\n const instrumented = instrumentQueueProducer(item, bindingName);\n instrumentedBindings.set(item, instrumented);\n return instrumented;\n }\n\n if (isR2Bucket(item)) {\n const bindingName = typeof prop === 'string' ? prop : String(prop);\n const instrumented = instrumentR2Bucket(item, bindingName);\n instrumentedBindings.set(item, instrumented);\n return instrumented;\n }\n\n if (isAiBinding(item)) {\n const instrumented = instrumentWorkersAiClient(item);\n instrumentedBindings.set(item, instrumented);\n return instrumented;\n }\n\n const propagateRpcTrace = shouldPropagateRpcTrace(String(prop));\n\n if (isDurableObjectNamespace(item)) {\n const instrumented = instrumentDurableObjectNamespace(item, propagateRpcTrace);\n instrumentedBindings.set(item, instrumented);\n return instrumented;\n }\n\n if (isJSRPC(item)) {\n const instrumented = new Proxy(item, {\n get(target, p) {\n const value = Reflect.get(target, p);\n\n if (p === 'fetch' && typeof value === 'function') {\n return instrumentFetcher((...args) => Reflect.apply(value, target, args));\n }\n\n if (p === 'connect' && typeof value === 'function') {\n return (...args: unknown[]) => Reflect.apply(value, target, args);\n }\n\n if (\n propagateRpcTrace &&\n typeof value === 'function' &&\n typeof p === 'string' &&\n !STUB_NON_RPC_METHODS.has(p)\n ) {\n return (...args: unknown[]) => Reflect.apply(value, target, appendRpcMeta(args));\n }\n\n return value;\n },\n });\n\n instrumentedBindings.set(item, instrumented);\n return instrumented;\n }\n\n return item;\n },\n });\n}\n"],"names":["isObjectLike","createRpcPropagationResolver","isD1Database","instrumentD1","isQueue","instrumentQueueProducer","isR2Bucket","instrumentR2Bucket","isAiBinding","instrumentWorkersAiClient","isDurableObjectNamespace","instrumentDurableObjectNamespace","isJSRPC","target","instrumentFetcher","STUB_NON_RPC_METHODS","appendRpcMeta"],"mappings":"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"}
@@ -72,7 +72,8 @@ function instrumentQueueProducer(queue, bindingName) {
72
72
  );
73
73
  };
74
74
  }
75
- return Reflect.get(target, prop, receiver);
75
+ const value = Reflect.get(target, prop);
76
+ return typeof value === "function" ? value.bind(target) : value;
76
77
  }
77
78
  });
78
79
  }
@@ -1 +1 @@
1
- {"version":3,"file":"instrumentQueueProducer.js","sources":["../../../../../src/instrumentations/worker/instrumentQueueProducer.ts"],"sourcesContent":["import type { MessageSendRequest, Queue, QueueSendBatchOptions, QueueSendOptions } from '@cloudflare/workers-types';\nimport { SENTRY_OP } from '@sentry/conventions/attributes';\nimport { QUEUE_PUBLISH } from '@sentry/conventions/op';\nimport { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startSpan } from '@sentry/core';\n\nconst ORIGIN = 'auto.faas.cloudflare.queue';\n\nfunction startPublishSpan<T>(\n options: {\n bindingName: string;\n bodySize: number | undefined;\n messageCount?: number;\n },\n callback: () => T,\n): T {\n const { bindingName, bodySize, messageCount } = options;\n\n return startSpan(\n {\n name: `send ${bindingName}`,\n attributes: {\n 'messaging.system': 'cloudflare',\n 'messaging.destination.name': bindingName,\n 'messaging.operation.type': 'send',\n 'messaging.operation.name': 'send',\n ...(messageCount !== undefined && { 'messaging.batch.message_count': messageCount }),\n 'messaging.message.body.size': bodySize,\n [SENTRY_OP]: QUEUE_PUBLISH,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n },\n },\n callback,\n );\n}\n\nfunction getBodySize(body: unknown): number | undefined {\n if (body == null) {\n return undefined;\n }\n\n if (typeof body === 'string') {\n return new TextEncoder().encode(body).byteLength;\n }\n\n if (body instanceof ArrayBuffer) {\n return body.byteLength;\n }\n\n if (ArrayBuffer.isView(body)) {\n return body.byteLength;\n }\n\n try {\n return new TextEncoder().encode(JSON.stringify(body)).byteLength;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Wraps a Queue producer binding to create `queue.publish` spans on\n * `send` and `sendBatch` calls.\n *\n * The queue's own name is not available on the binding object, so we use\n * the env binding key (e.g. `MY_QUEUE`) as `messaging.destination.name`.\n */\nexport function instrumentQueueProducer<T extends Queue>(queue: T, bindingName: string): T {\n return new Proxy(queue, {\n get(target, prop, receiver) {\n if (prop === 'send') {\n const original = Reflect.get(target, prop, receiver) as Queue['send'];\n\n return function (this: unknown, message: unknown, options?: QueueSendOptions): ReturnType<Queue['send']> {\n return startPublishSpan({ bindingName, bodySize: getBodySize(message) }, () =>\n Reflect.apply(original, target, [message, options]),\n );\n };\n }\n\n if (prop === 'sendBatch') {\n const original = Reflect.get(target, prop, receiver) as Queue['sendBatch'];\n return function (\n this: unknown,\n messages: Iterable<MessageSendRequest>,\n options?: QueueSendBatchOptions,\n ): ReturnType<Queue['sendBatch']> {\n const messageArray = Array.from(messages);\n const totalBodySize = messageArray.reduce<number | undefined>((acc, m) => {\n const size = getBodySize(m.body);\n if (size === undefined) {\n return acc;\n }\n return (acc ?? 0) + size;\n }, undefined);\n\n return startPublishSpan({ bindingName, bodySize: totalBodySize, messageCount: messageArray.length }, () =>\n Reflect.apply(original, target, [messageArray, options]),\n );\n };\n }\n\n return Reflect.get(target, prop, receiver);\n },\n });\n}\n"],"names":["startSpan","SENTRY_OP","QUEUE_PUBLISH","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN"],"mappings":"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"}
1
+ {"version":3,"file":"instrumentQueueProducer.js","sources":["../../../../../src/instrumentations/worker/instrumentQueueProducer.ts"],"sourcesContent":["import type { MessageSendRequest, Queue, QueueSendBatchOptions, QueueSendOptions } from '@cloudflare/workers-types';\nimport { SENTRY_OP } from '@sentry/conventions/attributes';\nimport { QUEUE_PUBLISH } from '@sentry/conventions/op';\nimport { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startSpan } from '@sentry/core';\n\nconst ORIGIN = 'auto.faas.cloudflare.queue';\n\nfunction startPublishSpan<T>(\n options: {\n bindingName: string;\n bodySize: number | undefined;\n messageCount?: number;\n },\n callback: () => T,\n): T {\n const { bindingName, bodySize, messageCount } = options;\n\n return startSpan(\n {\n name: `send ${bindingName}`,\n attributes: {\n 'messaging.system': 'cloudflare',\n 'messaging.destination.name': bindingName,\n 'messaging.operation.type': 'send',\n 'messaging.operation.name': 'send',\n ...(messageCount !== undefined && { 'messaging.batch.message_count': messageCount }),\n 'messaging.message.body.size': bodySize,\n [SENTRY_OP]: QUEUE_PUBLISH,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n },\n },\n callback,\n );\n}\n\nfunction getBodySize(body: unknown): number | undefined {\n if (body == null) {\n return undefined;\n }\n\n if (typeof body === 'string') {\n return new TextEncoder().encode(body).byteLength;\n }\n\n if (body instanceof ArrayBuffer) {\n return body.byteLength;\n }\n\n if (ArrayBuffer.isView(body)) {\n return body.byteLength;\n }\n\n try {\n return new TextEncoder().encode(JSON.stringify(body)).byteLength;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Wraps a Queue producer binding to create `queue.publish` spans on\n * `send` and `sendBatch` calls.\n *\n * The queue's own name is not available on the binding object, so we use\n * the env binding key (e.g. `MY_QUEUE`) as `messaging.destination.name`.\n */\nexport function instrumentQueueProducer<T extends Queue>(queue: T, bindingName: string): T {\n return new Proxy(queue, {\n get(target, prop, receiver) {\n if (prop === 'send') {\n const original = Reflect.get(target, prop, receiver) as Queue['send'];\n\n return function (this: unknown, message: unknown, options?: QueueSendOptions): ReturnType<Queue['send']> {\n return startPublishSpan({ bindingName, bodySize: getBodySize(message) }, () =>\n Reflect.apply(original, target, [message, options]),\n );\n };\n }\n\n if (prop === 'sendBatch') {\n const original = Reflect.get(target, prop, receiver) as Queue['sendBatch'];\n return function (\n this: unknown,\n messages: Iterable<MessageSendRequest>,\n options?: QueueSendBatchOptions,\n ): ReturnType<Queue['sendBatch']> {\n const messageArray = Array.from(messages);\n const totalBodySize = messageArray.reduce<number | undefined>((acc, m) => {\n const size = getBodySize(m.body);\n if (size === undefined) {\n return acc;\n }\n return (acc ?? 0) + size;\n }, undefined);\n\n return startPublishSpan({ bindingName, bodySize: totalBodySize, messageCount: messageArray.length }, () =>\n Reflect.apply(original, target, [messageArray, options]),\n );\n };\n }\n\n const value = Reflect.get(target, prop) as unknown;\n\n return typeof value === 'function' ? value.bind(target) : value;\n },\n });\n}\n"],"names":["startSpan","SENTRY_OP","QUEUE_PUBLISH","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN"],"mappings":"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"}
@@ -2,10 +2,12 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
 
3
3
  const baseSdk = require('./baseSdk.js');
4
4
  const wrapRequestHandlerWithInit = require('./wrapRequestHandlerWithInit.js');
5
+ const httpServer = require('./integrations/httpServer.js');
5
6
 
6
7
  function wrapRequestHandler(wrapperOptions, handler) {
7
8
  return wrapRequestHandlerWithInit.wrapRequestHandlerWithInit(wrapperOptions, handler, baseSdk.initBaseSdk);
8
9
  }
9
10
 
11
+ exports.httpServerIntegration = httpServer.httpServerIntegration;
10
12
  exports.wrapRequestHandler = wrapRequestHandler;
11
13
  //# sourceMappingURL=request.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"request.js","sources":["../../../src/request.ts"],"sourcesContent":["import { initBaseSdk } from './baseSdk';\nimport type { CloudflareOptions } from './client';\nimport { type RequestHandlerWrapperOptions, wrapRequestHandlerWithInit } from './wrapRequestHandlerWithInit';\n\n/**\n * Wraps a cloudflare request handler in Sentry instrumentation.\n *\n * The client is set up with the default integrations that work without the `nodejs_compat`\n * compatibility flag, so that this also works on runtimes that cannot enable it (e.g. Shopify\n * Oxygen). On a runtime that has `nodejs_compat`, pass `defaultIntegrations:\n * getDefaultIntegrations(options)` in `options` to get the full set instead.\n */\nexport function wrapRequestHandler(\n wrapperOptions: Omit<RequestHandlerWrapperOptions, 'options'> & {\n // `enableOpenTelemetrySetup` is only honored by `init` from `sdk.ts`; this entry point\n // initializes the SDK via `initBaseSdk`, where setting it would have no effect.\n options: Omit<CloudflareOptions, 'enableOpenTelemetrySetup'>;\n },\n handler: (...args: unknown[]) => Response | Promise<Response>,\n): Promise<Response> {\n return wrapRequestHandlerWithInit(wrapperOptions, handler, initBaseSdk);\n}\n"],"names":["wrapRequestHandlerWithInit","initBaseSdk"],"mappings":";;;;;AAYO,SAAS,kBAAA,CACd,gBAKA,OAAA,EACmB;AACnB,EAAA,OAAOA,qDAAA,CAA2B,cAAA,EAAgB,OAAA,EAASC,mBAAW,CAAA;AACxE;;;;"}
1
+ {"version":3,"file":"request.js","sources":["../../../src/request.ts"],"sourcesContent":["import { initBaseSdk } from './baseSdk';\nimport type { CloudflareOptions } from './client';\nimport { type RequestHandlerWrapperOptions, wrapRequestHandlerWithInit } from './wrapRequestHandlerWithInit';\n\n/**\n * Wraps a cloudflare request handler in Sentry instrumentation.\n *\n * The client is set up with the default integrations that work without the `nodejs_compat`\n * compatibility flag, so that this also works on runtimes that cannot enable it (e.g. Shopify\n * Oxygen). On a runtime that has `nodejs_compat`, pass `defaultIntegrations:\n * getDefaultIntegrations(options)` in `options` to get the full set instead.\n */\nexport function wrapRequestHandler(\n wrapperOptions: Omit<RequestHandlerWrapperOptions, 'options'> & {\n // `enableOpenTelemetrySetup` is only honored by `init` from `sdk.ts`; this entry point\n // initializes the SDK via `initBaseSdk`, where setting it would have no effect.\n options: Omit<CloudflareOptions, 'enableOpenTelemetrySetup'>;\n },\n handler: (...args: unknown[]) => Response | Promise<Response>,\n): Promise<Response> {\n return wrapRequestHandlerWithInit(wrapperOptions, handler, initBaseSdk);\n}\n\nexport { httpServerIntegration } from './integrations/httpServer';\n"],"names":["wrapRequestHandlerWithInit","initBaseSdk"],"mappings":";;;;;;AAYO,SAAS,kBAAA,CACd,gBAKA,OAAA,EACmB;AACnB,EAAA,OAAOA,qDAAA,CAA2B,cAAA,EAAgB,OAAA,EAASC,mBAAW,CAAA;AACxE;;;;;"}
@@ -25,15 +25,24 @@ class IsolatedPromiseBuffer {
25
25
  drain(timeout) {
26
26
  const oldTaskProducers = [...this._taskProducers];
27
27
  this._taskProducers = [];
28
+ const drainController = new AbortController();
29
+ this.drainSignal = drainController.signal;
30
+ let tasks;
31
+ try {
32
+ tasks = oldTaskProducers.map((taskProducer) => taskProducer());
33
+ } finally {
34
+ this.drainSignal = void 0;
35
+ }
28
36
  return new Promise((resolve) => {
29
37
  const timer = setTimeout(() => {
30
38
  if (timeout && timeout > 0) {
39
+ drainController.abort();
31
40
  resolve(false);
32
41
  }
33
42
  }, timeout);
34
43
  Promise.all(
35
- oldTaskProducers.map(
36
- (taskProducer) => taskProducer().then(null, () => {
44
+ tasks.map(
45
+ (task) => task.then(null, () => {
37
46
  })
38
47
  )
39
48
  ).then(() => {
@@ -44,12 +53,17 @@ class IsolatedPromiseBuffer {
44
53
  }
45
54
  }
46
55
  function makeCloudflareTransport(options) {
56
+ const buffer = new IsolatedPromiseBuffer(options.bufferSize);
47
57
  function makeRequest(request) {
58
+ const drainSignal = buffer.drainSignal;
59
+ const callerSignal = options.fetchOptions?.signal ?? void 0;
60
+ const signal = drainSignal && callerSignal ? AbortSignal.any([drainSignal, callerSignal]) : drainSignal ?? callerSignal;
48
61
  const requestOptions = {
49
62
  body: request.body,
50
63
  method: "POST",
51
64
  headers: options.headers,
52
- ...options.fetchOptions
65
+ ...options.fetchOptions,
66
+ ...signal ? { signal } : {}
53
67
  };
54
68
  return core.suppressTracing(() => {
55
69
  return (options.fetch ?? fetch)(options.url, requestOptions).then(async (response) => {
@@ -67,7 +81,7 @@ function makeCloudflareTransport(options) {
67
81
  });
68
82
  });
69
83
  }
70
- return core.createTransport(options, makeRequest, new IsolatedPromiseBuffer(options.bufferSize));
84
+ return core.createTransport(options, makeRequest, buffer);
71
85
  }
72
86
 
73
87
  exports.IsolatedPromiseBuffer = IsolatedPromiseBuffer;