@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
@@ -1 +1 @@
1
- {"version":3,"file":"workflows.js","sources":["../../../src/workflows.ts"],"sourcesContent":["import { SENTRY_SEGMENT_NAME_SOURCE, CODE_FUNCTION_NAME, SENTRY_OP } from '@sentry/conventions/attributes';\nimport { FUNCTION } from '@sentry/conventions/op';\nimport type { PropagationContext, Scope } from '@sentry/core';\nimport {\n captureException,\n flush,\n getCurrentScope,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n startSpan,\n withIsolationScope,\n withScope,\n} from '@sentry/core';\nimport type {\n WorkflowDelayDuration,\n WorkflowEntrypoint,\n WorkflowEvent,\n WorkflowSleepDuration,\n WorkflowStep,\n WorkflowStepConfig,\n WorkflowStepContext,\n WorkflowStepEvent,\n WorkflowStepRollbackOptions,\n WorkflowTimeoutDuration,\n} from 'cloudflare:workers';\nimport { setAsyncLocalStorageAsyncContextStrategy } from '@sentry/server-utils/no-diagnostic-channels';\nimport type { CloudflareOptions } from './client';\nimport { flushAndDispose, getOriginalWaitUntil } from './flush';\nimport { markAsInstrumented } from './instrument';\nimport { instrumentEnv } from './instrumentations/worker/instrumentEnv';\nimport { addCloudResourceContext } from './scope-utils';\nimport { init } from './sdk';\nimport { instrumentContext } from './utils/instrumentContext';\nimport type { DefaultEnv, ResolveEnv, StrictCloudflareOptions } from './types';\nimport { withInvocationIsolationScope } from './utils/invocationScope';\n\nconst UUID_REGEX = /^[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}$/i;\n\n/**\n * Hashes a string to a UUID using SHA-1.\n */\nexport async function deterministicTraceIdFromInstanceId(instanceId: string): Promise<string> {\n const buf = await crypto.subtle.digest('SHA-1', new TextEncoder().encode(instanceId));\n return (\n Array.from(new Uint8Array(buf))\n // We only need the first 16 bytes for the 32 characters\n .slice(0, 16)\n .map(b => b.toString(16).padStart(2, '0'))\n .join('')\n );\n}\n\nasync function propagationContextFromInstanceId(instanceId: string): Promise<PropagationContext> {\n const traceId = UUID_REGEX.test(instanceId)\n ? instanceId.replace(/-/g, '')\n : await deterministicTraceIdFromInstanceId(instanceId);\n\n // Derive sampleRand from last 4 characters of the random UUID\n //\n // We cannot store any state between workflow steps, so we derive the\n // sampleRand from the traceId itself. This ensures that the sampling is\n // consistent across all steps in the same workflow instance.\n const sampleRand = parseInt(traceId.slice(-4), 16) / 0xffff;\n\n return {\n traceId,\n sampleRand,\n };\n}\n\nclass WrappedWorkflowStep implements WorkflowStep {\n public constructor(\n private _instanceId: string,\n private _options: CloudflareOptions,\n private _step: WorkflowStep,\n private _waitUntil: ExecutionContext['waitUntil'],\n private _isolationScope: Scope,\n ) {}\n\n public async do<T extends Rpc.Serializable<T>>(\n name: string,\n callback: (ctx: WorkflowStepContext) => Promise<T>,\n rollbackOptions?: WorkflowStepRollbackOptions<T>,\n ): Promise<T>;\n public async do<T extends Rpc.Serializable<T>, const C extends WorkflowStepConfig>(\n name: string,\n config: C,\n callback: (\n ctx: WorkflowStepContext<C['retries'] extends { delay: infer D } ? D : WorkflowDelayDuration | number>,\n ) => Promise<T>,\n rollbackOptions?: WorkflowStepRollbackOptions<T>,\n ): Promise<T>;\n public async do<T extends Rpc.Serializable<T>>(\n name: string,\n configOrCallback: WorkflowStepConfig | ((ctx: WorkflowStepContext) => Promise<T>),\n callbackOrRollback?: ((ctx: WorkflowStepContext) => Promise<T>) | WorkflowStepRollbackOptions<T>,\n maybeRollback?: WorkflowStepRollbackOptions<T>,\n ): Promise<T> {\n // Capture the current scope, so parent span (e.g., a startSpan surrounding step.do) is preserved\n const scopeForStep = getCurrentScope();\n\n const hasConfig = typeof configOrCallback !== 'function';\n const config = hasConfig ? configOrCallback : undefined;\n const userCallback = (hasConfig ? callbackOrRollback : configOrCallback) as (\n ctx: WorkflowStepContext,\n ) => Promise<T>;\n const rollbackOptions = (hasConfig ? maybeRollback : callbackOrRollback) as\n | WorkflowStepRollbackOptions<T>\n | undefined;\n\n const instrumentedCallback = async (...args: unknown[]): Promise<T> => {\n // Feature detection: Cloudflare Workflows (April 2026+) pass a step context\n // with `attempt` and `config.retries.limit`. When available, we only capture\n // errors on the final attempt to avoid duplicates during retries.\n const stepContext = args[0] as { attempt?: number; config?: { retries?: { limit?: number } } } | undefined;\n const attempt = stepContext?.attempt;\n const retryLimit = stepContext?.config?.retries?.limit;\n const hasStepContext = typeof attempt === 'number' && typeof retryLimit === 'number';\n\n // Only capture error on final attempt (attempt > retryLimit means no more retries left)\n // or when step context is unavailable (legacy behavior - capture all errors)\n const isFinalAttempt = !hasStepContext || attempt > retryLimit;\n\n // The engine invokes step callbacks outside the async context of `run`, so the\n // run's isolation scope (and with it the invocation state that ties eager sends\n // to this invocation's `waitUntil`) has to be restored explicitly.\n return withIsolationScope(this._isolationScope, () => {\n const stepResult = startSpan(\n {\n name,\n scope: scopeForStep,\n attributes: {\n [SENTRY_OP]: FUNCTION,\n [CODE_FUNCTION_NAME]: name,\n 'workflow.step.name': name,\n 'cloudflare.workflow.timeout': config?.timeout,\n 'cloudflare.workflow.retries.backoff': config?.retries?.backoff,\n // In workers-types v5, `delay` may be a `WorkflowDelayFunction`, which isn't a valid span attribute value.\n 'cloudflare.workflow.retries.delay':\n typeof config?.retries?.delay === 'function' ? undefined : config?.retries?.delay,\n 'cloudflare.workflow.retries.limit': config?.retries?.limit,\n 'cloudflare.workflow.attempt': attempt,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.faas.cloudflare.workflow',\n [SENTRY_SEGMENT_NAME_SOURCE]: 'task',\n },\n },\n async span => {\n try {\n const result = await (userCallback as (...args: unknown[]) => Promise<T>)(...args);\n span.setStatus({ code: 1 });\n return result;\n } catch (error) {\n if (isFinalAttempt) {\n captureException(error, { mechanism: { handled: true, type: 'auto.faas.cloudflare.workflow' } });\n }\n throw error;\n }\n },\n );\n // Deliver after the step span has ended, so the span rides this flush instead of\n // starting an eager drain (same ordering as `wrapMethodWithSentry`'s teardown).\n return stepResult.then(\n result => {\n this._waitUntil(flush(2000));\n return result;\n },\n error => {\n this._waitUntil(flush(2000));\n throw error;\n },\n );\n });\n };\n\n if (config) {\n return rollbackOptions\n ? this._step.do(name, config, instrumentedCallback, rollbackOptions)\n : this._step.do(name, config, instrumentedCallback);\n }\n\n return rollbackOptions\n ? this._step.do(name, instrumentedCallback, rollbackOptions)\n : this._step.do(name, instrumentedCallback);\n }\n\n public async sleep(name: string, duration: WorkflowSleepDuration): Promise<void> {\n return this._step.sleep(name, duration);\n }\n\n public async sleepUntil(name: string, timestamp: Date | number): Promise<void> {\n return this._step.sleepUntil(name, timestamp);\n }\n\n public async waitForEvent<T extends Rpc.Serializable<T>>(\n name: string,\n options: { type: string; timeout?: WorkflowTimeoutDuration | number },\n ): Promise<WorkflowStepEvent<T>> {\n return this._step.waitForEvent<T>(name, options);\n }\n}\n\n/**\n * Instruments a Cloudflare Workflow class with Sentry.\n *\n * @example\n * ```typescript\n * const InstrumentedWorkflow = instrumentWorkflowWithSentry(\n * (env) => ({ dsn: env.SENTRY_DSN }),\n * MyWorkflowClass\n * );\n *\n * export default InstrumentedWorkflow;\n * ```\n *\n * @param optionsCallback - Function that returns Sentry options to initialize Sentry\n * @param WorkflowClass - The workflow class to instrument\n * @returns Instrumented workflow class with the same interface\n */\nexport function instrumentWorkflowWithSentry<\n E = DefaultEnv, // Environment type\n P = unknown, // Payload type\n // oxlint-disable-next-line typescript/no-explicit-any\n T extends WorkflowEntrypoint<any, any> = WorkflowEntrypoint<E, P>, // WorkflowEntrypoint type\n // The constraint must not route through `T`: workers-types defaults `WorkflowEntrypoint`'s\n // `Env` to `unknown` (unlike `WorkerEntrypoint`/`DurableObject`, which default to\n // `Cloudflare.Env`), so a bare subclass would be rejected in a `wrangler types` project.\n // The callback env is resolved from the inferred constructor via `ResolveEnv` instead.\n // oxlint-disable-next-line typescript/no-explicit-any\n C extends new (ctx: ExecutionContext, env: any) => WorkflowEntrypoint<any, any> = new (\n ctx: ExecutionContext,\n // oxlint-disable-next-line typescript/no-explicit-any\n env: any,\n ) => T, // Constructor type of the WorkflowEntrypoint class\n O = unknown,\n>(optionsCallback: (env: ResolveEnv<C, E>) => StrictCloudflareOptions<O>, WorkFlowClass: C): C {\n const InstrumentedClass = new Proxy(WorkFlowClass, {\n // oxlint-disable-next-line typescript/no-explicit-any\n construct(target: C, args: [ctx: ExecutionContext, env: any], newTarget) {\n const [ctx, env] = args;\n const context = instrumentContext(ctx);\n const options = optionsCallback(env);\n args[0] = context;\n args[1] = instrumentEnv(env as Record<string, unknown>, options) as E;\n const instance = Reflect.construct(target, args, newTarget) as T;\n return new Proxy(instance, {\n get(obj, prop, receiver) {\n if (prop === 'run') {\n return async function (event: WorkflowEvent<P>, step: WorkflowStep): Promise<unknown> {\n setAsyncLocalStorageAsyncContextStrategy();\n\n return withInvocationIsolationScope(async isolationScope => {\n const waitUntil = getOriginalWaitUntil(context).bind(context);\n const client = init({ ...options, ctx: context, enableDedupe: false });\n isolationScope.setClient(client);\n\n addCloudResourceContext(isolationScope);\n\n return withScope(async scope => {\n const propagationContext = await propagationContextFromInstanceId(event.instanceId);\n scope.setPropagationContext(propagationContext);\n\n try {\n return await obj.run.call(\n obj,\n event,\n new WrappedWorkflowStep(event.instanceId, options, step, waitUntil, isolationScope),\n );\n } finally {\n waitUntil(flushAndDispose(client));\n }\n });\n }, context);\n };\n }\n return Reflect.get(obj, prop, receiver);\n },\n });\n },\n });\n // Recognizable for `_INTERNAL_wrapUnlessInstrumented`, so auto-instrumentation never nests wrappers.\n markAsInstrumented(InstrumentedClass);\n return InstrumentedClass;\n}\n"],"names":["getCurrentScope","withIsolationScope","startSpan","SENTRY_OP","FUNCTION","CODE_FUNCTION_NAME","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","SENTRY_SEGMENT_NAME_SOURCE","captureException","flush","instrumentContext","instrumentEnv","setAsyncLocalStorageAsyncContextStrategy","withInvocationIsolationScope","getOriginalWaitUntil","init","addCloudResourceContext","withScope","flushAndDispose","markAsInstrumented"],"mappings":"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"}
1
+ {"version":3,"file":"workflows.js","sources":["../../../src/workflows.ts"],"sourcesContent":["import { SENTRY_SEGMENT_NAME_SOURCE, CODE_FUNCTION_NAME, SENTRY_OP } from '@sentry/conventions/attributes';\nimport { FUNCTION } from '@sentry/conventions/op';\nimport type { PropagationContext, Scope } from '@sentry/core';\nimport {\n captureException,\n flush,\n getCurrentScope,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n startSpan,\n withIsolationScope,\n withScope,\n} from '@sentry/core';\nimport type {\n WorkflowDelayDuration,\n WorkflowEntrypoint,\n WorkflowEvent,\n WorkflowSleepDuration,\n WorkflowStep,\n WorkflowStepConfig,\n WorkflowStepContext,\n WorkflowStepEvent,\n WorkflowStepRollbackOptions,\n WorkflowTimeoutDuration,\n} from 'cloudflare:workers';\nimport { setAsyncLocalStorageAsyncContextStrategy } from '@sentry/server-utils/no-diagnostic-channels';\nimport type { CloudflareOptions } from './client';\nimport { flushAndDispose, getOriginalWaitUntil } from './flush';\nimport { markAsInstrumented } from './instrument';\nimport { instrumentEnv } from './instrumentations/worker/instrumentEnv';\nimport { addCloudResourceContext } from './scope-utils';\nimport { init } from './sdk';\nimport { instrumentContext } from './utils/instrumentContext';\nimport type { DefaultEnv, ResolveEnv, StrictCloudflareOptions } from './types';\nimport { getInvocationState } from './utils/invocationContext';\nimport { withInvocationIsolationScope } from './utils/invocationScope';\n\nconst UUID_REGEX = /^[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}$/i;\n\n/**\n * Hashes a string to a UUID using SHA-1.\n */\nexport async function deterministicTraceIdFromInstanceId(instanceId: string): Promise<string> {\n const buf = await crypto.subtle.digest('SHA-1', new TextEncoder().encode(instanceId));\n return (\n Array.from(new Uint8Array(buf))\n // We only need the first 16 bytes for the 32 characters\n .slice(0, 16)\n .map(b => b.toString(16).padStart(2, '0'))\n .join('')\n );\n}\n\nasync function propagationContextFromInstanceId(instanceId: string): Promise<PropagationContext> {\n const traceId = UUID_REGEX.test(instanceId)\n ? instanceId.replace(/-/g, '')\n : await deterministicTraceIdFromInstanceId(instanceId);\n\n // Derive sampleRand from last 4 characters of the random UUID\n //\n // We cannot store any state between workflow steps, so we derive the\n // sampleRand from the traceId itself. This ensures that the sampling is\n // consistent across all steps in the same workflow instance.\n const sampleRand = parseInt(traceId.slice(-4), 16) / 0xffff;\n\n return {\n traceId,\n sampleRand,\n };\n}\n\nclass WrappedWorkflowStep implements WorkflowStep {\n public constructor(\n private _instanceId: string,\n private _options: CloudflareOptions,\n private _step: WorkflowStep,\n private _waitUntil: ExecutionContext['waitUntil'],\n private _isolationScope: Scope,\n ) {}\n\n public async do<T extends Rpc.Serializable<T>>(\n name: string,\n callback: (ctx: WorkflowStepContext) => Promise<T>,\n rollbackOptions?: WorkflowStepRollbackOptions<T>,\n ): Promise<T>;\n public async do<T extends Rpc.Serializable<T>, const C extends WorkflowStepConfig>(\n name: string,\n config: C,\n callback: (\n ctx: WorkflowStepContext<C['retries'] extends { delay: infer D } ? D : WorkflowDelayDuration | number>,\n ) => Promise<T>,\n rollbackOptions?: WorkflowStepRollbackOptions<T>,\n ): Promise<T>;\n public async do<T extends Rpc.Serializable<T>>(\n name: string,\n configOrCallback: WorkflowStepConfig | ((ctx: WorkflowStepContext) => Promise<T>),\n callbackOrRollback?: ((ctx: WorkflowStepContext) => Promise<T>) | WorkflowStepRollbackOptions<T>,\n maybeRollback?: WorkflowStepRollbackOptions<T>,\n ): Promise<T> {\n // Capture the current scope, so parent span (e.g., a startSpan surrounding step.do) is preserved\n const scopeForStep = getCurrentScope();\n\n const hasConfig = typeof configOrCallback !== 'function';\n const config = hasConfig ? configOrCallback : undefined;\n const userCallback = (hasConfig ? callbackOrRollback : configOrCallback) as (\n ctx: WorkflowStepContext,\n ) => Promise<T>;\n const rollbackOptions = (hasConfig ? maybeRollback : callbackOrRollback) as\n | WorkflowStepRollbackOptions<T>\n | undefined;\n\n const instrumentedCallback = async (...args: unknown[]): Promise<T> => {\n // Feature detection: Cloudflare Workflows (April 2026+) pass a step context\n // with `attempt` and `config.retries.limit`. When available, we only capture\n // errors on the final attempt to avoid duplicates during retries.\n const stepContext = args[0] as { attempt?: number; config?: { retries?: { limit?: number } } } | undefined;\n const attempt = stepContext?.attempt;\n const retryLimit = stepContext?.config?.retries?.limit;\n const hasStepContext = typeof attempt === 'number' && typeof retryLimit === 'number';\n\n // Only capture error on final attempt (attempt > retryLimit means no more retries left)\n // or when step context is unavailable (legacy behavior - capture all errors)\n const isFinalAttempt = !hasStepContext || attempt > retryLimit;\n\n // The engine invokes step callbacks outside the async context of `run`, so the\n // run's isolation scope (and with it the invocation state that ties eager sends\n // to this invocation's `waitUntil`) has to be restored explicitly.\n return withIsolationScope(this._isolationScope, () => {\n const invocationState = getInvocationState();\n\n if (invocationState) {\n invocationState.flushPointReached = false;\n }\n\n const stepResult = startSpan(\n {\n name,\n scope: scopeForStep,\n attributes: {\n [SENTRY_OP]: FUNCTION,\n [CODE_FUNCTION_NAME]: name,\n 'workflow.step.name': name,\n 'cloudflare.workflow.timeout': config?.timeout,\n 'cloudflare.workflow.retries.backoff': config?.retries?.backoff,\n // In workers-types v5, `delay` may be a `WorkflowDelayFunction`, which isn't a valid span attribute value.\n 'cloudflare.workflow.retries.delay':\n typeof config?.retries?.delay === 'function' ? undefined : config?.retries?.delay,\n 'cloudflare.workflow.retries.limit': config?.retries?.limit,\n 'cloudflare.workflow.attempt': attempt,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.faas.cloudflare.workflow',\n [SENTRY_SEGMENT_NAME_SOURCE]: 'task',\n },\n },\n async span => {\n try {\n const result = await (userCallback as (...args: unknown[]) => Promise<T>)(...args);\n span.setStatus({ code: 1 });\n return result;\n } catch (error) {\n if (isFinalAttempt) {\n captureException(error, { mechanism: { handled: true, type: 'auto.faas.cloudflare.workflow' } });\n }\n throw error;\n }\n },\n );\n // Deliver after the step span has ended, so the span rides this flush instead of\n // starting an eager drain (same ordering as `wrapMethodWithSentry`'s teardown). The flush runs on the\n // step's scope: the engine calls the step callback outside of `run()`, so the current scope here has\n // neither the client nor the run's trace, and envelopes without a DSC are dropped by Relay.\n return stepResult.then(\n result => {\n this._waitUntil(withScope(scopeForStep, () => flush(2000)));\n return result;\n },\n error => {\n this._waitUntil(withScope(scopeForStep, () => flush(2000)));\n throw error;\n },\n );\n });\n };\n\n if (config) {\n return rollbackOptions\n ? this._step.do(name, config, instrumentedCallback, rollbackOptions)\n : this._step.do(name, config, instrumentedCallback);\n }\n\n return rollbackOptions\n ? this._step.do(name, instrumentedCallback, rollbackOptions)\n : this._step.do(name, instrumentedCallback);\n }\n\n public async sleep(name: string, duration: WorkflowSleepDuration): Promise<void> {\n return this._step.sleep(name, duration);\n }\n\n public async sleepUntil(name: string, timestamp: Date | number): Promise<void> {\n return this._step.sleepUntil(name, timestamp);\n }\n\n public async waitForEvent<T extends Rpc.Serializable<T>>(\n name: string,\n options: { type: string; timeout?: WorkflowTimeoutDuration | number },\n ): Promise<WorkflowStepEvent<T>> {\n return this._step.waitForEvent<T>(name, options);\n }\n}\n\n/**\n * Instruments a Cloudflare Workflow class with Sentry.\n *\n * @example\n * ```typescript\n * const InstrumentedWorkflow = instrumentWorkflowWithSentry(\n * (env) => ({ dsn: env.SENTRY_DSN }),\n * MyWorkflowClass\n * );\n *\n * export default InstrumentedWorkflow;\n * ```\n *\n * @param optionsCallback - Function that returns Sentry options to initialize Sentry\n * @param WorkflowClass - The workflow class to instrument\n * @returns Instrumented workflow class with the same interface\n */\nexport function instrumentWorkflowWithSentry<\n E = DefaultEnv, // Environment type\n P = unknown, // Payload type\n // oxlint-disable-next-line typescript/no-explicit-any\n T extends WorkflowEntrypoint<any, any> = WorkflowEntrypoint<E, P>, // WorkflowEntrypoint type\n // The constraint must not route through `T`: workers-types defaults `WorkflowEntrypoint`'s\n // `Env` to `unknown` (unlike `WorkerEntrypoint`/`DurableObject`, which default to\n // `Cloudflare.Env`), so a bare subclass would be rejected in a `wrangler types` project.\n // The callback env is resolved from the inferred constructor via `ResolveEnv` instead.\n // oxlint-disable-next-line typescript/no-explicit-any\n C extends new (ctx: ExecutionContext, env: any) => WorkflowEntrypoint<any, any> = new (\n ctx: ExecutionContext,\n // oxlint-disable-next-line typescript/no-explicit-any\n env: any,\n ) => T, // Constructor type of the WorkflowEntrypoint class\n O = unknown,\n>(optionsCallback: (env: ResolveEnv<C, E>) => StrictCloudflareOptions<O>, WorkFlowClass: C): C {\n const InstrumentedClass = new Proxy(WorkFlowClass, {\n // oxlint-disable-next-line typescript/no-explicit-any\n construct(target: C, args: [ctx: ExecutionContext, env: any], newTarget) {\n const [ctx, env] = args;\n const context = instrumentContext(ctx);\n const options = optionsCallback(env);\n args[0] = context;\n args[1] = instrumentEnv(env as Record<string, unknown>, options) as E;\n const instance = Reflect.construct(target, args, newTarget) as T;\n return new Proxy(instance, {\n get(obj, prop, receiver) {\n if (prop === 'run') {\n return async function (event: WorkflowEvent<P>, step: WorkflowStep): Promise<unknown> {\n setAsyncLocalStorageAsyncContextStrategy();\n\n return withInvocationIsolationScope(async isolationScope => {\n const waitUntil = getOriginalWaitUntil(context).bind(context);\n const client = init({ ...options, ctx: context, enableDedupe: false });\n isolationScope.setClient(client);\n\n addCloudResourceContext(isolationScope);\n\n return withScope(async scope => {\n const propagationContext = await propagationContextFromInstanceId(event.instanceId);\n scope.setPropagationContext(propagationContext);\n\n try {\n return await obj.run.call(\n obj,\n event,\n new WrappedWorkflowStep(event.instanceId, options, step, waitUntil, isolationScope),\n );\n } finally {\n waitUntil(flushAndDispose(client));\n }\n });\n }, context);\n };\n }\n return Reflect.get(obj, prop, receiver);\n },\n });\n },\n });\n // Recognizable for `_INTERNAL_wrapUnlessInstrumented`, so auto-instrumentation never nests wrappers.\n markAsInstrumented(InstrumentedClass);\n return InstrumentedClass;\n}\n"],"names":["getCurrentScope","withIsolationScope","getInvocationState","startSpan","SENTRY_OP","FUNCTION","CODE_FUNCTION_NAME","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","SENTRY_SEGMENT_NAME_SOURCE","captureException","withScope","flush","instrumentContext","instrumentEnv","setAsyncLocalStorageAsyncContextStrategy","withInvocationIsolationScope","getOriginalWaitUntil","init","addCloudResourceContext","flushAndDispose","markAsInstrumented"],"mappings":"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"}
@@ -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;
@@ -1 +1 @@
1
- {"version":3,"file":"transport.js","sources":["../../../src/transport.ts"],"sourcesContent":["import type { BaseTransportOptions, Transport, TransportMakeRequestResponse, TransportRequest } from '@sentry/core';\nimport { createTransport, SENTRY_BUFFER_FULL_ERROR, suppressTracing } from '@sentry/core';\n\nexport interface CloudflareTransportOptions extends BaseTransportOptions {\n /** Custom fetch function to use. This allows usage of things like Workers VPC */\n fetch?: typeof fetch;\n /** Fetch API init parameters. */\n fetchOptions?: RequestInit;\n}\n\n/**\n * How many payloads the buffer holds before it starts rejecting new ones.\n *\n * With `cacheClient` a single client is reused across an isolate's invocations, so one buffer now has to absorb\n * the payloads of many invocations rather than one. 256 is the size that held up under load testing that scenario.\n */\nconst DEFAULT_TRANSPORT_BUFFER_SIZE = 256;\n\n/**\n * This is a modified promise buffer that collects tasks until drain is called.\n * We need this in the edge runtime because edge function invocations may not share I/O objects, like fetch requests\n * and responses, and the normal PromiseBuffer inherently buffers stuff inbetween incoming requests.\n *\n * A limitation we need to be aware of is that DEFAULT_TRANSPORT_BUFFER_SIZE is the maximum amount of payloads the\n * SDK can send for a given edge function invocation.\n */\nexport class IsolatedPromiseBuffer {\n // We just have this field because the promise buffer interface requires it.\n // If we ever remove it from the interface we should also remove it here.\n public $: Array<PromiseLike<TransportMakeRequestResponse>>;\n\n private _taskProducers: (() => PromiseLike<TransportMakeRequestResponse>)[];\n\n private readonly _bufferSize: number;\n\n public constructor(_bufferSize = DEFAULT_TRANSPORT_BUFFER_SIZE) {\n this.$ = [];\n this._taskProducers = [];\n this._bufferSize = _bufferSize;\n }\n\n /**\n * @inheritdoc\n */\n public add(taskProducer: () => PromiseLike<TransportMakeRequestResponse>): PromiseLike<TransportMakeRequestResponse> {\n if (this._taskProducers.length >= this._bufferSize) {\n return Promise.reject(SENTRY_BUFFER_FULL_ERROR);\n }\n\n this._taskProducers.push(taskProducer);\n return Promise.resolve({});\n }\n\n /**\n * @inheritdoc\n */\n public drain(timeout?: number): PromiseLike<boolean> {\n const oldTaskProducers = [...this._taskProducers];\n this._taskProducers = [];\n\n return new Promise(resolve => {\n const timer = setTimeout(() => {\n if (timeout && timeout > 0) {\n resolve(false);\n }\n }, timeout);\n\n // This cannot reject\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n Promise.all(\n oldTaskProducers.map(taskProducer =>\n taskProducer().then(null, () => {\n // catch all failed requests\n }),\n ),\n ).then(() => {\n // resolve to true if all fetch requests settled\n clearTimeout(timer);\n resolve(true);\n });\n });\n }\n}\n\n/**\n * Creates a Transport that uses the native fetch API to send events to Sentry.\n */\nexport function makeCloudflareTransport(options: CloudflareTransportOptions): Transport {\n function makeRequest(request: TransportRequest): PromiseLike<TransportMakeRequestResponse> {\n const requestOptions: RequestInit = {\n body: request.body as BodyInit,\n method: 'POST',\n headers: options.headers,\n ...options.fetchOptions,\n };\n\n return suppressTracing(() => {\n return (options.fetch ?? fetch)(options.url, requestOptions).then(async response => {\n // Consume the response body to satisfy Cloudflare Workers' fetch requirements.\n // The runtime requires all fetch response bodies to be read or explicitly canceled\n // to prevent connection stalls and potential deadlocks. We read the body as text\n // even though we don't use the content, as Sentry's response information is in the headers.\n // See: https://github.com/getsentry/sentry-javascript/issues/18534\n try {\n await response.text();\n } catch {\n // no-op\n }\n\n return {\n statusCode: response.status,\n headers: {\n 'x-sentry-rate-limits': response.headers.get('X-Sentry-Rate-Limits'),\n 'retry-after': response.headers.get('Retry-After'),\n },\n };\n });\n });\n }\n\n return createTransport(options, makeRequest, new IsolatedPromiseBuffer(options.bufferSize));\n}\n"],"names":["SENTRY_BUFFER_FULL_ERROR","suppressTracing","createTransport"],"mappings":";;;;AAgBA,MAAM,6BAAA,GAAgC,GAAA;AAU/B,MAAM,qBAAA,CAAsB;AAAA,EAS1B,WAAA,CAAY,cAAc,6BAAA,EAA+B;AAC9D,IAAA,IAAA,CAAK,IAAI,EAAC;AACV,IAAA,IAAA,CAAK,iBAAiB,EAAC;AACvB,IAAA,IAAA,CAAK,WAAA,GAAc,WAAA;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA,EAKO,IAAI,YAAA,EAA0G;AACnH,IAAA,IAAI,IAAA,CAAK,cAAA,CAAe,MAAA,IAAU,IAAA,CAAK,WAAA,EAAa;AAClD,MAAA,OAAO,OAAA,CAAQ,OAAOA,6BAAwB,CAAA;AAAA,IAChD;AAEA,IAAA,IAAA,CAAK,cAAA,CAAe,KAAK,YAAY,CAAA;AACrC,IAAA,OAAO,OAAA,CAAQ,OAAA,CAAQ,EAAE,CAAA;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKO,MAAM,OAAA,EAAwC;AACnD,IAAA,MAAM,gBAAA,GAAmB,CAAC,GAAG,IAAA,CAAK,cAAc,CAAA;AAChD,IAAA,IAAA,CAAK,iBAAiB,EAAC;AAEvB,IAAA,OAAO,IAAI,QAAQ,CAAA,OAAA,KAAW;AAC5B,MAAA,MAAM,KAAA,GAAQ,WAAW,MAAM;AAC7B,QAAA,IAAI,OAAA,IAAW,UAAU,CAAA,EAAG;AAC1B,UAAA,OAAA,CAAQ,KAAK,CAAA;AAAA,QACf;AAAA,MACF,GAAG,OAAO,CAAA;AAIV,MAAA,OAAA,CAAQ,GAAA;AAAA,QACN,gBAAA,CAAiB,GAAA;AAAA,UAAI,CAAA,YAAA,KACnB,YAAA,EAAa,CAAE,IAAA,CAAK,MAAM,MAAM;AAAA,UAEhC,CAAC;AAAA;AACH,OACF,CAAE,KAAK,MAAM;AAEX,QAAA,YAAA,CAAa,KAAK,CAAA;AAClB,QAAA,OAAA,CAAQ,IAAI,CAAA;AAAA,MACd,CAAC,CAAA;AAAA,IACH,CAAC,CAAA;AAAA,EACH;AACF;AAKO,SAAS,wBAAwB,OAAA,EAAgD;AACtF,EAAA,SAAS,YAAY,OAAA,EAAsE;AACzF,IAAA,MAAM,cAAA,GAA8B;AAAA,MAClC,MAAM,OAAA,CAAQ,IAAA;AAAA,MACd,MAAA,EAAQ,MAAA;AAAA,MACR,SAAS,OAAA,CAAQ,OAAA;AAAA,MACjB,GAAG,OAAA,CAAQ;AAAA,KACb;AAEA,IAAA,OAAOC,qBAAgB,MAAM;AAC3B,MAAA,OAAA,CAAQ,OAAA,CAAQ,SAAS,KAAA,EAAO,OAAA,CAAQ,KAAK,cAAc,CAAA,CAAE,IAAA,CAAK,OAAM,QAAA,KAAY;AAMlF,QAAA,IAAI;AACF,UAAA,MAAM,SAAS,IAAA,EAAK;AAAA,QACtB,CAAA,CAAA,MAAQ;AAAA,QAER;AAEA,QAAA,OAAO;AAAA,UACL,YAAY,QAAA,CAAS,MAAA;AAAA,UACrB,OAAA,EAAS;AAAA,YACP,sBAAA,EAAwB,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,sBAAsB,CAAA;AAAA,YACnE,aAAA,EAAe,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,aAAa;AAAA;AACnD,SACF;AAAA,MACF,CAAC,CAAA;AAAA,IACH,CAAC,CAAA;AAAA,EACH;AAEA,EAAA,OAAOC,qBAAgB,OAAA,EAAS,WAAA,EAAa,IAAI,qBAAA,CAAsB,OAAA,CAAQ,UAAU,CAAC,CAAA;AAC5F;;;;;"}
1
+ {"version":3,"file":"transport.js","sources":["../../../src/transport.ts"],"sourcesContent":["import type { BaseTransportOptions, Transport, TransportMakeRequestResponse, TransportRequest } from '@sentry/core';\nimport { createTransport, SENTRY_BUFFER_FULL_ERROR, suppressTracing } from '@sentry/core';\n\nexport interface CloudflareTransportOptions extends BaseTransportOptions {\n /** Custom fetch function to use. This allows usage of things like Workers VPC */\n fetch?: typeof fetch;\n /** Fetch API init parameters. */\n fetchOptions?: RequestInit;\n}\n\n/**\n * How many payloads the buffer holds before it starts rejecting new ones.\n *\n * With `cacheClient` a single client is reused across an isolate's invocations, so one buffer now has to absorb\n * the payloads of many invocations rather than one. 256 is the size that held up under load testing that scenario.\n */\nconst DEFAULT_TRANSPORT_BUFFER_SIZE = 256;\n\n/**\n * This is a modified promise buffer that collects tasks until drain is called.\n * We need this in the edge runtime because edge function invocations may not share I/O objects, like fetch requests\n * and responses, and the normal PromiseBuffer inherently buffers stuff inbetween incoming requests.\n *\n * A limitation we need to be aware of is that DEFAULT_TRANSPORT_BUFFER_SIZE is the maximum amount of payloads the\n * SDK can send for a given edge function invocation.\n */\nexport class IsolatedPromiseBuffer {\n // We just have this field because the promise buffer interface requires it.\n // If we ever remove it from the interface we should also remove it here.\n public $: Array<PromiseLike<TransportMakeRequestResponse>>;\n\n /**\n * Abort signal of the drain that is starting its requests. It is set only while `drain()` runs the task\n * producers, so a request reads the signal of the drain that sends it.\n */\n public drainSignal: AbortSignal | undefined;\n\n private _taskProducers: (() => PromiseLike<TransportMakeRequestResponse>)[];\n\n private readonly _bufferSize: number;\n\n public constructor(_bufferSize = DEFAULT_TRANSPORT_BUFFER_SIZE) {\n this.$ = [];\n this._taskProducers = [];\n this._bufferSize = _bufferSize;\n }\n\n /**\n * @inheritdoc\n */\n public add(taskProducer: () => PromiseLike<TransportMakeRequestResponse>): PromiseLike<TransportMakeRequestResponse> {\n if (this._taskProducers.length >= this._bufferSize) {\n return Promise.reject(SENTRY_BUFFER_FULL_ERROR);\n }\n\n this._taskProducers.push(taskProducer);\n return Promise.resolve({});\n }\n\n /**\n * @inheritdoc\n */\n public drain(timeout?: number): PromiseLike<boolean> {\n const oldTaskProducers = [...this._taskProducers];\n this._taskProducers = [];\n\n const drainController = new AbortController();\n this.drainSignal = drainController.signal;\n let tasks: PromiseLike<TransportMakeRequestResponse>[];\n try {\n tasks = oldTaskProducers.map(taskProducer => taskProducer());\n } finally {\n this.drainSignal = undefined;\n }\n\n return new Promise(resolve => {\n const timer = setTimeout(() => {\n if (timeout && timeout > 0) {\n // Requests still pending when the drain times out are aborted. Otherwise Cloudflare keeps them\n // until it cancels the invocation's `waitUntil` work and logs a warning.\n drainController.abort();\n resolve(false);\n }\n }, timeout);\n\n // This cannot reject\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n Promise.all(\n tasks.map(task =>\n task.then(null, () => {\n // catch all failed requests\n }),\n ),\n ).then(() => {\n // resolve to true if all fetch requests settled\n clearTimeout(timer);\n resolve(true);\n });\n });\n }\n}\n\n/**\n * Creates a Transport that uses the native fetch API to send events to Sentry.\n */\nexport function makeCloudflareTransport(options: CloudflareTransportOptions): Transport {\n const buffer = new IsolatedPromiseBuffer(options.bufferSize);\n\n function makeRequest(request: TransportRequest): PromiseLike<TransportMakeRequestResponse> {\n const drainSignal = buffer.drainSignal;\n const callerSignal = options.fetchOptions?.signal ?? undefined;\n const signal =\n drainSignal && callerSignal ? AbortSignal.any([drainSignal, callerSignal]) : (drainSignal ?? callerSignal);\n\n const requestOptions: RequestInit = {\n body: request.body as BodyInit,\n method: 'POST',\n headers: options.headers,\n ...options.fetchOptions,\n ...(signal ? { signal } : {}),\n };\n\n return suppressTracing(() => {\n return (options.fetch ?? fetch)(options.url, requestOptions).then(async response => {\n // Consume the response body to satisfy Cloudflare Workers' fetch requirements.\n // The runtime requires all fetch response bodies to be read or explicitly canceled\n // to prevent connection stalls and potential deadlocks. We read the body as text\n // even though we don't use the content, as Sentry's response information is in the headers.\n // See: https://github.com/getsentry/sentry-javascript/issues/18534\n try {\n await response.text();\n } catch {\n // no-op\n }\n\n return {\n statusCode: response.status,\n headers: {\n 'x-sentry-rate-limits': response.headers.get('X-Sentry-Rate-Limits'),\n 'retry-after': response.headers.get('Retry-After'),\n },\n };\n });\n });\n }\n\n return createTransport(options, makeRequest, buffer);\n}\n"],"names":["SENTRY_BUFFER_FULL_ERROR","suppressTracing","createTransport"],"mappings":"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"}
@@ -1,44 +1,15 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
 
