@sentry/cloudflare 11.0.0-beta.2 → 11.0.0-rc.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 (95) hide show
  1. package/build/cjs/dev/index.js +4 -0
  2. package/build/cjs/dev/index.js.map +1 -1
  3. package/build/cjs/dev/instrumentations/worker/instrumentD1.js +19 -17
  4. package/build/cjs/dev/instrumentations/worker/instrumentD1.js.map +1 -1
  5. package/build/cjs/dev/instrumentations/worker/instrumentEmail.js +4 -3
  6. package/build/cjs/dev/instrumentations/worker/instrumentEmail.js.map +1 -1
  7. package/build/cjs/dev/instrumentations/worker/instrumentScheduled.js +10 -5
  8. package/build/cjs/dev/instrumentations/worker/instrumentScheduled.js.map +1 -1
  9. package/build/cjs/dev/orchestrion-deferred-channels.js +39 -0
  10. package/build/cjs/dev/orchestrion-deferred-channels.js.map +1 -0
  11. package/build/cjs/dev/orchestrion-diagnostics-channel.js +83 -0
  12. package/build/cjs/dev/orchestrion-diagnostics-channel.js.map +1 -0
  13. package/build/cjs/dev/vite/index.js +6 -1
  14. package/build/cjs/dev/vite/index.js.map +1 -1
  15. package/build/cjs/dev/vite/mastraObservability.js +39 -0
  16. package/build/cjs/dev/vite/mastraObservability.js.map +1 -0
  17. package/build/cjs/dev/wrapMethodWithSentry.js +8 -4
  18. package/build/cjs/dev/wrapMethodWithSentry.js.map +1 -1
  19. package/build/cjs/dev/wrapRequestHandlerWithInit.js +2 -0
  20. package/build/cjs/dev/wrapRequestHandlerWithInit.js.map +1 -1
  21. package/build/cjs/prod/index.js +4 -0
  22. package/build/cjs/prod/index.js.map +1 -1
  23. package/build/cjs/prod/instrumentations/worker/instrumentD1.js +19 -17
  24. package/build/cjs/prod/instrumentations/worker/instrumentD1.js.map +1 -1
  25. package/build/cjs/prod/instrumentations/worker/instrumentEmail.js +4 -3
  26. package/build/cjs/prod/instrumentations/worker/instrumentEmail.js.map +1 -1
  27. package/build/cjs/prod/instrumentations/worker/instrumentScheduled.js +10 -5
  28. package/build/cjs/prod/instrumentations/worker/instrumentScheduled.js.map +1 -1
  29. package/build/cjs/prod/orchestrion-deferred-channels.js +39 -0
  30. package/build/cjs/prod/orchestrion-deferred-channels.js.map +1 -0
  31. package/build/cjs/prod/orchestrion-diagnostics-channel.js +83 -0
  32. package/build/cjs/prod/orchestrion-diagnostics-channel.js.map +1 -0
  33. package/build/cjs/prod/vite/index.js +6 -1
  34. package/build/cjs/prod/vite/index.js.map +1 -1
  35. package/build/cjs/prod/vite/mastraObservability.js +39 -0
  36. package/build/cjs/prod/vite/mastraObservability.js.map +1 -0
  37. package/build/cjs/prod/wrapMethodWithSentry.js +8 -4
  38. package/build/cjs/prod/wrapMethodWithSentry.js.map +1 -1
  39. package/build/cjs/prod/wrapRequestHandlerWithInit.js +2 -0
  40. package/build/cjs/prod/wrapRequestHandlerWithInit.js.map +1 -1
  41. package/build/esm/dev/index.js +1 -1
  42. package/build/esm/dev/instrumentations/worker/instrumentD1.js +20 -18
  43. package/build/esm/dev/instrumentations/worker/instrumentD1.js.map +1 -1
  44. package/build/esm/dev/instrumentations/worker/instrumentEmail.js +6 -5
  45. package/build/esm/dev/instrumentations/worker/instrumentEmail.js.map +1 -1
  46. package/build/esm/dev/instrumentations/worker/instrumentScheduled.js +12 -7
  47. package/build/esm/dev/instrumentations/worker/instrumentScheduled.js.map +1 -1
  48. package/build/esm/dev/orchestrion-deferred-channels.js +35 -0
  49. package/build/esm/dev/orchestrion-deferred-channels.js.map +1 -0
  50. package/build/esm/dev/orchestrion-diagnostics-channel.js +77 -0
  51. package/build/esm/dev/orchestrion-diagnostics-channel.js.map +1 -0
  52. package/build/esm/dev/package.json +1 -1
  53. package/build/esm/dev/vite/index.js +6 -1
  54. package/build/esm/dev/vite/index.js.map +1 -1
  55. package/build/esm/dev/vite/mastraObservability.js +36 -0
  56. package/build/esm/dev/vite/mastraObservability.js.map +1 -0
  57. package/build/esm/dev/wrapMethodWithSentry.js +7 -3
  58. package/build/esm/dev/wrapMethodWithSentry.js.map +1 -1
  59. package/build/esm/dev/wrapRequestHandlerWithInit.js +2 -0
  60. package/build/esm/dev/wrapRequestHandlerWithInit.js.map +1 -1
  61. package/build/esm/prod/index.js +1 -1
  62. package/build/esm/prod/instrumentations/worker/instrumentD1.js +20 -18
  63. package/build/esm/prod/instrumentations/worker/instrumentD1.js.map +1 -1
  64. package/build/esm/prod/instrumentations/worker/instrumentEmail.js +6 -5
  65. package/build/esm/prod/instrumentations/worker/instrumentEmail.js.map +1 -1
  66. package/build/esm/prod/instrumentations/worker/instrumentScheduled.js +12 -7
  67. package/build/esm/prod/instrumentations/worker/instrumentScheduled.js.map +1 -1
  68. package/build/esm/prod/orchestrion-deferred-channels.js +35 -0
  69. package/build/esm/prod/orchestrion-deferred-channels.js.map +1 -0
  70. package/build/esm/prod/orchestrion-diagnostics-channel.js +77 -0
  71. package/build/esm/prod/orchestrion-diagnostics-channel.js.map +1 -0
  72. package/build/esm/prod/package.json +1 -1
  73. package/build/esm/prod/vite/index.js +6 -1
  74. package/build/esm/prod/vite/index.js.map +1 -1
  75. package/build/esm/prod/vite/mastraObservability.js +36 -0
  76. package/build/esm/prod/vite/mastraObservability.js.map +1 -0
  77. package/build/esm/prod/wrapMethodWithSentry.js +7 -3
  78. package/build/esm/prod/wrapMethodWithSentry.js.map +1 -1
  79. package/build/esm/prod/wrapRequestHandlerWithInit.js +2 -0
  80. package/build/esm/prod/wrapRequestHandlerWithInit.js.map +1 -1
  81. package/build/types/index.d.ts +2 -2
  82. package/build/types/index.d.ts.map +1 -1
  83. package/build/types/instrumentations/worker/instrumentD1.d.ts.map +1 -1
  84. package/build/types/instrumentations/worker/instrumentEmail.d.ts.map +1 -1
  85. package/build/types/instrumentations/worker/instrumentScheduled.d.ts.map +1 -1
  86. package/build/types/orchestrion-deferred-channels.d.ts +26 -0
  87. package/build/types/orchestrion-deferred-channels.d.ts.map +1 -0
  88. package/build/types/orchestrion-diagnostics-channel.d.ts +44 -0
  89. package/build/types/orchestrion-diagnostics-channel.d.ts.map +1 -0
  90. package/build/types/vite/index.d.ts.map +1 -1
  91. package/build/types/vite/mastraObservability.d.ts +27 -0
  92. package/build/types/vite/mastraObservability.d.ts.map +1 -0
  93. package/build/types/wrapMethodWithSentry.d.ts.map +1 -1
  94. package/build/types/wrapRequestHandlerWithInit.d.ts.map +1 -1
  95. package/package.json +10 -5
@@ -1,6 +1,6 @@
1
- import { SENTRY_SEGMENT_NAME_SOURCE, SENTRY_OP } from '@sentry/conventions/attributes';
1
+ import { SENTRY_SEGMENT_NAME_SOURCE, SENTRY_ORIGIN, FAAS_TRIGGER, CODE_FUNCTION_NAME, SENTRY_OP } from '@sentry/conventions/attributes';
2
2
  import { FUNCTION } from '@sentry/conventions/op';
3
- import { withIsolationScope, startSpan, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, captureException } from '@sentry/core';
3
+ import { withIsolationScope, startSpan, captureException } from '@sentry/core';
4
4
  import { flushAndDispose } from '../../flush.js';
5
5
  import { ensureInstrumented } from '../../instrument.js';
6
6
  import { getFinalOptions } from '../../options.js';
