@sentry/cloudflare 10.48.0 → 10.49.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.
- package/build/cjs/client.js.map +1 -1
- package/build/cjs/durableobject.js +13 -5
- package/build/cjs/durableobject.js.map +1 -1
- package/build/cjs/index.js +1 -0
- package/build/cjs/index.js.map +1 -1
- package/build/cjs/instrumentations/instrumentDurableObjectNamespace.js +56 -0
- package/build/cjs/instrumentations/instrumentDurableObjectNamespace.js.map +1 -0
- package/build/cjs/instrumentations/instrumentDurableObjectStorage.js +38 -3
- package/build/cjs/instrumentations/instrumentDurableObjectStorage.js.map +1 -1
- package/build/cjs/instrumentations/worker/instrumentEmail.js +3 -2
- package/build/cjs/instrumentations/worker/instrumentEmail.js.map +1 -1
- package/build/cjs/instrumentations/worker/instrumentEnv.js +83 -0
- package/build/cjs/instrumentations/worker/instrumentEnv.js.map +1 -0
- package/build/cjs/instrumentations/worker/instrumentFetch.js +3 -2
- package/build/cjs/instrumentations/worker/instrumentFetch.js.map +1 -1
- package/build/cjs/instrumentations/worker/instrumentFetcher.js +38 -0
- package/build/cjs/instrumentations/worker/instrumentFetcher.js.map +1 -0
- package/build/cjs/instrumentations/worker/instrumentQueue.js +3 -2
- package/build/cjs/instrumentations/worker/instrumentQueue.js.map +1 -1
- package/build/cjs/instrumentations/worker/instrumentScheduled.js +3 -2
- package/build/cjs/instrumentations/worker/instrumentScheduled.js.map +1 -1
- package/build/cjs/instrumentations/worker/instrumentTail.js +3 -2
- package/build/cjs/instrumentations/worker/instrumentTail.js.map +1 -1
- package/build/cjs/integrations/tracing/vercelai.js +2 -1
- package/build/cjs/integrations/tracing/vercelai.js.map +1 -1
- package/build/cjs/sdk.js +6 -1
- package/build/cjs/sdk.js.map +1 -1
- package/build/cjs/utils/instrumentContext.js +2 -1
- package/build/cjs/utils/instrumentContext.js.map +1 -1
- package/build/cjs/utils/isBinding.js +32 -0
- package/build/cjs/utils/isBinding.js.map +1 -0
- package/build/cjs/utils/rpcOptions.js +84 -0
- package/build/cjs/utils/rpcOptions.js.map +1 -0
- package/build/cjs/utils/traceLinks.js +78 -0
- package/build/cjs/utils/traceLinks.js.map +1 -0
- package/build/cjs/workflows.js +3 -2
- package/build/cjs/workflows.js.map +1 -1
- package/build/cjs/wrapMethodWithSentry.js +90 -45
- package/build/cjs/wrapMethodWithSentry.js.map +1 -1
- package/build/esm/client.js.map +1 -1
- package/build/esm/durableobject.js +13 -5
- package/build/esm/durableobject.js.map +1 -1
- package/build/esm/index.js +1 -1
- package/build/esm/instrumentations/instrumentDurableObjectNamespace.js +54 -0
- package/build/esm/instrumentations/instrumentDurableObjectNamespace.js.map +1 -0
- package/build/esm/instrumentations/instrumentDurableObjectStorage.js +39 -4
- package/build/esm/instrumentations/instrumentDurableObjectStorage.js.map +1 -1
- package/build/esm/instrumentations/worker/instrumentEmail.js +3 -2
- package/build/esm/instrumentations/worker/instrumentEmail.js.map +1 -1
- package/build/esm/instrumentations/worker/instrumentEnv.js +81 -0
- package/build/esm/instrumentations/worker/instrumentEnv.js.map +1 -0
- package/build/esm/instrumentations/worker/instrumentFetch.js +3 -2
- package/build/esm/instrumentations/worker/instrumentFetch.js.map +1 -1
- package/build/esm/instrumentations/worker/instrumentFetcher.js +36 -0
- package/build/esm/instrumentations/worker/instrumentFetcher.js.map +1 -0
- package/build/esm/instrumentations/worker/instrumentQueue.js +3 -2
- package/build/esm/instrumentations/worker/instrumentQueue.js.map +1 -1
- package/build/esm/instrumentations/worker/instrumentScheduled.js +3 -2
- package/build/esm/instrumentations/worker/instrumentScheduled.js.map +1 -1
- package/build/esm/instrumentations/worker/instrumentTail.js +3 -2
- package/build/esm/instrumentations/worker/instrumentTail.js.map +1 -1
- package/build/esm/integrations/tracing/vercelai.js +2 -1
- package/build/esm/integrations/tracing/vercelai.js.map +1 -1
- package/build/esm/package.json +1 -1
- package/build/esm/sdk.js +7 -2
- package/build/esm/sdk.js.map +1 -1
- package/build/esm/utils/instrumentContext.js +2 -1
- package/build/esm/utils/instrumentContext.js.map +1 -1
- package/build/esm/utils/isBinding.js +29 -0
- package/build/esm/utils/isBinding.js.map +1 -0
- package/build/esm/utils/rpcOptions.js +81 -0
- package/build/esm/utils/rpcOptions.js.map +1 -0
- package/build/esm/utils/traceLinks.js +73 -0
- package/build/esm/utils/traceLinks.js.map +1 -0
- package/build/esm/workflows.js +3 -2
- package/build/esm/workflows.js.map +1 -1
- package/build/esm/wrapMethodWithSentry.js +91 -46
- package/build/esm/wrapMethodWithSentry.js.map +1 -1
- package/build/types/client.d.ts +39 -10
- package/build/types/client.d.ts.map +1 -1
- package/build/types/durableobject.d.ts.map +1 -1
- package/build/types/index.d.ts +1 -1
- package/build/types/index.d.ts.map +1 -1
- package/build/types/instrumentations/instrumentDurableObjectNamespace.d.ts +12 -0
- package/build/types/instrumentations/instrumentDurableObjectNamespace.d.ts.map +1 -0
- package/build/types/instrumentations/instrumentDurableObjectStorage.d.ts +8 -1
- package/build/types/instrumentations/instrumentDurableObjectStorage.d.ts.map +1 -1
- package/build/types/instrumentations/worker/instrumentEmail.d.ts.map +1 -1
- package/build/types/instrumentations/worker/instrumentEnv.d.ts +16 -0
- package/build/types/instrumentations/worker/instrumentEnv.d.ts.map +1 -0
- package/build/types/instrumentations/worker/instrumentFetch.d.ts.map +1 -1
- package/build/types/instrumentations/worker/instrumentFetcher.d.ts +10 -0
- package/build/types/instrumentations/worker/instrumentFetcher.d.ts.map +1 -0
- package/build/types/instrumentations/worker/instrumentQueue.d.ts.map +1 -1
- package/build/types/instrumentations/worker/instrumentScheduled.d.ts.map +1 -1
- package/build/types/instrumentations/worker/instrumentTail.d.ts.map +1 -1
- package/build/types/integrations/tracing/vercelai.d.ts +9 -1
- package/build/types/integrations/tracing/vercelai.d.ts.map +1 -1
- package/build/types/sdk.d.ts.map +1 -1
- package/build/types/utils/instrumentContext.d.ts.map +1 -1
- package/build/types/utils/isBinding.d.ts +49 -0
- package/build/types/utils/isBinding.d.ts.map +1 -0
- package/build/types/utils/rpcOptions.d.ts +24 -0
- package/build/types/utils/rpcOptions.d.ts.map +1 -0
- package/build/types/utils/traceLinks.d.ts +27 -0
- package/build/types/utils/traceLinks.d.ts.map +1 -0
- package/build/types/workflows.d.ts.map +1 -1
- package/build/types/wrapMethodWithSentry.d.ts +19 -2
- package/build/types/wrapMethodWithSentry.d.ts.map +1 -1
- package/build/types-ts3.8/client.d.ts +39 -10
- package/build/types-ts3.8/index.d.ts +1 -1
- package/build/types-ts3.8/instrumentations/instrumentDurableObjectNamespace.d.ts +12 -0
- package/build/types-ts3.8/instrumentations/instrumentDurableObjectStorage.d.ts +8 -1
- package/build/types-ts3.8/instrumentations/worker/instrumentEnv.d.ts +16 -0
- package/build/types-ts3.8/instrumentations/worker/instrumentFetcher.d.ts +10 -0
- package/build/types-ts3.8/integrations/tracing/vercelai.d.ts +9 -1
- package/build/types-ts3.8/utils/isBinding.d.ts +49 -0
- package/build/types-ts3.8/utils/rpcOptions.d.ts +24 -0
- package/build/types-ts3.8/utils/traceLinks.d.ts +27 -0
- package/build/types-ts3.8/wrapMethodWithSentry.d.ts +19 -2
- package/package.json +2 -2
|
@@ -5,6 +5,7 @@ import { getFinalOptions } from '../../options.js';
|
|
|
5
5
|
import { addCloudResourceContext } from '../../scope-utils.js';
|
|
6
6
|
import { init } from '../../sdk.js';
|
|
7
7
|
import { instrumentContext } from '../../utils/instrumentContext.js';
|
|
8
|
+
import { instrumentEnv } from './instrumentEnv.js';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Core email handler logic - wraps execution with Sentry instrumentation.
|
|
@@ -66,9 +67,9 @@ function instrumentExportedHandlerEmail(
|
|
|
66
67
|
apply(target, thisArg, args) {
|
|
67
68
|
const [emailMessage, env, ctx] = args;
|
|
68
69
|
const context = instrumentContext(ctx);
|
|
69
|
-
args[2] = context;
|
|
70
|
-
|
|
71
70
|
const options = getFinalOptions(optionsCallback(env), env);
|
|
71
|
+
args[1] = instrumentEnv(env, options);
|
|
72
|
+
args[2] = context;
|
|
72
73
|
|
|
73
74
|
return wrapEmailHandler(emailMessage, options, context, () => target.apply(thisArg, args));
|
|
74
75
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrumentEmail.js","sources":["../../../../src/instrumentations/worker/instrumentEmail.ts"],"sourcesContent":["import type { EmailMessage, ExportedHandler } from '@cloudflare/workers-types';\nimport type { env as cloudflareEnv } from 'cloudflare:workers';\nimport {\n captureException,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,\n startSpan,\n withIsolationScope,\n} 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';\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 const client = init({ ...options, ctx: context });\n isolationScope.setClient(client);\n\n addCloudResourceContext(isolationScope);\n\n return startSpan(\n {\n op: 'faas.email',\n name: `Handle Email ${emailMessage.to}`,\n attributes: {\n 'faas.trigger': 'email',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.faas.cloudflare.email',\n [SEMANTIC_ATTRIBUTE_SENTRY_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 */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function instrumentExportedHandlerEmail<T extends ExportedHandler<any, any, any>>(\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
|
|
1
|
+
{"version":3,"file":"instrumentEmail.js","sources":["../../../../src/instrumentations/worker/instrumentEmail.ts"],"sourcesContent":["import type { EmailMessage, ExportedHandler } from '@cloudflare/workers-types';\nimport type { env as cloudflareEnv } from 'cloudflare:workers';\nimport {\n captureException,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,\n startSpan,\n withIsolationScope,\n} 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 { 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 const client = init({ ...options, ctx: context });\n isolationScope.setClient(client);\n\n addCloudResourceContext(isolationScope);\n\n return startSpan(\n {\n op: 'faas.email',\n name: `Handle Email ${emailMessage.to}`,\n attributes: {\n 'faas.trigger': 'email',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.faas.cloudflare.email',\n [SEMANTIC_ATTRIBUTE_SENTRY_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 */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function instrumentExportedHandlerEmail<T extends ExportedHandler<any, any, any>>(\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":";;;;;;;;;AAkBA;AACA;AACA;AACA,SAAS,gBAAgB;AACzB,EAAE,YAAY;AACd,EAAE,OAAO;AACT,EAAE,OAAO;AACT,EAAE,EAAE;AACJ,EAAW;AACX,EAAE,OAAO,kBAAkB,CAAC,cAAA,IAAkB;AAC9C,IAAI,MAAM,SAAA,GAAY,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC;;AAErD,IAAI,MAAM,MAAA,GAAS,IAAI,CAAC,EAAE,GAAG,OAAO,EAAE,GAAG,EAAE,OAAA,EAAS,CAAC;AACrD,IAAI,cAAc,CAAC,SAAS,CAAC,MAAM,CAAC;;AAEpC,IAAI,uBAAuB,CAAC,cAAc,CAAC;;AAE3C,IAAI,OAAO,SAAS;AACpB,MAAM;AACN,QAAQ,EAAE,EAAE,YAAY;AACxB,QAAQ,IAAI,EAAE,CAAC,aAAa,EAAE,YAAY,CAAC,EAAE,CAAC,CAAA;AACA,QAAA,UAAA,EAAA;AACA,UAAA,cAAA,EAAA,OAAA;AACA,UAAA,CAAA,gCAAA,GAAA,4BAAA;AACA,UAAA,CAAA,gCAAA,GAAA,MAAA;AACA,SAAA;AACA,OAAA;AACA,MAAA,YAAA;AACA,QAAA,IAAA;AACA,UAAA,OAAA,MAAA,EAAA,EAAA;AACA,QAAA,CAAA,CAAA,OAAA,CAAA,EAAA;AACA,UAAA,gBAAA,CAAA,CAAA,EAAA,EAAA,SAAA,EAAA,EAAA,OAAA,EAAA,KAAA,EAAA,IAAA,EAAA,4BAAA,EAAA,EAAA,CAAA;AACA,UAAA,MAAA,CAAA;AACA,QAAA,CAAA,SAAA;AACA,UAAA,SAAA,CAAA,eAAA,CAAA,MAAA,CAAA,CAAA;AACA,QAAA;AACA,MAAA,CAAA;AACA,KAAA;AACA,EAAA,CAAA,CAAA;AACA;;AAEA;AACA;AACA;AACA;AACA,SAAA,8BAAA;AACA,EAAA,OAAA;AACA,EAAA,eAAA;AACA,EAAA;AACA,EAAA,IAAA,EAAA,OAAA,IAAA,OAAA,CAAA,IAAA,OAAA,OAAA,CAAA,KAAA,KAAA,UAAA,EAAA;AACA,IAAA;AACA,EAAA;;AAEA,EAAA,OAAA,CAAA,KAAA,GAAA,kBAAA;AACA,IAAA,OAAA,CAAA,KAAA;AACA,IAAA,QAAA;AACA,MAAA,IAAA,KAAA,CAAA,QAAA,EAAA;AACA,QAAA,KAAA,CAAA,MAAA,EAAA,OAAA,EAAA,IAAA,EAAA;AACA,UAAA,MAAA,CAAA,YAAA,EAAA,GAAA,EAAA,GAAA,CAAA,GAAA,IAAA;AACA,UAAA,MAAA,OAAA,GAAA,iBAAA,CAAA,GAAA,CAAA;AACA,UAAA,MAAA,OAAA,GAAA,eAAA,CAAA,eAAA,CAAA,GAAA,CAAA,EAAA,GAAA,CAAA;AACA,UAAA,IAAA,CAAA,CAAA,CAAA,GAAA,aAAA,CAAA,GAAA,EAAA,OAAA,CAAA;AACA,UAAA,IAAA,CAAA,CAAA,CAAA,GAAA,OAAA;;AAEA,UAAA,OAAA,gBAAA,CAAA,YAAA,EAAA,OAAA,EAAA,OAAA,EAAA,MAAA,MAAA,CAAA,KAAA,CAAA,OAAA,EAAA,IAAA,CAAA,CAAA;AACA,QAAA,CAAA;AACA,OAAA,CAAA;AACA,GAAA;AACA;;;;"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { isDurableObjectNamespace, isJSRPC } from '../../utils/isBinding.js';
|
|
2
|
+
import { getEffectiveRpcPropagation } from '../../utils/rpcOptions.js';
|
|
3
|
+
import { instrumentDurableObjectNamespace } from '../instrumentDurableObjectNamespace.js';
|
|
4
|
+
import { instrumentFetcher } from './instrumentFetcher.js';
|
|
5
|
+
|
|
6
|
+
function isProxyable(item) {
|
|
7
|
+
return item !== null && (typeof item === 'object' || typeof item === 'function');
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const instrumentedBindings = new WeakMap();
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Wraps the Cloudflare `env` object in a Proxy that detects binding types
|
|
14
|
+
* on property access and returns instrumented versions.
|
|
15
|
+
*
|
|
16
|
+
* Currently detects:
|
|
17
|
+
* - DurableObjectNamespace (via `idFromName` duck-typing)
|
|
18
|
+
* - Service bindings / JSRPC proxies (wraps `fetch` for trace propagation)
|
|
19
|
+
*
|
|
20
|
+
* Extensible for future binding types (KV, D1, Queue, etc.).
|
|
21
|
+
*
|
|
22
|
+
* @param env - The Cloudflare env object to instrument
|
|
23
|
+
* @param options - Optional CloudflareOptions to control RPC trace propagation
|
|
24
|
+
*/
|
|
25
|
+
function instrumentEnv(env, options) {
|
|
26
|
+
if (!env || typeof env !== 'object') {
|
|
27
|
+
return env;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const rpcPropagation = options ? getEffectiveRpcPropagation(options) : false;
|
|
31
|
+
|
|
32
|
+
// As of now only trace propagation is used for the instrumentEnv
|
|
33
|
+
// so this is an optimization to avoid wrapping the env in a proxy if trace propagation is disabled
|
|
34
|
+
if (!rpcPropagation) {
|
|
35
|
+
return env;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return new Proxy(env, {
|
|
39
|
+
get(target, prop, receiver) {
|
|
40
|
+
const item = Reflect.get(target, prop, receiver);
|
|
41
|
+
|
|
42
|
+
if (!isProxyable(item)) {
|
|
43
|
+
return item;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const cached = instrumentedBindings.get(item);
|
|
47
|
+
|
|
48
|
+
if (cached) {
|
|
49
|
+
return cached;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (isDurableObjectNamespace(item)) {
|
|
53
|
+
const instrumented = instrumentDurableObjectNamespace(item);
|
|
54
|
+
instrumentedBindings.set(item, instrumented);
|
|
55
|
+
return instrumented;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (isJSRPC(item)) {
|
|
59
|
+
const instrumented = new Proxy(item, {
|
|
60
|
+
get(target, p, rcv) {
|
|
61
|
+
const value = Reflect.get(target, p, rcv);
|
|
62
|
+
|
|
63
|
+
if (p === 'fetch' && typeof value === 'function') {
|
|
64
|
+
return instrumentFetcher(value.bind(target));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return value;
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
instrumentedBindings.set(item, instrumented);
|
|
72
|
+
return instrumented;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return item;
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export { instrumentEnv };
|
|
81
|
+
//# sourceMappingURL=instrumentEnv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instrumentEnv.js","sources":["../../../../src/instrumentations/worker/instrumentEnv.ts"],"sourcesContent":["import type { CloudflareOptions } from '../../client';\nimport { isDurableObjectNamespace, isJSRPC } from '../../utils/isBinding';\nimport { getEffectiveRpcPropagation } from '../../utils/rpcOptions';\nimport { instrumentDurableObjectNamespace } from '../instrumentDurableObjectNamespace';\nimport { instrumentFetcher } from './instrumentFetcher';\n\nfunction isProxyable(item: unknown): item is object {\n return item !== null && (typeof item === 'object' || typeof item === 'function');\n}\n\nconst instrumentedBindings = new WeakMap<object, unknown>();\n\n/**\n * Wraps the Cloudflare `env` object in a Proxy that detects binding types\n * on property access and returns instrumented versions.\n *\n * Currently detects:\n * - DurableObjectNamespace (via `idFromName` duck-typing)\n * - Service bindings / JSRPC proxies (wraps `fetch` for trace propagation)\n *\n * Extensible for future binding types (KV, D1, Queue, etc.).\n *\n * @param env - The Cloudflare env object to instrument\n * @param options - Optional CloudflareOptions to control RPC trace propagation\n */\nexport function instrumentEnv<Env extends Record<string, unknown>>(env: Env, options?: CloudflareOptions): Env {\n if (!env || typeof env !== 'object') {\n return env;\n }\n\n const rpcPropagation = options ? getEffectiveRpcPropagation(options) : false;\n\n // As of now only trace propagation is used for the instrumentEnv\n // so this is an optimization to avoid wrapping the env in a proxy if trace propagation is disabled\n if (!rpcPropagation) {\n return env;\n }\n\n return new Proxy(env, {\n get(target, prop, receiver) {\n const item = Reflect.get(target, prop, receiver);\n\n if (!isProxyable(item)) {\n return item;\n }\n\n const cached = instrumentedBindings.get(item);\n\n if (cached) {\n return cached;\n }\n\n if (isDurableObjectNamespace(item)) {\n const instrumented = instrumentDurableObjectNamespace(item);\n instrumentedBindings.set(item, instrumented);\n return instrumented;\n }\n\n if (isJSRPC(item)) {\n const instrumented = new Proxy(item, {\n get(target, p, rcv) {\n const value = Reflect.get(target, p, rcv);\n\n if (p === 'fetch' && typeof value === 'function') {\n return instrumentFetcher(value.bind(target));\n }\n\n return value;\n },\n });\n\n instrumentedBindings.set(item, instrumented);\n return instrumented;\n }\n\n return item;\n },\n });\n}\n"],"names":[],"mappings":";;;;;AAMA,SAAS,WAAW,CAAC,IAAI,EAA2B;AACpD,EAAE,OAAO,IAAA,KAAS,IAAA,KAAS,OAAO,IAAA,KAAS,YAAY,OAAO,IAAA,KAAS,UAAU,CAAC;AAClF;;AAEA,MAAM,oBAAA,GAAuB,IAAI,OAAO,EAAmB;;AAE3D;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,aAAa,CAAsC,GAAG,EAAO,OAAO,EAA2B;AAC/G,EAAE,IAAI,CAAC,GAAA,IAAO,OAAO,GAAA,KAAQ,QAAQ,EAAE;AACvC,IAAI,OAAO,GAAG;AACd,EAAE;;AAEF,EAAE,MAAM,cAAA,GAAiB,OAAA,GAAU,0BAA0B,CAAC,OAAO,CAAA,GAAI,KAAK;;AAE9E;AACA;AACA,EAAE,IAAI,CAAC,cAAc,EAAE;AACvB,IAAI,OAAO,GAAG;AACd,EAAE;;AAEF,EAAE,OAAO,IAAI,KAAK,CAAC,GAAG,EAAE;AACxB,IAAI,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE;AAChC,MAAM,MAAM,IAAA,GAAO,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC;;AAEtD,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE;AAC9B,QAAQ,OAAO,IAAI;AACnB,MAAM;;AAEN,MAAM,MAAM,SAAS,oBAAoB,CAAC,GAAG,CAAC,IAAI,CAAC;;AAEnD,MAAM,IAAI,MAAM,EAAE;AAClB,QAAQ,OAAO,MAAM;AACrB,MAAM;;AAEN,MAAM,IAAI,wBAAwB,CAAC,IAAI,CAAC,EAAE;AAC1C,QAAQ,MAAM,YAAA,GAAe,gCAAgC,CAAC,IAAI,CAAC;AACnE,QAAQ,oBAAoB,CAAC,GAAG,CAAC,IAAI,EAAE,YAAY,CAAC;AACpD,QAAQ,OAAO,YAAY;AAC3B,MAAM;;AAEN,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,EAAE;AACzB,QAAQ,MAAM,YAAA,GAAe,IAAI,KAAK,CAAC,IAAI,EAAE;AAC7C,UAAU,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE;AAC9B,YAAY,MAAM,KAAA,GAAQ,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,GAAG,CAAC;;AAErD,YAAY,IAAI,CAAA,KAAM,OAAA,IAAW,OAAO,KAAA,KAAU,UAAU,EAAE;AAC9D,cAAc,OAAO,iBAAiB,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC1D,YAAY;;AAEZ,YAAY,OAAO,KAAK;AACxB,UAAU,CAAC;AACX,SAAS,CAAC;;AAEV,QAAQ,oBAAoB,CAAC,GAAG,CAAC,IAAI,EAAE,YAAY,CAAC;AACpD,QAAQ,OAAO,YAAY;AAC3B,MAAM;;AAEN,MAAM,OAAO,IAAI;AACjB,IAAI,CAAC;AACL,GAAG,CAAC;AACJ;;;;"}
|
|
@@ -2,6 +2,7 @@ import { ensureInstrumented } from '../../instrument.js';
|
|
|
2
2
|
import { getFinalOptions } from '../../options.js';
|
|
3
3
|
import { wrapRequestHandler } from '../../request.js';
|
|
4
4
|
import { instrumentContext } from '../../utils/instrumentContext.js';
|
|
5
|
+
import { instrumentEnv } from './instrumentEnv.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Instruments a fetch handler for ExportedHandler (env/ctx come from args).
|
|
@@ -22,9 +23,9 @@ function instrumentExportedHandlerFetch(
|
|
|
22
23
|
apply(target, thisArg, args) {
|
|
23
24
|
const [request, env, ctx] = args;
|
|
24
25
|
const context = instrumentContext(ctx);
|
|
25
|
-
args[2] = context;
|
|
26
|
-
|
|
27
26
|
const options = getFinalOptions(optionsCallback(env), env);
|
|
27
|
+
args[1] = instrumentEnv(env, options);
|
|
28
|
+
args[2] = context;
|
|
28
29
|
|
|
29
30
|
return wrapRequestHandler({ options, request, context }, () => target.apply(thisArg, args));
|
|
30
31
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrumentFetch.js","sources":["../../../../src/instrumentations/worker/instrumentFetch.ts"],"sourcesContent":["import type { ExportedHandler } from '@cloudflare/workers-types';\nimport type { env as cloudflareEnv, WorkerEntrypoint } from 'cloudflare:workers';\nimport type { CloudflareOptions } from '../../client';\nimport { ensureInstrumented } from '../../instrument';\nimport { getFinalOptions } from '../../options';\nimport { wrapRequestHandler } from '../../request';\nimport { instrumentContext } from '../../utils/instrumentContext';\n\n/**\n * Instruments a fetch handler for ExportedHandler (env/ctx come from args).\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function instrumentExportedHandlerFetch<T extends ExportedHandler<any, any, any>>(\n handler: T,\n optionsCallback: (env: typeof cloudflareEnv) => CloudflareOptions | undefined,\n): void {\n if (!('fetch' in handler) || typeof handler.fetch !== 'function') {\n return;\n }\n\n handler.fetch = ensureInstrumented(\n handler.fetch,\n original =>\n new Proxy(original, {\n apply(target, thisArg, args: Parameters<NonNullable<T['fetch']>>) {\n const [request, env, ctx] = args;\n const context = instrumentContext(ctx);\n
|
|
1
|
+
{"version":3,"file":"instrumentFetch.js","sources":["../../../../src/instrumentations/worker/instrumentFetch.ts"],"sourcesContent":["import type { ExportedHandler } from '@cloudflare/workers-types';\nimport type { env as cloudflareEnv, WorkerEntrypoint } from 'cloudflare:workers';\nimport type { CloudflareOptions } from '../../client';\nimport { ensureInstrumented } from '../../instrument';\nimport { getFinalOptions } from '../../options';\nimport { wrapRequestHandler } from '../../request';\nimport { instrumentContext } from '../../utils/instrumentContext';\nimport { instrumentEnv } from './instrumentEnv';\n\n/**\n * Instruments a fetch handler for ExportedHandler (env/ctx come from args).\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function instrumentExportedHandlerFetch<T extends ExportedHandler<any, any, any>>(\n handler: T,\n optionsCallback: (env: typeof cloudflareEnv) => CloudflareOptions | undefined,\n): void {\n if (!('fetch' in handler) || typeof handler.fetch !== 'function') {\n return;\n }\n\n handler.fetch = ensureInstrumented(\n handler.fetch,\n original =>\n new Proxy(original, {\n apply(target, thisArg, args: Parameters<NonNullable<T['fetch']>>) {\n const [request, 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 wrapRequestHandler({ options, request, context }, () => target.apply(thisArg, args));\n },\n }),\n );\n}\n\n/**\n * Instruments a fetch method for WorkerEntrypoint (options/context already available).\n */\nexport function instrumentWorkerEntrypointFetch<T extends WorkerEntrypoint>(\n instance: T,\n options: CloudflareOptions,\n context: ExecutionContext,\n): void {\n if (!instance.fetch) {\n return;\n }\n\n const original = instance.fetch.bind(instance);\n instance.fetch = new Proxy(original, {\n apply(target, thisArg, args: [Request]) {\n const [request] = args;\n\n return wrapRequestHandler({ options, request, context }, () => Reflect.apply(target, thisArg, args));\n },\n });\n}\n"],"names":[],"mappings":";;;;;;AASA;AACA;AACA;AACA;AACO,SAAS,8BAA8B;AAC9C,EAAE,OAAO;AACT,EAAE,eAAe;AACjB,EAAQ;AACR,EAAE,IAAI,EAAE,WAAW,OAAO,CAAA,IAAK,OAAO,OAAO,CAAC,KAAA,KAAU,UAAU,EAAE;AACpE,IAAI;AACJ,EAAE;;AAEF,EAAE,OAAO,CAAC,KAAA,GAAQ,kBAAkB;AACpC,IAAI,OAAO,CAAC,KAAK;AACjB,IAAI,QAAA;AACJ,MAAM,IAAI,KAAK,CAAC,QAAQ,EAAE;AAC1B,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAuC;AAC1E,UAAU,MAAM,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,CAAA,GAAI,IAAI;AAC1C,UAAU,MAAM,OAAA,GAAU,iBAAiB,CAAC,GAAG,CAAC;AAChD,UAAU,MAAM,OAAA,GAAU,eAAe,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC;AACpE,UAAU,IAAI,CAAC,CAAC,CAAA,GAAI,aAAa,CAAC,GAAG,EAAE,OAAO,CAAC;AAC/C,UAAU,IAAI,CAAC,CAAC,CAAA,GAAI,OAAO;;AAE3B,UAAU,OAAO,kBAAkB,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,OAAA,EAAS,EAAE,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;AACrG,QAAQ,CAAC;AACT,OAAO,CAAC;AACR,GAAG;AACH;;AAEA;AACA;AACA;AACO,SAAS,+BAA+B;AAC/C,EAAE,QAAQ;AACV,EAAE,OAAO;AACT,EAAE,OAAO;AACT,EAAQ;AACR,EAAE,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE;AACvB,IAAI;AACJ,EAAE;;AAEF,EAAE,MAAM,QAAA,GAAW,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC;AAChD,EAAE,QAAQ,CAAC,KAAA,GAAQ,IAAI,KAAK,CAAC,QAAQ,EAAE;AACvC,IAAI,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAa;AAC5C,MAAM,MAAM,CAAC,OAAO,CAAA,GAAI,IAAI;;AAE5B,MAAM,OAAO,kBAAkB,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,OAAA,EAAS,EAAE,MAAM,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;AAC1G,IAAI,CAAC;AACL,GAAG,CAAC;AACJ;;;;"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { _INTERNAL_getTracingHeadersForFetchRequest } from '@sentry/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Wraps a fetch-like function to create a span and propagate trace headers
|
|
5
|
+
* (`sentry-trace` and `baggage`) on the outgoing request.
|
|
6
|
+
*
|
|
7
|
+
* Useful for instrumenting Cloudflare bindings that expose a `fetch` method
|
|
8
|
+
* (e.g. Durable Object stubs, Service bindings).
|
|
9
|
+
*/
|
|
10
|
+
function instrumentFetcher(fetchFn) {
|
|
11
|
+
return function (input, init) {
|
|
12
|
+
const headers = _INTERNAL_getTracingHeadersForFetchRequest(input, { headers: init?.headers });
|
|
13
|
+
|
|
14
|
+
if (input instanceof Request && init === undefined) {
|
|
15
|
+
if (!headers) {
|
|
16
|
+
return fetchFn(input);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Newly created headers already include the previous headers from the original request
|
|
20
|
+
// so we can clone the request and pass in all headers.
|
|
21
|
+
const requestWithTracing = new Request(input, { headers: headers });
|
|
22
|
+
|
|
23
|
+
return fetchFn(requestWithTracing);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const mergedInit = {
|
|
27
|
+
...init,
|
|
28
|
+
...(headers ? { headers } : {}),
|
|
29
|
+
} ;
|
|
30
|
+
|
|
31
|
+
return fetchFn(input, mergedInit);
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export { instrumentFetcher };
|
|
36
|
+
//# sourceMappingURL=instrumentFetcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instrumentFetcher.js","sources":["../../../../src/instrumentations/worker/instrumentFetcher.ts"],"sourcesContent":["import type { Fetcher } from '@cloudflare/workers-types';\nimport { _INTERNAL_getTracingHeadersForFetchRequest } from '@sentry/core';\n\n/**\n * Wraps a fetch-like function to create a span and propagate trace headers\n * (`sentry-trace` and `baggage`) on the outgoing request.\n *\n * Useful for instrumenting Cloudflare bindings that expose a `fetch` method\n * (e.g. Durable Object stubs, Service bindings).\n */\nexport function instrumentFetcher(fetchFn: Fetcher['fetch']): Fetcher['fetch'] {\n return function (input: RequestInfo | URL, init?: RequestInit): Promise<Response> {\n const headers = _INTERNAL_getTracingHeadersForFetchRequest(input, { headers: init?.headers });\n\n if (input instanceof Request && init === undefined) {\n if (!headers) {\n return fetchFn(input);\n }\n\n // Newly created headers already include the previous headers from the original request\n // so we can clone the request and pass in all headers.\n const requestWithTracing = new Request(input, { headers: headers as HeadersInit });\n\n return fetchFn(requestWithTracing);\n }\n\n const mergedInit = {\n ...init,\n ...(headers ? { headers } : {}),\n } as NonNullable<Parameters<Fetcher['fetch']>[1]>;\n\n return fetchFn(input, mergedInit);\n };\n}\n"],"names":[],"mappings":";;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,iBAAiB,CAAC,OAAO,EAAsC;AAC/E,EAAE,OAAO,UAAU,KAAK,EAAqB,IAAI,EAAmC;AACpF,IAAI,MAAM,OAAA,GAAU,0CAA0C,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,OAAA,EAAS,CAAC;;AAEjG,IAAI,IAAI,KAAA,YAAiB,WAAW,IAAA,KAAS,SAAS,EAAE;AACxD,MAAM,IAAI,CAAC,OAAO,EAAE;AACpB,QAAQ,OAAO,OAAO,CAAC,KAAK,CAAC;AAC7B,MAAM;;AAEN;AACA;AACA,MAAM,MAAM,kBAAA,GAAqB,IAAI,OAAO,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,OAAA,GAAwB,CAAC;;AAExF,MAAM,OAAO,OAAO,CAAC,kBAAkB,CAAC;AACxC,IAAI;;AAEJ,IAAI,MAAM,aAAa;AACvB,MAAM,GAAG,IAAI;AACb,MAAM,IAAI,OAAA,GAAU,EAAE,SAAQ,GAAI,EAAE,CAAC;AACrC,KAAI;;AAEJ,IAAI,OAAO,OAAO,CAAC,KAAK,EAAE,UAAU,CAAC;AACrC,EAAE,CAAC;AACH;;;;"}
|
|
@@ -5,6 +5,7 @@ import { getFinalOptions } from '../../options.js';
|
|
|
5
5
|
import { addCloudResourceContext } from '../../scope-utils.js';
|
|
6
6
|
import { init } from '../../sdk.js';
|
|
7
7
|
import { instrumentContext } from '../../utils/instrumentContext.js';
|
|
8
|
+
import { instrumentEnv } from './instrumentEnv.js';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Core queue handler logic - wraps execution with Sentry instrumentation.
|
|
@@ -71,9 +72,9 @@ function instrumentExportedHandlerQueue(
|
|
|
71
72
|
apply(target, thisArg, args) {
|
|
72
73
|
const [batch, env, ctx] = args;
|
|
73
74
|
const context = instrumentContext(ctx);
|
|
74
|
-
args[2] = context;
|
|
75
|
-
|
|
76
75
|
const options = getFinalOptions(optionsCallback(env), env);
|
|
76
|
+
args[1] = instrumentEnv(env, options);
|
|
77
|
+
args[2] = context;
|
|
77
78
|
|
|
78
79
|
return wrapQueueHandler(batch, options, context, () => target.apply(thisArg, args));
|
|
79
80
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrumentQueue.js","sources":["../../../../src/instrumentations/worker/instrumentQueue.ts"],"sourcesContent":["import type { ExportedHandler, MessageBatch } from '@cloudflare/workers-types';\nimport type { env as cloudflareEnv, WorkerEntrypoint } from 'cloudflare:workers';\nimport {\n captureException,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,\n startSpan,\n withIsolationScope,\n} 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';\n\n/**\n * Core queue handler logic - wraps execution with Sentry instrumentation.\n */\nfunction wrapQueueHandler(\n batch: MessageBatch,\n options: CloudflareOptions,\n context: ExecutionContext,\n fn: () => unknown,\n): unknown {\n return withIsolationScope(isolationScope => {\n const waitUntil = context.waitUntil.bind(context);\n\n const client = init({ ...options, ctx: context });\n isolationScope.setClient(client);\n\n addCloudResourceContext(isolationScope);\n\n return startSpan(\n {\n op: 'faas.queue',\n name: `process ${batch.queue}`,\n attributes: {\n 'faas.trigger': 'pubsub',\n 'messaging.destination.name': batch.queue,\n 'messaging.system': 'cloudflare',\n 'messaging.batch.message_count': batch.messages.length,\n 'messaging.message.retry.count': batch.messages.reduce((acc, message) => acc + message.attempts - 1, 0),\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'queue.process',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.faas.cloudflare.queue',\n [SEMANTIC_ATTRIBUTE_SENTRY_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.queue' } });\n throw e;\n } finally {\n waitUntil(flushAndDispose(client));\n }\n },\n );\n });\n}\n\n/**\n * Instruments a queue handler for ExportedHandler (env/ctx come from args).\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function instrumentExportedHandlerQueue<T extends ExportedHandler<any, any, any>>(\n handler: T,\n optionsCallback: (env: typeof cloudflareEnv) => CloudflareOptions | undefined,\n): void {\n if (!('queue' in handler) || typeof handler.queue !== 'function') {\n return;\n }\n\n handler.queue = ensureInstrumented(\n handler.queue,\n original =>\n new Proxy(original, {\n apply(target, thisArg, args: Parameters<NonNullable<T['queue']>>) {\n const [batch, env, ctx] = args;\n const context = instrumentContext(ctx);\n
|
|
1
|
+
{"version":3,"file":"instrumentQueue.js","sources":["../../../../src/instrumentations/worker/instrumentQueue.ts"],"sourcesContent":["import type { ExportedHandler, MessageBatch } from '@cloudflare/workers-types';\nimport type { env as cloudflareEnv, WorkerEntrypoint } from 'cloudflare:workers';\nimport {\n captureException,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,\n startSpan,\n withIsolationScope,\n} 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 { instrumentEnv } from './instrumentEnv';\n\n/**\n * Core queue handler logic - wraps execution with Sentry instrumentation.\n */\nfunction wrapQueueHandler(\n batch: MessageBatch,\n options: CloudflareOptions,\n context: ExecutionContext,\n fn: () => unknown,\n): unknown {\n return withIsolationScope(isolationScope => {\n const waitUntil = context.waitUntil.bind(context);\n\n const client = init({ ...options, ctx: context });\n isolationScope.setClient(client);\n\n addCloudResourceContext(isolationScope);\n\n return startSpan(\n {\n op: 'faas.queue',\n name: `process ${batch.queue}`,\n attributes: {\n 'faas.trigger': 'pubsub',\n 'messaging.destination.name': batch.queue,\n 'messaging.system': 'cloudflare',\n 'messaging.batch.message_count': batch.messages.length,\n 'messaging.message.retry.count': batch.messages.reduce((acc, message) => acc + message.attempts - 1, 0),\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'queue.process',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.faas.cloudflare.queue',\n [SEMANTIC_ATTRIBUTE_SENTRY_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.queue' } });\n throw e;\n } finally {\n waitUntil(flushAndDispose(client));\n }\n },\n );\n });\n}\n\n/**\n * Instruments a queue handler for ExportedHandler (env/ctx come from args).\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function instrumentExportedHandlerQueue<T extends ExportedHandler<any, any, any>>(\n handler: T,\n optionsCallback: (env: typeof cloudflareEnv) => CloudflareOptions | undefined,\n): void {\n if (!('queue' in handler) || typeof handler.queue !== 'function') {\n return;\n }\n\n handler.queue = ensureInstrumented(\n handler.queue,\n original =>\n new Proxy(original, {\n apply(target, thisArg, args: Parameters<NonNullable<T['queue']>>) {\n const [batch, 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 wrapQueueHandler(batch, options, context, () => target.apply(thisArg, args));\n },\n }),\n );\n}\n\n/**\n * Instruments a queue method for WorkerEntrypoint (options/context already available).\n */\nexport function instrumentWorkerEntrypointQueue<T extends WorkerEntrypoint>(\n instance: T,\n options: CloudflareOptions,\n context: ExecutionContext,\n): void {\n if (!instance.queue) {\n return;\n }\n\n const original = instance.queue.bind(instance);\n instance.queue = new Proxy(original, {\n apply(target, thisArg, args: [MessageBatch]) {\n const [batch] = args;\n\n return wrapQueueHandler(batch, options, context, () => Reflect.apply(target, thisArg, args));\n },\n });\n}\n"],"names":[],"mappings":";;;;;;;;;AAmBA;AACA;AACA;AACA,SAAS,gBAAgB;AACzB,EAAE,KAAK;AACP,EAAE,OAAO;AACT,EAAE,OAAO;AACT,EAAE,EAAE;AACJ,EAAW;AACX,EAAE,OAAO,kBAAkB,CAAC,cAAA,IAAkB;AAC9C,IAAI,MAAM,SAAA,GAAY,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC;;AAErD,IAAI,MAAM,MAAA,GAAS,IAAI,CAAC,EAAE,GAAG,OAAO,EAAE,GAAG,EAAE,OAAA,EAAS,CAAC;AACrD,IAAI,cAAc,CAAC,SAAS,CAAC,MAAM,CAAC;;AAEpC,IAAI,uBAAuB,CAAC,cAAc,CAAC;;AAE3C,IAAI,OAAO,SAAS;AACpB,MAAM;AACN,QAAQ,EAAE,EAAE,YAAY;AACxB,QAAQ,IAAI,EAAE,CAAC,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;AACA,QAAA,UAAA,EAAA;AACA,UAAA,cAAA,EAAA,QAAA;AACA,UAAA,4BAAA,EAAA,KAAA,CAAA,KAAA;AACA,UAAA,kBAAA,EAAA,YAAA;AACA,UAAA,+BAAA,EAAA,KAAA,CAAA,QAAA,CAAA,MAAA;AACA,UAAA,+BAAA,EAAA,KAAA,CAAA,QAAA,CAAA,MAAA,CAAA,CAAA,GAAA,EAAA,OAAA,KAAA,GAAA,GAAA,OAAA,CAAA,QAAA,GAAA,CAAA,EAAA,CAAA,CAAA;AACA,UAAA,CAAA,4BAAA,GAAA,eAAA;AACA,UAAA,CAAA,gCAAA,GAAA,4BAAA;AACA,UAAA,CAAA,gCAAA,GAAA,MAAA;AACA,SAAA;AACA,OAAA;AACA,MAAA,YAAA;AACA,QAAA,IAAA;AACA,UAAA,OAAA,MAAA,EAAA,EAAA;AACA,QAAA,CAAA,CAAA,OAAA,CAAA,EAAA;AACA,UAAA,gBAAA,CAAA,CAAA,EAAA,EAAA,SAAA,EAAA,EAAA,OAAA,EAAA,KAAA,EAAA,IAAA,EAAA,4BAAA,EAAA,EAAA,CAAA;AACA,UAAA,MAAA,CAAA;AACA,QAAA,CAAA,SAAA;AACA,UAAA,SAAA,CAAA,eAAA,CAAA,MAAA,CAAA,CAAA;AACA,QAAA;AACA,MAAA,CAAA;AACA,KAAA;AACA,EAAA,CAAA,CAAA;AACA;;AAEA;AACA;AACA;AACA;AACA,SAAA,8BAAA;AACA,EAAA,OAAA;AACA,EAAA,eAAA;AACA,EAAA;AACA,EAAA,IAAA,EAAA,OAAA,IAAA,OAAA,CAAA,IAAA,OAAA,OAAA,CAAA,KAAA,KAAA,UAAA,EAAA;AACA,IAAA;AACA,EAAA;;AAEA,EAAA,OAAA,CAAA,KAAA,GAAA,kBAAA;AACA,IAAA,OAAA,CAAA,KAAA;AACA,IAAA,QAAA;AACA,MAAA,IAAA,KAAA,CAAA,QAAA,EAAA;AACA,QAAA,KAAA,CAAA,MAAA,EAAA,OAAA,EAAA,IAAA,EAAA;AACA,UAAA,MAAA,CAAA,KAAA,EAAA,GAAA,EAAA,GAAA,CAAA,GAAA,IAAA;AACA,UAAA,MAAA,OAAA,GAAA,iBAAA,CAAA,GAAA,CAAA;AACA,UAAA,MAAA,OAAA,GAAA,eAAA,CAAA,eAAA,CAAA,GAAA,CAAA,EAAA,GAAA,CAAA;AACA,UAAA,IAAA,CAAA,CAAA,CAAA,GAAA,aAAA,CAAA,GAAA,EAAA,OAAA,CAAA;AACA,UAAA,IAAA,CAAA,CAAA,CAAA,GAAA,OAAA;;AAEA,UAAA,OAAA,gBAAA,CAAA,KAAA,EAAA,OAAA,EAAA,OAAA,EAAA,MAAA,MAAA,CAAA,KAAA,CAAA,OAAA,EAAA,IAAA,CAAA,CAAA;AACA,QAAA,CAAA;AACA,OAAA,CAAA;AACA,GAAA;AACA;;AAEA;AACA;AACA;AACA,SAAA,+BAAA;AACA,EAAA,QAAA;AACA,EAAA,OAAA;AACA,EAAA,OAAA;AACA,EAAA;AACA,EAAA,IAAA,CAAA,QAAA,CAAA,KAAA,EAAA;AACA,IAAA;AACA,EAAA;;AAEA,EAAA,MAAA,QAAA,GAAA,QAAA,CAAA,KAAA,CAAA,IAAA,CAAA,QAAA,CAAA;AACA,EAAA,QAAA,CAAA,KAAA,GAAA,IAAA,KAAA,CAAA,QAAA,EAAA;AACA,IAAA,KAAA,CAAA,MAAA,EAAA,OAAA,EAAA,IAAA,EAAA;AACA,MAAA,MAAA,CAAA,KAAA,CAAA,GAAA,IAAA;;AAEA,MAAA,OAAA,gBAAA,CAAA,KAAA,EAAA,OAAA,EAAA,OAAA,EAAA,MAAA,OAAA,CAAA,KAAA,CAAA,MAAA,EAAA,OAAA,EAAA,IAAA,CAAA,CAAA;AACA,IAAA,CAAA;AACA,GAAA,CAAA;AACA;;;;"}
|
|
@@ -5,6 +5,7 @@ import { getFinalOptions } from '../../options.js';
|
|
|
5
5
|
import { addCloudResourceContext } from '../../scope-utils.js';
|
|
6
6
|
import { init } from '../../sdk.js';
|
|
7
7
|
import { instrumentContext } from '../../utils/instrumentContext.js';
|
|
8
|
+
import { instrumentEnv } from './instrumentEnv.js';
|
|
8
9
|
|
|
9
10
|
function wrapScheduledHandler(
|
|
10
11
|
controller,
|
|
@@ -65,9 +66,9 @@ function instrumentExportedHandlerScheduled(
|
|
|
65
66
|
apply(target, thisArg, args) {
|
|
66
67
|
const [controller, env, ctx] = args;
|
|
67
68
|
const context = instrumentContext(ctx);
|
|
68
|
-
args[2] = context;
|
|
69
|
-
|
|
70
69
|
const options = getFinalOptions(optionsCallback(env), env);
|
|
70
|
+
args[1] = instrumentEnv(env, options);
|
|
71
|
+
args[2] = context;
|
|
71
72
|
|
|
72
73
|
return wrapScheduledHandler(controller, options, context, () => target.apply(thisArg, args));
|
|
73
74
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrumentScheduled.js","sources":["../../../../src/instrumentations/worker/instrumentScheduled.ts"],"sourcesContent":["import type { ExportedHandler, ScheduledController } from '@cloudflare/workers-types';\nimport type { env as cloudflareEnv, WorkerEntrypoint } from 'cloudflare:workers';\nimport {\n captureException,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,\n startSpan,\n withIsolationScope,\n} 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';\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 const client = init({ ...options, ctx: context });\n isolationScope.setClient(client);\n\n addCloudResourceContext(isolationScope);\n\n return startSpan(\n {\n op: 'faas.cron',\n name: `Scheduled Cron ${controller.cron}`,\n attributes: {\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 [SEMANTIC_ATTRIBUTE_SENTRY_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 */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function instrumentExportedHandlerScheduled<T extends ExportedHandler<any, any, any>>(\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
|
|
1
|
+
{"version":3,"file":"instrumentScheduled.js","sources":["../../../../src/instrumentations/worker/instrumentScheduled.ts"],"sourcesContent":["import type { ExportedHandler, ScheduledController } from '@cloudflare/workers-types';\nimport type { env as cloudflareEnv, WorkerEntrypoint } from 'cloudflare:workers';\nimport {\n captureException,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,\n startSpan,\n withIsolationScope,\n} 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 { 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 const client = init({ ...options, ctx: context });\n isolationScope.setClient(client);\n\n addCloudResourceContext(isolationScope);\n\n return startSpan(\n {\n op: 'faas.cron',\n name: `Scheduled Cron ${controller.cron}`,\n attributes: {\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 [SEMANTIC_ATTRIBUTE_SENTRY_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 */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function instrumentExportedHandlerScheduled<T extends ExportedHandler<any, any, any>>(\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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"}
|
|
@@ -5,6 +5,7 @@ import { getFinalOptions } from '../../options.js';
|
|
|
5
5
|
import { addCloudResourceContext } from '../../scope-utils.js';
|
|
6
6
|
import { init } from '../../sdk.js';
|
|
7
7
|
import { instrumentContext } from '../../utils/instrumentContext.js';
|
|
8
|
+
import { instrumentEnv } from './instrumentEnv.js';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Core tail handler logic - wraps execution with Sentry instrumentation.
|
|
@@ -49,9 +50,9 @@ function instrumentExportedHandlerTail(
|
|
|
49
50
|
apply(target, thisArg, args) {
|
|
50
51
|
const [, env, ctx] = args;
|
|
51
52
|
const context = instrumentContext(ctx);
|
|
52
|
-
args[2] = context;
|
|
53
|
-
|
|
54
53
|
const options = getFinalOptions(optionsCallback(env), env);
|
|
54
|
+
args[1] = instrumentEnv(env, options);
|
|
55
|
+
args[2] = context;
|
|
55
56
|
|
|
56
57
|
return wrapTailHandler(options, context, () => target.apply(thisArg, args));
|
|
57
58
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrumentTail.js","sources":["../../../../src/instrumentations/worker/instrumentTail.ts"],"sourcesContent":["import type { ExportedHandler, TraceItem } from '@cloudflare/workers-types';\nimport type { env as cloudflareEnv, WorkerEntrypoint } from 'cloudflare:workers';\nimport { captureException, 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';\n\n/**\n * Core tail handler logic - wraps execution with Sentry instrumentation.\n * Note: tail handlers don't create spans, just error capture.\n */\nfunction wrapTailHandler(options: CloudflareOptions, context: ExecutionContext, fn: () => unknown): unknown {\n return withIsolationScope(async isolationScope => {\n const waitUntil = context.waitUntil.bind(context);\n\n const client = init({ ...options, ctx: context });\n isolationScope.setClient(client);\n\n addCloudResourceContext(isolationScope);\n\n try {\n return await fn();\n } catch (e) {\n captureException(e, { mechanism: { handled: false, type: 'auto.faas.cloudflare.tail' } });\n throw e;\n } finally {\n waitUntil(flushAndDispose(client));\n }\n });\n}\n\n/**\n * Instruments a tail handler for ExportedHandler (env/ctx come from args).\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function instrumentExportedHandlerTail<T extends ExportedHandler<any, any, any>>(\n handler: T,\n optionsCallback: (env: typeof cloudflareEnv) => CloudflareOptions | undefined,\n): void {\n if (!('tail' in handler) || typeof handler.tail !== 'function') {\n return;\n }\n\n handler.tail = ensureInstrumented(\n handler.tail,\n original =>\n new Proxy(original, {\n apply(target, thisArg, args: Parameters<NonNullable<T['tail']>>) {\n const [, env, ctx] = args;\n const context = instrumentContext(ctx);\n
|
|
1
|
+
{"version":3,"file":"instrumentTail.js","sources":["../../../../src/instrumentations/worker/instrumentTail.ts"],"sourcesContent":["import type { ExportedHandler, TraceItem } from '@cloudflare/workers-types';\nimport type { env as cloudflareEnv, WorkerEntrypoint } from 'cloudflare:workers';\nimport { captureException, 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 { instrumentEnv } from './instrumentEnv';\n\n/**\n * Core tail handler logic - wraps execution with Sentry instrumentation.\n * Note: tail handlers don't create spans, just error capture.\n */\nfunction wrapTailHandler(options: CloudflareOptions, context: ExecutionContext, fn: () => unknown): unknown {\n return withIsolationScope(async isolationScope => {\n const waitUntil = context.waitUntil.bind(context);\n\n const client = init({ ...options, ctx: context });\n isolationScope.setClient(client);\n\n addCloudResourceContext(isolationScope);\n\n try {\n return await fn();\n } catch (e) {\n captureException(e, { mechanism: { handled: false, type: 'auto.faas.cloudflare.tail' } });\n throw e;\n } finally {\n waitUntil(flushAndDispose(client));\n }\n });\n}\n\n/**\n * Instruments a tail handler for ExportedHandler (env/ctx come from args).\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function instrumentExportedHandlerTail<T extends ExportedHandler<any, any, any>>(\n handler: T,\n optionsCallback: (env: typeof cloudflareEnv) => CloudflareOptions | undefined,\n): void {\n if (!('tail' in handler) || typeof handler.tail !== 'function') {\n return;\n }\n\n handler.tail = ensureInstrumented(\n handler.tail,\n original =>\n new Proxy(original, {\n apply(target, thisArg, args: Parameters<NonNullable<T['tail']>>) {\n const [, 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 wrapTailHandler(options, context, () => target.apply(thisArg, args));\n },\n }),\n );\n}\n\n/**\n * Instruments a tail method for WorkerEntrypoint (options/context already available).\n */\nexport function instrumentWorkerEntrypointTail<T extends WorkerEntrypoint>(\n instance: T,\n options: CloudflareOptions,\n context: ExecutionContext,\n): void {\n if (!instance.tail) {\n return;\n }\n\n const original = instance.tail.bind(instance);\n instance.tail = new Proxy(original, {\n apply(target, thisArg, args: [TraceItem[]]) {\n return wrapTailHandler(options, context, () => Reflect.apply(target, thisArg, args));\n },\n });\n}\n"],"names":[],"mappings":";;;;;;;;;AAYA;AACA;AACA;AACA;AACA,SAAS,eAAe,CAAC,OAAO,EAAqB,OAAO,EAAoB,EAAE,EAA0B;AAC5G,EAAE,OAAO,kBAAkB,CAAC,MAAM,kBAAkB;AACpD,IAAI,MAAM,SAAA,GAAY,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC;;AAErD,IAAI,MAAM,MAAA,GAAS,IAAI,CAAC,EAAE,GAAG,OAAO,EAAE,GAAG,EAAE,OAAA,EAAS,CAAC;AACrD,IAAI,cAAc,CAAC,SAAS,CAAC,MAAM,CAAC;;AAEpC,IAAI,uBAAuB,CAAC,cAAc,CAAC;;AAE3C,IAAI,IAAI;AACR,MAAM,OAAO,MAAM,EAAE,EAAE;AACvB,IAAI,CAAA,CAAE,OAAO,CAAC,EAAE;AAChB,MAAM,gBAAgB,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,2BAAA,EAA4B,EAAG,CAAC;AAC/F,MAAM,MAAM,CAAC;AACb,IAAI,UAAU;AACd,MAAM,SAAS,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;AACxC,IAAI;AACJ,EAAE,CAAC,CAAC;AACJ;;AAEA;AACA;AACA;AACA;AACO,SAAS,6BAA6B;AAC7C,EAAE,OAAO;AACT,EAAE,eAAe;AACjB,EAAQ;AACR,EAAE,IAAI,EAAE,UAAU,OAAO,CAAA,IAAK,OAAO,OAAO,CAAC,IAAA,KAAS,UAAU,EAAE;AAClE,IAAI;AACJ,EAAE;;AAEF,EAAE,OAAO,CAAC,IAAA,GAAO,kBAAkB;AACnC,IAAI,OAAO,CAAC,IAAI;AAChB,IAAI,QAAA;AACJ,MAAM,IAAI,KAAK,CAAC,QAAQ,EAAE;AAC1B,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAsC;AACzE,UAAU,MAAM,GAAG,GAAG,EAAE,GAAG,CAAA,GAAI,IAAI;AACnC,UAAU,MAAM,OAAA,GAAU,iBAAiB,CAAC,GAAG,CAAC;AAChD,UAAU,MAAM,OAAA,GAAU,eAAe,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC;AACpE,UAAU,IAAI,CAAC,CAAC,CAAA,GAAI,aAAa,CAAC,GAAG,EAAE,OAAO,CAAC;AAC/C,UAAU,IAAI,CAAC,CAAC,CAAA,GAAI,OAAO;;AAE3B,UAAU,OAAO,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;AACrF,QAAQ,CAAC;AACT,OAAO,CAAC;AACR,GAAG;AACH;;AAEA;AACA;AACA;AACO,SAAS,8BAA8B;AAC9C,EAAE,QAAQ;AACV,EAAE,OAAO;AACT,EAAE,OAAO;AACT,EAAQ;AACR,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE;AACtB,IAAI;AACJ,EAAE;;AAEF,EAAE,MAAM,QAAA,GAAW,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC;AAC/C,EAAE,QAAQ,CAAC,IAAA,GAAO,IAAI,KAAK,CAAC,QAAQ,EAAE;AACtC,IAAI,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAiB;AAChD,MAAM,OAAO,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;AAC1F,IAAI,CAAC;AACL,GAAG,CAAC;AACJ;;;;"}
|
|
@@ -2,9 +2,10 @@ import { defineIntegration, addVercelAiProcessors } from '@sentry/core';
|
|
|
2
2
|
|
|
3
3
|
const INTEGRATION_NAME = 'VercelAI';
|
|
4
4
|
|
|
5
|
-
const _vercelAIIntegration = (() => {
|
|
5
|
+
const _vercelAIIntegration = ((options = {}) => {
|
|
6
6
|
return {
|
|
7
7
|
name: INTEGRATION_NAME,
|
|
8
|
+
options,
|
|
8
9
|
setup(client) {
|
|
9
10
|
addVercelAiProcessors(client);
|
|
10
11
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vercelai.js","sources":["../../../../src/integrations/tracing/vercelai.ts"],"sourcesContent":["/**\n * This is a copy of the Vercel AI integration from the node SDK.\n *\n * The only difference is that it does not use `@opentelemetry/instrumentation`\n * because Cloudflare Workers do not support it.\n *\n * Therefore, we cannot automatically patch setting `experimental_telemetry: { isEnabled: true }`\n * and users have to manually set this to get spans.\n */\n\nimport type { IntegrationFn } from '@sentry/core';\nimport { addVercelAiProcessors, defineIntegration } from '@sentry/core';\n\nconst INTEGRATION_NAME = 'VercelAI';\n\nconst _vercelAIIntegration = (() => {\n return {\n name: INTEGRATION_NAME,\n setup(client) {\n addVercelAiProcessors(client);\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Adds Sentry tracing instrumentation for the [ai](https://www.npmjs.com/package/ai) library.\n * This integration is not enabled by default, you need to manually add it.\n *\n * For more information, see the [`ai` documentation](https://sdk.vercel.ai/docs/ai-sdk-core/telemetry).\n *\n * You need to enable collecting spans for a specific call by setting\n * `experimental_telemetry.isEnabled` to `true` in the first argument of the function call.\n *\n * ```javascript\n * const result = await generateText({\n * model: openai('gpt-4-turbo'),\n * experimental_telemetry: { isEnabled: true },\n * });\n * ```\n *\n * If you want to collect inputs and outputs for a specific call, you must specifically opt-in to each\n * function call by setting `experimental_telemetry.recordInputs` and `experimental_telemetry.recordOutputs`\n * to `true`.\n *\n * ```javascript\n * const result = await generateText({\n * model: openai('gpt-4-turbo'),\n * experimental_telemetry: { isEnabled: true, recordInputs: true, recordOutputs: true },\n * });\n */\nexport const vercelAIIntegration = defineIntegration(_vercelAIIntegration);\n"],"names":[],"mappings":";;AAaA,MAAM,gBAAA,GAAmB,UAAU;;
|
|
1
|
+
{"version":3,"file":"vercelai.js","sources":["../../../../src/integrations/tracing/vercelai.ts"],"sourcesContent":["/**\n * This is a copy of the Vercel AI integration from the node SDK.\n *\n * The only difference is that it does not use `@opentelemetry/instrumentation`\n * because Cloudflare Workers do not support it.\n *\n * Therefore, we cannot automatically patch setting `experimental_telemetry: { isEnabled: true }`\n * and users have to manually set this to get spans.\n */\n\nimport type { IntegrationFn } from '@sentry/core';\nimport { addVercelAiProcessors, defineIntegration } from '@sentry/core';\n\nconst INTEGRATION_NAME = 'VercelAI';\n\ninterface VercelAiOptions {\n /**\n * Enable or disable truncation of recorded input messages.\n * Defaults to `true`.\n */\n enableTruncation?: boolean;\n}\n\nconst _vercelAIIntegration = ((options: VercelAiOptions = {}) => {\n return {\n name: INTEGRATION_NAME,\n options,\n setup(client) {\n addVercelAiProcessors(client);\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Adds Sentry tracing instrumentation for the [ai](https://www.npmjs.com/package/ai) library.\n * This integration is not enabled by default, you need to manually add it.\n *\n * For more information, see the [`ai` documentation](https://sdk.vercel.ai/docs/ai-sdk-core/telemetry).\n *\n * You need to enable collecting spans for a specific call by setting\n * `experimental_telemetry.isEnabled` to `true` in the first argument of the function call.\n *\n * ```javascript\n * const result = await generateText({\n * model: openai('gpt-4-turbo'),\n * experimental_telemetry: { isEnabled: true },\n * });\n * ```\n *\n * If you want to collect inputs and outputs for a specific call, you must specifically opt-in to each\n * function call by setting `experimental_telemetry.recordInputs` and `experimental_telemetry.recordOutputs`\n * to `true`.\n *\n * ```javascript\n * const result = await generateText({\n * model: openai('gpt-4-turbo'),\n * experimental_telemetry: { isEnabled: true, recordInputs: true, recordOutputs: true },\n * });\n */\nexport const vercelAIIntegration = defineIntegration(_vercelAIIntegration);\n"],"names":[],"mappings":";;AAaA,MAAM,gBAAA,GAAmB,UAAU;;AAUnC,MAAM,oBAAA,IAAwB,CAAC,OAAO,GAAoB,EAAE,KAAK;AACjE,EAAE,OAAO;AACT,IAAI,IAAI,EAAE,gBAAgB;AAC1B,IAAI,OAAO;AACX,IAAI,KAAK,CAAC,MAAM,EAAE;AAClB,MAAM,qBAAqB,CAAC,MAAM,CAAC;AACnC,IAAI,CAAC;AACL,GAAG;AACH,CAAC,CAAA;;AAED;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;MACa,mBAAA,GAAsB,iBAAiB,CAAC,oBAAoB;;;;"}
|
package/build/esm/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"type":"module","version":"10.
|
|
1
|
+
{"type":"module","version":"10.49.0","sideEffects":false}
|
package/build/esm/sdk.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { dedupeIntegration, inboundFiltersIntegration, functionToStringIntegration, conversationIdIntegration, linkedErrorsIntegration, requestDataIntegration, consoleIntegration, getIntegrationsToSetup, stackParserFromStackParserOptions, initAndBind } from '@sentry/core';
|
|
1
|
+
import { dedupeIntegration, inboundFiltersIntegration, functionToStringIntegration, conversationIdIntegration, linkedErrorsIntegration, requestDataIntegration, consoleIntegration, getIntegrationsToSetup, spanStreamingIntegration, stackParserFromStackParserOptions, initAndBind } from '@sentry/core';
|
|
2
2
|
import { CloudflareClient } from './client.js';
|
|
3
3
|
import { makeFlushLock } from './flush.js';
|
|
4
4
|
import { fetchIntegration } from './integrations/fetch.js';
|
|
@@ -39,10 +39,15 @@ function init(options) {
|
|
|
39
39
|
const flushLock = options.ctx ? makeFlushLock(options.ctx) : undefined;
|
|
40
40
|
delete options.ctx;
|
|
41
41
|
|
|
42
|
+
const resolvedIntegrations = getIntegrationsToSetup(options);
|
|
43
|
+
if (options.traceLifecycle === 'stream' && !resolvedIntegrations.some(i => i.name === 'SpanStreaming')) {
|
|
44
|
+
resolvedIntegrations.push(spanStreamingIntegration());
|
|
45
|
+
}
|
|
46
|
+
|
|
42
47
|
const clientOptions = {
|
|
43
48
|
...options,
|
|
44
49
|
stackParser: stackParserFromStackParserOptions(options.stackParser || defaultStackParser),
|
|
45
|
-
integrations:
|
|
50
|
+
integrations: resolvedIntegrations,
|
|
46
51
|
transport: options.transport || makeCloudflareTransport,
|
|
47
52
|
flushLock,
|
|
48
53
|
};
|
package/build/esm/sdk.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sdk.js","sources":["../../src/sdk.ts"],"sourcesContent":["import type { Integration } from '@sentry/core';\nimport {\n consoleIntegration,\n conversationIdIntegration,\n dedupeIntegration,\n functionToStringIntegration,\n getIntegrationsToSetup,\n inboundFiltersIntegration,\n initAndBind,\n linkedErrorsIntegration,\n requestDataIntegration,\n stackParserFromStackParserOptions,\n} from '@sentry/core';\nimport type { CloudflareClientOptions, CloudflareOptions } from './client';\nimport { CloudflareClient } from './client';\nimport { makeFlushLock } from './flush';\nimport { fetchIntegration } from './integrations/fetch';\nimport { honoIntegration } from './integrations/hono';\nimport { setupOpenTelemetryTracer } from './opentelemetry/tracer';\nimport { makeCloudflareTransport } from './transport';\nimport { defaultStackParser } from './vendor/stacktrace';\n\n/** Get the default integrations for the Cloudflare SDK. */\nexport function getDefaultIntegrations(options: CloudflareOptions): Integration[] {\n const sendDefaultPii = options.sendDefaultPii ?? false;\n return [\n // The Dedupe integration should not be used in workflows because we want to\n // capture all step failures, even if they are the same error.\n ...(options.enableDedupe === false ? [] : [dedupeIntegration()]),\n // TODO(v11): Replace with `eventFiltersIntegration` once we remove the deprecated `inboundFiltersIntegration`\n // eslint-disable-next-line deprecation/deprecation\n inboundFiltersIntegration(),\n functionToStringIntegration(),\n conversationIdIntegration(),\n linkedErrorsIntegration(),\n fetchIntegration(),\n honoIntegration(),\n // TODO(v11): the `include` object should be defined directly in the integration based on `sendDefaultPii`\n requestDataIntegration(sendDefaultPii ? undefined : { include: { cookies: false } }),\n consoleIntegration(),\n ];\n}\n\n/**\n * Initializes the cloudflare SDK.\n */\nexport function init(options: CloudflareOptions): CloudflareClient | undefined {\n if (options.defaultIntegrations === undefined) {\n options.defaultIntegrations = getDefaultIntegrations(options);\n }\n\n const flushLock = options.ctx ? makeFlushLock(options.ctx) : undefined;\n delete options.ctx;\n\n const clientOptions: CloudflareClientOptions = {\n ...options,\n stackParser: stackParserFromStackParserOptions(options.stackParser || defaultStackParser),\n integrations:
|
|
1
|
+
{"version":3,"file":"sdk.js","sources":["../../src/sdk.ts"],"sourcesContent":["import type { Integration } from '@sentry/core';\nimport {\n consoleIntegration,\n conversationIdIntegration,\n dedupeIntegration,\n functionToStringIntegration,\n getIntegrationsToSetup,\n inboundFiltersIntegration,\n initAndBind,\n linkedErrorsIntegration,\n requestDataIntegration,\n spanStreamingIntegration,\n stackParserFromStackParserOptions,\n} from '@sentry/core';\nimport type { CloudflareClientOptions, CloudflareOptions } from './client';\nimport { CloudflareClient } from './client';\nimport { makeFlushLock } from './flush';\nimport { fetchIntegration } from './integrations/fetch';\nimport { honoIntegration } from './integrations/hono';\nimport { setupOpenTelemetryTracer } from './opentelemetry/tracer';\nimport { makeCloudflareTransport } from './transport';\nimport { defaultStackParser } from './vendor/stacktrace';\n\n/** Get the default integrations for the Cloudflare SDK. */\nexport function getDefaultIntegrations(options: CloudflareOptions): Integration[] {\n const sendDefaultPii = options.sendDefaultPii ?? false;\n return [\n // The Dedupe integration should not be used in workflows because we want to\n // capture all step failures, even if they are the same error.\n ...(options.enableDedupe === false ? [] : [dedupeIntegration()]),\n // TODO(v11): Replace with `eventFiltersIntegration` once we remove the deprecated `inboundFiltersIntegration`\n // eslint-disable-next-line deprecation/deprecation\n inboundFiltersIntegration(),\n functionToStringIntegration(),\n conversationIdIntegration(),\n linkedErrorsIntegration(),\n fetchIntegration(),\n honoIntegration(),\n // TODO(v11): the `include` object should be defined directly in the integration based on `sendDefaultPii`\n requestDataIntegration(sendDefaultPii ? undefined : { include: { cookies: false } }),\n consoleIntegration(),\n ];\n}\n\n/**\n * Initializes the cloudflare SDK.\n */\nexport function init(options: CloudflareOptions): CloudflareClient | undefined {\n if (options.defaultIntegrations === undefined) {\n options.defaultIntegrations = getDefaultIntegrations(options);\n }\n\n const flushLock = options.ctx ? makeFlushLock(options.ctx) : undefined;\n delete options.ctx;\n\n const resolvedIntegrations = getIntegrationsToSetup(options);\n if (options.traceLifecycle === 'stream' && !resolvedIntegrations.some(i => i.name === 'SpanStreaming')) {\n resolvedIntegrations.push(spanStreamingIntegration());\n }\n\n const clientOptions: CloudflareClientOptions = {\n ...options,\n stackParser: stackParserFromStackParserOptions(options.stackParser || defaultStackParser),\n integrations: resolvedIntegrations,\n transport: options.transport || makeCloudflareTransport,\n flushLock,\n };\n\n /**\n * The Cloudflare SDK is not OpenTelemetry native, however, we set up some OpenTelemetry compatibility\n * via a custom trace provider.\n * This ensures that any spans emitted via `@opentelemetry/api` will be captured by Sentry.\n * HOWEVER, big caveat: This does not handle custom context handling, it will always work off the current scope.\n * This should be good enough for many, but not all integrations.\n */\n if (!options.skipOpenTelemetrySetup) {\n setupOpenTelemetryTracer();\n }\n\n return initAndBind(CloudflareClient, clientOptions) as CloudflareClient;\n}\n"],"names":[],"mappings":";;;;;;;;;AAuBA;AACO,SAAS,sBAAsB,CAAC,OAAO,EAAoC;AAClF,EAAE,MAAM,cAAA,GAAiB,OAAO,CAAC,cAAA,IAAkB,KAAK;AACxD,EAAE,OAAO;AACT;AACA;AACA,IAAI,IAAI,OAAO,CAAC,YAAA,KAAiB,KAAA,GAAQ,EAAC,GAAI,CAAC,iBAAiB,EAAE,CAAC,CAAC;AACpE;AACA;AACA,IAAI,yBAAyB,EAAE;AAC/B,IAAI,2BAA2B,EAAE;AACjC,IAAI,yBAAyB,EAAE;AAC/B,IAAI,uBAAuB,EAAE;AAC7B,IAAI,gBAAgB,EAAE;AACtB,IAAI,eAAe,EAAE;AACrB;AACA,IAAI,sBAAsB,CAAC,cAAA,GAAiB,YAAY,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,KAAA,EAAM,EAAG,CAAC;AACxF,IAAI,kBAAkB,EAAE;AACxB,GAAG;AACH;;AAEA;AACA;AACA;AACO,SAAS,IAAI,CAAC,OAAO,EAAmD;AAC/E,EAAE,IAAI,OAAO,CAAC,mBAAA,KAAwB,SAAS,EAAE;AACjD,IAAI,OAAO,CAAC,mBAAA,GAAsB,sBAAsB,CAAC,OAAO,CAAC;AACjE,EAAE;;AAEF,EAAE,MAAM,SAAA,GAAY,OAAO,CAAC,GAAA,GAAM,aAAa,CAAC,OAAO,CAAC,GAAG,CAAA,GAAI,SAAS;AACxE,EAAE,OAAO,OAAO,CAAC,GAAG;;AAEpB,EAAE,MAAM,oBAAA,GAAuB,sBAAsB,CAAC,OAAO,CAAC;AAC9D,EAAE,IAAI,OAAO,CAAC,mBAAmB,QAAA,IAAY,CAAC,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAA,KAAS,eAAe,CAAC,EAAE;AAC1G,IAAI,oBAAoB,CAAC,IAAI,CAAC,wBAAwB,EAAE,CAAC;AACzD,EAAE;;AAEF,EAAE,MAAM,aAAa,GAA4B;AACjD,IAAI,GAAG,OAAO;AACd,IAAI,WAAW,EAAE,iCAAiC,CAAC,OAAO,CAAC,WAAA,IAAe,kBAAkB,CAAC;AAC7F,IAAI,YAAY,EAAE,oBAAoB;AACtC,IAAI,SAAS,EAAE,OAAO,CAAC,SAAA,IAAa,uBAAuB;AAC3D,IAAI,SAAS;AACb,GAAG;;AAEH;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAE,IAAI,CAAC,OAAO,CAAC,sBAAsB,EAAE;AACvC,IAAI,wBAAwB,EAAE;AAC9B,EAAE;;AAEF,EAAE,OAAO,WAAW,CAAC,gBAAgB,EAAE,aAAa,CAAA;AACpD;;;;"}
|
|
@@ -37,13 +37,14 @@ function instrumentContext(ctx) {
|
|
|
37
37
|
// If so, wrap the storage with instrumentation
|
|
38
38
|
if ('storage' in ctx && ctx.storage) {
|
|
39
39
|
const originalStorage = ctx.storage;
|
|
40
|
+
const waitUntil = 'waitUntil' in ctx && typeof ctx.waitUntil === 'function' ? ctx.waitUntil.bind(ctx) : undefined;
|
|
40
41
|
let instrumentedStorage;
|
|
41
42
|
descriptors.storage = {
|
|
42
43
|
configurable: true,
|
|
43
44
|
enumerable: true,
|
|
44
45
|
get: () => {
|
|
45
46
|
if (!instrumentedStorage) {
|
|
46
|
-
instrumentedStorage = instrumentDurableObjectStorage(originalStorage);
|
|
47
|
+
instrumentedStorage = instrumentDurableObjectStorage(originalStorage, waitUntil);
|
|
47
48
|
}
|
|
48
49
|
return instrumentedStorage;
|
|
49
50
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrumentContext.js","sources":["../../../src/utils/instrumentContext.ts"],"sourcesContent":["import { type DurableObjectState, type DurableObjectStorage, type ExecutionContext } from '@cloudflare/workers-types';\nimport { instrumentDurableObjectStorage } from '../instrumentations/instrumentDurableObjectStorage';\n\ntype ContextType = ExecutionContext | DurableObjectState;\ntype OverridesStore<T extends ContextType> = Map<keyof T, (...args: unknown[]) => unknown>;\n\n/**\n * Instruments an execution context or DurableObjectState with Sentry tracing.\n *\n * Creates a copy of the context that:\n * - Allows overriding of methods (e.g., waitUntil)\n * - For DurableObjectState: instruments storage operations (get, put, delete, list, etc.)\n * to create Sentry spans automatically\n *\n * @param ctx - The execution context or DurableObjectState to instrument\n * @returns An instrumented copy of the context\n */\nexport function instrumentContext<T extends ContextType>(ctx: T): T {\n if (!ctx) return ctx;\n\n const overrides: OverridesStore<T> = new Map();\n const contextPrototype = Object.getPrototypeOf(ctx);\n const prototypeMethodNames = Object.getOwnPropertyNames(contextPrototype) as unknown as (keyof T)[];\n const ownPropertyNames = Object.getOwnPropertyNames(ctx) as unknown as (keyof T)[];\n const instrumented = new Set<unknown>(['constructor']);\n const descriptors: PropertyDescriptorMap = [...ownPropertyNames, ...prototypeMethodNames].reduce(\n (prevDescriptors, methodName) => {\n if (instrumented.has(methodName)) return prevDescriptors;\n if (typeof ctx[methodName] !== 'function') return prevDescriptors;\n instrumented.add(methodName);\n const overridableDescriptor = makeOverridableDescriptor(overrides, ctx, methodName);\n return {\n ...prevDescriptors,\n [methodName]: overridableDescriptor,\n };\n },\n {} as PropertyDescriptorMap,\n );\n\n // Check if this is a DurableObjectState context with a storage property\n // If so, wrap the storage with instrumentation\n if ('storage' in ctx && ctx.storage) {\n const originalStorage = ctx.storage;\n let instrumentedStorage: DurableObjectStorage | undefined;\n descriptors.storage = {\n configurable: true,\n enumerable: true,\n get: () => {\n if (!instrumentedStorage) {\n instrumentedStorage = instrumentDurableObjectStorage(originalStorage);\n }\n return instrumentedStorage;\n },\n };\n // Expose the original uninstrumented storage for internal Sentry operations\n // This avoids creating spans for internal storage operations\n descriptors.originalStorage = {\n configurable: true,\n enumerable: false,\n get: () => originalStorage,\n };\n }\n\n return Object.create(ctx, descriptors);\n}\n\n/**\n * Creates a property descriptor that allows overriding of a method on the given context object.\n *\n * This descriptor supports property overriding with functions only. It delegates method calls to\n * the provided store if an override exists or to the original method on the context otherwise.\n *\n * @param {OverridesStore<ContextType>} store - The storage for overridden methods specific to the context type.\n * @param {ContextType} ctx - The context object that contains the method to be overridden.\n * @param {keyof ContextType} method - The method on the context object to create the overridable descriptor for.\n * @return {PropertyDescriptor} A property descriptor enabling the overriding of the specified method.\n */\nfunction makeOverridableDescriptor<T extends ContextType>(\n store: OverridesStore<T>,\n ctx: T,\n method: keyof T,\n): PropertyDescriptor {\n return {\n configurable: true,\n enumerable: true,\n set: newValue => {\n if (typeof newValue == 'function') {\n store.set(method, newValue);\n return;\n }\n Reflect.set(ctx, method, newValue);\n },\n\n get: () => {\n if (store.has(method)) return store.get(method);\n const methodFunction = Reflect.get(ctx, method);\n if (typeof methodFunction !== 'function') return methodFunction;\n // We should do bind() to make sure that the method is bound to the context object - otherwise it will not work\n return methodFunction.bind(ctx);\n },\n };\n}\n"],"names":[],"mappings":";;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,iBAAiB,CAAwB,GAAG,EAAQ;AACpE,EAAE,IAAI,CAAC,GAAG,EAAE,OAAO,GAAG;;AAEtB,EAAE,MAAM,SAAS,GAAsB,IAAI,GAAG,EAAE;AAChD,EAAE,MAAM,mBAAmB,MAAM,CAAC,cAAc,CAAC,GAAG,CAAC;AACrD,EAAE,MAAM,uBAAuB,MAAM,CAAC,mBAAmB,CAAC,gBAAgB,CAAA;AAC1E,EAAE,MAAM,mBAAmB,MAAM,CAAC,mBAAmB,CAAC,GAAG,CAAA;AACzD,EAAE,MAAM,eAAe,IAAI,GAAG,CAAU,CAAC,aAAa,CAAC,CAAC;AACxD,EAAE,MAAM,WAAW,GAA0B,CAAC,GAAG,gBAAgB,EAAE,GAAG,oBAAoB,CAAC,CAAC,MAAM;AAClG,IAAI,CAAC,eAAe,EAAE,UAAU,KAAK;AACrC,MAAM,IAAI,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,OAAO,eAAe;AAC9D,MAAM,IAAI,OAAO,GAAG,CAAC,UAAU,CAAA,KAAM,UAAU,EAAE,OAAO,eAAe;AACvE,MAAM,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC;AAClC,MAAM,MAAM,qBAAA,GAAwB,yBAAyB,CAAC,SAAS,EAAE,GAAG,EAAE,UAAU,CAAC;AACzF,MAAM,OAAO;AACb,QAAQ,GAAG,eAAe;AAC1B,QAAQ,CAAC,UAAU,GAAG,qBAAqB;AAC3C,OAAO;AACP,IAAI,CAAC;AACL,IAAI,EAAC;AACL,GAAG;;AAEH;AACA;AACA,EAAE,IAAI,SAAA,IAAa,OAAO,GAAG,CAAC,OAAO,EAAE;AACvC,IAAI,MAAM,eAAA,GAAkB,GAAG,CAAC,OAAO;AACvC,IAAI,IAAI,mBAAmB;AAC3B,IAAI,WAAW,CAAC,OAAA,GAAU;AAC1B,MAAM,YAAY,EAAE,IAAI;AACxB,MAAM,UAAU,EAAE,IAAI;AACtB,MAAM,GAAG,EAAE,MAAM;AACjB,QAAQ,IAAI,CAAC,mBAAmB,EAAE;AAClC,UAAU,
|
|
1
|
+
{"version":3,"file":"instrumentContext.js","sources":["../../../src/utils/instrumentContext.ts"],"sourcesContent":["import { type DurableObjectState, type DurableObjectStorage, type ExecutionContext } from '@cloudflare/workers-types';\nimport { instrumentDurableObjectStorage } from '../instrumentations/instrumentDurableObjectStorage';\n\ntype ContextType = ExecutionContext | DurableObjectState;\ntype OverridesStore<T extends ContextType> = Map<keyof T, (...args: unknown[]) => unknown>;\n\n/**\n * Instruments an execution context or DurableObjectState with Sentry tracing.\n *\n * Creates a copy of the context that:\n * - Allows overriding of methods (e.g., waitUntil)\n * - For DurableObjectState: instruments storage operations (get, put, delete, list, etc.)\n * to create Sentry spans automatically\n *\n * @param ctx - The execution context or DurableObjectState to instrument\n * @returns An instrumented copy of the context\n */\nexport function instrumentContext<T extends ContextType>(ctx: T): T {\n if (!ctx) return ctx;\n\n const overrides: OverridesStore<T> = new Map();\n const contextPrototype = Object.getPrototypeOf(ctx);\n const prototypeMethodNames = Object.getOwnPropertyNames(contextPrototype) as unknown as (keyof T)[];\n const ownPropertyNames = Object.getOwnPropertyNames(ctx) as unknown as (keyof T)[];\n const instrumented = new Set<unknown>(['constructor']);\n const descriptors: PropertyDescriptorMap = [...ownPropertyNames, ...prototypeMethodNames].reduce(\n (prevDescriptors, methodName) => {\n if (instrumented.has(methodName)) return prevDescriptors;\n if (typeof ctx[methodName] !== 'function') return prevDescriptors;\n instrumented.add(methodName);\n const overridableDescriptor = makeOverridableDescriptor(overrides, ctx, methodName);\n return {\n ...prevDescriptors,\n [methodName]: overridableDescriptor,\n };\n },\n {} as PropertyDescriptorMap,\n );\n\n // Check if this is a DurableObjectState context with a storage property\n // If so, wrap the storage with instrumentation\n if ('storage' in ctx && ctx.storage) {\n const originalStorage = ctx.storage;\n const waitUntil = 'waitUntil' in ctx && typeof ctx.waitUntil === 'function' ? ctx.waitUntil.bind(ctx) : undefined;\n let instrumentedStorage: DurableObjectStorage | undefined;\n descriptors.storage = {\n configurable: true,\n enumerable: true,\n get: () => {\n if (!instrumentedStorage) {\n instrumentedStorage = instrumentDurableObjectStorage(originalStorage, waitUntil);\n }\n return instrumentedStorage;\n },\n };\n // Expose the original uninstrumented storage for internal Sentry operations\n // This avoids creating spans for internal storage operations\n descriptors.originalStorage = {\n configurable: true,\n enumerable: false,\n get: () => originalStorage,\n };\n }\n\n return Object.create(ctx, descriptors);\n}\n\n/**\n * Creates a property descriptor that allows overriding of a method on the given context object.\n *\n * This descriptor supports property overriding with functions only. It delegates method calls to\n * the provided store if an override exists or to the original method on the context otherwise.\n *\n * @param {OverridesStore<ContextType>} store - The storage for overridden methods specific to the context type.\n * @param {ContextType} ctx - The context object that contains the method to be overridden.\n * @param {keyof ContextType} method - The method on the context object to create the overridable descriptor for.\n * @return {PropertyDescriptor} A property descriptor enabling the overriding of the specified method.\n */\nfunction makeOverridableDescriptor<T extends ContextType>(\n store: OverridesStore<T>,\n ctx: T,\n method: keyof T,\n): PropertyDescriptor {\n return {\n configurable: true,\n enumerable: true,\n set: newValue => {\n if (typeof newValue == 'function') {\n store.set(method, newValue);\n return;\n }\n Reflect.set(ctx, method, newValue);\n },\n\n get: () => {\n if (store.has(method)) return store.get(method);\n const methodFunction = Reflect.get(ctx, method);\n if (typeof methodFunction !== 'function') return methodFunction;\n // We should do bind() to make sure that the method is bound to the context object - otherwise it will not work\n return methodFunction.bind(ctx);\n },\n };\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks if a value is a JSRPC proxy (service binding).
|
|
3
|
+
*
|
|
4
|
+
* JSRPC proxies return a truthy value for ANY property access, including
|
|
5
|
+
* properties that don't exist. This makes other duck-type checks unreliable
|
|
6
|
+
* unless we exclude JSRPC first.
|
|
7
|
+
*
|
|
8
|
+
* Must be checked before other binding type checks.
|
|
9
|
+
*/
|
|
10
|
+
function isJSRPC(item) {
|
|
11
|
+
try {
|
|
12
|
+
return !!(item )[`__some_property_that_will_never_exist__${Math.random()}`];
|
|
13
|
+
} catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const isNotJSRPC = (item) => !isJSRPC(item);
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Duck-type check for DurableObjectNamespace bindings.
|
|
22
|
+
* DurableObjectNamespace has `idFromName`, `idFromString`, `get`, `newUniqueId`.
|
|
23
|
+
*/
|
|
24
|
+
function isDurableObjectNamespace(item) {
|
|
25
|
+
return item != null && isNotJSRPC(item) && typeof item.idFromName === 'function';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export { isDurableObjectNamespace, isJSRPC };
|
|
29
|
+
//# sourceMappingURL=isBinding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"isBinding.js","sources":["../../../src/utils/isBinding.ts"],"sourcesContent":["/**\n * Adapted from https://github.com/evanderkoogh/otel-cf-workers/blob/effeb549f0a4ed1c55ea0c4f0d8e8e37e5494fb3/src/instrumentation/env.ts\n *\n * BSD 3-Clause License\n *\n * Copyright (c) 2023, Erwin van der Koogh\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\nimport type { DurableObjectNamespace } from '@cloudflare/workers-types';\n\n/**\n * Checks if a value is a JSRPC proxy (service binding).\n *\n * JSRPC proxies return a truthy value for ANY property access, including\n * properties that don't exist. This makes other duck-type checks unreliable\n * unless we exclude JSRPC first.\n *\n * Must be checked before other binding type checks.\n */\nexport function isJSRPC(item: unknown): item is Service {\n try {\n return !!(item as Record<string, unknown>)[`__some_property_that_will_never_exist__${Math.random()}`];\n } catch {\n return false;\n }\n}\n\nconst isNotJSRPC = (item: unknown): item is Record<string, unknown> => !isJSRPC(item);\n\n/**\n * Duck-type check for DurableObjectNamespace bindings.\n * DurableObjectNamespace has `idFromName`, `idFromString`, `get`, `newUniqueId`.\n */\nexport function isDurableObjectNamespace(item: unknown): item is DurableObjectNamespace {\n return item != null && isNotJSRPC(item) && typeof item.idFromName === 'function';\n}\n"],"names":[],"mappings":"AAmCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,OAAO,CAAC,IAAI,EAA4B;AACxD,EAAE,IAAI;AACN,IAAI,OAAO,CAAC,CAAC,CAAC,IAAA,GAAiC,CAAC,uCAAuC,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAA,CAAA;AACA,EAAA,CAAA,CAAA,MAAA;AACA,IAAA,OAAA,KAAA;AACA,EAAA;AACA;;AAEA,MAAA,UAAA,GAAA,CAAA,IAAA,KAAA,CAAA,OAAA,CAAA,IAAA,CAAA;;AAEA;AACA;AACA;AACA;AACA,SAAA,wBAAA,CAAA,IAAA,EAAA;AACA,EAAA,OAAA,IAAA,IAAA,IAAA,IAAA,UAAA,CAAA,IAAA,CAAA,IAAA,OAAA,IAAA,CAAA,UAAA,KAAA,UAAA;AACA;;;;"}
|