3
- const node_module = require('node:module');
4
- const node_path = require('node:path');
5
- const MagicString = require('magic-string');
3
+ const providedModulePlugin = require('./providedModulePlugin.js');
6
4
 
7
- const PROVIDER_IDENTIFIER = "__SENTRY_FLUE_RUNTIME__";
8
- const FLUE_MODULE = "@flue/runtime";
9
- const FLUE_INTEGRATION_ID = /@sentry\/server-utils\/build\/esm\/integrations\/flue\.js$/;
10
- function isFlueIntegrationModuleId(id) {
11
- const normalizedId = id.replace(/\\/g, "/").replace(/[?#].*$/, "");
12
- return FLUE_INTEGRATION_ID.test(normalizedId);
13
- }
14
5
  function sentryFlueRuntimeProviderPlugin() {
15
- let providerSnippet;
16
- return {
6
+ return providedModulePlugin.createProvidedModulePlugin({
17
7
  name: "sentry-cloudflare-flue-runtime-provider",
18
- configResolved(config) {
19
- try {
20
- node_module.createRequire(node_path.resolve(config.root, "noop.js")).resolve(FLUE_MODULE);
21
- } catch (error) {
22
- const code = error?.code;
23
- if (code === "MODULE_NOT_FOUND" || code === "ERR_MODULE_NOT_FOUND") {
24
- return;
25
- }
26
- }
27
- providerSnippet = `import * as ${PROVIDER_IDENTIFIER} from '${FLUE_MODULE}';
28
- (globalThis.__SENTRY_ORCHESTRION__ = globalThis.__SENTRY_ORCHESTRION__ || {});
29
- (globalThis.__SENTRY_ORCHESTRION__.providedModules = globalThis.__SENTRY_ORCHESTRION__.providedModules || {});
30
- Object.defineProperty(globalThis.__SENTRY_ORCHESTRION__.providedModules, '${FLUE_MODULE}', { configurable: true, enumerable: true, get() { return ${PROVIDER_IDENTIFIER}; } });
31
- `;
32
- },
33
- transform(code, id) {
34
- if (!providerSnippet || !isFlueIntegrationModuleId(id) || code.includes(PROVIDER_IDENTIFIER)) return void 0;
35
- const ms = new MagicString.default(code);
36
- ms.prepend(providerSnippet);
37
- return { code: ms.toString(), map: ms.generateMap({ hires: true }) };
38
- }
39
- };
8
+ moduleName: "@flue/runtime",
9
+ identifier: "__SENTRY_FLUE_RUNTIME__",
10
+ integrationModule: "flue"
11
+ });
40
12
  }
41
13
 
42
- exports.isFlueIntegrationModuleId = isFlueIntegrationModuleId;
43
14
  exports.sentryFlueRuntimeProviderPlugin = sentryFlueRuntimeProviderPlugin;
44
15
  //# sourceMappingURL=flueRuntime.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"flueRuntime.js","sources":["../../../../src/vite/flueRuntime.ts"],"sourcesContent":["import { createRequire } from 'node:module';\nimport { resolve } from 'node:path';\nimport MagicString from 'magic-string';\n\n// Namespace binding the injected provider import uses; read back by the integration\n// off the global marker.\nconst PROVIDER_IDENTIFIER = '__SENTRY_FLUE_RUNTIME__';\n\nconst FLUE_MODULE = '@flue/runtime';\n\n// The bundled `@sentry/server-utils` Flue integration module (ESM build — the only one a\n// worker loads). It reads `@flue/runtime` off the global marker this provider populates,\n// because `instrument()` registers into module-scope state no channel payload can carry.\nconst FLUE_INTEGRATION_ID = /@sentry\\/server-utils\\/build\\/esm\\/integrations\\/flue\\.js$/;\n\n/** Whether `id` is the Sentry Flue integration module the provider injects into. */\nexport function isFlueIntegrationModuleId(id: string): boolean {\n const normalizedId = id.replace(/\\\\/g, '/').replace(/[?#].*$/, '');\n return FLUE_INTEGRATION_ID.test(normalizedId);\n}\n\n/**\n * Splices a static `import * as … from '@flue/runtime'` into Sentry's own Flue integration module\n * and exposes the namespace on the global orchestrion marker.\n *\n * Flue is registered rather than patched — `instrument()` writes into module-scope state — so\n * instrumenting it needs that module's own binding, and no channel payload carries one. On Node the\n * user passes it by calling `instrument()` themselves; a bundled worker has no `node_modules` to\n * resolve from, so it is supplied at build time instead.\n */\nexport function sentryFlueRuntimeProviderPlugin(): {\n name: string;\n configResolved(config: { root: string }): void;\n transform(code: string, id: string): { code: string; map: ReturnType<MagicString['generateMap']> } | undefined;\n} {\n let providerSnippet: string | undefined;\n\n return {\n name: 'sentry-cloudflare-flue-runtime-provider',\n\n configResolved(config: { root: string }): void {\n // Build-time only; never ships to the worker. Probed with CJS resolution, which an ESM-only\n // `@flue/runtime` fails with `ERR_PACKAGE_PATH_NOT_EXPORTED` — so only a module-not-found\n // counts as absent, and any other failure still injects and lets Vite report it. Not\n // `import.meta.resolve`: `parentURL` is ignored without a flag, and it is absent from the\n // CJS build.\n try {\n createRequire(resolve(config.root, 'noop.js')).resolve(FLUE_MODULE);\n } catch (error) {\n const code = (error as NodeJS.ErrnoException | undefined)?.code;\n if (code === 'MODULE_NOT_FOUND' || code === 'ERR_MODULE_NOT_FOUND') {\n return;\n }\n }\n // A getter where Mastra assigns: the bundler may evaluate Sentry's module before\n // `@flue/runtime` is initialized, and assigning there would store `undefined`.\n providerSnippet =\n `import * as ${PROVIDER_IDENTIFIER} from '${FLUE_MODULE}';\\n` +\n '(globalThis.__SENTRY_ORCHESTRION__ = globalThis.__SENTRY_ORCHESTRION__ || {});\\n' +\n '(globalThis.__SENTRY_ORCHESTRION__.providedModules = globalThis.__SENTRY_ORCHESTRION__.providedModules || {});\\n' +\n `Object.defineProperty(globalThis.__SENTRY_ORCHESTRION__.providedModules, '${FLUE_MODULE}', ` +\n `{ configurable: true, enumerable: true, get() { return ${PROVIDER_IDENTIFIER}; } });\\n`;\n },\n\n transform(code: string, id: string): { code: string; map: ReturnType<MagicString['generateMap']> } | undefined {\n // `code.includes` keeps this idempotent: a second pass over already-injected output would\n // otherwise emit a duplicate `import * as` binding, which is a syntax error.\n if (!providerSnippet || !isFlueIntegrationModuleId(id) || code.includes(PROVIDER_IDENTIFIER)) return undefined;\n\n const ms = new MagicString(code);\n ms.prepend(providerSnippet);\n return { code: ms.toString(), map: ms.generateMap({ hires: true }) };\n },\n };\n}\n"],"names":["createRequire","resolve","MagicString"],"mappings":";;;;;;AAMA,MAAM,mBAAA,GAAsB,yBAAA;AAE5B,MAAM,WAAA,GAAc,eAAA;AAKpB,MAAM,mBAAA,GAAsB,4DAAA;AAGrB,SAAS,0BAA0B,EAAA,EAAqB;AAC7D,EAAA,MAAM,YAAA,GAAe,GAAG,OAAA,CAAQ,KAAA,EAAO,GAAG,CAAA,CAAE,OAAA,CAAQ,WAAW,EAAE,CAAA;AACjE,EAAA,OAAO,mBAAA,CAAoB,KAAK,YAAY,CAAA;AAC9C;AAWO,SAAS,+BAAA,GAId;AACA,EAAA,IAAI,eAAA;AAEJ,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,yCAAA;AAAA,IAEN,eAAe,MAAA,EAAgC;AAM7C,MAAA,IAAI;AACF,QAAAA,yBAAA,CAAcC,kBAAQ,MAAA,CAAO,IAAA,EAAM,SAAS,CAAC,CAAA,CAAE,QAAQ,WAAW,CAAA;AAAA,MACpE,SAAS,KAAA,EAAO;AACd,QAAA,MAAM,OAAQ,KAAA,EAA6C,IAAA;AAC3D,QAAA,IAAI,IAAA,KAAS,kBAAA,IAAsB,IAAA,KAAS,sBAAA,EAAwB;AAClE,UAAA;AAAA,QACF;AAAA,MACF;AAGA,MAAA,eAAA,GACE,CAAA,YAAA,EAAe,mBAAmB,CAAA,OAAA,EAAU,WAAW,CAAA;AAAA;AAAA;AAAA,0EAAA,EAGsB,WAAW,6DAC9B,mBAAmB,CAAA;AAAA,CAAA;AAAA,IACjF,CAAA;AAAA,IAEA,SAAA,CAAU,MAAc,EAAA,EAAuF;AAG7G,MAAA,IAAI,CAAC,eAAA,IAAmB,CAAC,yBAAA,CAA0B,EAAE,KAAK,IAAA,CAAK,QAAA,CAAS,mBAAmB,CAAA,EAAG,OAAO,MAAA;AAErG,MAAA,MAAM,EAAA,GAAK,IAAIC,mBAAA,CAAY,IAAI,CAAA;AAC/B,MAAA,EAAA,CAAG,QAAQ,eAAe,CAAA;AAC1B,MAAA,OAAO,EAAE,IAAA,EAAM,EAAA,CAAG,QAAA,EAAS,EAAG,GAAA,EAAK,EAAA,CAAG,WAAA,CAAY,EAAE,KAAA,EAAO,IAAA,EAAM,CAAA,EAAE;AAAA,IACrE;AAAA,GACF;AACF;;;;;"}
1
+ {"version":3,"file":"flueRuntime.js","sources":["../../../../src/vite/flueRuntime.ts"],"sourcesContent":["import type { ProvidedModulePlugin } from './providedModulePlugin';\nimport { createProvidedModulePlugin } from './providedModulePlugin';\n\n/**\n * Splices a static `import * as … from '@flue/runtime'` into Sentry's own Flue integration module\n * and exposes the namespace on the global orchestrion marker.\n *\n * Flue is registered rather than patched — `instrument()` writes into module-scope state — so\n * instrumenting it needs that module's own binding, and no channel payload carries one. On Node the\n * user passes it by calling `instrument()` themselves; a bundled worker has no `node_modules` to\n * resolve from, so it is supplied at build time instead.\n */\nexport function sentryFlueRuntimeProviderPlugin(): ProvidedModulePlugin {\n return createProvidedModulePlugin({\n name: 'sentry-cloudflare-flue-runtime-provider',\n moduleName: '@flue/runtime',\n identifier: '__SENTRY_FLUE_RUNTIME__',\n integrationModule: 'flue',\n });\n}\n"],"names":["createProvidedModulePlugin"],"mappings":";;;;AAYO,SAAS,+BAAA,GAAwD;AACtE,EAAA,OAAOA,+CAAA,CAA2B;AAAA,IAChC,IAAA,EAAM,yCAAA;AAAA,IACN,UAAA,EAAY,eAAA;AAAA,IACZ,UAAA,EAAY,yBAAA;AAAA,IACZ,iBAAA,EAAmB;AAAA,GACpB,CAAA;AACH;;;;"}