@@ -19,11 +19,12 @@ function wrapEmailHandler(emailMessage, options, context, fn) {
19
19
  addCloudResourceContext(isolationScope);
20
20
  return startSpan(
21
21
  {
22
- name: `Handle Email ${emailMessage.to}`,
22
+ name: "email",
23
23
  attributes: {
24
24
  [SENTRY_OP]: FUNCTION,
25
- "faas.trigger": "email",
26
- [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.faas.cloudflare.email",
25
+ [CODE_FUNCTION_NAME]: "email",
26
+ [FAAS_TRIGGER]: "email",
27
+ [SENTRY_ORIGIN]: "auto.faas.cloudflare.email",
27
28
  [SENTRY_SEGMENT_NAME_SOURCE]: "task"
28
29
  }
29
30
  },
@@ -1 +1 @@
1
- {"version":3,"file":"instrumentEmail.js","sources":["../../../../../src/instrumentations/worker/instrumentEmail.ts"],"sourcesContent":["import type { EmailMessage } from '@cloudflare/workers-types';\nimport type { AnyExportedHandler } from '../../types';\nimport type { env as cloudflareEnv } from 'cloudflare:workers';\nimport { SENTRY_SEGMENT_NAME_SOURCE, SENTRY_OP } from '@sentry/conventions/attributes';\nimport { FUNCTION } from '@sentry/conventions/op';\nimport { captureException, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startSpan, withIsolationScope } from '@sentry/core';\nimport type { CloudflareOptions } from '../../client';\nimport { flushAndDispose } from '../../flush';\nimport { ensureInstrumented } from '../../instrument';\nimport { getFinalOptions } from '../../options';\nimport { addCloudResourceContext } from '../../scope-utils';\nimport { init } from '../../sdk';\nimport { instrumentContext } from '../../utils/instrumentContext';\nimport { setInvocationState } from '../../utils/invocationContext';\nimport { instrumentEnv } from './instrumentEnv';\n\n/**\n * Core email handler logic - wraps execution with Sentry instrumentation.\n */\nfunction wrapEmailHandler(\n emailMessage: EmailMessage,\n options: CloudflareOptions,\n context: ExecutionContext,\n fn: () => unknown,\n): unknown {\n return withIsolationScope(isolationScope => {\n const waitUntil = context.waitUntil.bind(context);\n\n setInvocationState(isolationScope, { ctx: context });\n\n const client = init({ ...options, ctx: context });\n isolationScope.setClient(client);\n\n addCloudResourceContext(isolationScope);\n\n return startSpan(\n {\n name: `Handle Email ${emailMessage.to}`,\n attributes: {\n [SENTRY_OP]: FUNCTION,\n 'faas.trigger': 'email',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.faas.cloudflare.email',\n [SENTRY_SEGMENT_NAME_SOURCE]: 'task',\n },\n },\n async () => {\n try {\n return await fn();\n } catch (e) {\n captureException(e, { mechanism: { handled: false, type: 'auto.faas.cloudflare.email' } });\n throw e;\n } finally {\n waitUntil(flushAndDispose(client));\n }\n },\n );\n });\n}\n\n/**\n * Instruments an email handler for ExportedHandler (env/ctx come from args).\n */\nexport function instrumentExportedHandlerEmail<T extends AnyExportedHandler>(\n handler: T,\n optionsCallback: (env: typeof cloudflareEnv) => CloudflareOptions | undefined,\n): void {\n if (!('email' in handler) || typeof handler.email !== 'function') {\n return;\n }\n\n handler.email = ensureInstrumented(\n handler.email,\n original =>\n new Proxy(original, {\n apply(target, thisArg, args: Parameters<NonNullable<T['email']>>) {\n const [emailMessage, env, ctx] = args;\n const context = instrumentContext(ctx);\n const options = getFinalOptions(optionsCallback(env), env);\n args[1] = instrumentEnv(env, options);\n args[2] = context;\n\n return wrapEmailHandler(emailMessage, options, context, () => target.apply(thisArg, args));\n },\n }),\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;AAmBA,SAAS,gBAAA,CACP,YAAA,EACA,OAAA,EACA,OAAA,EACA,EAAA,EACS;AACT,EAAA,OAAO,mBAAmB,CAAA,cAAA,KAAkB;AAC1C,IAAA,MAAM,SAAA,GAAY,OAAA,CAAQ,SAAA,CAAU,IAAA,CAAK,OAAO,CAAA;AAEhD,IAAA,kBAAA,CAAmB,cAAA,EAAgB,EAAE,GAAA,EAAK,OAAA,EAAS,CAAA;AAEnD,IAAA,MAAM,SAAS,IAAA,CAAK,EAAE,GAAG,OAAA,EAAS,GAAA,EAAK,SAAS,CAAA;AAChD,IAAA,cAAA,CAAe,UAAU,MAAM,CAAA;AAE/B,IAAA,uBAAA,CAAwB,cAAc,CAAA;AAEtC,IAAA,OAAO,SAAA;AAAA,MACL;AAAA,QACE,IAAA,EAAM,CAAA,aAAA,EAAgB,YAAA,CAAa,EAAE,CAAA,CAAA;AAAA,QACrC,UAAA,EAAY;AAAA,UACV,CAAC,SAAS,GAAG,QAAA;AAAA,UACb,cAAA,EAAgB,OAAA;AAAA,UAChB,CAAC,gCAAgC,GAAG,4BAAA;AAAA,UACpC,CAAC,0BAA0B,GAAG;AAAA;AAChC,OACF;AAAA,MACA,YAAY;AACV,QAAA,IAAI;AACF,UAAA,OAAO,MAAM,EAAA,EAAG;AAAA,QAClB,SAAS,CAAA,EAAG;AACV,UAAA,gBAAA,CAAiB,CAAA,EAAG,EAAE,SAAA,EAAW,EAAE,SAAS,KAAA,EAAO,IAAA,EAAM,4BAAA,EAA6B,EAAG,CAAA;AACzF,UAAA,MAAM,CAAA;AAAA,QACR,CAAA,SAAE;AACA,UAAA,SAAA,CAAU,eAAA,CAAgB,MAAM,CAAC,CAAA;AAAA,QACnC;AAAA,MACF;AAAA,KACF;AAAA,EACF,CAAC,CAAA;AACH;AAKO,SAAS,8BAAA,CACd,SACA,eAAA,EACM;AACN,EAAA,IAAI,EAAE,OAAA,IAAW,OAAA,CAAA,IAAY,OAAO,OAAA,CAAQ,UAAU,UAAA,EAAY;AAChE,IAAA;AAAA,EACF;AAEA,EAAA,OAAA,CAAQ,KAAA,GAAQ,kBAAA;AAAA,IACd,OAAA,CAAQ,KAAA;AAAA,IACR,CAAA,QAAA,KACE,IAAI,KAAA,CAAM,QAAA,EAAU;AAAA,MAClB,KAAA,CAAM,MAAA,EAAQ,OAAA,EAAS,IAAA,EAA2C;AAChE,QAAA,MAAM,CAAC,YAAA,EAAc,GAAA,EAAK,GAAG,CAAA,GAAI,IAAA;AACjC,QAAA,MAAM,OAAA,GAAU,kBAAkB,GAAG,CAAA;AACrC,QAAA,MAAM,OAAA,GAAU,eAAA,CAAgB,eAAA,CAAgB,GAAG,GAAG,GAAG,CAAA;AACzD,QAAA,IAAA,CAAK,CAAC,CAAA,GAAI,aAAA,CAAc,GAAA,EAAK,OAAO,CAAA;AACpC,QAAA,IAAA,CAAK,CAAC,CAAA,GAAI,OAAA;AAEV,QAAA,OAAO,gBAAA,CAAiB,cAAc,OAAA,EAAS,OAAA,EAAS,MAAM,MAAA,CAAO,KAAA,CAAM,OAAA,EAAS,IAAI,CAAC,CAAA;AAAA,MAC3F;AAAA,KACD;AAAA,GACL;AACF;;;;"}
1
+ {"version":3,"file":"instrumentEmail.js","sources":["../../../../../src/instrumentations/worker/instrumentEmail.ts"],"sourcesContent":["import type { EmailMessage } from '@cloudflare/workers-types';\nimport type { AnyExportedHandler } from '../../types';\nimport type { env as cloudflareEnv } from 'cloudflare:workers';\nimport {\n SENTRY_SEGMENT_NAME_SOURCE,\n CODE_FUNCTION_NAME,\n SENTRY_OP,\n FAAS_TRIGGER,\n SENTRY_ORIGIN,\n} from '@sentry/conventions/attributes';\nimport { FUNCTION } from '@sentry/conventions/op';\nimport { captureException, startSpan, withIsolationScope } from '@sentry/core';\nimport type { CloudflareOptions } from '../../client';\nimport { flushAndDispose } from '../../flush';\nimport { ensureInstrumented } from '../../instrument';\nimport { getFinalOptions } from '../../options';\nimport { addCloudResourceContext } from '../../scope-utils';\nimport { init } from '../../sdk';\nimport { instrumentContext } from '../../utils/instrumentContext';\nimport { setInvocationState } from '../../utils/invocationContext';\nimport { instrumentEnv } from './instrumentEnv';\n\n/**\n * Core email handler logic - wraps execution with Sentry instrumentation.\n */\nfunction wrapEmailHandler(\n emailMessage: EmailMessage,\n options: CloudflareOptions,\n context: ExecutionContext,\n fn: () => unknown,\n): unknown {\n return withIsolationScope(isolationScope => {\n const waitUntil = context.waitUntil.bind(context);\n\n setInvocationState(isolationScope, { ctx: context });\n\n const client = init({ ...options, ctx: context });\n isolationScope.setClient(client);\n\n addCloudResourceContext(isolationScope);\n\n return startSpan(\n {\n name: 'email',\n attributes: {\n [SENTRY_OP]: FUNCTION,\n [CODE_FUNCTION_NAME]: 'email',\n [FAAS_TRIGGER]: 'email',\n [SENTRY_ORIGIN]: 'auto.faas.cloudflare.email',\n [SENTRY_SEGMENT_NAME_SOURCE]: 'task',\n },\n },\n async () => {\n try {\n return await fn();\n } catch (e) {\n captureException(e, { mechanism: { handled: false, type: 'auto.faas.cloudflare.email' } });\n throw e;\n } finally {\n waitUntil(flushAndDispose(client));\n }\n },\n );\n });\n}\n\n/**\n * Instruments an email handler for ExportedHandler (env/ctx come from args).\n */\nexport function instrumentExportedHandlerEmail<T extends AnyExportedHandler>(\n handler: T,\n optionsCallback: (env: typeof cloudflareEnv) => CloudflareOptions | undefined,\n): void {\n if (!('email' in handler) || typeof handler.email !== 'function') {\n return;\n }\n\n handler.email = ensureInstrumented(\n handler.email,\n original =>\n new Proxy(original, {\n apply(target, thisArg, args: Parameters<NonNullable<T['email']>>) {\n const [emailMessage, env, ctx] = args;\n const context = instrumentContext(ctx);\n const options = getFinalOptions(optionsCallback(env), env);\n args[1] = instrumentEnv(env, options);\n args[2] = context;\n\n return wrapEmailHandler(emailMessage, options, context, () => target.apply(thisArg, args));\n },\n }),\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;AAyBA,SAAS,gBAAA,CACP,YAAA,EACA,OAAA,EACA,OAAA,EACA,EAAA,EACS;AACT,EAAA,OAAO,mBAAmB,CAAA,cAAA,KAAkB;AAC1C,IAAA,MAAM,SAAA,GAAY,OAAA,CAAQ,SAAA,CAAU,IAAA,CAAK,OAAO,CAAA;AAEhD,IAAA,kBAAA,CAAmB,cAAA,EAAgB,EAAE,GAAA,EAAK,OAAA,EAAS,CAAA;AAEnD,IAAA,MAAM,SAAS,IAAA,CAAK,EAAE,GAAG,OAAA,EAAS,GAAA,EAAK,SAAS,CAAA;AAChD,IAAA,cAAA,CAAe,UAAU,MAAM,CAAA;AAE/B,IAAA,uBAAA,CAAwB,cAAc,CAAA;AAEtC,IAAA,OAAO,SAAA;AAAA,MACL;AAAA,QACE,IAAA,EAAM,OAAA;AAAA,QACN,UAAA,EAAY;AAAA,UACV,CAAC,SAAS,GAAG,QAAA;AAAA,UACb,CAAC,kBAAkB,GAAG,OAAA;AAAA,UACtB,CAAC,YAAY,GAAG,OAAA;AAAA,UAChB,CAAC,aAAa,GAAG,4BAAA;AAAA,UACjB,CAAC,0BAA0B,GAAG;AAAA;AAChC,OACF;AAAA,MACA,YAAY;AACV,QAAA,IAAI;AACF,UAAA,OAAO,MAAM,EAAA,EAAG;AAAA,QAClB,SAAS,CAAA,EAAG;AACV,UAAA,gBAAA,CAAiB,CAAA,EAAG,EAAE,SAAA,EAAW,EAAE,SAAS,KAAA,EAAO,IAAA,EAAM,4BAAA,EAA6B,EAAG,CAAA;AACzF,UAAA,MAAM,CAAA;AAAA,QACR,CAAA,SAAE;AACA,UAAA,SAAA,CAAU,eAAA,CAAgB,MAAM,CAAC,CAAA;AAAA,QACnC;AAAA,MACF;AAAA,KACF;AAAA,EACF,CAAC,CAAA;AACH;AAKO,SAAS,8BAAA,CACd,SACA,eAAA,EACM;AACN,EAAA,IAAI,EAAE,OAAA,IAAW,OAAA,CAAA,IAAY,OAAO,OAAA,CAAQ,UAAU,UAAA,EAAY;AAChE,IAAA;AAAA,EACF;AAEA,EAAA,OAAA,CAAQ,KAAA,GAAQ,kBAAA;AAAA,IACd,OAAA,CAAQ,KAAA;AAAA,IACR,CAAA,QAAA,KACE,IAAI,KAAA,CAAM,QAAA,EAAU;AAAA,MAClB,KAAA,CAAM,MAAA,EAAQ,OAAA,EAAS,IAAA,EAA2C;AAChE,QAAA,MAAM,CAAC,YAAA,EAAc,GAAA,EAAK,GAAG,CAAA,GAAI,IAAA;AACjC,QAAA,MAAM,OAAA,GAAU,kBAAkB,GAAG,CAAA;AACrC,QAAA,MAAM,OAAA,GAAU,eAAA,CAAgB,eAAA,CAAgB,GAAG,GAAG,GAAG,CAAA;AACzD,QAAA,IAAA,CAAK,CAAC,CAAA,GAAI,aAAA,CAAc,GAAA,EAAK,OAAO,CAAA;AACpC,QAAA,IAAA,CAAK,CAAC,CAAA,GAAI,OAAA;AAEV,QAAA,OAAO,gBAAA,CAAiB,cAAc,OAAA,EAAS,OAAA,EAAS,MAAM,MAAA,CAAO,KAAA,CAAM,OAAA,EAAS,IAAI,CAAC,CAAA;AAAA,MAC3F;AAAA,KACD;AAAA,GACL;AACF;;;;"}
@@ -1,6 +1,6 @@
1
- import { SENTRY_SEGMENT_NAME_SOURCE, SENTRY_OP } from '@sentry/conventions/attributes';
1
+ import { SENTRY_SEGMENT_NAME_SOURCE, SENTRY_ORIGIN, FAAS_TRIGGER, FAAS_TIME, FAAS_CRON, CODE_FUNCTION_NAME, SENTRY_DESCRIPTION, SENTRY_OP } from '@sentry/conventions/attributes';
2
2
  import { FUNCTION } from '@sentry/conventions/op';
3
- import { withIsolationScope, startSpan, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, captureException } from '@sentry/core';
3
+ import { withIsolationScope, startSpan, hasSpanStreamingEnabled, captureException } from '@sentry/core';
4
4
  import { flushAndDispose } from '../../flush.js';
5
5
  import { ensureInstrumented } from '../../instrument.js';
6
6
  import { getFinalOptions } from '../../options.js';
@@ -17,15 +17,20 @@ function wrapScheduledHandler(controller, options, context, fn) {
17
17
  const client = init({ ...options, ctx: context });
18
18
  isolationScope.setClient(client);
19
19
  addCloudResourceContext(isolationScope);
20
+ const description = `Scheduled Cron ${controller.cron}`;
20
21
  return startSpan(
21
22
  {
22
- name: `Scheduled Cron ${controller.cron}`,
23
+ name: client && hasSpanStreamingEnabled(client) ? "scheduled" : description,
23
24
  attributes: {
24
25
  [SENTRY_OP]: FUNCTION,
25
- "faas.cron": controller.cron,
26
- "faas.time": new Date(controller.scheduledTime).toISOString(),
27
- "faas.trigger": "timer",
28
- [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.faas.cloudflare.scheduled",
26
+ // override description inference by Relay to preserve the original (transaction-based) description.
27
+ // sentry-conventions can't map the special case for the "Scheduled Cron" prefix and the chron string.
28
+ [SENTRY_DESCRIPTION]: description,
29
+ [CODE_FUNCTION_NAME]: "scheduled",
30
+ [FAAS_CRON]: controller.cron,
31
+ [FAAS_TIME]: new Date(controller.scheduledTime).toISOString(),
32
+ [FAAS_TRIGGER]: "timer",
33
+ [SENTRY_ORIGIN]: "auto.faas.cloudflare.scheduled",
29
34
  [SENTRY_SEGMENT_NAME_SOURCE]: "task"
30
35
  }
31
36
  },
@@ -1 +1 @@
1
- {"version":3,"file":"instrumentScheduled.js","sources":["../../../../../src/instrumentations/worker/instrumentScheduled.ts"],"sourcesContent":["import type { ScheduledController } from '@cloudflare/workers-types';\nimport type { AnyExportedHandler } from '../../types';\nimport type { env as cloudflareEnv, WorkerEntrypoint } from 'cloudflare:workers';\nimport { SENTRY_SEGMENT_NAME_SOURCE, SENTRY_OP } from '@sentry/conventions/attributes';\nimport { FUNCTION } from '@sentry/conventions/op';\nimport { captureException, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startSpan, withIsolationScope } from '@sentry/core';\nimport type { CloudflareOptions } from '../../client';\nimport { flushAndDispose } from '../../flush';\nimport { ensureInstrumented } from '../../instrument';\nimport { getFinalOptions } from '../../options';\nimport { addCloudResourceContext } from '../../scope-utils';\nimport { init } from '../../sdk';\nimport { instrumentContext } from '../../utils/instrumentContext';\nimport { setInvocationState } from '../../utils/invocationContext';\nimport { instrumentEnv } from './instrumentEnv';\n\nfunction wrapScheduledHandler(\n controller: ScheduledController,\n options: CloudflareOptions,\n context: ExecutionContext,\n fn: () => unknown,\n): unknown {\n return withIsolationScope(isolationScope => {\n const waitUntil = context.waitUntil.bind(context);\n\n setInvocationState(isolationScope, { ctx: context });\n\n const client = init({ ...options, ctx: context });\n isolationScope.setClient(client);\n\n addCloudResourceContext(isolationScope);\n\n return startSpan(\n {\n name: `Scheduled Cron ${controller.cron}`,\n attributes: {\n [SENTRY_OP]: FUNCTION,\n 'faas.cron': controller.cron,\n 'faas.time': new Date(controller.scheduledTime).toISOString(),\n 'faas.trigger': 'timer',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.faas.cloudflare.scheduled',\n [SENTRY_SEGMENT_NAME_SOURCE]: 'task',\n },\n },\n async () => {\n try {\n return await fn();\n } catch (e) {\n captureException(e, { mechanism: { handled: false, type: 'auto.faas.cloudflare.scheduled' } });\n throw e;\n } finally {\n waitUntil(flushAndDispose(client));\n }\n },\n );\n });\n}\n\n/**\n * Instruments a scheduled handler for ExportedHandler (env/ctx come from args).\n */\nexport function instrumentExportedHandlerScheduled<T extends AnyExportedHandler>(\n handler: T,\n optionsCallback: (env: typeof cloudflareEnv) => CloudflareOptions | undefined,\n): void {\n if (!('scheduled' in handler) || typeof handler.scheduled !== 'function') {\n return;\n }\n\n handler.scheduled = ensureInstrumented(\n handler.scheduled,\n original =>\n new Proxy(original, {\n apply(target, thisArg, args: Parameters<NonNullable<T['scheduled']>>) {\n const [controller, env, ctx] = args;\n const context = instrumentContext(ctx);\n const options = getFinalOptions(optionsCallback(env), env);\n args[1] = instrumentEnv(env, options);\n args[2] = context;\n\n return wrapScheduledHandler(controller, options, context, () => target.apply(thisArg, args));\n },\n }),\n );\n}\n\n/**\n * Instruments a scheduled method for WorkerEntrypoint (options/context already available).\n */\nexport function instrumentWorkerEntrypointScheduled<T extends WorkerEntrypoint>(\n instance: T,\n options: CloudflareOptions,\n context: ExecutionContext,\n): void {\n if (!instance.scheduled) {\n return;\n }\n\n const original = instance.scheduled.bind(instance);\n instance.scheduled = new Proxy(original, {\n apply(target, thisArg, args: [ScheduledController]) {\n const [controller] = args;\n\n return wrapScheduledHandler(controller, options, context, () => Reflect.apply(target, thisArg, args));\n },\n });\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"instrumentScheduled.js","sources":["../../../../../src/instrumentations/worker/instrumentScheduled.ts"],"sourcesContent":["import type { ScheduledController } from '@cloudflare/workers-types';\nimport type { AnyExportedHandler } from '../../types';\nimport type { env as cloudflareEnv, WorkerEntrypoint } from 'cloudflare:workers';\nimport {\n SENTRY_SEGMENT_NAME_SOURCE,\n CODE_FUNCTION_NAME,\n SENTRY_OP,\n FAAS_CRON,\n FAAS_TIME,\n FAAS_TRIGGER,\n SENTRY_DESCRIPTION,\n SENTRY_ORIGIN,\n} from '@sentry/conventions/attributes';\nimport { FUNCTION } from '@sentry/conventions/op';\nimport { captureException, hasSpanStreamingEnabled, startSpan, withIsolationScope } from '@sentry/core';\nimport type { CloudflareOptions } from '../../client';\nimport { flushAndDispose } from '../../flush';\nimport { ensureInstrumented } from '../../instrument';\nimport { getFinalOptions } from '../../options';\nimport { addCloudResourceContext } from '../../scope-utils';\nimport { init } from '../../sdk';\nimport { instrumentContext } from '../../utils/instrumentContext';\nimport { setInvocationState } from '../../utils/invocationContext';\nimport { instrumentEnv } from './instrumentEnv';\n\nfunction wrapScheduledHandler(\n controller: ScheduledController,\n options: CloudflareOptions,\n context: ExecutionContext,\n fn: () => unknown,\n): unknown {\n return withIsolationScope(isolationScope => {\n const waitUntil = context.waitUntil.bind(context);\n\n setInvocationState(isolationScope, { ctx: context });\n\n const client = init({ ...options, ctx: context });\n isolationScope.setClient(client);\n\n addCloudResourceContext(isolationScope);\n\n const description = `Scheduled Cron ${controller.cron}`;\n\n return startSpan(\n {\n name: client && hasSpanStreamingEnabled(client) ? 'scheduled' : description,\n attributes: {\n [SENTRY_OP]: FUNCTION,\n // override description inference by Relay to preserve the original (transaction-based) description.\n // sentry-conventions can't map the special case for the \"Scheduled Cron\" prefix and the chron string.\n [SENTRY_DESCRIPTION]: description,\n [CODE_FUNCTION_NAME]: 'scheduled',\n [FAAS_CRON]: controller.cron,\n [FAAS_TIME]: new Date(controller.scheduledTime).toISOString(),\n [FAAS_TRIGGER]: 'timer',\n [SENTRY_ORIGIN]: 'auto.faas.cloudflare.scheduled',\n [SENTRY_SEGMENT_NAME_SOURCE]: 'task',\n },\n },\n async () => {\n try {\n return await fn();\n } catch (e) {\n captureException(e, { mechanism: { handled: false, type: 'auto.faas.cloudflare.scheduled' } });\n throw e;\n } finally {\n waitUntil(flushAndDispose(client));\n }\n },\n );\n });\n}\n\n/**\n * Instruments a scheduled handler for ExportedHandler (env/ctx come from args).\n */\nexport function instrumentExportedHandlerScheduled<T extends AnyExportedHandler>(\n handler: T,\n optionsCallback: (env: typeof cloudflareEnv) => CloudflareOptions | undefined,\n): void {\n if (!('scheduled' in handler) || typeof handler.scheduled !== 'function') {\n return;\n }\n\n handler.scheduled = ensureInstrumented(\n handler.scheduled,\n original =>\n new Proxy(original, {\n apply(target, thisArg, args: Parameters<NonNullable<T['scheduled']>>) {\n const [controller, env, ctx] = args;\n const context = instrumentContext(ctx);\n const options = getFinalOptions(optionsCallback(env), env);\n args[1] = instrumentEnv(env, options);\n args[2] = context;\n\n return wrapScheduledHandler(controller, options, context, () => target.apply(thisArg, args));\n },\n }),\n );\n}\n\n/**\n * Instruments a scheduled method for WorkerEntrypoint (options/context already available).\n */\nexport function instrumentWorkerEntrypointScheduled<T extends WorkerEntrypoint>(\n instance: T,\n options: CloudflareOptions,\n context: ExecutionContext,\n): void {\n if (!instance.scheduled) {\n return;\n }\n\n const original = instance.scheduled.bind(instance);\n instance.scheduled = new Proxy(original, {\n apply(target, thisArg, args: [ScheduledController]) {\n const [controller] = args;\n\n return wrapScheduledHandler(controller, options, context, () => Reflect.apply(target, thisArg, args));\n },\n });\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import { debug } from '@sentry/core';
2
+ import { DEBUG_BUILD } from './debug-build.js';
3
+
4
+ const deferred = [];
5
+ const DEFERRED = /* @__PURE__ */ Symbol("sentryOrchestrionDeferred");
6
+ function isGlobalScopeError(error) {
7
+ return error instanceof Error && /within global scope/.test(error.message);
8
+ }
9
+ function deferChannelEvent(channel, ctx) {
10
+ if (ctx[DEFERRED]) {
11
+ return;
12
+ }
13
+ ctx[DEFERRED] = true;
14
+ deferred.push({ channel, ctx, hadError: "error" in ctx });
15
+ }
16
+ function flushDeferredChannelEvents() {
17
+ if (deferred.length === 0) {
18
+ return;
19
+ }
20
+ const pending = deferred.splice(0, deferred.length);
21
+ for (const { channel, ctx, hadError } of pending) {
22
+ try {
23
+ channel.start.publish(ctx);
24
+ if (hadError) {
25
+ channel.error.publish(ctx);
26
+ }
27
+ channel.end.publish(ctx);
28
+ } catch (error) {
29
+ DEBUG_BUILD && debug.warn("[orchestrion] failed to emit a deferred channel event", error);
30
+ }
31
+ }
32
+ }
33
+
34
+ export { deferChannelEvent, flushDeferredChannelEvents, isGlobalScopeError };
35
+ //# sourceMappingURL=orchestrion-deferred-channels.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orchestrion-deferred-channels.js","sources":["../../../src/orchestrion-deferred-channels.ts"],"sourcesContent":["/**\n * Deferral for orchestrion channel events that could not be published when they occurred.\n *\n * On workerd, `channel.publish()`/`runStores()` throw at module/global scope, so a library\n * instantiated at module scope (`const app = new Hono()`) cannot emit its wrapping event\n * there. {@link ./orchestrion-diagnostics-channel} defers those events here;\n * {@link flushDeferredChannelEvents} emits them once a request is being handled (where\n * publish works) and the SDK's `init()` has registered subscribers.\n *\n * This module imports NO `node:*` builtin, so it is safe to pull into the request wrapper on\n * runtimes without `nodejs_compat` — there the queue is always empty (no channels were ever\n * injected) and the flush is a no-op.\n */\nimport { debug } from '@sentry/core';\nimport { DEBUG_BUILD } from './debug-build';\n\ntype Ctx = Record<string, unknown>;\n\n// The minimal slice of a `node:diagnostics_channel` TracingChannel we emit on. Declared locally\n// (rather than imported from `node:diagnostics_channel`) so this module's graph carries no `node:`\n// specifier — `wrapRequestHandler` must load on runtimes without `nodejs_compat`, and the\n// module-graph test forbids any `node:` import reachable from it.\ninterface PublishableTracingChannel {\n start: { publish(ctx: Ctx): void };\n end: { publish(ctx: Ctx): void };\n error: { publish(ctx: Ctx): void };\n}\n\ninterface DeferredChannelEvent {\n channel: PublishableTracingChannel;\n ctx: Ctx;\n hadError: boolean;\n}\n\nconst deferred: DeferredChannelEvent[] = [];\nconst DEFERRED = Symbol('sentryOrchestrionDeferred');\n\n/** workerd's global-scope guard is the only error we degrade on; anything else is a real fault. */\nexport function isGlobalScopeError(error: unknown): boolean {\n return error instanceof Error && /within global scope/.test(error.message);\n}\n\n/** Queue a channel event that could not be published now, at most once per context object. */\nexport function deferChannelEvent(channel: PublishableTracingChannel, ctx: Ctx): void {\n if ((ctx as Record<symbol, unknown>)[DEFERRED]) {\n return;\n }\n (ctx as Record<symbol, unknown>)[DEFERRED] = true;\n deferred.push({ channel, ctx, hadError: 'error' in ctx });\n}\n\n/**\n * Emit every deferred channel event via the real channel. Called at the top of the request\n * handler, after `init()` has subscribed. Uses bare `start`/`end` publish (never `runStores`),\n * so no `bindStore` producer runs and no span is parented to this request — only the plain\n * subscribers (the instance patchers) fire. Idempotent: it drains the queue, so later requests\n * and re-entrant calls are no-ops.\n */\nexport function flushDeferredChannelEvents(): void {\n if (deferred.length === 0) {\n return;\n }\n const pending = deferred.splice(0, deferred.length);\n for (const { channel, ctx, hadError } of pending) {\n try {\n channel.start.publish(ctx);\n if (hadError) {\n channel.error.publish(ctx);\n }\n channel.end.publish(ctx);\n } catch (error) {\n DEBUG_BUILD && debug.warn('[orchestrion] failed to emit a deferred channel event', error);\n }\n }\n}\n"],"names":[],"mappings":";;;AAkCA,MAAM,WAAmC,EAAC;AAC1C,MAAM,QAAA,0BAAkB,2BAA2B,CAAA;AAG5C,SAAS,mBAAmB,KAAA,EAAyB;AAC1D,EAAA,OAAO,KAAA,YAAiB,KAAA,IAAS,qBAAA,CAAsB,IAAA,CAAK,MAAM,OAAO,CAAA;AAC3E;AAGO,SAAS,iBAAA,CAAkB,SAAoC,GAAA,EAAgB;AACpF,EAAA,IAAK,GAAA,CAAgC,QAAQ,CAAA,EAAG;AAC9C,IAAA;AAAA,EACF;AACA,EAAC,GAAA,CAAgC,QAAQ,CAAA,GAAI,IAAA;AAC7C,EAAA,QAAA,CAAS,KAAK,EAAE,OAAA,EAAS,KAAK,QAAA,EAAU,OAAA,IAAW,KAAK,CAAA;AAC1D;AASO,SAAS,0BAAA,GAAmC;AACjD,EAAA,IAAI,QAAA,CAAS,WAAW,CAAA,EAAG;AACzB,IAAA;AAAA,EACF;AACA,EAAA,MAAM,OAAA,GAAU,QAAA,CAAS,MAAA,CAAO,CAAA,EAAG,SAAS,MAAM,CAAA;AAClD,EAAA,KAAA,MAAW,EAAE,OAAA,EAAS,GAAA,EAAK,QAAA,MAAc,OAAA,EAAS;AAChD,IAAA,IAAI;AACF,MAAA,OAAA,CAAQ,KAAA,CAAM,QAAQ,GAAG,CAAA;AACzB,MAAA,IAAI,QAAA,EAAU;AACZ,QAAA,OAAA,CAAQ,KAAA,CAAM,QAAQ,GAAG,CAAA;AAAA,MAC3B;AACA,MAAA,OAAA,CAAQ,GAAA,CAAI,QAAQ,GAAG,CAAA;AAAA,IACzB,SAAS,KAAA,EAAO;AACd,MAAA,WAAA,IAAe,KAAA,CAAM,IAAA,CAAK,uDAAA,EAAyD,KAAK,CAAA;AAAA,IAC1F;AAAA,EACF;AACF;;;;"}
@@ -0,0 +1,77 @@
1
+ import * as realDc from 'node:diagnostics_channel';
2
+ import { isGlobalScopeError, deferChannelEvent } from './orchestrion-deferred-channels.js';
3
+
4
+ const cache = /* @__PURE__ */ new Map();
5
+ function channelHasSubscribers(ch) {
6
+ const value = ch.hasSubscribers;
7
+ return typeof value === "function" ? Boolean(value.call(ch)) : Boolean(value);
8
+ }
9
+ function wrapSubChannel(tracing, real) {
10
+ return {
11
+ name: real.name,
12
+ // A method (not a getter) so it reads truthy as a property in the emit guard *and* is callable,
13
+ // matching workerd's native shape. Forced `true`: on Cloudflare a channel is only injected for a
14
+ // bundled integration, so the wrapper's no-subscriber short-circuit buys nothing — and disabling
15
+ // it is what lets the wrapper run (and reach us) at module scope before `init()` has subscribed.
16
+ hasSubscribers() {
17
+ return true;
18
+ },
19
+ publish(ctx) {
20
+ const hadSubscribers = channelHasSubscribers(real);
21
+ try {
22
+ real.publish(ctx);
23
+ } catch (error) {
24
+ if (!isGlobalScopeError(error)) {
25
+ throw error;
26
+ }
27
+ if (!hadSubscribers) {
28
+ deferChannelEvent(tracing, ctx);
29
+ }
30
+ }
31
+ },
32
+ runStores(ctx, fn, ...rest) {
33
+ try {
34
+ return real.runStores(ctx, fn, ...rest);
35
+ } catch (error) {
36
+ if (!isGlobalScopeError(error)) {
37
+ throw error;
38
+ }
39
+ return fn();
40
+ }
41
+ },
42
+ subscribe: real.subscribe.bind(real),
43
+ unsubscribe: real.unsubscribe.bind(real),
44
+ bindStore: real.bindStore.bind(real),
45
+ unbindStore: real.unbindStore.bind(real)
46
+ };
47
+ }
48
+ function tracingChannel(name) {
49
+ const cached = cache.get(name);
50
+ if (cached) {
51
+ return cached;
52
+ }
53
+ const real = realDc.tracingChannel(name);
54
+ const facade = {
55
+ start: wrapSubChannel(real, real.start),
56
+ end: wrapSubChannel(real, real.end),
57
+ asyncStart: wrapSubChannel(real, real.asyncStart),
58
+ asyncEnd: wrapSubChannel(real, real.asyncEnd),
59
+ error: wrapSubChannel(real, real.error),
60
+ // Iterator (`:next`) channels call these on the tracingChannel itself; delegate to the real
61
+ // implementation (they only run in-request, where publish/runStores are allowed).
62
+ traceSync: real.traceSync.bind(real),
63
+ tracePromise: real.tracePromise.bind(real),
64
+ traceCallback: real.traceCallback.bind(real),
65
+ subscribe: real.subscribe.bind(real),
66
+ unsubscribe: real.unsubscribe.bind(real)
67
+ };
68
+ cache.set(name, facade);
69
+ return facade;
70
+ }
71
+ const channel = realDc.channel;
72
+ const subscribe = realDc.subscribe;
73
+ const unsubscribe = realDc.unsubscribe;
74
+ const orchestrionDiagnosticsChannel = { tracingChannel, channel, subscribe, unsubscribe };
75
+
76
+ export { channel, orchestrionDiagnosticsChannel as default, subscribe, tracingChannel, unsubscribe };
77
+ //# sourceMappingURL=orchestrion-diagnostics-channel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orchestrion-diagnostics-channel.js","sources":["../../../src/orchestrion-diagnostics-channel.ts"],"sourcesContent":["/**\n * A workerd-safe `diagnostics_channel` façade for the orchestrion EMIT side, and the\n * `dcModule` target for the Cloudflare orchestrion build (see `vite/index.ts`): orchestrion's\n * transformed code imports `tracingChannel` from here (its default export) instead of\n * `node:diagnostics_channel`.\n *\n * On workerd, `channel.publish()` and `channel.runStores()` throw at module/global scope\n * (\"Disallowed operation called within global scope …\"); only inside a request handler do they\n * work. `subscribe` and channel creation are fine anywhere. Orchestrion's transformed code emits\n * `start.runStores(...)` / `end.publish(...)` at each wrapped call site, so a library\n * instantiated at module scope (`const app = new Hono()`) throws the moment it is wrapped — or,\n * if no subscriber exists yet, is skipped by the wrapper's `if (!hasSubscribers) return\n * __apm$traced()` short-circuit and never instrumented.\n *\n * This façade delegates to the real `node:diagnostics_channel` singletons — so subscribers, which\n * keep subscribing through `node:diagnostics_channel`, are reached unchanged — and changes two\n * things:\n *\n * 1. `hasSubscribers` always reports `true`, so the wrapper never short-circuits. On Cloudflare a\n * channel is only injected for an integration that is actually bundled, so the short-circuit\n * buys nothing — and disabling it is what lets the wrapper run (and reach us) at module scope,\n * before the per-request cached `init()` has subscribed.\n * 2. When a delegated `publish`/`runStores` throws the global-scope error, the event is queued via\n * {@link deferChannelEvent} instead. {@link flushDeferredChannelEvents}, called at the top of\n * the request handler after `init()`, emits it via the real channel — now inside a request,\n * where publish works.\n *\n * In a request handler nothing throws, so every call is a transparent pass-through to the real\n * channel: lifecycle channels (spans, context binding) behave exactly as on Node.\n */\nimport * as realDc from 'node:diagnostics_channel';\nimport { deferChannelEvent, isGlobalScopeError } from './orchestrion-deferred-channels';\n\ntype Ctx = Record<string, unknown>;\n\n// One façade per channel name (node's `tracingChannel` is itself a per-name singleton), built\n// once when the transformed module declares `const CH = tracingChannel(name)`.\nconst cache = new Map<string, realDc.TracingChannel>();\n\n// `Channel.hasSubscribers` is a getter on Node but a method on workerd — normalize both to a\n// boolean. (This shape difference is also why the emit wrapper never short-circuits on workerd:\n// its `if (!ch.start.hasSubscribers)` guard reads a truthy method reference.)\nfunction channelHasSubscribers(ch: realDc.Channel): boolean {\n const value = (ch as { hasSubscribers: unknown }).hasSubscribers;\n return typeof value === 'function' ? Boolean((value as () => unknown).call(ch)) : Boolean(value);\n}\n\nfunction wrapSubChannel(tracing: realDc.TracingChannel, real: realDc.Channel): realDc.Channel {\n return {\n name: real.name,\n // A method (not a getter) so it reads truthy as a property in the emit guard *and* is callable,\n // matching workerd's native shape. Forced `true`: on Cloudflare a channel is only injected for a\n // bundled integration, so the wrapper's no-subscriber short-circuit buys nothing — and disabling\n // it is what lets the wrapper run (and reach us) at module scope before `init()` has subscribed.\n hasSubscribers(): boolean {\n return true;\n },\n publish(ctx: Ctx): void {\n // Snapshot subscriber presence before publishing — it decides whether to defer if the\n // global-scope error fires below.\n const hadSubscribers = channelHasSubscribers(real);\n try {\n real.publish(ctx);\n } catch (error) {\n if (!isGlobalScopeError(error)) {\n throw error;\n }\n // We're at module scope (workerd throws here even with no subscribers). If someone was\n // already subscribed — top-level `Sentry.init()` — workerd delivered to them before\n // throwing, so they got it exactly once; don't defer. Otherwise — the common per-request\n // cached-init case — nobody received it, so defer for a subscriber that registers later\n // (at `init()`), which then gets it exactly once. In a request `publish` doesn't throw,\n // so this path never defers a normal in-request event.\n if (!hadSubscribers) {\n deferChannelEvent(tracing, ctx);\n }\n }\n },\n runStores(ctx: Ctx, fn: (...args: unknown[]) => unknown, ...rest: unknown[]): unknown {\n try {\n return real.runStores(ctx, fn, ...rest);\n } catch (error) {\n if (!isGlobalScopeError(error)) {\n throw error;\n }\n // publish() is gated too, so the store can't be established — just run the wrapped call\n // so its return value (e.g. the constructed instance) is produced now. Its own\n // `end.publish` runs back through this façade and is deferred.\n return fn();\n }\n },\n subscribe: real.subscribe.bind(real),\n unsubscribe: real.unsubscribe.bind(real),\n bindStore: real.bindStore.bind(real),\n unbindStore: real.unbindStore.bind(real),\n } as unknown as realDc.Channel;\n}\n\nexport function tracingChannel<T = Ctx>(name: string): realDc.TracingChannel<T> {\n const cached = cache.get(name);\n if (cached) {\n return cached;\n }\n\n const real = realDc.tracingChannel(name);\n const facade = {\n start: wrapSubChannel(real, real.start),\n end: wrapSubChannel(real, real.end),\n asyncStart: wrapSubChannel(real, real.asyncStart),\n asyncEnd: wrapSubChannel(real, real.asyncEnd),\n error: wrapSubChannel(real, real.error),\n // Iterator (`:next`) channels call these on the tracingChannel itself; delegate to the real\n // implementation (they only run in-request, where publish/runStores are allowed).\n traceSync: real.traceSync.bind(real),\n tracePromise: real.tracePromise.bind(real),\n traceCallback: real.traceCallback.bind(real),\n subscribe: real.subscribe.bind(real),\n unsubscribe: real.unsubscribe.bind(real),\n } as unknown as realDc.TracingChannel<T>;\n\n cache.set(name, facade);\n return facade;\n}\n\nexport const channel = realDc.channel;\nexport const subscribe = realDc.subscribe;\nexport const unsubscribe = realDc.unsubscribe;\n\n// Orchestrion's emit imports the default and destructures `tracingChannel`.\nexport default { tracingChannel, channel, subscribe, unsubscribe };\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"type":"module","version":"11.0.0-beta.2","sideEffects":false}
1
+ {"type":"module","version":"11.0.0-rc.0","sideEffects":false}
@@ -1,11 +1,16 @@
1
1
  import { sentryOrchestrionPlugin } from '@sentry/server-utils/orchestrion/vite';
2
2
  import { sentryCloudflareAutoInstrumentPlugin } from './autoInstrument.js';
3
+ import { sentryMastraObservabilityProviderPlugin } from './mastraObservability.js';
3
4
 
4
5
  function sentryCloudflareVitePlugin(options = {}) {
5
6
  return [
6
7
  sentryOrchestrionPlugin({
7
- buildTimeInstrumentation: options.buildTimeInstrumentation
8
+ buildTimeInstrumentation: options.buildTimeInstrumentation,
9
+ // Route the injected `tracingChannel` imports through the workerd-safe façade so a
10
+ // dependency wrapped at module scope (`const app = new Hono()`) doesn't throw on workerd.
11
+ dcModule: "@sentry/cloudflare/orchestrion-diagnostics-channel"
8
12
  }),
13
+ sentryMastraObservabilityProviderPlugin(),
9
14
  ...options.autoInstrumentation !== false ? [sentryCloudflareAutoInstrumentPlugin({ wranglerConfigPath: options.wranglerConfigPath })] : []
10
15
  ];
11
16
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../../src/vite/index.ts"],"sourcesContent":["// Published ESM-only via the `@sentry/cloudflare/vite` subpath export:\n// `@sentry/server-utils/orchestrion/vite` exposes no `require` condition, so a\n// CJS entry here would fail at resolution time (ERR_PACKAGE_PATH_NOT_EXPORTED).\n// The CJS rollup variant still emits this file, but `package.json` doesn't\n// expose it — same setup as `@sentry/server-utils/orchestrion/vite` itself.\nimport { sentryOrchestrionPlugin } from '@sentry/server-utils/orchestrion/vite';\nimport { sentryCloudflareAutoInstrumentPlugin } from './autoInstrument';\n\n/**\n * Options for {@link sentryCloudflareVitePlugin}.\n */\nexport interface SentryCloudflareVitePluginOptions {\n /**\n * Path to the wrangler config, relative to the Vite root (or absolute).\n * Set this when your config doesn't use a default name — e.g. when you\n * pass `configPath: './wrangler.agent.jsonc'` to `@cloudflare/vite-plugin`,\n * which the Sentry plugin cannot see. When set, only this file is read\n * (no default-name probing), and a warning is emitted if it is missing or\n * unparseable.\n *\n * @default undefined (probes `wrangler.json`, `wrangler.jsonc`, `wrangler.toml` at the Vite root)\n */\n wranglerConfigPath?: string;\n /**\n * Build-time automatic instrumentation of supported dependencies (e.g.\n * database clients like `mysql`) so the Sentry Cloudflare SDK can trace them\n * without monkey-patching, which wouldn't work in workerd anyway.\n *\n * When enabled, the plugin injects `diagnostics_channel.tracingChannel` calls\n * into the bundled packages and, next to each, a snippet that registers the\n * matching Sentry channel-subscriber factory on the global marker, which the\n * SDK picks up in `Sentry.withSentry()`. Both `vite build` and `vite dev` are\n * instrumented. Set to `false` to opt out.\n *\n * @default true\n */\n buildTimeInstrumentation?: boolean;\n /**\n * Automatically wraps your Worker at build time so you don't have to edit\n * your entry: the plugin reads your wrangler config, wraps the default\n * export with `Sentry.withSentry()` (sourcing options from a co-located\n * `instrument.*` file, falling back to env), and wraps any configured\n * Durable Object class with `instrumentDurableObjectWithSentry`. Both\n * `vite build` and `vite dev` are instrumented. Already-wrapped entries are\n * left alone, so this is safe alongside manual instrumentation. Set to\n * `false` to opt out.\n *\n * The plugin also adds the bindings that resolve to the wrapped classes (this worker's own\n * Durable Objects and self service bindings) to `rpcTracePropagationBindings`. Bindings to\n * other workers stay opt-in, their receivers may not run Sentry.\n *\n * @default true\n */\n autoInstrumentation?: boolean;\n}\n\n/**\n * Sentry Vite plugin for Cloudflare Workers.\n *\n * Add this plugin to your Vite configuration to enable additional Sentry\n * instrumentation for Cloudflare Workers built with Vite.\n *\n * By default, the plugin\n * - build-time instruments supported dependencies (such as database clients) so\n * they are traced without changing your application code — opt out with\n * `buildTimeInstrumentation: false`, and\n * - wraps your Worker entry (and any Durable Object, Workflow or\n * WorkerEntrypoint class in your wrangler config) with the matching Sentry\n * helper — opt out with `autoInstrumentation: false` and call\n * `Sentry.withSentry()` yourself.\n *\n * @example\n * ```ts\n * // vite.config.ts\n * import { cloudflare } from '@cloudflare/vite-plugin';\n * import { sentryCloudflareVitePlugin } from '@sentry/cloudflare/vite';\n * import { defineConfig } from 'vite';\n *\n * export default defineConfig({\n * plugins: [cloudflare(), sentryCloudflareVitePlugin()],\n * });\n * ```\n */\nexport function sentryCloudflareVitePlugin(options: SentryCloudflareVitePluginOptions = {}): Array<{ name: string }> {\n return [\n sentryOrchestrionPlugin({\n buildTimeInstrumentation: options.buildTimeInstrumentation,\n }),\n ...(options.autoInstrumentation !== false\n ? [sentryCloudflareAutoInstrumentPlugin({ wranglerConfigPath: options.wranglerConfigPath })]\n : []),\n ];\n}\n"],"names":[],"mappings":";;;AAmFO,SAAS,0BAAA,CAA2B,OAAA,GAA6C,EAAC,EAA4B;AACnH,EAAA,OAAO;AAAA,IACL,uBAAA,CAAwB;AAAA,MACtB,0BAA0B,OAAA,CAAQ;AAAA,KACnC,CAAA;AAAA,IACD,GAAI,OAAA,CAAQ,mBAAA,KAAwB,KAAA,GAChC,CAAC,oCAAA,CAAqC,EAAE,kBAAA,EAAoB,OAAA,CAAQ,kBAAA,EAAoB,CAAC,IACzF;AAAC,GACP;AACF;;;;"}
1
+ {"version":3,"file":"index.js","sources":["../../../../src/vite/index.ts"],"sourcesContent":["// Published ESM-only via the `@sentry/cloudflare/vite` subpath export:\n// `@sentry/server-utils/orchestrion/vite` exposes no `require` condition, so a\n// CJS entry here would fail at resolution time (ERR_PACKAGE_PATH_NOT_EXPORTED).\n// The CJS rollup variant still emits this file, but `package.json` doesn't\n// expose it — same setup as `@sentry/server-utils/orchestrion/vite` itself.\nimport { sentryOrchestrionPlugin } from '@sentry/server-utils/orchestrion/vite';\nimport { sentryCloudflareAutoInstrumentPlugin } from './autoInstrument';\nimport { sentryMastraObservabilityProviderPlugin } from './mastraObservability';\n\n/**\n * Options for {@link sentryCloudflareVitePlugin}.\n */\nexport interface SentryCloudflareVitePluginOptions {\n /**\n * Path to the wrangler config, relative to the Vite root (or absolute).\n * Set this when your config doesn't use a default name — e.g. when you\n * pass `configPath: './wrangler.agent.jsonc'` to `@cloudflare/vite-plugin`,\n * which the Sentry plugin cannot see. When set, only this file is read\n * (no default-name probing), and a warning is emitted if it is missing or\n * unparseable.\n *\n * @default undefined (probes `wrangler.json`, `wrangler.jsonc`, `wrangler.toml` at the Vite root)\n */\n wranglerConfigPath?: string;\n /**\n * Build-time automatic instrumentation of supported dependencies (e.g.\n * database clients like `mysql`) so the Sentry Cloudflare SDK can trace them\n * without monkey-patching, which wouldn't work in workerd anyway.\n *\n * When enabled, the plugin injects `diagnostics_channel.tracingChannel` calls\n * into the bundled packages and, next to each, a snippet that registers the\n * matching Sentry channel-subscriber factory on the global marker, which the\n * SDK picks up in `Sentry.withSentry()`. Both `vite build` and `vite dev` are\n * instrumented. Set to `false` to opt out.\n *\n * @default true\n */\n buildTimeInstrumentation?: boolean;\n /**\n * Automatically wraps your Worker at build time so you don't have to edit\n * your entry: the plugin reads your wrangler config, wraps the default\n * export with `Sentry.withSentry()` (sourcing options from a co-located\n * `instrument.*` file, falling back to env), and wraps any configured\n * Durable Object class with `instrumentDurableObjectWithSentry`. Both\n * `vite build` and `vite dev` are instrumented. Already-wrapped entries are\n * left alone, so this is safe alongside manual instrumentation. Set to\n * `false` to opt out.\n *\n * The plugin also adds the bindings that resolve to the wrapped classes (this worker's own\n * Durable Objects and self service bindings) to `rpcTracePropagationBindings`. Bindings to\n * other workers stay opt-in, their receivers may not run Sentry.\n *\n * @default true\n */\n autoInstrumentation?: boolean;\n}\n\n/**\n * Sentry Vite plugin for Cloudflare Workers.\n *\n * Add this plugin to your Vite configuration to enable additional Sentry\n * instrumentation for Cloudflare Workers built with Vite.\n *\n * By default, the plugin\n * - build-time instruments supported dependencies (such as database clients) so\n * they are traced without changing your application code — opt out with\n * `buildTimeInstrumentation: false`, and\n * - wraps your Worker entry (and any Durable Object, Workflow or\n * WorkerEntrypoint class in your wrangler config) with the matching Sentry\n * helper — opt out with `autoInstrumentation: false` and call\n * `Sentry.withSentry()` yourself.\n *\n * @example\n * ```ts\n * // vite.config.ts\n * import { cloudflare } from '@cloudflare/vite-plugin';\n * import { sentryCloudflareVitePlugin } from '@sentry/cloudflare/vite';\n * import { defineConfig } from 'vite';\n *\n * export default defineConfig({\n * plugins: [cloudflare(), sentryCloudflareVitePlugin()],\n * });\n * ```\n */\nexport function sentryCloudflareVitePlugin(options: SentryCloudflareVitePluginOptions = {}): Array<{ name: string }> {\n return [\n sentryOrchestrionPlugin({\n buildTimeInstrumentation: options.buildTimeInstrumentation,\n // Route the injected `tracingChannel` imports through the workerd-safe façade so a\n // dependency wrapped at module scope (`const app = new Hono()`) doesn't throw on workerd.\n dcModule: '@sentry/cloudflare/orchestrion-diagnostics-channel',\n }),\n sentryMastraObservabilityProviderPlugin(),\n ...(options.autoInstrumentation !== false\n ? [sentryCloudflareAutoInstrumentPlugin({ wranglerConfigPath: options.wranglerConfigPath })]\n : []),\n ];\n}\n"],"names":[],"mappings":";;;;AAoFO,SAAS,0BAAA,CAA2B,OAAA,GAA6C,EAAC,EAA4B;AACnH,EAAA,OAAO;AAAA,IACL,uBAAA,CAAwB;AAAA,MACtB,0BAA0B,OAAA,CAAQ,wBAAA;AAAA;AAAA;AAAA,MAGlC,QAAA,EAAU;AAAA,KACX,CAAA;AAAA,IACD,uCAAA,EAAwC;AAAA,IACxC,GAAI,OAAA,CAAQ,mBAAA,KAAwB,KAAA,GAChC,CAAC,oCAAA,CAAqC,EAAE,kBAAA,EAAoB,OAAA,CAAQ,kBAAA,EAAoB,CAAC,IACzF;AAAC,GACP;AACF;;;;"}
@@ -0,0 +1,36 @@
1
+ import { createRequire } from 'node:module';
2
+ import { resolve } from 'node:path';
3
+ import MagicString from 'magic-string';
4
+
5
+ const PROVIDER_IDENTIFIER = "__SENTRY_MASTRA_OBSERVABILITY__";
6
+ const MASTRA_INTEGRATION_ID = /@sentry\/server-utils\/build\/esm\/integrations\/mastra\.js$/;
7
+ function isMastraIntegrationModuleId(id) {
8
+ const normalizedId = id.replace(/\\/g, "/").replace(/[?#].*$/, "");
9
+ return MASTRA_INTEGRATION_ID.test(normalizedId);
10
+ }
11
+ function sentryMastraObservabilityProviderPlugin() {
12
+ let providerSnippet;
13
+ return {
14
+ name: "sentry-cloudflare-mastra-observability-provider",
15
+ configResolved(config) {
16
+ try {
17
+ createRequire(resolve(config.root, "noop.js")).resolve("@mastra/observability");
18
+ } catch {
19
+ return;
20
+ }
21
+ providerSnippet = `import * as ${PROVIDER_IDENTIFIER} from '@mastra/observability';
22
+ (globalThis.__SENTRY_ORCHESTRION__ = globalThis.__SENTRY_ORCHESTRION__ || {});
23
+ (globalThis.__SENTRY_ORCHESTRION__.providedModules = globalThis.__SENTRY_ORCHESTRION__.providedModules || {})['@mastra/observability'] = ${PROVIDER_IDENTIFIER};
24
+ `;
25
+ },
26
+ transform(code, id) {
27
+ if (!providerSnippet || !isMastraIntegrationModuleId(id)) return void 0;
28
+ const ms = new MagicString(code);
29
+ ms.prepend(providerSnippet);
30
+ return { code: ms.toString(), map: ms.generateMap({ hires: true }) };
31
+ }
32
+ };
33
+ }
34
+
35
+ export { isMastraIntegrationModuleId, sentryMastraObservabilityProviderPlugin };
36
+ //# sourceMappingURL=mastraObservability.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mastraObservability.js","sources":["../../../../src/vite/mastraObservability.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_MASTRA_OBSERVABILITY__';\n\n// The bundled `@sentry/server-utils` Mastra integration module (ESM build — the only\n// one a worker loads). Its `loadMastraObservability` reads `@mastra/observability` off\n// the global marker this provider populates, instead of `createRequire`, which cannot\n// resolve a package inside a bundled worker.\nconst MASTRA_INTEGRATION_ID = /@sentry\\/server-utils\\/build\\/esm\\/integrations\\/mastra\\.js$/;\n\n/** Whether `id` is the Sentry Mastra integration module the provider injects into. */\nexport function isMastraIntegrationModuleId(id: string): boolean {\n const normalizedId = id.replace(/\\\\/g, '/').replace(/[?#].*$/, '');\n return MASTRA_INTEGRATION_ID.test(normalizedId);\n}\n\n/**\n * Splices a static `import * as … from '@mastra/observability'` into Sentry's own\n * Mastra integration module and stashes the namespace on the global orchestrion\n * marker.\n *\n * On Cloudflare the integration cannot `createRequire('@mastra/observability')` to\n * bootstrap Mastra's observability pipeline — there is no on-disk `node_modules` in\n * workerd — so without this the user has to construct and wire up an `Observability`\n * themselves. The import is static (statically analyzable, no lazy `import()`), lands\n * in Sentry's module rather than the user's code, and is only emitted when the package\n * actually resolves; if it is absent, the integration keeps its Node `createRequire`\n * fallback and the marker stays empty.\n */\nexport function sentryMastraObservabilityProviderPlugin(): {\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-mastra-observability-provider',\n\n configResolved(config: { root: string }): void {\n // Resolved at build time (Node), so this `createRequire` never ships to the worker.\n try {\n createRequire(resolve(config.root, 'noop.js')).resolve('@mastra/observability');\n } catch {\n return;\n }\n providerSnippet =\n `import * as ${PROVIDER_IDENTIFIER} from '@mastra/observability';\\n` +\n '(globalThis.__SENTRY_ORCHESTRION__ = globalThis.__SENTRY_ORCHESTRION__ || {});\\n' +\n '(globalThis.__SENTRY_ORCHESTRION__.providedModules = globalThis.__SENTRY_ORCHESTRION__.providedModules || {})' +\n `['@mastra/observability'] = ${PROVIDER_IDENTIFIER};\\n`;\n },\n\n transform(code: string, id: string): { code: string; map: ReturnType<MagicString['generateMap']> } | undefined {\n if (!providerSnippet || !isMastraIntegrationModuleId(id)) 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":[],"mappings":";;;;AAMA,MAAM,mBAAA,GAAsB,iCAAA;AAM5B,MAAM,qBAAA,GAAwB,8DAAA;AAGvB,SAAS,4BAA4B,EAAA,EAAqB;AAC/D,EAAA,MAAM,YAAA,GAAe,GAAG,OAAA,CAAQ,KAAA,EAAO,GAAG,CAAA,CAAE,OAAA,CAAQ,WAAW,EAAE,CAAA;AACjE,EAAA,OAAO,qBAAA,CAAsB,KAAK,YAAY,CAAA;AAChD;AAeO,SAAS,uCAAA,GAId;AACA,EAAA,IAAI,eAAA;AAEJ,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,iDAAA;AAAA,IAEN,eAAe,MAAA,EAAgC;AAE7C,MAAA,IAAI;AACF,QAAA,aAAA,CAAc,QAAQ,MAAA,CAAO,IAAA,EAAM,SAAS,CAAC,CAAA,CAAE,QAAQ,uBAAuB,CAAA;AAAA,MAChF,CAAA,CAAA,MAAQ;AACN,QAAA;AAAA,MACF;AACA,MAAA,eAAA,GACE,eAAe,mBAAmB,CAAA;AAAA;AAAA,yIAAA,EAGH,mBAAmB,CAAA;AAAA,CAAA;AAAA,IACtD,CAAA;AAAA,IAEA,SAAA,CAAU,MAAc,EAAA,EAAuF;AAC7G,MAAA,IAAI,CAAC,eAAA,IAAmB,CAAC,2BAAA,CAA4B,EAAE,GAAG,OAAO,MAAA;AAEjE,MAAA,MAAM,EAAA,GAAK,IAAI,WAAA,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,4 +1,6 @@
1
- import { isThenable, SEMANTIC_ATTRIBUTE_SENTRY_OP, continueTrace, startNewTrace, isObjectLike, captureException, startSpan, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core';
1
+ import { CODE_FUNCTION_NAME, SENTRY_OP, SENTRY_ORIGIN } from '@sentry/conventions/attributes';
2
+ import { FUNCTION } from '@sentry/conventions/op';
3
+ import { isThenable, continueTrace, startNewTrace, isObjectLike, captureException, startSpan } from '@sentry/core';
2
4
  import { getOriginalWaitUntil, flushAndDispose } from './flush.js';
3
5
  import { ensureInstrumented } from './instrument.js';
4
6
  import { init } from './sdk.js';
@@ -91,8 +93,10 @@ function wrapMethodWithSentry(wrapperOptions, handler, callback, noMark) {
91
93
  }
92
94
  }
93
95
  const attributes = wrapperOptions.spanOp ? {
94
- [SEMANTIC_ATTRIBUTE_SENTRY_OP]: wrapperOptions.spanOp,
95
- [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: origin
96
+ [SENTRY_OP]: wrapperOptions.spanOp,
97
+ [SENTRY_ORIGIN]: origin,
98
+ // `function` spans are already named like their function name, so we just set `code.function.name` here.
99
+ ...wrapperOptions.spanOp === FUNCTION && { [CODE_FUNCTION_NAME]: methodName }
96
100
  } : {};
97
101
  const executeSpan = () => {
98
102
  return startSpan({ name: methodName, attributes }, (span) => {
@@ -1 +1 @@
1
- {"version":3,"file":"wrapMethodWithSentry.js","sources":["../../../src/wrapMethodWithSentry.ts"],"sourcesContent":["import type { DurableObjectStorage } from '@cloudflare/workers-types';\nimport type { SerializedTraceData } from '@sentry/core';\nimport {\n isObjectLike,\n captureException,\n continueTrace,\n isThenable,\n type Scope,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n startNewTrace as startNewTraceCore,\n startSpan,\n} from '@sentry/core';\nimport type { CloudflareOptions } from './client';\nimport type { ExecutionContextCompat } from './executionContext';\nimport { flushAndDispose, getOriginalWaitUntil } from './flush';\nimport { ensureInstrumented } from './instrument';\nimport { init } from './sdk';\nimport { getInvocationState, getInvocationWaitUntil } from './utils/invocationContext';\nimport { withInvocationIsolationScope } from './utils/invocationScope';\nimport { extractRpcMeta } from './utils/rpcMeta';\nimport { buildSpanLinks, getStoredSpanContext, storeSpanContext } from './utils/traceLinks';\n\n/** Extended DurableObjectState with originalStorage exposed by instrumentContext */\nexport interface InstrumentedDurableObjectState extends DurableObjectState {\n originalStorage?: DurableObjectStorage;\n}\n\n/**\n * Resolves uninstrumented DO storage for the current invocation.\n * Prefer `thisArg.ctx` (the live Durable Object instance) over the context captured at\n * construction time to avoid cross-DO I/O errors in the same isolate.\n */\nfunction resolveOriginalStorage(\n context: ExecutionContext | InstrumentedDurableObjectState | undefined,\n thisArg: unknown,\n): DurableObjectStorage | undefined {\n if (isObjectLike(thisArg) && 'ctx' in thisArg) {\n const doCtx = (thisArg as { ctx: InstrumentedDurableObjectState }).ctx;\n if (doCtx?.originalStorage) {\n return doCtx.originalStorage;\n }\n }\n\n if (context && 'originalStorage' in context && context.originalStorage) {\n return context.originalStorage;\n }\n\n return undefined;\n}\n\ntype MethodWrapperOptions = {\n /**\n * The span name, or a resolver called with the RPC metadata of the current call.\n * Returning `undefined` skips the span and only captures errors.\n */\n spanName?: string | ((rpcMeta: SerializedTraceData | undefined) => string | undefined);\n spanOp?: string;\n options: CloudflareOptions;\n context: ExecutionContext | InstrumentedDurableObjectState;\n /**\n * If true, starts a fresh trace instead of inheriting from a parent trace.\n * Useful for scheduled/independent invocations like alarms.\n *\n * If true, it also stores the current span context and links to the previous invocation's span.\n * Uses Durable Object storage to persist the link. The link is set asynchronously via `span.addLinks()`\n * in a `waitUntil` to avoid blocking.\n *\n * @default false\n */\n startNewTrace?: boolean;\n /**\n * The trace origin identifying which instrumentation created the span, e.g. `auto.faas.cloudflare.durable_object`.\n * Used both as the span's `sentry.origin` attribute and as the `mechanism.type` for captured exceptions.\n */\n origin: string;\n};\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type UncheckedMethod = (...args: any[]) => any;\ntype OriginalMethod = UncheckedMethod;\n\n/**\n * Wraps a method with Sentry error tracking and optional tracing.\n * Supports starting new traces and linking to previous invocations via Durable Object storage.\n *\n * @param wrapperOptions - The options for the wrapper.\n * @param handler - The method to wrap.\n * @param callback - The callback to call.\n * @param noMark - Whether to mark the method as instrumented.\n * @returns The wrapped method.\n */\nexport function wrapMethodWithSentry<T extends OriginalMethod>(\n wrapperOptions: MethodWrapperOptions,\n handler: T,\n callback?: (...args: Parameters<T>) => void,\n noMark?: true,\n): T {\n return ensureInstrumented(\n handler,\n original =>\n new Proxy(original, {\n apply(target, thisArg, rawArgs: Parameters<T>) {\n const { startNewTrace, origin } = wrapperOptions;\n\n // For RPC methods, extract Sentry trace context from the trailing argument.\n // The caller side (instrumentDurableObjectStub / JSRPC proxy) appends it;\n // we strip it here so the user's method never sees it.\n let args = rawArgs;\n let rpcMeta: SerializedTraceData | undefined;\n\n if (wrapperOptions.spanOp === 'rpc') {\n const extracted = extractRpcMeta(rawArgs);\n args = extracted.args;\n rpcMeta = extracted.rpcMeta;\n }\n\n const spanName =\n typeof wrapperOptions.spanName === 'function' ? wrapperOptions.spanName(rpcMeta) : wrapperOptions.spanName;\n\n const wrappedFunction = (scope: Scope): unknown | Promise<unknown> => {\n // In certain situations, the passed context can become undefined.\n // For example, for Astro while prerendering pages at build time.\n // see: https://github.com/getsentry/sentry-javascript/issues/13217\n const context: typeof wrapperOptions.context | undefined = wrapperOptions.context;\n\n // see: https://github.com/getsentry/sentry-javascript/issues/22328\n // Resolved once per invocation and cached on its state: reading `ctx.waitUntil`\n // on a native context is a runtime getter call.\n const invocationState = getInvocationState();\n const waitUntil = invocationState\n ? getInvocationWaitUntil(invocationState)\n : context\n ? getOriginalWaitUntil(context as ExecutionContextCompat)?.bind(context)\n : undefined;\n const storage = resolveOriginalStorage(context, thisArg);\n\n let scopeClient = scope.getClient();\n // Re-init when the scope has no usable client (a previous handler disposed it, e.g.\n // DO alarm handlers) or for `startNewTrace`, so the client points at this\n // invocation's context. With `cacheClient: true` `init()` returns the isolate's\n // cached client and only creates (and caches) a fresh one when nothing is cached yet;\n // `cacheClient: false` creates one per invocation.\n if (startNewTrace || !scopeClient?.getTransport()) {\n const client = init({\n ...wrapperOptions.options,\n ctx: context as unknown as ExecutionContext | undefined,\n });\n scope.setClient(client);\n scopeClient = client;\n }\n\n const clientToDispose = scopeClient;\n const methodName = spanName || 'unknown';\n\n const teardown = async (): Promise<void> => {\n if (startNewTrace && storage) {\n storeSpanContext(storage, methodName);\n }\n await flushAndDispose(clientToDispose);\n };\n\n const onFulfilled = (res: unknown) => {\n waitUntil?.(teardown());\n return res;\n };\n\n const captureAndRethrow = (e: unknown): never => {\n captureException(e, {\n mechanism: {\n type: origin,\n handled: false,\n },\n });\n throw e;\n };\n\n const onRejected = (e: unknown) => {\n try {\n return captureAndRethrow(e);\n } finally {\n waitUntil?.(teardown());\n }\n };\n\n if (!spanName) {\n try {\n if (callback) {\n callback(...args);\n }\n\n const result = Reflect.apply(target, thisArg, args);\n\n if (isThenable(result)) {\n return result.then(onFulfilled, onRejected);\n } else {\n return onFulfilled(result);\n }\n } catch (e) {\n return onRejected(e);\n }\n }\n\n const attributes = wrapperOptions.spanOp\n ? {\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: wrapperOptions.spanOp,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: origin,\n }\n : {};\n\n const executeSpan = (): unknown => {\n return startSpan({ name: methodName, attributes }, span => {\n if (startNewTrace && storage) {\n const storedContext = getStoredSpanContext(storage, methodName);\n\n if (storedContext) {\n span.addLinks(buildSpanLinks(storedContext));\n // TODO: Remove this once EAP can store span links. We currently only set this attribute so that we\n // can obtain the previous trace information from the EAP store. Long-term, EAP will handle\n // span links and then we should remove this again. Also throwing in a TODO(v11), to remind us\n // to check this at v11 time :)\n const sampledFlag = storedContext.sampled ? '1' : '0';\n span.setAttribute(\n 'sentry.previous_trace',\n `${storedContext.traceId}-${storedContext.spanId}-${sampledFlag}`,\n );\n }\n }\n\n try {\n const result = Reflect.apply(target, thisArg, args);\n\n if (isThenable(result)) {\n return result.then(undefined, captureAndRethrow);\n }\n return result;\n } catch (e) {\n return captureAndRethrow(e);\n }\n });\n };\n\n // The boundary flush (teardown) must run after the method span has ended, so the\n // span is in the buffer when that flush drains it. Running it inside the span\n // callback (before `startSpan` ends the span) would leave the span for the eager\n // path and cost a second flush per invocation.\n const runWithTeardown = (run: () => unknown): unknown => {\n let out: unknown;\n try {\n out = run();\n } catch (e) {\n // Synchronous throw: `startSpan` already ended the span and rethrew.\n waitUntil?.(teardown());\n throw e;\n }\n if (isThenable(out)) {\n return out.then(\n res => {\n waitUntil?.(teardown());\n return res;\n },\n e => {\n waitUntil?.(teardown());\n throw e;\n },\n );\n }\n waitUntil?.(teardown());\n return out;\n };\n\n if (rpcMeta) {\n return runWithTeardown(() =>\n continueTrace(\n { sentryTrace: rpcMeta['sentry-trace'] || '', baggage: rpcMeta.baggage || '' },\n executeSpan,\n ),\n );\n }\n\n if (startNewTrace) {\n return runWithTeardown(() => startNewTraceCore(() => executeSpan()));\n }\n\n return runWithTeardown(executeSpan);\n };\n\n return withInvocationIsolationScope(\n wrappedFunction,\n wrapperOptions.context as ExecutionContextCompat | undefined,\n );\n },\n }),\n noMark,\n );\n}\n"],"names":["startNewTrace","startNewTraceCore"],"mappings":"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"}
1
+ {"version":3,"file":"wrapMethodWithSentry.js","sources":["../../../src/wrapMethodWithSentry.ts"],"sourcesContent":["import type { DurableObjectStorage } from '@cloudflare/workers-types';\nimport type { SerializedTraceData } from '@sentry/core';\nimport { CODE_FUNCTION_NAME, SENTRY_OP, SENTRY_ORIGIN } from '@sentry/conventions/attributes';\nimport { FUNCTION } from '@sentry/conventions/op';\nimport {\n isObjectLike,\n captureException,\n continueTrace,\n isThenable,\n type Scope,\n startNewTrace as startNewTraceCore,\n startSpan,\n} from '@sentry/core';\nimport type { CloudflareOptions } from './client';\nimport type { ExecutionContextCompat } from './executionContext';\nimport { flushAndDispose, getOriginalWaitUntil } from './flush';\nimport { ensureInstrumented } from './instrument';\nimport { init } from './sdk';\nimport { getInvocationState, getInvocationWaitUntil } from './utils/invocationContext';\nimport { withInvocationIsolationScope } from './utils/invocationScope';\nimport { extractRpcMeta } from './utils/rpcMeta';\nimport { buildSpanLinks, getStoredSpanContext, storeSpanContext } from './utils/traceLinks';\n\n/** Extended DurableObjectState with originalStorage exposed by instrumentContext */\nexport interface InstrumentedDurableObjectState extends DurableObjectState {\n originalStorage?: DurableObjectStorage;\n}\n\n/**\n * Resolves uninstrumented DO storage for the current invocation.\n * Prefer `thisArg.ctx` (the live Durable Object instance) over the context captured at\n * construction time to avoid cross-DO I/O errors in the same isolate.\n */\nfunction resolveOriginalStorage(\n context: ExecutionContext | InstrumentedDurableObjectState | undefined,\n thisArg: unknown,\n): DurableObjectStorage | undefined {\n if (isObjectLike(thisArg) && 'ctx' in thisArg) {\n const doCtx = (thisArg as { ctx: InstrumentedDurableObjectState }).ctx;\n if (doCtx?.originalStorage) {\n return doCtx.originalStorage;\n }\n }\n\n if (context && 'originalStorage' in context && context.originalStorage) {\n return context.originalStorage;\n }\n\n return undefined;\n}\n\ntype MethodWrapperOptions = {\n /**\n * The span name, or a resolver called with the RPC metadata of the current call.\n * Returning `undefined` skips the span and only captures errors.\n */\n spanName?: string | ((rpcMeta: SerializedTraceData | undefined) => string | undefined);\n spanOp?: string;\n options: CloudflareOptions;\n context: ExecutionContext | InstrumentedDurableObjectState;\n /**\n * If true, starts a fresh trace instead of inheriting from a parent trace.\n * Useful for scheduled/independent invocations like alarms.\n *\n * If true, it also stores the current span context and links to the previous invocation's span.\n * Uses Durable Object storage to persist the link. The link is set asynchronously via `span.addLinks()`\n * in a `waitUntil` to avoid blocking.\n *\n * @default false\n */\n startNewTrace?: boolean;\n /**\n * The trace origin identifying which instrumentation created the span, e.g. `auto.faas.cloudflare.durable_object`.\n * Used both as the span's `sentry.origin` attribute and as the `mechanism.type` for captured exceptions.\n */\n origin: string;\n};\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type UncheckedMethod = (...args: any[]) => any;\ntype OriginalMethod = UncheckedMethod;\n\n/**\n * Wraps a method with Sentry error tracking and optional tracing.\n * Supports starting new traces and linking to previous invocations via Durable Object storage.\n *\n * @param wrapperOptions - The options for the wrapper.\n * @param handler - The method to wrap.\n * @param callback - The callback to call.\n * @param noMark - Whether to mark the method as instrumented.\n * @returns The wrapped method.\n */\nexport function wrapMethodWithSentry<T extends OriginalMethod>(\n wrapperOptions: MethodWrapperOptions,\n handler: T,\n callback?: (...args: Parameters<T>) => void,\n noMark?: true,\n): T {\n return ensureInstrumented(\n handler,\n original =>\n new Proxy(original, {\n apply(target, thisArg, rawArgs: Parameters<T>) {\n const { startNewTrace, origin } = wrapperOptions;\n\n // For RPC methods, extract Sentry trace context from the trailing argument.\n // The caller side (instrumentDurableObjectStub / JSRPC proxy) appends it;\n // we strip it here so the user's method never sees it.\n let args = rawArgs;\n let rpcMeta: SerializedTraceData | undefined;\n\n if (wrapperOptions.spanOp === 'rpc') {\n const extracted = extractRpcMeta(rawArgs);\n args = extracted.args;\n rpcMeta = extracted.rpcMeta;\n }\n\n const spanName =\n typeof wrapperOptions.spanName === 'function' ? wrapperOptions.spanName(rpcMeta) : wrapperOptions.spanName;\n\n const wrappedFunction = (scope: Scope): unknown | Promise<unknown> => {\n // In certain situations, the passed context can become undefined.\n // For example, for Astro while prerendering pages at build time.\n // see: https://github.com/getsentry/sentry-javascript/issues/13217\n const context: typeof wrapperOptions.context | undefined = wrapperOptions.context;\n\n // see: https://github.com/getsentry/sentry-javascript/issues/22328\n // Resolved once per invocation and cached on its state: reading `ctx.waitUntil`\n // on a native context is a runtime getter call.\n const invocationState = getInvocationState();\n const waitUntil = invocationState\n ? getInvocationWaitUntil(invocationState)\n : context\n ? getOriginalWaitUntil(context as ExecutionContextCompat)?.bind(context)\n : undefined;\n const storage = resolveOriginalStorage(context, thisArg);\n\n let scopeClient = scope.getClient();\n // Re-init when the scope has no usable client (a previous handler disposed it, e.g.\n // DO alarm handlers) or for `startNewTrace`, so the client points at this\n // invocation's context. With `cacheClient: true` `init()` returns the isolate's\n // cached client and only creates (and caches) a fresh one when nothing is cached yet;\n // `cacheClient: false` creates one per invocation.\n if (startNewTrace || !scopeClient?.getTransport()) {\n const client = init({\n ...wrapperOptions.options,\n ctx: context as unknown as ExecutionContext | undefined,\n });\n scope.setClient(client);\n scopeClient = client;\n }\n\n const clientToDispose = scopeClient;\n const methodName = spanName || 'unknown';\n\n const teardown = async (): Promise<void> => {\n if (startNewTrace && storage) {\n storeSpanContext(storage, methodName);\n }\n await flushAndDispose(clientToDispose);\n };\n\n const onFulfilled = (res: unknown) => {\n waitUntil?.(teardown());\n return res;\n };\n\n const captureAndRethrow = (e: unknown): never => {\n captureException(e, {\n mechanism: {\n type: origin,\n handled: false,\n },\n });\n throw e;\n };\n\n const onRejected = (e: unknown) => {\n try {\n return captureAndRethrow(e);\n } finally {\n waitUntil?.(teardown());\n }\n };\n\n if (!spanName) {\n try {\n if (callback) {\n callback(...args);\n }\n\n const result = Reflect.apply(target, thisArg, args);\n\n if (isThenable(result)) {\n return result.then(onFulfilled, onRejected);\n } else {\n return onFulfilled(result);\n }\n } catch (e) {\n return onRejected(e);\n }\n }\n\n const attributes = wrapperOptions.spanOp\n ? {\n [SENTRY_OP]: wrapperOptions.spanOp,\n [SENTRY_ORIGIN]: origin,\n // `function` spans are already named like their function name, so we just set `code.function.name` here.\n ...(wrapperOptions.spanOp === FUNCTION && { [CODE_FUNCTION_NAME]: methodName }),\n }\n : {};\n\n const executeSpan = (): unknown => {\n return startSpan({ name: methodName, attributes }, span => {\n if (startNewTrace && storage) {\n const storedContext = getStoredSpanContext(storage, methodName);\n\n if (storedContext) {\n span.addLinks(buildSpanLinks(storedContext));\n // TODO(v12): Remove this once the Sentry trace view finds linked traces via span links.\n // EAP stores span links, but the trace view still reads this attribute to navigate to the\n // previous/next trace.\n const sampledFlag = storedContext.sampled ? '1' : '0';\n span.setAttribute(\n 'sentry.previous_trace',\n `${storedContext.traceId}-${storedContext.spanId}-${sampledFlag}`,\n );\n }\n }\n\n try {\n const result = Reflect.apply(target, thisArg, args);\n\n if (isThenable(result)) {\n return result.then(undefined, captureAndRethrow);\n }\n return result;\n } catch (e) {\n return captureAndRethrow(e);\n }\n });\n };\n\n // The boundary flush (teardown) must run after the method span has ended, so the\n // span is in the buffer when that flush drains it. Running it inside the span\n // callback (before `startSpan` ends the span) would leave the span for the eager\n // path and cost a second flush per invocation.\n const runWithTeardown = (run: () => unknown): unknown => {\n let out: unknown;\n try {\n out = run();\n } catch (e) {\n // Synchronous throw: `startSpan` already ended the span and rethrew.\n waitUntil?.(teardown());\n throw e;\n }\n if (isThenable(out)) {\n return out.then(\n res => {\n waitUntil?.(teardown());\n return res;\n },\n e => {\n waitUntil?.(teardown());\n throw e;\n },\n );\n }\n waitUntil?.(teardown());\n return out;\n };\n\n if (rpcMeta) {\n return runWithTeardown(() =>\n continueTrace(\n { sentryTrace: rpcMeta['sentry-trace'] || '', baggage: rpcMeta.baggage || '' },\n executeSpan,\n ),\n );\n }\n\n if (startNewTrace) {\n return runWithTeardown(() => startNewTraceCore(() => executeSpan()));\n }\n\n return runWithTeardown(executeSpan);\n };\n\n return withInvocationIsolationScope(\n wrappedFunction,\n wrapperOptions.context as ExecutionContextCompat | undefined,\n );\n },\n }),\n noMark,\n );\n}\n"],"names":["startNewTrace","startNewTraceCore"],"mappings":"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"}
@@ -2,6 +2,7 @@ import { SENTRY_SEGMENT_NAME_SOURCE, SENTRY_OP, NETWORK_PROTOCOL_NAME, NETWORK_P
2
2
  import { HTTP_SERVER } from '@sentry/conventions/op';
3
3
  import { parseStringToURLObject, getHttpSpanDetailsFromUrlObject, hasSpanStreamingEnabled, HTTP_SPAN_NAME_FALLBACK, httpHeadersToSpanAttributes, winterCGHeadersToDict, captureException, continueTrace, startSpanManual, setHttpStatus } from '@sentry/core';
4
4
  import { captureIncomingRequestBody } from './integrations/httpServer.js';
5
+ import { flushDeferredChannelEvents } from './orchestrion-deferred-channels.js';
5
6
  import { getOriginalWaitUntil, flushAndDispose } from './flush.js';
6
7
  import { addCloudResourceContext, addRequest, addCultureContext } from './scope-utils.js';
7
8
  import { getInvocationState, getInvocationWaitUntil } from './utils/invocationContext.js';
@@ -20,6 +21,7 @@ function wrapRequestHandlerWithInit(wrapperOptions, handler, initSdk) {
20
21
  const errorMechanismType = getRequestErrorMechanismType(context);
21
22
  const client = initSdk({ ...options, ctx: context });
22
23
  isolationScope.setClient(client);
24
+ flushDeferredChannelEvents();
23
25
  const urlObject = parseStringToURLObject(request.url);
24
26
  const [rawName, attributes] = getHttpSpanDetailsFromUrlObject(
25
27
  urlObject,
@@ -1 +1 @@
1
- {"version":3,"file":"wrapRequestHandlerWithInit.js","sources":["../../../src/wrapRequestHandlerWithInit.ts"],"sourcesContent":["import type { CfProperties, IncomingRequestCfProperties } from '@cloudflare/workers-types';\nimport {\n NETWORK_PROTOCOL_NAME,\n NETWORK_PROTOCOL_VERSION,\n SENTRY_OP,\n SENTRY_SEGMENT_NAME_SOURCE,\n} from '@sentry/conventions/attributes';\nimport { HTTP_SERVER } from '@sentry/conventions/op';\nimport {\n captureException,\n continueTrace,\n getHttpSpanDetailsFromUrlObject,\n hasSpanStreamingEnabled,\n httpHeadersToSpanAttributes,\n HTTP_SPAN_NAME_FALLBACK,\n parseStringToURLObject,\n setHttpStatus,\n startSpanManual,\n winterCGHeadersToDict,\n} from '@sentry/core';\nimport { captureIncomingRequestBody } from './integrations/httpServer';\nimport type { CloudflareClient, CloudflareOptions } from './client';\nimport type { ExecutionContextCompat } from './executionContext';\nimport { flushAndDispose, getOriginalWaitUntil } from './flush';\nimport { addCloudResourceContext, addCultureContext, addRequest } from './scope-utils';\nimport { getInvocationState, getInvocationWaitUntil } from './utils/invocationContext';\nimport { withInvocationIsolationScope } from './utils/invocationScope';\nimport { classifyResponseStreaming } from './utils/streaming';\n\nfunction getRequestErrorMechanismType(context: ExecutionContextCompat | undefined): string {\n // Durable Object fetch handlers use DO state as context (see instrumentDurableObjectWithSentry)\n return context && 'storage' in context ? 'auto.faas.cloudflare.durable_object' : 'auto.http.cloudflare';\n}\n\nexport interface RequestHandlerWrapperOptions {\n options: CloudflareOptions;\n request: Request<unknown, IncomingRequestCfProperties<unknown> | CfProperties<unknown>>;\n context: ExecutionContextCompat | undefined;\n /**\n * If true, errors will be captured, rethrown and sent to Sentry.\n * Otherwise, errors are rethrown but not captured.\n *\n * You most likely don't want to set this to `false`, if you use `wrapRequestHandler` directly.\n * This is primarily meant as an escape hatch for higher-level SDKs relying on additional error\n * capturing mechanisms where this wrapper captures errors too early or too generally.\n *\n * @default true\n */\n captureErrors?: boolean;\n}\n\ntype InitSdk = (options: CloudflareOptions) => CloudflareClient | undefined;\n\n/**\n * Same as {@link wrapRequestHandler}, but with the SDK initialization injected.\n *\n * Wrappers that are only reachable from the main entry point — where `nodejs_compat` is a\n * requirement anyway — pass `init` from `sdk.ts` to get the full default integrations.\n *\n * @internal\n */\nexport function wrapRequestHandlerWithInit(\n wrapperOptions: RequestHandlerWrapperOptions,\n handler: (...args: unknown[]) => Response | Promise<Response>,\n initSdk: InitSdk,\n): Promise<Response> {\n return withInvocationIsolationScope(async isolationScope => {\n const { options, request, captureErrors = true } = wrapperOptions;\n const context = wrapperOptions.context;\n\n // The un-instrumented waitUntil, so flushAndDispose cannot deadlock on the flush\n // lock's own wrapper. Resolved once per invocation and cached on its state; reading\n // `ctx.waitUntil` on a native context is a runtime getter call.\n const invocationState = getInvocationState();\n const waitUntil = invocationState\n ? getInvocationWaitUntil(invocationState)\n : context\n ? getOriginalWaitUntil(context).bind(context)\n : undefined;\n const errorMechanismType = getRequestErrorMechanismType(context);\n\n const client = initSdk({ ...options, ctx: context });\n isolationScope.setClient(client);\n\n const urlObject = parseStringToURLObject(request.url);\n const [rawName, attributes] = getHttpSpanDetailsFromUrlObject(\n urlObject,\n 'server',\n 'auto.http.cloudflare',\n request,\n undefined,\n client,\n );\n // With span streaming, span names have to be low cardinality, so we can't fall back to the URL.\n // A `route` source means the name already is (e.g. the `/` path), so it is kept as-is.\n const name =\n attributes[SENTRY_SEGMENT_NAME_SOURCE] === 'route' || !client || !hasSpanStreamingEnabled(client)\n ? rawName\n : request.method?.toUpperCase() || HTTP_SPAN_NAME_FALLBACK;\n\n const contentLength = request.headers.get('content-length');\n if (contentLength) {\n attributes['http.request.body.size'] = parseInt(contentLength, 10);\n }\n\n const userAgentHeader = request.headers.get('user-agent');\n if (userAgentHeader) {\n attributes['user_agent.original'] = userAgentHeader;\n }\n\n if (client) {\n Object.assign(\n attributes,\n httpHeadersToSpanAttributes(winterCGHeadersToDict(request.headers), client.getDataCollectionOptions()),\n );\n }\n\n attributes[SENTRY_OP] = HTTP_SERVER;\n\n addCloudResourceContext(isolationScope);\n addRequest(isolationScope, request);\n if (request.cf) {\n addCultureContext(isolationScope, request.cf);\n\n if (typeof request.cf.httpProtocol === 'string') {\n const [protocolName, protocolVersion] = request.cf.httpProtocol.toLowerCase().split('/');\n attributes[NETWORK_PROTOCOL_NAME] = protocolName;\n attributes[NETWORK_PROTOCOL_VERSION] = protocolVersion;\n }\n }\n\n // Do not capture spans for OPTIONS and HEAD requests\n if (request.method === 'OPTIONS' || request.method === 'HEAD') {\n try {\n return await handler();\n } catch (e) {\n if (captureErrors) {\n captureException(e, { mechanism: { handled: false, type: errorMechanismType } });\n }\n throw e;\n } finally {\n waitUntil?.(flushAndDispose(client));\n }\n }\n\n if (client) {\n await captureIncomingRequestBody(client, request);\n }\n\n return continueTrace(\n { sentryTrace: request.headers.get('sentry-trace') || '', baggage: request.headers.get('baggage') },\n () => {\n // Note: This span will not have a duration unless I/O happens in the handler. This is\n // because of how the cloudflare workers runtime works.\n // See: https://developers.cloudflare.com/workers/runtime-apis/performance/\n\n // Use startSpanManual to control when span ends (needed for streaming responses)\n return startSpanManual({ name, attributes }, async span => {\n let res: Response;\n\n try {\n res = await handler();\n setHttpStatus(span, res.status);\n\n // After the handler runs, the span name might have been updated by nested instrumentation\n // (e.g., Remix parameterizing routes). The span should already have the correct name\n // from that instrumentation, so we don't need to do anything here.\n } catch (e) {\n span.end();\n if (captureErrors) {\n captureException(e, { mechanism: { handled: false, type: errorMechanismType } });\n }\n waitUntil?.(flushAndDispose(client));\n throw e;\n }\n\n // Classify response to detect actual streaming\n const classification = classifyResponseStreaming(res);\n\n if (classification.isStreaming && res.body) {\n try {\n let ended = false;\n\n const endSpanOnce = (): void => {\n if (ended) return;\n\n ended = true;\n span.end();\n waitUntil?.(flushAndDispose(client));\n };\n\n const transform = new TransformStream({\n flush() {\n // Source stream completed normally.\n endSpanOnce();\n },\n cancel() {\n // Client disconnected (or downstream cancelled). The `cancel`\n // is being called while the response is still considered\n // active, so this is a safe place to end the span.\n endSpanOnce();\n },\n });\n\n return new Response(res.body.pipeThrough(transform), {\n status: res.status,\n statusText: res.statusText,\n headers: res.headers,\n });\n } catch {\n span.end();\n waitUntil?.(flushAndDispose(client));\n return res;\n }\n }\n\n // Non-streaming response - end span immediately and return original\n span.end();\n\n // Don't dispose for protocol upgrades (101 Switching Protocols) - the connection stays alive.\n // This includes WebSocket upgrades where webSocketMessage/webSocketClose handlers\n // will still be called and may need the client to capture errors.\n if (res.status === 101) {\n waitUntil?.(client?.flush(2000));\n } else {\n waitUntil?.(flushAndDispose(client));\n }\n return res;\n });\n },\n );\n }, wrapperOptions.context);\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"wrapRequestHandlerWithInit.js","sources":["../../../src/wrapRequestHandlerWithInit.ts"],"sourcesContent":["import type { CfProperties, IncomingRequestCfProperties } from '@cloudflare/workers-types';\nimport {\n NETWORK_PROTOCOL_NAME,\n NETWORK_PROTOCOL_VERSION,\n SENTRY_OP,\n SENTRY_SEGMENT_NAME_SOURCE,\n} from '@sentry/conventions/attributes';\nimport { HTTP_SERVER } from '@sentry/conventions/op';\nimport {\n captureException,\n continueTrace,\n getHttpSpanDetailsFromUrlObject,\n hasSpanStreamingEnabled,\n httpHeadersToSpanAttributes,\n HTTP_SPAN_NAME_FALLBACK,\n parseStringToURLObject,\n setHttpStatus,\n startSpanManual,\n winterCGHeadersToDict,\n} from '@sentry/core';\nimport { captureIncomingRequestBody } from './integrations/httpServer';\nimport { flushDeferredChannelEvents } from './orchestrion-deferred-channels';\nimport type { CloudflareClient, CloudflareOptions } from './client';\nimport type { ExecutionContextCompat } from './executionContext';\nimport { flushAndDispose, getOriginalWaitUntil } from './flush';\nimport { addCloudResourceContext, addCultureContext, addRequest } from './scope-utils';\nimport { getInvocationState, getInvocationWaitUntil } from './utils/invocationContext';\nimport { withInvocationIsolationScope } from './utils/invocationScope';\nimport { classifyResponseStreaming } from './utils/streaming';\n\nfunction getRequestErrorMechanismType(context: ExecutionContextCompat | undefined): string {\n // Durable Object fetch handlers use DO state as context (see instrumentDurableObjectWithSentry)\n return context && 'storage' in context ? 'auto.faas.cloudflare.durable_object' : 'auto.http.cloudflare';\n}\n\nexport interface RequestHandlerWrapperOptions {\n options: CloudflareOptions;\n request: Request<unknown, IncomingRequestCfProperties<unknown> | CfProperties<unknown>>;\n context: ExecutionContextCompat | undefined;\n /**\n * If true, errors will be captured, rethrown and sent to Sentry.\n * Otherwise, errors are rethrown but not captured.\n *\n * You most likely don't want to set this to `false`, if you use `wrapRequestHandler` directly.\n * This is primarily meant as an escape hatch for higher-level SDKs relying on additional error\n * capturing mechanisms where this wrapper captures errors too early or too generally.\n *\n * @default true\n */\n captureErrors?: boolean;\n}\n\ntype InitSdk = (options: CloudflareOptions) => CloudflareClient | undefined;\n\n/**\n * Same as {@link wrapRequestHandler}, but with the SDK initialization injected.\n *\n * Wrappers that are only reachable from the main entry point — where `nodejs_compat` is a\n * requirement anyway — pass `init` from `sdk.ts` to get the full default integrations.\n *\n * @internal\n */\nexport function wrapRequestHandlerWithInit(\n wrapperOptions: RequestHandlerWrapperOptions,\n handler: (...args: unknown[]) => Response | Promise<Response>,\n initSdk: InitSdk,\n): Promise<Response> {\n return withInvocationIsolationScope(async isolationScope => {\n const { options, request, captureErrors = true } = wrapperOptions;\n const context = wrapperOptions.context;\n\n // The un-instrumented waitUntil, so flushAndDispose cannot deadlock on the flush\n // lock's own wrapper. Resolved once per invocation and cached on its state; reading\n // `ctx.waitUntil` on a native context is a runtime getter call.\n const invocationState = getInvocationState();\n const waitUntil = invocationState\n ? getInvocationWaitUntil(invocationState)\n : context\n ? getOriginalWaitUntil(context).bind(context)\n : undefined;\n const errorMechanismType = getRequestErrorMechanismType(context);\n\n const client = initSdk({ ...options, ctx: context });\n isolationScope.setClient(client);\n\n // Emit any orchestrion channel events deferred at module scope (e.g. a top-level\n // `const app = new Hono()` wrapped by build-time instrumentation): we're now in a\n // request, where workerd allows publish, and init() above has registered subscribers.\n flushDeferredChannelEvents();\n\n const urlObject = parseStringToURLObject(request.url);\n const [rawName, attributes] = getHttpSpanDetailsFromUrlObject(\n urlObject,\n 'server',\n 'auto.http.cloudflare',\n request,\n undefined,\n client,\n );\n // With span streaming, span names have to be low cardinality, so we can't fall back to the URL.\n // A `route` source means the name already is (e.g. the `/` path), so it is kept as-is.\n const name =\n attributes[SENTRY_SEGMENT_NAME_SOURCE] === 'route' || !client || !hasSpanStreamingEnabled(client)\n ? rawName\n : request.method?.toUpperCase() || HTTP_SPAN_NAME_FALLBACK;\n\n const contentLength = request.headers.get('content-length');\n if (contentLength) {\n attributes['http.request.body.size'] = parseInt(contentLength, 10);\n }\n\n const userAgentHeader = request.headers.get('user-agent');\n if (userAgentHeader) {\n attributes['user_agent.original'] = userAgentHeader;\n }\n\n if (client) {\n Object.assign(\n attributes,\n httpHeadersToSpanAttributes(winterCGHeadersToDict(request.headers), client.getDataCollectionOptions()),\n );\n }\n\n attributes[SENTRY_OP] = HTTP_SERVER;\n\n addCloudResourceContext(isolationScope);\n addRequest(isolationScope, request);\n if (request.cf) {\n addCultureContext(isolationScope, request.cf);\n\n if (typeof request.cf.httpProtocol === 'string') {\n const [protocolName, protocolVersion] = request.cf.httpProtocol.toLowerCase().split('/');\n attributes[NETWORK_PROTOCOL_NAME] = protocolName;\n attributes[NETWORK_PROTOCOL_VERSION] = protocolVersion;\n }\n }\n\n // Do not capture spans for OPTIONS and HEAD requests\n if (request.method === 'OPTIONS' || request.method === 'HEAD') {\n try {\n return await handler();\n } catch (e) {\n if (captureErrors) {\n captureException(e, { mechanism: { handled: false, type: errorMechanismType } });\n }\n throw e;\n } finally {\n waitUntil?.(flushAndDispose(client));\n }\n }\n\n if (client) {\n await captureIncomingRequestBody(client, request);\n }\n\n return continueTrace(\n { sentryTrace: request.headers.get('sentry-trace') || '', baggage: request.headers.get('baggage') },\n () => {\n // Note: This span will not have a duration unless I/O happens in the handler. This is\n // because of how the cloudflare workers runtime works.\n // See: https://developers.cloudflare.com/workers/runtime-apis/performance/\n\n // Use startSpanManual to control when span ends (needed for streaming responses)\n return startSpanManual({ name, attributes }, async span => {\n let res: Response;\n\n try {\n res = await handler();\n setHttpStatus(span, res.status);\n\n // After the handler runs, the span name might have been updated by nested instrumentation\n // (e.g., Remix parameterizing routes). The span should already have the correct name\n // from that instrumentation, so we don't need to do anything here.\n } catch (e) {\n span.end();\n if (captureErrors) {\n captureException(e, { mechanism: { handled: false, type: errorMechanismType } });\n }\n waitUntil?.(flushAndDispose(client));\n throw e;\n }\n\n // Classify response to detect actual streaming\n const classification = classifyResponseStreaming(res);\n\n if (classification.isStreaming && res.body) {\n try {\n let ended = false;\n\n const endSpanOnce = (): void => {\n if (ended) return;\n\n ended = true;\n span.end();\n waitUntil?.(flushAndDispose(client));\n };\n\n const transform = new TransformStream({\n flush() {\n // Source stream completed normally.\n endSpanOnce();\n },\n cancel() {\n // Client disconnected (or downstream cancelled). The `cancel`\n // is being called while the response is still considered\n // active, so this is a safe place to end the span.\n endSpanOnce();\n },\n });\n\n return new Response(res.body.pipeThrough(transform), {\n status: res.status,\n statusText: res.statusText,\n headers: res.headers,\n });\n } catch {\n span.end();\n waitUntil?.(flushAndDispose(client));\n return res;\n }\n }\n\n // Non-streaming response - end span immediately and return original\n span.end();\n\n // Don't dispose for protocol upgrades (101 Switching Protocols) - the connection stays alive.\n // This includes WebSocket upgrades where webSocketMessage/webSocketClose handlers\n // will still be called and may need the client to capture errors.\n if (res.status === 101) {\n waitUntil?.(client?.flush(2000));\n } else {\n waitUntil?.(flushAndDispose(client));\n }\n return res;\n });\n },\n );\n }, wrapperOptions.context);\n}\n"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  export { SDK_VERSION, SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE, SENTRY_SEGMENT_NAME_SOURCE, Scope, addBreadcrumb, addEventProcessor, addIntegration, bindScopeToEmitter, captureCheckIn, captureConsoleIntegration, captureEvent, captureException, captureFeedback, captureMessage, close, consoleIntegration, consoleLoggingIntegration, continueTrace, createConsolaReporter, createTransport, dedupeIntegration, eventFiltersIntegration, extraErrorDataIntegration, featureFlagsIntegration, flush, functionToStringIntegration, getActiveSpan, getClient, getCurrentScope, getGlobalScope, getIsolationScope, getRootSpan, getSpanDescendants, getSpanStatusFromHttpCode, getTraceData, getTraceMetaTags, growthbookIntegration, instrumentSupabaseClient, isEnabled, isInitialized, lastEventId, linkedErrorsIntegration, logger, metrics, moduleMetadataIntegration, requestDataIntegration, rewriteFramesIntegration, setAttribute, setAttributes, setContext, setConversationId, setCurrentClient, setExtra, setExtras, setHttpStatus, setMeasurement, setTag, setTags, setUser, spanStreamingIntegration, spanToBaggageHeader, spanToJSON, spanToStaticSpanJSON, spanToTraceHeader, startInactiveSpan, startNewTrace, startSpan, startSpanManual, supabaseIntegration, suppressTracing, updateSpanName, withActiveSpan, withIsolationScope, withMonitor, withScope, withStaticSpan, withStreamedSpan, zodErrorsIntegration } from '@sentry/core';
2
2
  export { trpcMiddleware, wrapMcpServerWithSentry } from '@sentry/core/server';
3
- export { createLangChainCallbackHandler, getOtlpTracesEndpoint, instrumentAnthropicAiClient, instrumentCreateReactAgent, instrumentGoogleGenAIClient, instrumentLangChainEmbeddings, instrumentOpenAiClient, instrumentPostgresJsSql, instrumentStateGraph, instrumentWorkersAiClient, openTelemetryIntegration, prismaIntegration, vercelAIIntegration } from '@sentry/server-utils';
3
+ export { createFlueInstrumentation, createLangChainCallbackHandler, eveConversationHook, getInstrumentedModuleNames, getOtlpTracesEndpoint, instrumentAnthropicAiClient, instrumentCreateReactAgent, instrumentGoogleGenAIClient, instrumentLangChainEmbeddings, instrumentMistralAiClient, instrumentOpenAiClient, instrumentPostgresJsSql, instrumentStateGraph, instrumentWorkersAiClient, openTelemetryIntegration, prismaIntegration, vercelAIIntegration } from '@sentry/server-utils';
4
4
  export { withSentry } from './withSentry.js';
5
5
  export { defineCloudflareOptions } from './defineCloudflareOptions.js';
6
6
  export { instrumentAgentWithSentry, instrumentDurableObjectWithSentry } from './durableobject.js';