@sentry/cloudflare 10.72.0 → 10.74.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/async.js +7 -1
- package/build/cjs/async.js.map +1 -1
- package/build/cjs/durableobject.js +8 -55
- package/build/cjs/durableobject.js.map +1 -1
- package/build/cjs/index.js +2 -0
- package/build/cjs/index.js.map +1 -1
- package/build/cjs/instrument.js +4 -0
- package/build/cjs/instrument.js.map +1 -1
- package/build/cjs/instrumentations/instrumentDurableObjectHandlers.js +114 -0
- package/build/cjs/instrumentations/instrumentDurableObjectHandlers.js.map +1 -0
- package/build/cjs/instrumentations/instrumentWorkerEntrypoint.js +4 -1
- package/build/cjs/instrumentations/instrumentWorkerEntrypoint.js.map +1 -1
- package/build/cjs/nodejs_compat/index.js +2 -0
- package/build/cjs/nodejs_compat/index.js.map +1 -1
- package/build/cjs/vite/agentClass.js +3 -2
- package/build/cjs/vite/agentClass.js.map +1 -1
- package/build/cjs/vite/autoInstrument.js +1 -1
- package/build/cjs/vite/autoInstrument.js.map +1 -1
- package/build/cjs/vite/transform.js +79 -16
- package/build/cjs/vite/transform.js.map +1 -1
- package/build/cjs/workflows.js +4 -1
- package/build/cjs/workflows.js.map +1 -1
- package/build/esm/async.js +8 -2
- package/build/esm/async.js.map +1 -1
- package/build/esm/durableobject.js +9 -56
- package/build/esm/durableobject.js.map +1 -1
- package/build/esm/index.js +1 -0
- package/build/esm/index.js.map +1 -1
- package/build/esm/instrument.js +4 -1
- package/build/esm/instrument.js.map +1 -1
- package/build/esm/instrumentations/instrumentDurableObjectHandlers.js +112 -0
- package/build/esm/instrumentations/instrumentDurableObjectHandlers.js.map +1 -0
- package/build/esm/instrumentations/instrumentWorkerEntrypoint.js +4 -1
- package/build/esm/instrumentations/instrumentWorkerEntrypoint.js.map +1 -1
- package/build/esm/nodejs_compat/index.js +1 -0
- package/build/esm/nodejs_compat/index.js.map +1 -1
- package/build/esm/package.json +1 -1
- package/build/esm/vite/agentClass.js +3 -2
- package/build/esm/vite/agentClass.js.map +1 -1
- package/build/esm/vite/autoInstrument.js +1 -1
- package/build/esm/vite/autoInstrument.js.map +1 -1
- package/build/esm/vite/transform.js +79 -16
- package/build/esm/vite/transform.js.map +1 -1
- package/build/esm/workflows.js +4 -1
- package/build/esm/workflows.js.map +1 -1
- package/build/types/async.d.ts.map +1 -1
- package/build/types/durableobject.d.ts.map +1 -1
- package/build/types/index.d.ts +1 -0
- package/build/types/index.d.ts.map +1 -1
- package/build/types/instrument.d.ts +10 -0
- package/build/types/instrument.d.ts.map +1 -1
- package/build/types/instrumentations/instrumentDurableObjectHandlers.d.ts +28 -0
- package/build/types/instrumentations/instrumentDurableObjectHandlers.d.ts.map +1 -0
- package/build/types/instrumentations/instrumentWorkerEntrypoint.d.ts.map +1 -1
- package/build/types/vite/agentClass.d.ts +4 -3
- package/build/types/vite/agentClass.d.ts.map +1 -1
- package/build/types/vite/autoInstrument.d.ts.map +1 -1
- package/build/types/vite/transform.d.ts +5 -4
- package/build/types/vite/transform.d.ts.map +1 -1
- package/build/types/workflows.d.ts.map +1 -1
- package/build/types-ts3.8/index.d.ts +1 -0
- package/build/types-ts3.8/instrument.d.ts +10 -0
- package/build/types-ts3.8/instrumentations/instrumentDurableObjectHandlers.d.ts +28 -0
- package/build/types-ts3.8/vite/agentClass.d.ts +4 -3
- package/build/types-ts3.8/vite/transform.d.ts +5 -4
- package/package.json +3 -3
package/build/cjs/async.js
CHANGED
|
@@ -29,8 +29,14 @@ function setAsyncLocalStorageAsyncContextStrategy() {
|
|
|
29
29
|
});
|
|
30
30
|
}
|
|
31
31
|
function withIsolationScope(callback) {
|
|
32
|
-
const scope = getScopes().scope;
|
|
32
|
+
const scope = getScopes().scope.clone();
|
|
33
33
|
const isolationScope = getScopes().isolationScope.clone();
|
|
34
|
+
if (!core.isContinuingTrace(scope.getPropagationContext())) {
|
|
35
|
+
scope.setPropagationContext({
|
|
36
|
+
traceId: core.generateTraceId(),
|
|
37
|
+
sampleRand: core._INTERNAL_safeMathRandom()
|
|
38
|
+
});
|
|
39
|
+
}
|
|
34
40
|
return asyncStorage.run({ scope, isolationScope }, () => {
|
|
35
41
|
return callback(isolationScope);
|
|
36
42
|
});
|
package/build/cjs/async.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"async.js","sources":["../../src/async.ts"],"sourcesContent":["// Need to use node: prefix for cloudflare workers compatibility\n// Note: Because we are using node:async_hooks, we need to set `node_compat` in the wrangler.toml\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport type { Scope } from '@sentry/core';\nimport {\n _INTERNAL_createTracingChannelBinding,\n getDefaultCurrentScope,\n getDefaultIsolationScope,\n setAsyncContextStrategy,\n} from '@sentry/core';\n\n/**\n * Sets the async context strategy to use AsyncLocalStorage.\n *\n * AsyncLocalStorage is only available in the cloudflare workers runtime if you set\n * compatibility_flags = [\"nodejs_compat\"]\n *\n * @internal Only exported to be used in higher-level Sentry packages\n * @hidden Only exported to be used in higher-level Sentry packages\n */\nexport function setAsyncLocalStorageAsyncContextStrategy(): void {\n const asyncStorage = new AsyncLocalStorage<{\n scope: Scope;\n isolationScope: Scope;\n }>();\n\n function getScopes(): { scope: Scope; isolationScope: Scope } {\n const scopes = asyncStorage.getStore();\n\n if (scopes) {\n return scopes;\n }\n\n // fallback behavior:\n // if, for whatever reason, we can't find scopes on the context here, we have to fix this somehow\n return {\n scope: getDefaultCurrentScope(),\n isolationScope: getDefaultIsolationScope(),\n };\n }\n\n function withScope<T>(callback: (scope: Scope) => T): T {\n const scope = getScopes().scope.clone();\n const isolationScope = getScopes().isolationScope;\n return asyncStorage.run({ scope, isolationScope }, () => {\n return callback(scope);\n });\n }\n\n function withSetScope<T>(scope: Scope, callback: (scope: Scope) => T): T {\n const isolationScope = getScopes().isolationScope.clone();\n return asyncStorage.run({ scope, isolationScope }, () => {\n return callback(scope);\n });\n }\n\n function withIsolationScope<T>(callback: (isolationScope: Scope) => T): T {\n const scope = getScopes().scope;\n const isolationScope = getScopes().isolationScope.clone();\n return asyncStorage.run({ scope, isolationScope }, () => {\n return callback(isolationScope);\n });\n }\n\n function withSetIsolationScope<T>(isolationScope: Scope, callback: (isolationScope: Scope) => T): T {\n const scope = getScopes().scope;\n return asyncStorage.run({ scope, isolationScope }, () => {\n return callback(isolationScope);\n });\n }\n\n // In contrast to the browser, we can rely on async context isolation here\n function suppressTracing<T>(callback: () => T): T {\n return withScope(scope => {\n scope.setSDKProcessingMetadata({ __SENTRY_SUPPRESS_TRACING__: true });\n return callback();\n });\n }\n\n setAsyncContextStrategy({\n suppressTracing,\n withScope,\n withSetScope,\n withIsolationScope,\n withSetIsolationScope,\n getCurrentScope: () => getScopes().scope,\n getIsolationScope: () => getScopes().isolationScope,\n getTracingChannelBinding: () => _INTERNAL_createTracingChannelBinding(asyncStorage, getScopes),\n });\n}\n"],"names":["AsyncLocalStorage","getDefaultCurrentScope","getDefaultIsolationScope","setAsyncContextStrategy","_INTERNAL_createTracingChannelBinding"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"async.js","sources":["../../src/async.ts"],"sourcesContent":["// Need to use node: prefix for cloudflare workers compatibility\n// Note: Because we are using node:async_hooks, we need to set `node_compat` in the wrangler.toml\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport type { Scope } from '@sentry/core';\nimport {\n _INTERNAL_createTracingChannelBinding,\n _INTERNAL_safeMathRandom,\n generateTraceId,\n getDefaultCurrentScope,\n getDefaultIsolationScope,\n isContinuingTrace,\n setAsyncContextStrategy,\n} from '@sentry/core';\n\n/**\n * Sets the async context strategy to use AsyncLocalStorage.\n *\n * AsyncLocalStorage is only available in the cloudflare workers runtime if you set\n * compatibility_flags = [\"nodejs_compat\"]\n *\n * @internal Only exported to be used in higher-level Sentry packages\n * @hidden Only exported to be used in higher-level Sentry packages\n */\nexport function setAsyncLocalStorageAsyncContextStrategy(): void {\n const asyncStorage = new AsyncLocalStorage<{\n scope: Scope;\n isolationScope: Scope;\n }>();\n\n function getScopes(): { scope: Scope; isolationScope: Scope } {\n const scopes = asyncStorage.getStore();\n\n if (scopes) {\n return scopes;\n }\n\n // fallback behavior:\n // if, for whatever reason, we can't find scopes on the context here, we have to fix this somehow\n return {\n scope: getDefaultCurrentScope(),\n isolationScope: getDefaultIsolationScope(),\n };\n }\n\n function withScope<T>(callback: (scope: Scope) => T): T {\n const scope = getScopes().scope.clone();\n const isolationScope = getScopes().isolationScope;\n return asyncStorage.run({ scope, isolationScope }, () => {\n return callback(scope);\n });\n }\n\n function withSetScope<T>(scope: Scope, callback: (scope: Scope) => T): T {\n const isolationScope = getScopes().isolationScope.clone();\n return asyncStorage.run({ scope, isolationScope }, () => {\n return callback(scope);\n });\n }\n\n function withIsolationScope<T>(callback: (isolationScope: Scope) => T): T {\n // The current scope is forked alongside the isolation scope, matching the OpenTelemetry strategy\n // (`buildContextWithSentryScopes` clones it on every fork). Sharing it by reference would let\n // the propagation context reset below leak back out to the caller.\n const scope = getScopes().scope.clone();\n const isolationScope = getScopes().isolationScope.clone();\n\n // When forking an isolation scope, unless we are continuing an incoming\n // trace, we give the freshly forked scope its own trace. This way, new\n // root spans in an isolation scope will get separate traces. The previous\n // trace's `sampled` and `propagationSpanId` are dropped on purpose.\n // Carrying them over would apply the old trace's sampling decision to the\n // new one and propagate a span id from a different trace.\n if (!isContinuingTrace(scope.getPropagationContext())) {\n scope.setPropagationContext({\n traceId: generateTraceId(),\n sampleRand: _INTERNAL_safeMathRandom(),\n });\n }\n\n return asyncStorage.run({ scope, isolationScope }, () => {\n return callback(isolationScope);\n });\n }\n\n function withSetIsolationScope<T>(isolationScope: Scope, callback: (isolationScope: Scope) => T): T {\n const scope = getScopes().scope;\n return asyncStorage.run({ scope, isolationScope }, () => {\n return callback(isolationScope);\n });\n }\n\n // In contrast to the browser, we can rely on async context isolation here\n function suppressTracing<T>(callback: () => T): T {\n return withScope(scope => {\n scope.setSDKProcessingMetadata({ __SENTRY_SUPPRESS_TRACING__: true });\n return callback();\n });\n }\n\n setAsyncContextStrategy({\n suppressTracing,\n withScope,\n withSetScope,\n withIsolationScope,\n withSetIsolationScope,\n getCurrentScope: () => getScopes().scope,\n getIsolationScope: () => getScopes().isolationScope,\n getTracingChannelBinding: () => _INTERNAL_createTracingChannelBinding(asyncStorage, getScopes),\n });\n}\n"],"names":["AsyncLocalStorage","getDefaultCurrentScope","getDefaultIsolationScope","isContinuingTrace","generateTraceId","_INTERNAL_safeMathRandom","setAsyncContextStrategy","_INTERNAL_createTracingChannelBinding"],"mappings":";;;;;AAuBO,SAAS,wCAAA,GAAiD;AAC/D,EAAA,MAAM,YAAA,GAAe,IAAIA,kCAAA,EAGtB;AAEH,EAAA,SAAS,SAAA,GAAqD;AAC5D,IAAA,MAAM,MAAA,GAAS,aAAa,QAAA,EAAS;AAErC,IAAA,IAAI,MAAA,EAAQ;AACV,MAAA,OAAO,MAAA;AAAA,IACT;AAIA,IAAA,OAAO;AAAA,MACL,OAAOC,2BAAA,EAAuB;AAAA,MAC9B,gBAAgBC,6BAAA;AAAyB,KAC3C;AAAA,EACF;AAEA,EAAA,SAAS,UAAa,QAAA,EAAkC;AACtD,IAAA,MAAM,KAAA,GAAQ,SAAA,EAAU,CAAE,KAAA,CAAM,KAAA,EAAM;AACtC,IAAA,MAAM,cAAA,GAAiB,WAAU,CAAE,cAAA;AACnC,IAAA,OAAO,aAAa,GAAA,CAAI,EAAE,KAAA,EAAO,cAAA,IAAkB,MAAM;AACvD,MAAA,OAAO,SAAS,KAAK,CAAA;AAAA,IACvB,CAAC,CAAA;AAAA,EACH;AAEA,EAAA,SAAS,YAAA,CAAgB,OAAc,QAAA,EAAkC;AACvE,IAAA,MAAM,cAAA,GAAiB,SAAA,EAAU,CAAE,cAAA,CAAe,KAAA,EAAM;AACxD,IAAA,OAAO,aAAa,GAAA,CAAI,EAAE,KAAA,EAAO,cAAA,IAAkB,MAAM;AACvD,MAAA,OAAO,SAAS,KAAK,CAAA;AAAA,IACvB,CAAC,CAAA;AAAA,EACH;AAEA,EAAA,SAAS,mBAAsB,QAAA,EAA2C;AAIxE,IAAA,MAAM,KAAA,GAAQ,SAAA,EAAU,CAAE,KAAA,CAAM,KAAA,EAAM;AACtC,IAAA,MAAM,cAAA,GAAiB,SAAA,EAAU,CAAE,cAAA,CAAe,KAAA,EAAM;AAQxD,IAAA,IAAI,CAACC,sBAAA,CAAkB,KAAA,CAAM,qBAAA,EAAuB,CAAA,EAAG;AACrD,MAAA,KAAA,CAAM,qBAAA,CAAsB;AAAA,QAC1B,SAASC,oBAAA,EAAgB;AAAA,QACzB,YAAYC,6BAAA;AAAyB,OACtC,CAAA;AAAA,IACH;AAEA,IAAA,OAAO,aAAa,GAAA,CAAI,EAAE,KAAA,EAAO,cAAA,IAAkB,MAAM;AACvD,MAAA,OAAO,SAAS,cAAc,CAAA;AAAA,IAChC,CAAC,CAAA;AAAA,EACH;AAEA,EAAA,SAAS,qBAAA,CAAyB,gBAAuB,QAAA,EAA2C;AAClG,IAAA,MAAM,KAAA,GAAQ,WAAU,CAAE,KAAA;AAC1B,IAAA,OAAO,aAAa,GAAA,CAAI,EAAE,KAAA,EAAO,cAAA,IAAkB,MAAM;AACvD,MAAA,OAAO,SAAS,cAAc,CAAA;AAAA,IAChC,CAAC,CAAA;AAAA,EACH;AAGA,EAAA,SAAS,gBAAmB,QAAA,EAAsB;AAChD,IAAA,OAAO,UAAU,CAAA,KAAA,KAAS;AACxB,MAAA,KAAA,CAAM,wBAAA,CAAyB,EAAE,2BAAA,EAA6B,IAAA,EAAM,CAAA;AACpE,MAAA,OAAO,QAAA,EAAS;AAAA,IAClB,CAAC,CAAA;AAAA,EACH;AAEA,EAAAC,4BAAA,CAAwB;AAAA,IACtB,eAAA;AAAA,IACA,SAAA;AAAA,IACA,YAAA;AAAA,IACA,kBAAA;AAAA,IACA,qBAAA;AAAA,IACA,eAAA,EAAiB,MAAM,SAAA,EAAU,CAAE,KAAA;AAAA,IACnC,iBAAA,EAAmB,MAAM,SAAA,EAAU,CAAE,cAAA;AAAA,IACrC,wBAAA,EAA0B,MAAMC,0CAAA,CAAsC,YAAA,EAAc,SAAS;AAAA,GAC9F,CAAA;AACH;;;;"}
|
|
@@ -3,9 +3,9 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
3
3
|
const core = require('@sentry/core');
|
|
4
4
|
const async = require('./async.js');
|
|
5
5
|
const instrument = require('./instrument.js');
|
|
6
|
+
const instrumentDurableObjectHandlers = require('./instrumentations/instrumentDurableObjectHandlers.js');
|
|
6
7
|
const instrumentEnv = require('./instrumentations/worker/instrumentEnv.js');
|
|
7
8
|
const options = require('./options.js');
|
|
8
|
-
const request = require('./request.js');
|
|
9
9
|
const instrumentContext = require('./utils/instrumentContext.js');
|
|
10
10
|
const rpcMeta = require('./utils/rpcMeta.js');
|
|
11
11
|
const rpcOptions = require('./utils/rpcOptions.js');
|
|
@@ -27,7 +27,7 @@ function constructInstrumentedDurableObject(target, ctx, env, newTarget, options
|
|
|
27
27
|
methodsBeforeConstruction,
|
|
28
28
|
cachedFrameworkManagedMethods
|
|
29
29
|
);
|
|
30
|
-
instrumentDurableObjectHandlers(obj, options$1, context);
|
|
30
|
+
instrumentDurableObjectHandlers.instrumentDurableObjectHandlers(obj, options$1, context);
|
|
31
31
|
return { obj, options: options$1, context, frameworkManagedMethods };
|
|
32
32
|
}
|
|
33
33
|
const frameworkManagedMethodsCache = /* @__PURE__ */ new WeakMap();
|
|
@@ -61,57 +61,6 @@ function resolveFrameworkManagedMethods(prototype, obj, methodsBeforeConstructio
|
|
|
61
61
|
frameworkManagedMethodsCache.set(prototype, managed);
|
|
62
62
|
return managed;
|
|
63
63
|
}
|
|
64
|
-
function instrumentDurableObjectHandlers(obj, options, context) {
|
|
65
|
-
if (obj.fetch && typeof obj.fetch === "function") {
|
|
66
|
-
obj.fetch = instrument.ensureInstrumented(
|
|
67
|
-
obj.fetch.bind(obj),
|
|
68
|
-
(original) => new Proxy(original, {
|
|
69
|
-
apply(target, thisArg, args) {
|
|
70
|
-
return request.wrapRequestHandler({ options, request: args[0], context }, () => {
|
|
71
|
-
return Reflect.apply(target, thisArg, args);
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
})
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
if (obj.alarm && typeof obj.alarm === "function") {
|
|
78
|
-
obj.alarm = wrapMethodWithSentry.wrapMethodWithSentry(
|
|
79
|
-
{
|
|
80
|
-
options,
|
|
81
|
-
context,
|
|
82
|
-
spanName: "alarm",
|
|
83
|
-
spanOp: "function",
|
|
84
|
-
startNewTrace: true,
|
|
85
|
-
origin: "auto.faas.cloudflare.durable_object"
|
|
86
|
-
},
|
|
87
|
-
obj.alarm.bind(obj)
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
if (obj.webSocketMessage && typeof obj.webSocketMessage === "function") {
|
|
91
|
-
obj.webSocketMessage = wrapMethodWithSentry.wrapMethodWithSentry(
|
|
92
|
-
{ options, context, spanName: "webSocketMessage", origin: "auto.faas.cloudflare.durable_object" },
|
|
93
|
-
obj.webSocketMessage.bind(obj)
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
if (obj.webSocketClose && typeof obj.webSocketClose === "function") {
|
|
97
|
-
obj.webSocketClose = wrapMethodWithSentry.wrapMethodWithSentry(
|
|
98
|
-
{ options, context, spanName: "webSocketClose", origin: "auto.faas.cloudflare.durable_object" },
|
|
99
|
-
obj.webSocketClose.bind(obj)
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
if (obj.webSocketError && typeof obj.webSocketError === "function") {
|
|
103
|
-
obj.webSocketError = wrapMethodWithSentry.wrapMethodWithSentry(
|
|
104
|
-
{ options, context, spanName: "webSocketError", origin: "auto.faas.cloudflare.durable_object" },
|
|
105
|
-
obj.webSocketError.bind(obj),
|
|
106
|
-
(_, error) => core.captureException(error, {
|
|
107
|
-
mechanism: {
|
|
108
|
-
type: "auto.faas.cloudflare.durable_object_websocket",
|
|
109
|
-
handled: false
|
|
110
|
-
}
|
|
111
|
-
})
|
|
112
|
-
);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
64
|
const RESERVED_RPC_METHOD_NAMES = /* @__PURE__ */ new Set([
|
|
116
65
|
"constructor",
|
|
117
66
|
"fetch",
|
|
@@ -197,7 +146,7 @@ function createRpcPrototypeWrapper(methodName, originalMethod) {
|
|
|
197
146
|
return wrapper;
|
|
198
147
|
}
|
|
199
148
|
function instrumentDurableObjectWithSentry(optionsCallback, DurableObjectClass) {
|
|
200
|
-
|
|
149
|
+
const InstrumentedClass = new Proxy(DurableObjectClass, {
|
|
201
150
|
construct(target, [ctx, env], newTarget) {
|
|
202
151
|
const { obj, options, context, frameworkManagedMethods } = constructInstrumentedDurableObject(
|
|
203
152
|
target,
|
|
@@ -209,9 +158,11 @@ function instrumentDurableObjectWithSentry(optionsCallback, DurableObjectClass)
|
|
|
209
158
|
return finalizeWithRpcInstrumentation(obj, options, context, frameworkManagedMethods);
|
|
210
159
|
}
|
|
211
160
|
});
|
|
161
|
+
instrument.markAsInstrumented(InstrumentedClass);
|
|
162
|
+
return InstrumentedClass;
|
|
212
163
|
}
|
|
213
164
|
function instrumentAgentWithSentry(optionsCallback, AgentClass) {
|
|
214
|
-
|
|
165
|
+
const InstrumentedClass = new Proxy(AgentClass, {
|
|
215
166
|
construct(target, [ctx, env], newTarget) {
|
|
216
167
|
const { obj, options, context, frameworkManagedMethods } = constructInstrumentedDurableObject(
|
|
217
168
|
target,
|
|
@@ -224,6 +175,8 @@ function instrumentAgentWithSentry(optionsCallback, AgentClass) {
|
|
|
224
175
|
return finalizeWithRpcInstrumentation(obj, options, context, frameworkManagedMethods);
|
|
225
176
|
}
|
|
226
177
|
});
|
|
178
|
+
instrument.markAsInstrumented(InstrumentedClass);
|
|
179
|
+
return InstrumentedClass;
|
|
227
180
|
}
|
|
228
181
|
|
|
229
182
|
exports.constructInstrumentedDurableObject = constructInstrumentedDurableObject;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"durableobject.js","sources":["../../src/durableobject.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/unbound-method */\nimport { captureException, isObjectLike } from '@sentry/core';\nimport type { DurableObject } from 'cloudflare:workers';\nimport { setAsyncLocalStorageAsyncContextStrategy } from './async';\nimport type { CloudflareOptions } from './client';\nimport { ensureInstrumented, getInstrumented, markAsInstrumented } from './instrument';\nimport { instrumentEnv } from './instrumentations/worker/instrumentEnv';\nimport { getFinalOptions } from './options';\nimport { wrapRequestHandler } from './request';\nimport { instrumentContext } from './utils/instrumentContext';\nimport { hasRpcMeta } from './utils/rpcMeta';\nimport { getEffectiveRpcPropagation } from './utils/rpcOptions';\nimport { instrumentCloudflareAgent } from './instrumentations/agents';\nimport { type UncheckedMethod, wrapMethodWithSentry } from './wrapMethodWithSentry';\n\n/**\n * The instrumented context passed between the shared construction helpers.\n *\n * This is intentionally `any` rather than `ReturnType<typeof instrumentContext<DurableObjectState>>`.\n * A concrete `DurableObjectState` here forces `tsc` to structurally relate its `storage: SqlStorage`\n * graph against the `ExecutionContext | InstrumentedDurableObjectState` parameter of\n * `wrapMethodWithSentry`, while the RPC-branded `DurableObject` from `cloudflare:workers` is also in\n * scope. That union comparison explodes (1296×1296) and hangs the type build. The original inline\n * implementation avoided this only incidentally, because `ctx` reached it as `any` through the Proxy\n * `construct` trap. Keeping the shared context `any` preserves that behavior.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype InstrumentedDurableObjectContext = any;\n\n/**\n * Constructs a Durable Object instance and instruments its built-in handler methods\n * (`fetch`, `alarm`, `webSocketMessage`, `webSocketClose`, `webSocketError`).\n *\n * This is the shared construction path used by both {@link instrumentDurableObjectWithSentry}\n * and {@link instrumentAgentWithSentry}. It intentionally does NOT apply the RPC prototype-method\n * instrumentation — callers apply that last via {@link finalizeWithRpcInstrumentation}, after any\n * additional per-instance instrumentation has been layered onto the returned object.\n *\n * @internal\n */\nexport function constructInstrumentedDurableObject<E, T extends DurableObject<E>>(\n target: new (state: DurableObjectState, env: E) => T,\n ctx: DurableObjectState,\n env: E,\n newTarget: NewableFunction,\n optionsCallback: (env: E) => CloudflareOptions,\n): {\n obj: T;\n options: CloudflareOptions;\n context: InstrumentedDurableObjectContext;\n frameworkManagedMethods: ReadonlySet<string>;\n} {\n setAsyncLocalStorageAsyncContextStrategy();\n const options = getFinalOptions(optionsCallback(env), env);\n // See InstrumentedDurableObjectContext — `ctx` is widened to `any` so the concrete\n // `DurableObjectState` type never enters the checker's relation graph in this module.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const context = instrumentContext(ctx as any);\n const instrumentedEnv = instrumentEnv(env as Record<string, unknown>, options);\n\n const prototype = (newTarget as unknown as { prototype?: object }).prototype ?? target.prototype;\n const cachedFrameworkManagedMethods = frameworkManagedMethodsCache.get(prototype);\n const methodsBeforeConstruction = cachedFrameworkManagedMethods ? undefined : resolvePrototypeMethods(prototype);\n\n // Pass `newTarget` so that subclasses of the instrumented class (e.g. the wrapper classes\n // created by wrangler's local dev tooling or `@cloudflare/vitest-pool-workers`) keep their\n // own prototype — otherwise subclass methods disappear and `instanceof` checks break.\n const obj = Reflect.construct(target, [context, instrumentedEnv], newTarget) as T;\n\n const frameworkManagedMethods = resolveFrameworkManagedMethods(\n prototype,\n obj,\n methodsBeforeConstruction,\n cachedFrameworkManagedMethods,\n );\n\n instrumentDurableObjectHandlers(obj, options, context);\n\n return { obj, options, context, frameworkManagedMethods };\n}\n\nconst frameworkManagedMethodsCache = new WeakMap<object, ReadonlySet<string>>();\n\n/**\n * Collects the methods visible from a prototype, using normal property lookup precedence.\n * Methods inherited from `Object.prototype` are excluded because they cannot be Durable Object RPC methods.\n */\nfunction resolvePrototypeMethods(prototype: object | null): Map<string, unknown> {\n const methods = new Map<string, unknown>();\n\n for (let current = prototype; current && current !== Object.prototype; current = Object.getPrototypeOf(current)) {\n for (const name of Object.getOwnPropertyNames(current)) {\n // The first occurrence wins, mirroring what a property lookup on the instance would find\n if (name === 'constructor' || methods.has(name)) {\n continue;\n }\n\n const descriptor = Object.getOwnPropertyDescriptor(current, name);\n\n if (descriptor && typeof descriptor.value === 'function') {\n methods.set(name, descriptor.value);\n }\n }\n }\n\n return methods;\n}\n\n/**\n * Finds methods that a framework replaced while constructing the first instance.\n *\n * Some frameworks register methods by function identity, so replacing one of their wrappers would\n * break dispatch. The result is cached because frameworks commonly install their wrappers only\n * once; later constructions would no longer reveal which methods they manage.\n */\nfunction resolveFrameworkManagedMethods(\n prototype: object,\n obj: object,\n methodsBeforeConstruction: Map<string, unknown> | undefined,\n cached: ReadonlySet<string> | undefined,\n): ReadonlySet<string> {\n if (cached) {\n return cached;\n }\n\n const methodsAfterConstruction = resolvePrototypeMethods(Object.getPrototypeOf(obj) as object);\n const managed = new Set<string>();\n\n for (const [name, method] of methodsAfterConstruction) {\n const before = methodsBeforeConstruction?.get(name);\n\n if (before && before !== method) {\n managed.add(name);\n }\n }\n\n frameworkManagedMethodsCache.set(prototype, managed);\n\n return managed;\n}\n\n/**\n * Instruments the built-in Durable Object handler methods on a constructed instance.\n *\n * These are the methods that are available on a Durable Object\n * ref: https://developers.cloudflare.com/durable-objects/api/base/\n * - obj.alarm\n * - obj.fetch\n * - obj.webSocketError\n * - obj.webSocketClose\n * - obj.webSocketMessage\n *\n * Any other public methods on the Durable Object instance are RPC calls.\n */\nfunction instrumentDurableObjectHandlers<E, T extends DurableObject<E>>(\n obj: T,\n options: CloudflareOptions,\n context: InstrumentedDurableObjectContext,\n): void {\n // Bind each built-in handler to this instance before wrapping.\n // See https://github.com/getsentry/sentry-javascript/issues/22328\n if (obj.fetch && typeof obj.fetch === 'function') {\n obj.fetch = ensureInstrumented(\n obj.fetch.bind(obj),\n original =>\n new Proxy(original, {\n apply(target, thisArg, args) {\n return wrapRequestHandler({ options, request: args[0], context }, () => {\n return Reflect.apply(target, thisArg, args);\n });\n },\n }),\n );\n }\n\n if (obj.alarm && typeof obj.alarm === 'function') {\n // Alarms are independent invocations, so we start a new trace and link to the previous alarm\n obj.alarm = wrapMethodWithSentry(\n {\n options,\n context,\n spanName: 'alarm',\n spanOp: 'function',\n startNewTrace: true,\n origin: 'auto.faas.cloudflare.durable_object',\n },\n obj.alarm.bind(obj),\n );\n }\n\n if (obj.webSocketMessage && typeof obj.webSocketMessage === 'function') {\n obj.webSocketMessage = wrapMethodWithSentry(\n { options, context, spanName: 'webSocketMessage', origin: 'auto.faas.cloudflare.durable_object' },\n obj.webSocketMessage.bind(obj),\n );\n }\n\n if (obj.webSocketClose && typeof obj.webSocketClose === 'function') {\n obj.webSocketClose = wrapMethodWithSentry(\n { options, context, spanName: 'webSocketClose', origin: 'auto.faas.cloudflare.durable_object' },\n obj.webSocketClose.bind(obj),\n );\n }\n\n if (obj.webSocketError && typeof obj.webSocketError === 'function') {\n obj.webSocketError = wrapMethodWithSentry(\n { options, context, spanName: 'webSocketError', origin: 'auto.faas.cloudflare.durable_object' },\n obj.webSocketError.bind(obj),\n (_, error) =>\n captureException(error, {\n mechanism: {\n type: 'auto.faas.cloudflare.durable_object_websocket',\n handled: false,\n },\n }),\n );\n }\n}\n\ntype RpcInstanceState = {\n options: CloudflareOptions;\n context: InstrumentedDurableObjectContext;\n /**\n * Whether to trace calls that carry no RPC metadata. Only the deprecated\n * `instrumentPrototypeMethods` option does, because it predates metadata propagation.\n */\n alwaysTrace: boolean;\n /** Per-instance cache of the traced method wrappers, keyed by method name. Created on first use. */\n tracedMethods?: Map<string, UncheckedMethod>;\n};\n\n/**\n * Method names the runtime never dispatches over RPC, so wrapping them buys no tracing.\n *\n * Mirrors `isReservedName` in workerd (`src/workerd/api/worker-rpc.c++`): the runtime rejects these\n * before any property lookup happens. `fetch`, `alarm` and the `webSocket*` handlers are also\n * instrumented per-instance as own properties, and `constructor` is on every prototype.\n */\nconst RESERVED_RPC_METHOD_NAMES: ReadonlySet<string> = new Set([\n 'constructor',\n 'fetch',\n 'connect',\n 'alarm',\n 'webSocketMessage',\n 'webSocketClose',\n 'webSocketError',\n 'dup',\n]);\n\n// Prototype wrappers are shared by all instances, while SDK options and traced method caches are not.\nconst rpcInstanceStates = new WeakMap<object, RpcInstanceState>();\n\n/**\n * Adds trace propagation to a constructed Durable Object's RPC methods.\n *\n * RPC methods are wrapped on the prototype because Cloudflare dispatches them with the Durable\n * Object instance as the receiver. This preserves native private-field access and keeps the methods\n * visible to Cloudflare's RPC dispatcher. Built-in handlers, Agent handlers, and methods managed by\n * another framework are left untouched.\n *\n * Call this after all per-instance instrumentation has been applied. If RPC trace propagation is\n * disabled, the object is returned unchanged.\n *\n * @param obj The constructed Durable Object instance.\n * @param options The resolved SDK options for this instance.\n * @param context The instrumented execution context for this instance.\n * @param excludedMethods Method names owned by another framework and therefore not safe to wrap.\n * @returns The same Durable Object instance, with eligible prototype methods instrumented.\n * @internal\n */\nexport function finalizeWithRpcInstrumentation<T extends object>(\n obj: T,\n options: CloudflareOptions,\n context: InstrumentedDurableObjectContext,\n excludedMethods?: ReadonlySet<string>,\n): T {\n // Get effective RPC propagation setting (handles deprecation of instrumentPrototypeMethods)\n const rpcPropagation = getEffectiveRpcPropagation(options);\n\n // Skip RPC instrumentation if not enabled\n if (!rpcPropagation) {\n return obj;\n }\n\n // If `instrumentPrototypeMethods` was passed as an array (deprecated),\n // only the listed method names should be instrumented.\n // eslint-disable-next-line typescript/no-deprecated\n const allowedMethods = Array.isArray(options.instrumentPrototypeMethods)\n ? // eslint-disable-next-line typescript/no-deprecated\n new Set(options.instrumentPrototypeMethods)\n : undefined;\n\n // When using the deprecated `instrumentPrototypeMethods` option, always create spans.\n // When using the new `enableRpcTracePropagation`, only create spans when RPC metadata is present.\n // eslint-disable-next-line typescript/no-deprecated\n const alwaysTrace = options.enableRpcTracePropagation === undefined;\n\n rpcInstanceStates.set(obj, { options, context, alwaysTrace });\n\n instrumentPrototypeRpcMethods(obj, excludedMethods, allowedMethods);\n\n return obj;\n}\n\n/**\n * Returns a prototype method when it is eligible for RPC instrumentation.\n */\nfunction getRpcMethodDescriptor(\n obj: object,\n prototype: object,\n methodName: string,\n excludedMethods?: ReadonlySet<string>,\n allowedMethods?: ReadonlySet<string>,\n): PropertyDescriptor | undefined {\n if (\n RESERVED_RPC_METHOD_NAMES.has(methodName) ||\n Object.prototype.hasOwnProperty.call(obj, methodName) ||\n excludedMethods?.has(methodName) ||\n (allowedMethods && !allowedMethods.has(methodName))\n ) {\n return undefined;\n }\n\n const descriptor = Object.getOwnPropertyDescriptor(prototype, methodName);\n\n if (!descriptor || typeof descriptor.value !== 'function' || getInstrumented(descriptor.value)) {\n return undefined;\n }\n\n return descriptor;\n}\n\n/**\n * Wraps eligible methods on the instance's prototype chain once per class.\n *\n * Because the wrappers live on the prototype, the allow-list of the first constructed instance\n * decides which methods carry a wrapper for every later instance of that class. This only affects\n * the deprecated array form of `instrumentPrototypeMethods`, where differing options per instance\n * of the same class were never a supported configuration.\n */\nfunction instrumentPrototypeRpcMethods(\n obj: object,\n excludedMethods?: ReadonlySet<string>,\n allowedMethods?: ReadonlySet<string>,\n): void {\n let prototype: object | null = Object.getPrototypeOf(obj);\n\n while (prototype && prototype !== Object.prototype) {\n for (const methodName of Object.getOwnPropertyNames(prototype)) {\n const descriptor = getRpcMethodDescriptor(obj, prototype, methodName, excludedMethods, allowedMethods);\n\n if (!descriptor) {\n continue;\n }\n\n const wrapped = createRpcPrototypeWrapper(methodName, descriptor.value as UncheckedMethod);\n\n try {\n Object.defineProperty(prototype, methodName, { ...descriptor, value: wrapped });\n } catch {}\n\n // Only the wrapper is marked, not the original method: `wrapMethodWithSentry` resolves\n // through the same global map and must not resolve the original to this wrapper,\n // which would recurse.\n markAsInstrumented(wrapped);\n }\n\n prototype = Object.getPrototypeOf(prototype);\n }\n}\n\n/**\n * Creates a prototype wrapper that traces RPC calls carrying Sentry metadata.\n *\n * The wrapper looks up SDK state from its receiver, allowing one prototype function to serve every\n * instance. Calls without instance state use the original method directly, as do calls without RPC\n * metadata unless the instance opted into unconditional tracing. The original function name and\n * arity are preserved because frameworks may inspect them for dispatch.\n */\nfunction createRpcPrototypeWrapper(methodName: string, originalMethod: UncheckedMethod): UncheckedMethod {\n const wrapper = function (this: unknown, ...args: unknown[]): unknown {\n const state = isObjectLike(this) ? rpcInstanceStates.get(this) : undefined;\n\n // Untraced calls are the common case — every internal call the instance makes to one of its\n // own methods lands here too, and those never carry RPC metadata.\n if (!state || (!state.alwaysTrace && !hasRpcMeta(args))) {\n return Reflect.apply(originalMethod, this, args);\n }\n\n const tracedMethods = (state.tracedMethods ??= new Map());\n let traced = tracedMethods.get(methodName);\n\n if (!traced) {\n traced = wrapMethodWithSentry(\n {\n options: state.options,\n context: state.context,\n spanName: methodName,\n spanOp: 'rpc',\n origin: 'auto.faas.cloudflare.durable_object',\n },\n originalMethod,\n undefined,\n true,\n );\n tracedMethods.set(methodName, traced);\n }\n\n return Reflect.apply(traced, this, args);\n };\n\n Object.defineProperties(wrapper, {\n name: { value: originalMethod.name, configurable: true },\n length: { value: originalMethod.length, configurable: true },\n });\n\n return wrapper as UncheckedMethod;\n}\n\n/**\n * Instruments a Durable Object class to capture errors and performance data.\n *\n * Instruments the following methods by default:\n * - fetch\n * - alarm\n * - webSocketMessage\n * - webSocketClose\n * - webSocketError\n *\n * To instrument RPC methods (prototype methods), enable the `enableRpcTracePropagation` option.\n *\n * @param optionsCallback Function that returns the options for the SDK initialization.\n * @param DurableObjectClass The Durable Object class to instrument.\n * @returns The instrumented Durable Object class.\n *\n * @example\n * ```ts\n * class MyDurableObjectBase extends DurableObject {\n * constructor(ctx: DurableObjectState, env: Env) {\n * super(ctx, env);\n * }\n * }\n *\n * export const MyDurableObject = instrumentDurableObjectWithSentry(\n * env => ({\n * dsn: env.SENTRY_DSN,\n * tracesSampleRate: 1.0,\n * }),\n * MyDurableObjectBase,\n * );\n * ```\n */\nexport function instrumentDurableObjectWithSentry<\n E,\n T extends DurableObject<E>,\n C extends new (state: DurableObjectState, env: E) => T,\n>(optionsCallback: (env: E) => CloudflareOptions, DurableObjectClass: C): C {\n return new Proxy(DurableObjectClass, {\n construct(target, [ctx, env], newTarget) {\n const { obj, options, context, frameworkManagedMethods } = constructInstrumentedDurableObject(\n target,\n ctx,\n env,\n newTarget,\n optionsCallback,\n );\n\n return finalizeWithRpcInstrumentation(obj, options, context, frameworkManagedMethods);\n },\n });\n}\n\n/**\n * Instruments a Cloudflare [`agents`](https://www.npmjs.com/package/agents) Agent class with Sentry.\n *\n * An `Agent` is a Durable Object under the hood, so this applies the same instrumentation as\n * {@link instrumentDurableObjectWithSentry} (request transactions, `alarm`, WebSocket handlers, RPC\n * trace propagation, SQL spans) and additionally captures Agent-specific telemetry via\n * `instrumentCloudflareAgent`:\n *\n * - **Callable RPC spans** — a span (op `rpc`) for each `@callable()` method invoked over WebSocket.\n * - **Breadcrumbs** — for every Agent observability event (`rpc`, `state:update`, `connect`,\n * `disconnect`, `schedule:*`, `queue:*`, `workflow:*`, `email:*`, `mcp:*`, ...).\n *\n * Cloudflare Workers cannot auto-instrument, so the Agent class must be wrapped manually.\n *\n * @param optionsCallback Function that returns the options for the SDK initialization.\n * @param AgentClass The Agent class to instrument.\n * @returns The instrumented Agent class.\n *\n * @example\n * ```ts\n * import { Agent, callable, routeAgentRequest } from 'agents';\n * import * as Sentry from '@sentry/cloudflare';\n *\n * class MyAgentBase extends Agent<Env> {\n * @callable()\n * async greet(name: string): Promise<string> {\n * return `Hello, ${name}!`;\n * }\n * }\n *\n * export const MyAgent = Sentry.instrumentAgentWithSentry(\n * env => ({\n * dsn: env.SENTRY_DSN,\n * tracesSampleRate: 1.0,\n * enableRpcTracePropagation: true,\n * }),\n * MyAgentBase,\n * );\n * ```\n */\nexport function instrumentAgentWithSentry<\n E,\n T extends DurableObject<E>,\n C extends new (state: DurableObjectState, env: E) => T,\n>(optionsCallback: (env: E) => CloudflareOptions, AgentClass: C): C {\n return new Proxy(AgentClass, {\n construct(target, [ctx, env], newTarget) {\n const { obj, options, context, frameworkManagedMethods } = constructInstrumentedDurableObject(\n target,\n ctx,\n env,\n newTarget,\n optionsCallback,\n );\n\n instrumentCloudflareAgent(obj);\n\n // Apply RPC prototype-method instrumentation last, so the Agent-specific own-property\n // handlers we just installed are excluded from RPC method tracing. Methods the Agent\n // framework installed itself are excluded too — `instrumentCloudflareAgent` traces those by\n // wrapping the dispatch instead of the method.\n return finalizeWithRpcInstrumentation(obj, options, context, frameworkManagedMethods);\n },\n });\n}\n"],"names":["setAsyncLocalStorageAsyncContextStrategy","options","getFinalOptions","instrumentContext","instrumentEnv","ensureInstrumented","wrapRequestHandler","wrapMethodWithSentry","captureException","getEffectiveRpcPropagation","getInstrumented","markAsInstrumented","isObjectLike","hasRpcMeta","instrumentCloudflareAgent"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"durableobject.js","sources":["../../src/durableobject.ts"],"sourcesContent":["/* eslint-disable max-lines */\n/* eslint-disable @typescript-eslint/unbound-method */\nimport { isObjectLike } from '@sentry/core';\nimport type { DurableObject } from 'cloudflare:workers';\nimport { setAsyncLocalStorageAsyncContextStrategy } from './async';\nimport type { CloudflareOptions } from './client';\nimport { getInstrumented, markAsInstrumented } from './instrument';\nimport { instrumentDurableObjectHandlers } from './instrumentations/instrumentDurableObjectHandlers';\nimport { instrumentEnv } from './instrumentations/worker/instrumentEnv';\nimport { getFinalOptions } from './options';\nimport { instrumentContext } from './utils/instrumentContext';\nimport { hasRpcMeta } from './utils/rpcMeta';\nimport { getEffectiveRpcPropagation } from './utils/rpcOptions';\nimport { instrumentCloudflareAgent } from './instrumentations/agents';\nimport { type UncheckedMethod, wrapMethodWithSentry } from './wrapMethodWithSentry';\n\n/**\n * The instrumented context passed between the shared construction helpers.\n *\n * This is intentionally `any` rather than `ReturnType<typeof instrumentContext<DurableObjectState>>`.\n * A concrete `DurableObjectState` here forces `tsc` to structurally relate its `storage: SqlStorage`\n * graph against the `ExecutionContext | InstrumentedDurableObjectState` parameter of\n * `wrapMethodWithSentry`, while the RPC-branded `DurableObject` from `cloudflare:workers` is also in\n * scope. That union comparison explodes (1296×1296) and hangs the type build. The original inline\n * implementation avoided this only incidentally, because `ctx` reached it as `any` through the Proxy\n * `construct` trap. Keeping the shared context `any` preserves that behavior.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype InstrumentedDurableObjectContext = any;\n\n/**\n * Constructs a Durable Object instance and instruments its built-in handler methods\n * (`fetch`, `alarm`, `webSocketMessage`, `webSocketClose`, `webSocketError`).\n *\n * This is the shared construction path used by both {@link instrumentDurableObjectWithSentry}\n * and {@link instrumentAgentWithSentry}. It intentionally does NOT apply the RPC prototype-method\n * instrumentation — callers apply that last via {@link finalizeWithRpcInstrumentation}, after any\n * additional per-instance instrumentation has been layered onto the returned object.\n *\n * @internal\n */\nexport function constructInstrumentedDurableObject<E, T extends DurableObject<E>>(\n target: new (state: DurableObjectState, env: E) => T,\n ctx: DurableObjectState,\n env: E,\n newTarget: NewableFunction,\n optionsCallback: (env: E) => CloudflareOptions,\n): {\n obj: T;\n options: CloudflareOptions;\n context: InstrumentedDurableObjectContext;\n frameworkManagedMethods: ReadonlySet<string>;\n} {\n setAsyncLocalStorageAsyncContextStrategy();\n const options = getFinalOptions(optionsCallback(env), env);\n // See InstrumentedDurableObjectContext — `ctx` is widened to `any` so the concrete\n // `DurableObjectState` type never enters the checker's relation graph in this module.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const context = instrumentContext(ctx as any);\n const instrumentedEnv = instrumentEnv(env as Record<string, unknown>, options);\n\n const prototype = (newTarget as unknown as { prototype?: object }).prototype ?? target.prototype;\n const cachedFrameworkManagedMethods = frameworkManagedMethodsCache.get(prototype);\n const methodsBeforeConstruction = cachedFrameworkManagedMethods ? undefined : resolvePrototypeMethods(prototype);\n\n // Pass `newTarget` so that subclasses of the instrumented class (e.g. the wrapper classes\n // created by wrangler's local dev tooling or `@cloudflare/vitest-pool-workers`) keep their\n // own prototype — otherwise subclass methods disappear and `instanceof` checks break.\n const obj = Reflect.construct(target, [context, instrumentedEnv], newTarget) as T;\n\n const frameworkManagedMethods = resolveFrameworkManagedMethods(\n prototype,\n obj,\n methodsBeforeConstruction,\n cachedFrameworkManagedMethods,\n );\n\n instrumentDurableObjectHandlers(obj, options, context);\n\n return { obj, options, context, frameworkManagedMethods };\n}\n\nconst frameworkManagedMethodsCache = new WeakMap<object, ReadonlySet<string>>();\n\n/**\n * Collects the methods visible from a prototype, using normal property lookup precedence.\n * Methods inherited from `Object.prototype` are excluded because they cannot be Durable Object RPC methods.\n */\nfunction resolvePrototypeMethods(prototype: object | null): Map<string, unknown> {\n const methods = new Map<string, unknown>();\n\n for (let current = prototype; current && current !== Object.prototype; current = Object.getPrototypeOf(current)) {\n for (const name of Object.getOwnPropertyNames(current)) {\n // The first occurrence wins, mirroring what a property lookup on the instance would find\n if (name === 'constructor' || methods.has(name)) {\n continue;\n }\n\n const descriptor = Object.getOwnPropertyDescriptor(current, name);\n\n if (descriptor && typeof descriptor.value === 'function') {\n methods.set(name, descriptor.value);\n }\n }\n }\n\n return methods;\n}\n\n/**\n * Finds methods that a framework replaced while constructing the first instance.\n *\n * Some frameworks register methods by function identity, so replacing one of their wrappers would\n * break dispatch. The result is cached because frameworks commonly install their wrappers only\n * once; later constructions would no longer reveal which methods they manage.\n */\nfunction resolveFrameworkManagedMethods(\n prototype: object,\n obj: object,\n methodsBeforeConstruction: Map<string, unknown> | undefined,\n cached: ReadonlySet<string> | undefined,\n): ReadonlySet<string> {\n if (cached) {\n return cached;\n }\n\n const methodsAfterConstruction = resolvePrototypeMethods(Object.getPrototypeOf(obj) as object);\n const managed = new Set<string>();\n\n for (const [name, method] of methodsAfterConstruction) {\n const before = methodsBeforeConstruction?.get(name);\n\n if (before && before !== method) {\n managed.add(name);\n }\n }\n\n frameworkManagedMethodsCache.set(prototype, managed);\n\n return managed;\n}\n\ntype RpcInstanceState = {\n options: CloudflareOptions;\n context: InstrumentedDurableObjectContext;\n /**\n * Whether to trace calls that carry no RPC metadata. Only the deprecated\n * `instrumentPrototypeMethods` option does, because it predates metadata propagation.\n */\n alwaysTrace: boolean;\n /** Per-instance cache of the traced method wrappers, keyed by method name. Created on first use. */\n tracedMethods?: Map<string, UncheckedMethod>;\n};\n\n/**\n * Method names the runtime never dispatches over RPC, so wrapping them buys no tracing.\n *\n * Mirrors `isReservedName` in workerd (`src/workerd/api/worker-rpc.c++`): the runtime rejects these\n * before any property lookup happens. `fetch`, `alarm` and the `webSocket*` handlers are also\n * instrumented per-instance as own properties, and `constructor` is on every prototype.\n */\nconst RESERVED_RPC_METHOD_NAMES: ReadonlySet<string> = new Set([\n 'constructor',\n 'fetch',\n 'connect',\n 'alarm',\n 'webSocketMessage',\n 'webSocketClose',\n 'webSocketError',\n 'dup',\n]);\n\n// Prototype wrappers are shared by all instances, while SDK options and traced method caches are not.\nconst rpcInstanceStates = new WeakMap<object, RpcInstanceState>();\n\n/**\n * Adds trace propagation to a constructed Durable Object's RPC methods.\n *\n * RPC methods are wrapped on the prototype because Cloudflare dispatches them with the Durable\n * Object instance as the receiver. This preserves native private-field access and keeps the methods\n * visible to Cloudflare's RPC dispatcher. Built-in handlers, Agent handlers, and methods managed by\n * another framework are left untouched.\n *\n * Call this after all per-instance instrumentation has been applied. If RPC trace propagation is\n * disabled, the object is returned unchanged.\n *\n * @param obj The constructed Durable Object instance.\n * @param options The resolved SDK options for this instance.\n * @param context The instrumented execution context for this instance.\n * @param excludedMethods Method names owned by another framework and therefore not safe to wrap.\n * @returns The same Durable Object instance, with eligible prototype methods instrumented.\n * @internal\n */\nexport function finalizeWithRpcInstrumentation<T extends object>(\n obj: T,\n options: CloudflareOptions,\n context: InstrumentedDurableObjectContext,\n excludedMethods?: ReadonlySet<string>,\n): T {\n // Get effective RPC propagation setting (handles deprecation of instrumentPrototypeMethods)\n const rpcPropagation = getEffectiveRpcPropagation(options);\n\n // Skip RPC instrumentation if not enabled\n if (!rpcPropagation) {\n return obj;\n }\n\n // If `instrumentPrototypeMethods` was passed as an array (deprecated),\n // only the listed method names should be instrumented.\n // eslint-disable-next-line typescript/no-deprecated\n const allowedMethods = Array.isArray(options.instrumentPrototypeMethods)\n ? // eslint-disable-next-line typescript/no-deprecated\n new Set(options.instrumentPrototypeMethods)\n : undefined;\n\n // When using the deprecated `instrumentPrototypeMethods` option, always create spans.\n // When using the new `enableRpcTracePropagation`, only create spans when RPC metadata is present.\n // eslint-disable-next-line typescript/no-deprecated\n const alwaysTrace = options.enableRpcTracePropagation === undefined;\n\n rpcInstanceStates.set(obj, { options, context, alwaysTrace });\n\n instrumentPrototypeRpcMethods(obj, excludedMethods, allowedMethods);\n\n return obj;\n}\n\n/**\n * Returns a prototype method when it is eligible for RPC instrumentation.\n */\nfunction getRpcMethodDescriptor(\n obj: object,\n prototype: object,\n methodName: string,\n excludedMethods?: ReadonlySet<string>,\n allowedMethods?: ReadonlySet<string>,\n): PropertyDescriptor | undefined {\n if (\n RESERVED_RPC_METHOD_NAMES.has(methodName) ||\n Object.prototype.hasOwnProperty.call(obj, methodName) ||\n excludedMethods?.has(methodName) ||\n (allowedMethods && !allowedMethods.has(methodName))\n ) {\n return undefined;\n }\n\n const descriptor = Object.getOwnPropertyDescriptor(prototype, methodName);\n\n if (!descriptor || typeof descriptor.value !== 'function' || getInstrumented(descriptor.value)) {\n return undefined;\n }\n\n return descriptor;\n}\n\n/**\n * Wraps eligible methods on the instance's prototype chain once per class.\n *\n * Because the wrappers live on the prototype, the allow-list of the first constructed instance\n * decides which methods carry a wrapper for every later instance of that class. This only affects\n * the deprecated array form of `instrumentPrototypeMethods`, where differing options per instance\n * of the same class were never a supported configuration.\n */\nfunction instrumentPrototypeRpcMethods(\n obj: object,\n excludedMethods?: ReadonlySet<string>,\n allowedMethods?: ReadonlySet<string>,\n): void {\n let prototype: object | null = Object.getPrototypeOf(obj);\n\n while (prototype && prototype !== Object.prototype) {\n for (const methodName of Object.getOwnPropertyNames(prototype)) {\n const descriptor = getRpcMethodDescriptor(obj, prototype, methodName, excludedMethods, allowedMethods);\n\n if (!descriptor) {\n continue;\n }\n\n const wrapped = createRpcPrototypeWrapper(methodName, descriptor.value as UncheckedMethod);\n\n try {\n Object.defineProperty(prototype, methodName, { ...descriptor, value: wrapped });\n } catch {}\n\n // Only the wrapper is marked, not the original method: `wrapMethodWithSentry` resolves\n // through the same global map and must not resolve the original to this wrapper,\n // which would recurse.\n markAsInstrumented(wrapped);\n }\n\n prototype = Object.getPrototypeOf(prototype);\n }\n}\n\n/**\n * Creates a prototype wrapper that traces RPC calls carrying Sentry metadata.\n *\n * The wrapper looks up SDK state from its receiver, allowing one prototype function to serve every\n * instance. Calls without instance state use the original method directly, as do calls without RPC\n * metadata unless the instance opted into unconditional tracing. The original function name and\n * arity are preserved because frameworks may inspect them for dispatch.\n */\nfunction createRpcPrototypeWrapper(methodName: string, originalMethod: UncheckedMethod): UncheckedMethod {\n const wrapper = function (this: unknown, ...args: unknown[]): unknown {\n const state = isObjectLike(this) ? rpcInstanceStates.get(this) : undefined;\n\n // Untraced calls are the common case — every internal call the instance makes to one of its\n // own methods lands here too, and those never carry RPC metadata.\n if (!state || (!state.alwaysTrace && !hasRpcMeta(args))) {\n return Reflect.apply(originalMethod, this, args);\n }\n\n const tracedMethods = (state.tracedMethods ??= new Map());\n let traced = tracedMethods.get(methodName);\n\n if (!traced) {\n traced = wrapMethodWithSentry(\n {\n options: state.options,\n context: state.context,\n spanName: methodName,\n spanOp: 'rpc',\n origin: 'auto.faas.cloudflare.durable_object',\n },\n originalMethod,\n undefined,\n true,\n );\n tracedMethods.set(methodName, traced);\n }\n\n return Reflect.apply(traced, this, args);\n };\n\n Object.defineProperties(wrapper, {\n name: { value: originalMethod.name, configurable: true },\n length: { value: originalMethod.length, configurable: true },\n });\n\n return wrapper as UncheckedMethod;\n}\n\n/**\n * Instruments a Durable Object class to capture errors and performance data.\n *\n * Instruments the following methods by default:\n * - fetch\n * - alarm\n * - webSocketMessage\n * - webSocketClose\n * - webSocketError\n *\n * To instrument RPC methods (prototype methods), enable the `enableRpcTracePropagation` option.\n *\n * @param optionsCallback Function that returns the options for the SDK initialization.\n * @param DurableObjectClass The Durable Object class to instrument.\n * @returns The instrumented Durable Object class.\n *\n * @example\n * ```ts\n * class MyDurableObjectBase extends DurableObject {\n * constructor(ctx: DurableObjectState, env: Env) {\n * super(ctx, env);\n * }\n * }\n *\n * export const MyDurableObject = instrumentDurableObjectWithSentry(\n * env => ({\n * dsn: env.SENTRY_DSN,\n * tracesSampleRate: 1.0,\n * }),\n * MyDurableObjectBase,\n * );\n * ```\n */\nexport function instrumentDurableObjectWithSentry<\n E,\n T extends DurableObject<E>,\n C extends new (state: DurableObjectState, env: E) => T,\n>(optionsCallback: (env: E) => CloudflareOptions, DurableObjectClass: C): C {\n const InstrumentedClass = new Proxy(DurableObjectClass, {\n construct(target, [ctx, env], newTarget) {\n const { obj, options, context, frameworkManagedMethods } = constructInstrumentedDurableObject(\n target,\n ctx,\n env,\n newTarget,\n optionsCallback,\n );\n\n return finalizeWithRpcInstrumentation(obj, options, context, frameworkManagedMethods);\n },\n });\n // Recognizable for `_INTERNAL_wrapUnlessInstrumented`, so auto-instrumentation never nests wrappers.\n markAsInstrumented(InstrumentedClass);\n return InstrumentedClass;\n}\n\n/**\n * Instruments a Cloudflare [`agents`](https://www.npmjs.com/package/agents) Agent class with Sentry.\n *\n * An `Agent` is a Durable Object under the hood, so this applies the same instrumentation as\n * {@link instrumentDurableObjectWithSentry} (request transactions, `alarm`, WebSocket handlers, RPC\n * trace propagation, SQL spans) and additionally captures Agent-specific telemetry via\n * `instrumentCloudflareAgent`:\n *\n * - **Callable RPC spans** — a span (op `rpc`) for each `@callable()` method invoked over WebSocket.\n * - **Breadcrumbs** — for every Agent observability event (`rpc`, `state:update`, `connect`,\n * `disconnect`, `schedule:*`, `queue:*`, `workflow:*`, `email:*`, `mcp:*`, ...).\n *\n * Cloudflare Workers cannot auto-instrument, so the Agent class must be wrapped manually.\n *\n * @param optionsCallback Function that returns the options for the SDK initialization.\n * @param AgentClass The Agent class to instrument.\n * @returns The instrumented Agent class.\n *\n * @example\n * ```ts\n * import { Agent, callable, routeAgentRequest } from 'agents';\n * import * as Sentry from '@sentry/cloudflare';\n *\n * class MyAgentBase extends Agent<Env> {\n * @callable()\n * async greet(name: string): Promise<string> {\n * return `Hello, ${name}!`;\n * }\n * }\n *\n * export const MyAgent = Sentry.instrumentAgentWithSentry(\n * env => ({\n * dsn: env.SENTRY_DSN,\n * tracesSampleRate: 1.0,\n * enableRpcTracePropagation: true,\n * }),\n * MyAgentBase,\n * );\n * ```\n */\nexport function instrumentAgentWithSentry<\n E,\n T extends DurableObject<E>,\n C extends new (state: DurableObjectState, env: E) => T,\n>(optionsCallback: (env: E) => CloudflareOptions, AgentClass: C): C {\n const InstrumentedClass = new Proxy(AgentClass, {\n construct(target, [ctx, env], newTarget) {\n const { obj, options, context, frameworkManagedMethods } = constructInstrumentedDurableObject(\n target,\n ctx,\n env,\n newTarget,\n optionsCallback,\n );\n\n instrumentCloudflareAgent(obj);\n\n // Apply RPC prototype-method instrumentation last, so the Agent-specific own-property\n // handlers we just installed are excluded from RPC method tracing. Methods the Agent\n // framework installed itself are excluded too — `instrumentCloudflareAgent` traces those by\n // wrapping the dispatch instead of the method.\n return finalizeWithRpcInstrumentation(obj, options, context, frameworkManagedMethods);\n },\n });\n markAsInstrumented(InstrumentedClass);\n return InstrumentedClass;\n}\n"],"names":["setAsyncLocalStorageAsyncContextStrategy","options","getFinalOptions","instrumentContext","instrumentEnv","instrumentDurableObjectHandlers","getEffectiveRpcPropagation","getInstrumented","markAsInstrumented","isObjectLike","hasRpcMeta","wrapMethodWithSentry","instrumentCloudflareAgent"],"mappings":"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"}
|
package/build/cjs/index.js
CHANGED
|
@@ -4,6 +4,7 @@ const core = require('@sentry/core');
|
|
|
4
4
|
const withSentry = require('./withSentry.js');
|
|
5
5
|
const defineCloudflareOptions = require('./defineCloudflareOptions.js');
|
|
6
6
|
const durableobject = require('./durableobject.js');
|
|
7
|
+
const instrument = require('./instrument.js');
|
|
7
8
|
const pagesPlugin = require('./pages-plugin.js');
|
|
8
9
|
const request = require('./request.js');
|
|
9
10
|
const client = require('./client.js');
|
|
@@ -113,6 +114,7 @@ exports.withSentry = withSentry.withSentry;
|
|
|
113
114
|
exports.defineCloudflareOptions = defineCloudflareOptions.defineCloudflareOptions;
|
|
114
115
|
exports.instrumentAgentWithSentry = durableobject.instrumentAgentWithSentry;
|
|
115
116
|
exports.instrumentDurableObjectWithSentry = durableobject.instrumentDurableObjectWithSentry;
|
|
117
|
+
exports._INTERNAL_wrapUnlessInstrumented = instrument._INTERNAL_wrapUnlessInstrumented;
|
|
116
118
|
exports.sentryPagesPlugin = pagesPlugin.sentryPagesPlugin;
|
|
117
119
|
exports.wrapRequestHandler = request.wrapRequestHandler;
|
|
118
120
|
exports.CloudflareClient = client.CloudflareClient;
|
package/build/cjs/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/build/cjs/instrument.js
CHANGED
|
@@ -45,7 +45,11 @@ function ensureInstrumented(original, instrumentFn, noMark) {
|
|
|
45
45
|
}
|
|
46
46
|
return instrumented;
|
|
47
47
|
}
|
|
48
|
+
function _INTERNAL_wrapUnlessInstrumented(wrap, optionsCallback, Class) {
|
|
49
|
+
return getInstrumented(Class) === Class ? Class : wrap(optionsCallback, Class);
|
|
50
|
+
}
|
|
48
51
|
|
|
52
|
+
exports._INTERNAL_wrapUnlessInstrumented = _INTERNAL_wrapUnlessInstrumented;
|
|
49
53
|
exports.ensureInstrumented = ensureInstrumented;
|
|
50
54
|
exports.getInstrumented = getInstrumented;
|
|
51
55
|
exports.markAsInstrumented = markAsInstrumented;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrument.js","sources":["../../src/instrument.ts"],"sourcesContent":["// Use a global WeakMap to track instrumented objects across module instances.\n// This is important for Cloudflare Workers where the module may be bundled\n// separately for workers and Durable Objects, but they share the same global scope.\n// The WeakMap stores original -> instrumented mappings so we can retrieve the\n// instrumented version even if we only have a reference to the original.\nimport { isObjectLike } from '@sentry/core';\n\nconst GLOBAL_KEY = '__SENTRY_INSTRUMENTED_MAP__' as const;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction getInstrumentedMap(): WeakMap<any, any> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const globalObj = globalThis as typeof globalThis & { [GLOBAL_KEY]?: WeakMap<any, any> };\n if (!globalObj[GLOBAL_KEY]) {\n globalObj[GLOBAL_KEY] = new WeakMap();\n }\n return globalObj[GLOBAL_KEY];\n}\n\n/**\n * Check if a value can be used as a WeakMap key.\n * WeakMap keys must be objects or non-registered symbols.\n */\nfunction isWeakMapKey(value: unknown): value is object {\n return isObjectLike(value) || typeof value === 'function';\n}\n\n/**\n * Mark an object as instrumented, storing the instrumented version.\n * @param original The original uninstrumented object\n * @param instrumented The instrumented version (defaults to original if not provided)\n */\nexport function markAsInstrumented<T>(original: T, instrumented?: T): void {\n try {\n if (isWeakMapKey(original)) {\n // Store mapping from original to instrumented version\n // If instrumented is not provided, store original (for backwards compat)\n getInstrumentedMap().set(original, instrumented ?? original);\n }\n // Also mark the instrumented version itself so we recognize it\n if (isWeakMapKey(instrumented) && instrumented !== original) {\n getInstrumentedMap().set(instrumented, instrumented);\n }\n } catch {\n // ignore errors here\n }\n}\n\n/**\n * Get the instrumented version of an object, if available.\n * Returns the instrumented version if the object was previously instrumented,\n * or undefined if not found.\n */\nexport function getInstrumented<T>(obj: T): T | undefined {\n try {\n if (isWeakMapKey(obj)) {\n return getInstrumentedMap().get(obj) as T | undefined;\n }\n\n return undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Returns the already-instrumented version of `original` if one exists,\n * otherwise calls `instrumentFn` to create it, marks the mapping, and returns it.\n *\n * @param noMark - If true, skips storing the original→instrumented mapping.\n */\nexport function ensureInstrumented<T>(original: T, instrumentFn: (original: T) => T, noMark?: boolean): T {\n const existing = getInstrumented(original);\n\n if (existing) {\n return existing;\n }\n\n const instrumented = instrumentFn(original);\n\n if (!noMark) {\n markAsInstrumented(original, instrumented);\n }\n\n return instrumented;\n}\n"],"names":["isObjectLike"],"mappings":";;;;AAOA,MAAM,UAAA,GAAa,6BAAA;AAGnB,SAAS,kBAAA,GAAwC;AAE/C,EAAA,MAAM,SAAA,GAAY,UAAA;AAClB,EAAA,IAAI,CAAC,SAAA,CAAU,UAAU,CAAA,EAAG;AAC1B,IAAA,SAAA,CAAU,UAAU,CAAA,mBAAI,IAAI,OAAA,EAAQ;AAAA,EACtC;AACA,EAAA,OAAO,UAAU,UAAU,CAAA;AAC7B;AAMA,SAAS,aAAa,KAAA,EAAiC;AACrD,EAAA,OAAOA,iBAAA,CAAa,KAAK,CAAA,IAAK,OAAO,KAAA,KAAU,UAAA;AACjD;AAOO,SAAS,kBAAA,CAAsB,UAAa,YAAA,EAAwB;AACzE,EAAA,IAAI;AACF,IAAA,IAAI,YAAA,CAAa,QAAQ,CAAA,EAAG;AAG1B,MAAA,kBAAA,EAAmB,CAAE,GAAA,CAAI,QAAA,EAAU,YAAA,IAAgB,QAAQ,CAAA;AAAA,IAC7D;AAEA,IAAA,IAAI,YAAA,CAAa,YAAY,CAAA,IAAK,YAAA,KAAiB,QAAA,EAAU;AAC3D,MAAA,kBAAA,EAAmB,CAAE,GAAA,CAAI,YAAA,EAAc,YAAY,CAAA;AAAA,IACrD;AAAA,EACF,CAAA,CAAA,MAAQ;AAAA,EAER;AACF;AAOO,SAAS,gBAAmB,GAAA,EAAuB;AACxD,EAAA,IAAI;AACF,IAAA,IAAI,YAAA,CAAa,GAAG,CAAA,EAAG;AACrB,MAAA,OAAO,kBAAA,EAAmB,CAAE,GAAA,CAAI,GAAG,CAAA;AAAA,IACrC;AAEA,IAAA,OAAO,KAAA,CAAA;AAAA,EACT,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,MAAA;AAAA,EACT;AACF;AAQO,SAAS,kBAAA,CAAsB,QAAA,EAAa,YAAA,EAAkC,MAAA,EAAqB;AACxG,EAAA,MAAM,QAAA,GAAW,gBAAgB,QAAQ,CAAA;AAEzC,EAAA,IAAI,QAAA,EAAU;AACZ,IAAA,OAAO,QAAA;AAAA,EACT;AAEA,EAAA,MAAM,YAAA,GAAe,aAAa,QAAQ,CAAA;AAE1C,EAAA,IAAI,CAAC,MAAA,EAAQ;AACX,IAAA,kBAAA,CAAmB,UAAU,YAAY,CAAA;AAAA,EAC3C;AAEA,EAAA,OAAO,YAAA;AACT
|
|
1
|
+
{"version":3,"file":"instrument.js","sources":["../../src/instrument.ts"],"sourcesContent":["// Use a global WeakMap to track instrumented objects across module instances.\n// This is important for Cloudflare Workers where the module may be bundled\n// separately for workers and Durable Objects, but they share the same global scope.\n// The WeakMap stores original -> instrumented mappings so we can retrieve the\n// instrumented version even if we only have a reference to the original.\nimport { isObjectLike } from '@sentry/core';\n\nconst GLOBAL_KEY = '__SENTRY_INSTRUMENTED_MAP__' as const;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction getInstrumentedMap(): WeakMap<any, any> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const globalObj = globalThis as typeof globalThis & { [GLOBAL_KEY]?: WeakMap<any, any> };\n if (!globalObj[GLOBAL_KEY]) {\n globalObj[GLOBAL_KEY] = new WeakMap();\n }\n return globalObj[GLOBAL_KEY];\n}\n\n/**\n * Check if a value can be used as a WeakMap key.\n * WeakMap keys must be objects or non-registered symbols.\n */\nfunction isWeakMapKey(value: unknown): value is object {\n return isObjectLike(value) || typeof value === 'function';\n}\n\n/**\n * Mark an object as instrumented, storing the instrumented version.\n * @param original The original uninstrumented object\n * @param instrumented The instrumented version (defaults to original if not provided)\n */\nexport function markAsInstrumented<T>(original: T, instrumented?: T): void {\n try {\n if (isWeakMapKey(original)) {\n // Store mapping from original to instrumented version\n // If instrumented is not provided, store original (for backwards compat)\n getInstrumentedMap().set(original, instrumented ?? original);\n }\n // Also mark the instrumented version itself so we recognize it\n if (isWeakMapKey(instrumented) && instrumented !== original) {\n getInstrumentedMap().set(instrumented, instrumented);\n }\n } catch {\n // ignore errors here\n }\n}\n\n/**\n * Get the instrumented version of an object, if available.\n * Returns the instrumented version if the object was previously instrumented,\n * or undefined if not found.\n */\nexport function getInstrumented<T>(obj: T): T | undefined {\n try {\n if (isWeakMapKey(obj)) {\n return getInstrumentedMap().get(obj) as T | undefined;\n }\n\n return undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Returns the already-instrumented version of `original` if one exists,\n * otherwise calls `instrumentFn` to create it, marks the mapping, and returns it.\n *\n * @param noMark - If true, skips storing the original→instrumented mapping.\n */\nexport function ensureInstrumented<T>(original: T, instrumentFn: (original: T) => T, noMark?: boolean): T {\n const existing = getInstrumented(original);\n\n if (existing) {\n return existing;\n }\n\n const instrumented = instrumentFn(original);\n\n if (!noMark) {\n markAsInstrumented(original, instrumented);\n }\n\n return instrumented;\n}\n\n/**\n * Applies `wrap` to `Class` unless `Class` is already an instrumented wrapper.\n *\n * Emitted by the Vite auto-instrumentation transform for classes the worker entry only re-exports:\n * such a class may already be hand-wrapped with `instrument*WithSentry` in the module it comes\n * from, which the build cannot see. Wrapping again would nest two proxies and instrument the same\n * work twice, duplicated child spans, so the hand-wrapped class is returned as-is. Not part of the\n * public API, hand-written wrapper calls are deliberately left untouched by this guard.\n */\nexport function _INTERNAL_wrapUnlessInstrumented<C>(\n wrap: (optionsCallback: (env: never) => unknown, Class: C) => C,\n optionsCallback: (env: never) => unknown,\n Class: C,\n): C {\n return getInstrumented(Class) === Class ? Class : wrap(optionsCallback, Class);\n}\n"],"names":["isObjectLike"],"mappings":";;;;AAOA,MAAM,UAAA,GAAa,6BAAA;AAGnB,SAAS,kBAAA,GAAwC;AAE/C,EAAA,MAAM,SAAA,GAAY,UAAA;AAClB,EAAA,IAAI,CAAC,SAAA,CAAU,UAAU,CAAA,EAAG;AAC1B,IAAA,SAAA,CAAU,UAAU,CAAA,mBAAI,IAAI,OAAA,EAAQ;AAAA,EACtC;AACA,EAAA,OAAO,UAAU,UAAU,CAAA;AAC7B;AAMA,SAAS,aAAa,KAAA,EAAiC;AACrD,EAAA,OAAOA,iBAAA,CAAa,KAAK,CAAA,IAAK,OAAO,KAAA,KAAU,UAAA;AACjD;AAOO,SAAS,kBAAA,CAAsB,UAAa,YAAA,EAAwB;AACzE,EAAA,IAAI;AACF,IAAA,IAAI,YAAA,CAAa,QAAQ,CAAA,EAAG;AAG1B,MAAA,kBAAA,EAAmB,CAAE,GAAA,CAAI,QAAA,EAAU,YAAA,IAAgB,QAAQ,CAAA;AAAA,IAC7D;AAEA,IAAA,IAAI,YAAA,CAAa,YAAY,CAAA,IAAK,YAAA,KAAiB,QAAA,EAAU;AAC3D,MAAA,kBAAA,EAAmB,CAAE,GAAA,CAAI,YAAA,EAAc,YAAY,CAAA;AAAA,IACrD;AAAA,EACF,CAAA,CAAA,MAAQ;AAAA,EAER;AACF;AAOO,SAAS,gBAAmB,GAAA,EAAuB;AACxD,EAAA,IAAI;AACF,IAAA,IAAI,YAAA,CAAa,GAAG,CAAA,EAAG;AACrB,MAAA,OAAO,kBAAA,EAAmB,CAAE,GAAA,CAAI,GAAG,CAAA;AAAA,IACrC;AAEA,IAAA,OAAO,KAAA,CAAA;AAAA,EACT,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,MAAA;AAAA,EACT;AACF;AAQO,SAAS,kBAAA,CAAsB,QAAA,EAAa,YAAA,EAAkC,MAAA,EAAqB;AACxG,EAAA,MAAM,QAAA,GAAW,gBAAgB,QAAQ,CAAA;AAEzC,EAAA,IAAI,QAAA,EAAU;AACZ,IAAA,OAAO,QAAA;AAAA,EACT;AAEA,EAAA,MAAM,YAAA,GAAe,aAAa,QAAQ,CAAA;AAE1C,EAAA,IAAI,CAAC,MAAA,EAAQ;AACX,IAAA,kBAAA,CAAmB,UAAU,YAAY,CAAA;AAAA,EAC3C;AAEA,EAAA,OAAO,YAAA;AACT;AAWO,SAAS,gCAAA,CACd,IAAA,EACA,eAAA,EACA,KAAA,EACG;AACH,EAAA,OAAO,gBAAgB,KAAK,CAAA,KAAM,QAAQ,KAAA,GAAQ,IAAA,CAAK,iBAAiB,KAAK,CAAA;AAC/E;;;;;;;"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
|
|
3
|
+
const core = require('@sentry/core');
|
|
4
|
+
const debugBuild = require('../debug-build.js');
|
|
5
|
+
const instrument = require('../instrument.js');
|
|
6
|
+
const request = require('../request.js');
|
|
7
|
+
const wrapMethodWithSentry = require('../wrapMethodWithSentry.js');
|
|
8
|
+
|
|
9
|
+
function instrumentDurableObjectHandlers(obj, options, context) {
|
|
10
|
+
if (obj.fetch && typeof obj.fetch === "function") {
|
|
11
|
+
setInstanceHandler(
|
|
12
|
+
obj,
|
|
13
|
+
"fetch",
|
|
14
|
+
instrument.ensureInstrumented(
|
|
15
|
+
obj.fetch.bind(obj),
|
|
16
|
+
(original) => new Proxy(original, {
|
|
17
|
+
apply(target, thisArg, args) {
|
|
18
|
+
return request.wrapRequestHandler({ options, request: args[0], context }, () => {
|
|
19
|
+
return Reflect.apply(target, thisArg, args);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
)
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
if (obj.alarm && typeof obj.alarm === "function") {
|
|
27
|
+
setInstanceHandler(
|
|
28
|
+
obj,
|
|
29
|
+
"alarm",
|
|
30
|
+
wrapMethodWithSentry.wrapMethodWithSentry(
|
|
31
|
+
{
|
|
32
|
+
options,
|
|
33
|
+
context,
|
|
34
|
+
spanName: "alarm",
|
|
35
|
+
spanOp: "function",
|
|
36
|
+
startNewTrace: true,
|
|
37
|
+
origin: "auto.faas.cloudflare.durable_object"
|
|
38
|
+
},
|
|
39
|
+
obj.alarm.bind(obj)
|
|
40
|
+
)
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
if (obj.webSocketMessage && typeof obj.webSocketMessage === "function") {
|
|
44
|
+
setInstanceHandler(
|
|
45
|
+
obj,
|
|
46
|
+
"webSocketMessage",
|
|
47
|
+
wrapMethodWithSentry.wrapMethodWithSentry(
|
|
48
|
+
{
|
|
49
|
+
options,
|
|
50
|
+
context,
|
|
51
|
+
spanName: "webSocketMessage",
|
|
52
|
+
origin: "auto.faas.cloudflare.durable_object"
|
|
53
|
+
},
|
|
54
|
+
obj.webSocketMessage.bind(obj)
|
|
55
|
+
)
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
if (obj.webSocketClose && typeof obj.webSocketClose === "function") {
|
|
59
|
+
setInstanceHandler(
|
|
60
|
+
obj,
|
|
61
|
+
"webSocketClose",
|
|
62
|
+
wrapMethodWithSentry.wrapMethodWithSentry(
|
|
63
|
+
{
|
|
64
|
+
options,
|
|
65
|
+
context,
|
|
66
|
+
spanName: "webSocketClose",
|
|
67
|
+
origin: "auto.faas.cloudflare.durable_object"
|
|
68
|
+
},
|
|
69
|
+
obj.webSocketClose.bind(obj)
|
|
70
|
+
)
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
if (obj.webSocketError && typeof obj.webSocketError === "function") {
|
|
74
|
+
setInstanceHandler(
|
|
75
|
+
obj,
|
|
76
|
+
"webSocketError",
|
|
77
|
+
wrapMethodWithSentry.wrapMethodWithSentry(
|
|
78
|
+
{
|
|
79
|
+
options,
|
|
80
|
+
context,
|
|
81
|
+
spanName: "webSocketError",
|
|
82
|
+
origin: "auto.faas.cloudflare.durable_object"
|
|
83
|
+
},
|
|
84
|
+
obj.webSocketError.bind(obj),
|
|
85
|
+
(_, error) => core.captureException(error, {
|
|
86
|
+
mechanism: {
|
|
87
|
+
type: "auto.faas.cloudflare.durable_object_websocket",
|
|
88
|
+
handled: false
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
)
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function setInstanceHandler(obj, name, handler) {
|
|
96
|
+
const descriptor = Object.getOwnPropertyDescriptor(obj, name);
|
|
97
|
+
try {
|
|
98
|
+
if (descriptor?.writable === false) {
|
|
99
|
+
Object.defineProperty(obj, name, {
|
|
100
|
+
value: handler,
|
|
101
|
+
writable: true,
|
|
102
|
+
enumerable: descriptor.enumerable,
|
|
103
|
+
configurable: descriptor.configurable
|
|
104
|
+
});
|
|
105
|
+
} else {
|
|
106
|
+
obj[name] = handler;
|
|
107
|
+
}
|
|
108
|
+
} catch (error) {
|
|
109
|
+
debugBuild.DEBUG_BUILD && core.debug.warn(`Failed to instrument Durable Object handler "${name}"`, error);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
exports.instrumentDurableObjectHandlers = instrumentDurableObjectHandlers;
|
|
114
|
+
//# sourceMappingURL=instrumentDurableObjectHandlers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instrumentDurableObjectHandlers.js","sources":["../../../src/instrumentations/instrumentDurableObjectHandlers.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/unbound-method */\nimport { captureException, debug } from '@sentry/core';\nimport type { DurableObject } from 'cloudflare:workers';\nimport type { CloudflareOptions } from '../client';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { ensureInstrumented } from '../instrument';\nimport { wrapRequestHandler } from '../request';\nimport { wrapMethodWithSentry } from '../wrapMethodWithSentry';\n\n/**\n * The instrumented context of the Durable Object being wrapped.\n *\n * Kept as `any` for the same reason as in `durableobject.ts`: a concrete `DurableObjectState` here\n * makes `tsc` relate its `SqlStorage` graph against the parameter union of `wrapMethodWithSentry`,\n * which hangs the type build.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype InstrumentedDurableObjectContext = any;\n\n/**\n * Instruments the built-in Durable Object handler methods on a constructed instance.\n *\n * These are the methods that are available on a Durable Object\n * ref: https://developers.cloudflare.com/durable-objects/api/base/\n * - obj.alarm\n * - obj.fetch\n * - obj.webSocketError\n * - obj.webSocketClose\n * - obj.webSocketMessage\n *\n * Any other public methods on the Durable Object instance are RPC calls.\n *\n * @internal\n */\nexport function instrumentDurableObjectHandlers<E, T extends DurableObject<E>>(\n obj: T,\n options: CloudflareOptions,\n context: InstrumentedDurableObjectContext,\n): void {\n // Bind each built-in handler to this instance before wrapping.\n // See https://github.com/getsentry/sentry-javascript/issues/22328\n if (obj.fetch && typeof obj.fetch === 'function') {\n setInstanceHandler(\n obj,\n 'fetch',\n ensureInstrumented(\n obj.fetch.bind(obj),\n original =>\n new Proxy(original, {\n apply(target, thisArg, args) {\n return wrapRequestHandler({ options, request: args[0], context }, () => {\n return Reflect.apply(target, thisArg, args);\n });\n },\n }),\n ),\n );\n }\n\n if (obj.alarm && typeof obj.alarm === 'function') {\n // Alarms are independent invocations, so we start a new trace and link to the previous alarm\n setInstanceHandler(\n obj,\n 'alarm',\n wrapMethodWithSentry(\n {\n options,\n context,\n spanName: 'alarm',\n spanOp: 'function',\n startNewTrace: true,\n origin: 'auto.faas.cloudflare.durable_object',\n },\n obj.alarm.bind(obj),\n ),\n );\n }\n\n if (obj.webSocketMessage && typeof obj.webSocketMessage === 'function') {\n setInstanceHandler(\n obj,\n 'webSocketMessage',\n wrapMethodWithSentry(\n {\n options,\n context,\n spanName: 'webSocketMessage',\n origin: 'auto.faas.cloudflare.durable_object',\n },\n obj.webSocketMessage.bind(obj),\n ),\n );\n }\n\n if (obj.webSocketClose && typeof obj.webSocketClose === 'function') {\n setInstanceHandler(\n obj,\n 'webSocketClose',\n wrapMethodWithSentry(\n {\n options,\n context,\n spanName: 'webSocketClose',\n origin: 'auto.faas.cloudflare.durable_object',\n },\n obj.webSocketClose.bind(obj),\n ),\n );\n }\n\n if (obj.webSocketError && typeof obj.webSocketError === 'function') {\n setInstanceHandler(\n obj,\n 'webSocketError',\n wrapMethodWithSentry(\n {\n options,\n context,\n spanName: 'webSocketError',\n origin: 'auto.faas.cloudflare.durable_object',\n },\n obj.webSocketError.bind(obj),\n (_, error) =>\n captureException(error, {\n mechanism: {\n type: 'auto.faas.cloudflare.durable_object_websocket',\n handled: false,\n },\n }),\n ),\n );\n }\n}\n\n/**\n * Installs an instrumented handler as an own property of the Durable Object instance.\n *\n * A plain assignment is not always possible. The `agents` package installs its handlers with\n * `Object.defineProperty(instance, name, { value, configurable: true })`, and `defineProperty`\n * leaves `writable` at `false`. Assigning to such a property throws a `TypeError` in strict mode,\n * so a read-only property is redefined instead. When the property can be neither assigned nor\n * redefined, the handler stays uninstrumented rather than breaking the object.\n */\nfunction setInstanceHandler(obj: object, name: string, handler: unknown): void {\n const descriptor = Object.getOwnPropertyDescriptor(obj, name);\n\n try {\n if (descriptor?.writable === false) {\n Object.defineProperty(obj, name, {\n value: handler,\n writable: true,\n enumerable: descriptor.enumerable,\n configurable: descriptor.configurable,\n });\n } else {\n (obj as Record<string, unknown>)[name] = handler;\n }\n } catch (error) {\n DEBUG_BUILD && debug.warn(`Failed to instrument Durable Object handler \"${name}\"`, error);\n }\n}\n"],"names":["ensureInstrumented","wrapRequestHandler","wrapMethodWithSentry","captureException","DEBUG_BUILD","debug"],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
|
|
3
3
|
const async = require('../async.js');
|
|
4
|
+
const instrument = require('../instrument.js');
|
|
4
5
|
const options = require('../options.js');
|
|
5
6
|
const instrumentContext = require('../utils/instrumentContext.js');
|
|
6
7
|
const rpcMeta = require('../utils/rpcMeta.js');
|
|
@@ -74,7 +75,7 @@ function instrumentMethod(instance, proxy, prop, method, options, context) {
|
|
|
74
75
|
}
|
|
75
76
|
function instrumentWorkerEntrypoint(optionsCallback, WorkerEntrypointClass) {
|
|
76
77
|
async.setAsyncLocalStorageAsyncContextStrategy();
|
|
77
|
-
|
|
78
|
+
const InstrumentedClass = new Proxy(WorkerEntrypointClass, {
|
|
78
79
|
construct(target, [ctx, env]) {
|
|
79
80
|
const context = instrumentContext.instrumentContext(ctx);
|
|
80
81
|
const options$1 = options.getFinalOptions(optionsCallback(env), env);
|
|
@@ -133,6 +134,8 @@ function instrumentWorkerEntrypoint(optionsCallback, WorkerEntrypointClass) {
|
|
|
133
134
|
return proxy;
|
|
134
135
|
}
|
|
135
136
|
});
|
|
137
|
+
instrument.markAsInstrumented(InstrumentedClass);
|
|
138
|
+
return InstrumentedClass;
|
|
136
139
|
}
|
|
137
140
|
|
|
138
141
|
exports.instrumentWorkerEntrypoint = instrumentWorkerEntrypoint;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrumentWorkerEntrypoint.js","sources":["../../../src/instrumentations/instrumentWorkerEntrypoint.ts"],"sourcesContent":["import type { RpcStub, WorkerEntrypoint } from 'cloudflare:workers';\nimport { setAsyncLocalStorageAsyncContextStrategy } from '../async';\nimport type { CloudflareOptions } from '../client';\nimport { getFinalOptions } from '../options';\nimport { instrumentContext } from '../utils/instrumentContext';\nimport { extractRpcMeta } from '../utils/rpcMeta';\nimport { type UncheckedMethod, wrapMethodWithSentry } from '../wrapMethodWithSentry';\nimport { instrumentEnv } from './worker/instrumentEnv';\nimport { instrumentWorkerEntrypointFetch } from './worker/instrumentFetch';\nimport { instrumentWorkerEntrypointQueue } from './worker/instrumentQueue';\nimport { instrumentWorkerEntrypointScheduled } from './worker/instrumentScheduled';\nimport { instrumentWorkerEntrypointTail } from './worker/instrumentTail';\n\nconst WORKER_ENTRYPOINT_ORIGIN = 'auto.faas.cloudflare.worker_entrypoint';\n\ntype ReservedMethod =\n | Exclude<Extract<keyof WorkerEntrypoint, string>, '__WORKER_ENTRYPOINT_BRAND'>\n | Extract<keyof ExportedHandler, string>\n | Exclude<Extract<keyof RpcStub<(...args: unknown[]) => unknown>, string>, '__RPC_STUB_BRAND'>\n | 'constructor';\n\nconst RESERVED_METHODS = new Set<string>([\n 'connect',\n 'constructor',\n 'dup',\n 'email',\n 'fetch',\n 'queue',\n 'scheduled',\n 'tail',\n 'tailStream',\n 'test',\n 'trace',\n] satisfies ReservedMethod[]);\n\ninterface CachedMethod {\n source: UncheckedMethod;\n wrapped: UncheckedMethod;\n}\n\nexport type WorkerEntrypointConstructor<Env = unknown, Props = {}> = new (\n ctx: ExecutionContext,\n // WorkerEntrypoint subclasses can define different `env` shapes, so this constructor must accept all of them.\n // oxlint-disable-next-line typescript/no-explicit-any\n env: any,\n) => InstanceType<typeof WorkerEntrypoint<Env, Props>>;\n\nfunction isPrototypeMethod(instance: object, prop: string, method: UncheckedMethod): boolean {\n let prototype = Object.getPrototypeOf(instance);\n\n while (prototype && prototype !== Object.prototype) {\n const descriptor = Object.getOwnPropertyDescriptor(prototype, prop);\n if (descriptor) {\n return descriptor.value === method;\n }\n prototype = Object.getPrototypeOf(prototype);\n }\n\n return false;\n}\n\nfunction bindToInstance(instance: object, proxy: object, method: UncheckedMethod): UncheckedMethod {\n return new Proxy(method, {\n apply(target, thisArg, args) {\n return Reflect.apply(target, thisArg === proxy ? instance : thisArg, args);\n },\n });\n}\n\nfunction instrumentMethod(\n instance: object,\n proxy: object,\n prop: PropertyKey,\n method: UncheckedMethod,\n options: CloudflareOptions,\n context: ExecutionContext,\n): UncheckedMethod {\n const ownDescriptor = Object.getOwnPropertyDescriptor(instance, prop);\n if (ownDescriptor && 'value' in ownDescriptor && !ownDescriptor.configurable && !ownDescriptor.writable) {\n return method;\n }\n\n const boundMethod = bindToInstance(instance, proxy, method);\n\n if (typeof prop !== 'string' || RESERVED_METHODS.has(prop) || !isPrototypeMethod(instance, prop, method)) {\n return boundMethod;\n }\n\n const captureMethod = wrapMethodWithSentry(\n { options, context, spanOp: 'rpc', origin: WORKER_ENTRYPOINT_ORIGIN },\n boundMethod,\n undefined,\n true,\n );\n\n // eslint-disable-next-line typescript/no-deprecated\n if (!options.enableRpcTracePropagation) {\n return captureMethod;\n }\n\n const tracedMethod = wrapMethodWithSentry(\n { options, context, spanName: prop, spanOp: 'rpc', origin: WORKER_ENTRYPOINT_ORIGIN },\n boundMethod,\n undefined,\n true,\n );\n\n return (...args: unknown[]) => {\n const { rpcMeta } = extractRpcMeta(args);\n return rpcMeta ? tracedMethod.call(proxy, ...args) : captureMethod.call(proxy, ...args);\n };\n}\n\n/**\n * Instruments a WorkerEntrypoint class to capture errors and performance data.\n *\n * Instruments the following methods (same as `withSentry` for ExportedHandler):\n * - fetch (HTTP requests)\n * - scheduled (cron triggers)\n * - queue (queue consumers)\n * - email (email handlers)\n * - tail (tail workers)\n *\n * as well as any other public RPC methods on the WorkerEntrypoint instance.\n *\n * @param optionsCallback Function that returns the options for the SDK initialization.\n * @param WorkerEntrypointClass The WorkerEntrypoint class to instrument.\n * @returns The instrumented WorkerEntrypoint class.\n *\n * @example\n * ```ts\n * class MyWorkerBase extends WorkerEntrypoint<Env> {\n * async fetch(request: Request): Promise<Response> {\n * return new Response('Hello World!');\n * }\n *\n * async myRpcMethod(arg: string): Promise<string> {\n * return `Hello ${arg}!`;\n * }\n * }\n *\n * export default instrumentWorkerEntrypoint(\n * env => ({\n * dsn: env.SENTRY_DSN,\n * tracesSampleRate: 1.0,\n * }),\n * MyWorkerBase,\n * );\n * ```\n */\nexport function instrumentWorkerEntrypoint<\n Env,\n Props,\n T extends InstanceType<typeof WorkerEntrypoint<Env, Props>>,\n C extends new (ctx: ExecutionContext, env: Env) => T,\n>(optionsCallback: (env: Env) => CloudflareOptions | undefined, WorkerEntrypointClass: C): C {\n // Set up AsyncLocalStorage strategy ONCE at instrumentation time, not per-request\n // This is critical - calling this per-request would create a new AsyncLocalStorage\n // each time, breaking scope isolation for concurrent requests\n setAsyncLocalStorageAsyncContextStrategy();\n\n return new Proxy(WorkerEntrypointClass, {\n construct(target, [ctx, env]) {\n const context = instrumentContext(ctx);\n const options = getFinalOptions(optionsCallback(env), env);\n const instrumentedEnv = instrumentEnv(env, options);\n const obj = new target(context, instrumentedEnv);\n\n // Override this.ctx to ensure the instrumented context is used\n // This is necessary because the base WorkerEntrypoint class sets this.ctx\n // from the constructor parameter, but we want users accessing this.ctx\n // to get the instrumented version\n if ('ctx' in obj) {\n Object.defineProperty(obj, 'ctx', {\n value: context,\n writable: true,\n enumerable: true,\n configurable: true,\n });\n }\n\n if ('env' in obj) {\n Object.defineProperty(obj, 'env', {\n value: instrumentedEnv,\n writable: true,\n enumerable: true,\n configurable: true,\n });\n }\n\n Object.defineProperty(obj, '__SENTRY_CONTEXT__', {\n value: context,\n enumerable: false,\n writable: false,\n configurable: false,\n });\n\n Object.defineProperty(obj, '__SENTRY_OPTIONS__', {\n value: options,\n enumerable: false,\n writable: false,\n configurable: false,\n });\n\n const entrypoint = obj as WorkerEntrypoint;\n instrumentWorkerEntrypointFetch(entrypoint, options, context);\n instrumentWorkerEntrypointScheduled(entrypoint, options, context);\n instrumentWorkerEntrypointQueue(entrypoint, options, context);\n instrumentWorkerEntrypointTail(entrypoint, options, context);\n\n // workerd resolves RPC methods through property access.\n const methodCache = new Map<PropertyKey, CachedMethod>();\n\n const proxy: typeof obj = new Proxy(obj, {\n get(instance, prop) {\n const value = Reflect.get(instance, prop, instance);\n\n if (typeof value !== 'function') {\n return value;\n }\n\n const method = value as UncheckedMethod;\n\n const cached = methodCache.get(prop);\n if (cached?.source === method) {\n return cached.wrapped;\n }\n\n const wrapped = instrumentMethod(instance, proxy, prop, method, options, context);\n methodCache.set(prop, { source: method, wrapped });\n return wrapped;\n },\n });\n\n return proxy;\n },\n });\n}\n"],"names":["wrapMethodWithSentry","rpcMeta","extractRpcMeta","setAsyncLocalStorageAsyncContextStrategy","instrumentContext","options","getFinalOptions","instrumentEnv","instrumentWorkerEntrypointFetch","instrumentWorkerEntrypointScheduled","instrumentWorkerEntrypointQueue","instrumentWorkerEntrypointTail"],"mappings":";;;;;;;;;;;;;AAaA,MAAM,wBAAA,GAA2B,wCAAA;AAQjC,MAAM,gBAAA,uBAAuB,GAAA,CAAY;AAAA,EACvC,SAAA;AAAA,EACA,aAAA;AAAA,EACA,KAAA;AAAA,EACA,OAAA;AAAA,EACA,OAAA;AAAA,EACA,OAAA;AAAA,EACA,WAAA;AAAA,EACA,MAAA;AAAA,EACA,YAAA;AAAA,EACA,MAAA;AAAA,EACA;AACF,CAA4B,CAAA;AAc5B,SAAS,iBAAA,CAAkB,QAAA,EAAkB,IAAA,EAAc,MAAA,EAAkC;AAC3F,EAAA,IAAI,SAAA,GAAY,MAAA,CAAO,cAAA,CAAe,QAAQ,CAAA;AAE9C,EAAA,OAAO,SAAA,IAAa,SAAA,KAAc,MAAA,CAAO,SAAA,EAAW;AAClD,IAAA,MAAM,UAAA,GAAa,MAAA,CAAO,wBAAA,CAAyB,SAAA,EAAW,IAAI,CAAA;AAClE,IAAA,IAAI,UAAA,EAAY;AACd,MAAA,OAAO,WAAW,KAAA,KAAU,MAAA;AAAA,IAC9B;AACA,IAAA,SAAA,GAAY,MAAA,CAAO,eAAe,SAAS,CAAA;AAAA,EAC7C;AAEA,EAAA,OAAO,KAAA;AACT;AAEA,SAAS,cAAA,CAAe,QAAA,EAAkB,KAAA,EAAe,MAAA,EAA0C;AACjG,EAAA,OAAO,IAAI,MAAM,MAAA,EAAQ;AAAA,IACvB,KAAA,CAAM,MAAA,EAAQ,OAAA,EAAS,IAAA,EAAM;AAC3B,MAAA,OAAO,QAAQ,KAAA,CAAM,MAAA,EAAQ,YAAY,KAAA,GAAQ,QAAA,GAAW,SAAS,IAAI,CAAA;AAAA,IAC3E;AAAA,GACD,CAAA;AACH;AAEA,SAAS,iBACP,QAAA,EACA,KAAA,EACA,IAAA,EACA,MAAA,EACA,SACA,OAAA,EACiB;AACjB,EAAA,MAAM,aAAA,GAAgB,MAAA,CAAO,wBAAA,CAAyB,QAAA,EAAU,IAAI,CAAA;AACpE,EAAA,IAAI,aAAA,IAAiB,WAAW,aAAA,IAAiB,CAAC,cAAc,YAAA,IAAgB,CAAC,cAAc,QAAA,EAAU;AACvG,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,MAAM,WAAA,GAAc,cAAA,CAAe,QAAA,EAAU,KAAA,EAAO,MAAM,CAAA;AAE1D,EAAA,IAAI,OAAO,IAAA,KAAS,QAAA,IAAY,gBAAA,CAAiB,GAAA,CAAI,IAAI,CAAA,IAAK,CAAC,iBAAA,CAAkB,QAAA,EAAU,IAAA,EAAM,MAAM,CAAA,EAAG;AACxG,IAAA,OAAO,WAAA;AAAA,EACT;AAEA,EAAA,MAAM,aAAA,GAAgBA,yCAAA;AAAA,IACpB,EAAE,OAAA,EAAS,OAAA,EAAS,MAAA,EAAQ,KAAA,EAAO,QAAQ,wBAAA,EAAyB;AAAA,IACpE,WAAA;AAAA,IACA,KAAA,CAAA;AAAA,IACA;AAAA,GACF;AAGA,EAAA,IAAI,CAAC,QAAQ,yBAAA,EAA2B;AACtC,IAAA,OAAO,aAAA;AAAA,EACT;AAEA,EAAA,MAAM,YAAA,GAAeA,yCAAA;AAAA,IACnB,EAAE,SAAS,OAAA,EAAS,QAAA,EAAU,MAAM,MAAA,EAAQ,KAAA,EAAO,QAAQ,wBAAA,EAAyB;AAAA,IACpF,WAAA;AAAA,IACA,KAAA,CAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,OAAO,IAAI,IAAA,KAAoB;AAC7B,IAAA,MAAM,WAAEC,SAAA,EAAQ,GAAIC,sBAAA,CAAe,IAAI,CAAA;AACvC,IAAA,OAAOD,SAAA,GAAU,YAAA,CAAa,IAAA,CAAK,KAAA,EAAO,GAAG,IAAI,CAAA,GAAI,aAAA,CAAc,IAAA,CAAK,KAAA,EAAO,GAAG,IAAI,CAAA;AAAA,EACxF,CAAA;AACF;AAuCO,SAAS,0BAAA,CAKd,iBAA8D,qBAAA,EAA6B;AAI3F,EAAAE,8CAAA,EAAyC;AAEzC,EAAA,OAAO,IAAI,MAAM,qBAAA,EAAuB;AAAA,IACtC,SAAA,CAAU,MAAA,EAAQ,CAAC,GAAA,EAAK,GAAG,CAAA,EAAG;AAC5B,MAAA,MAAM,OAAA,GAAUC,oCAAkB,GAAG,CAAA;AACrC,MAAA,MAAMC,SAAA,GAAUC,uBAAA,CAAgB,eAAA,CAAgB,GAAG,GAAG,GAAG,CAAA;AACzD,MAAA,MAAM,eAAA,GAAkBC,2BAAA,CAAc,GAAA,EAAKF,SAAO,CAAA;AAClD,MAAA,MAAM,GAAA,GAAM,IAAI,MAAA,CAAO,OAAA,EAAS,eAAe,CAAA;AAM/C,MAAA,IAAI,SAAS,GAAA,EAAK;AAChB,QAAA,MAAA,CAAO,cAAA,CAAe,KAAK,KAAA,EAAO;AAAA,UAChC,KAAA,EAAO,OAAA;AAAA,UACP,QAAA,EAAU,IAAA;AAAA,UACV,UAAA,EAAY,IAAA;AAAA,UACZ,YAAA,EAAc;AAAA,SACf,CAAA;AAAA,MACH;AAEA,MAAA,IAAI,SAAS,GAAA,EAAK;AAChB,QAAA,MAAA,CAAO,cAAA,CAAe,KAAK,KAAA,EAAO;AAAA,UAChC,KAAA,EAAO,eAAA;AAAA,UACP,QAAA,EAAU,IAAA;AAAA,UACV,UAAA,EAAY,IAAA;AAAA,UACZ,YAAA,EAAc;AAAA,SACf,CAAA;AAAA,MACH;AAEA,MAAA,MAAA,CAAO,cAAA,CAAe,KAAK,oBAAA,EAAsB;AAAA,QAC/C,KAAA,EAAO,OAAA;AAAA,QACP,UAAA,EAAY,KAAA;AAAA,QACZ,QAAA,EAAU,KAAA;AAAA,QACV,YAAA,EAAc;AAAA,OACf,CAAA;AAED,MAAA,MAAA,CAAO,cAAA,CAAe,KAAK,oBAAA,EAAsB;AAAA,QAC/C,KAAA,EAAOA,SAAA;AAAA,QACP,UAAA,EAAY,KAAA;AAAA,QACZ,QAAA,EAAU,KAAA;AAAA,QACV,YAAA,EAAc;AAAA,OACf,CAAA;AAED,MAAA,MAAM,UAAA,GAAa,GAAA;AACnB,MAAAG,+CAAA,CAAgC,UAAA,EAAYH,WAAS,OAAO,CAAA;AAC5D,MAAAI,uDAAA,CAAoC,UAAA,EAAYJ,WAAS,OAAO,CAAA;AAChE,MAAAK,+CAAA,CAAgC,UAAA,EAAYL,WAAS,OAAO,CAAA;AAC5D,MAAAM,6CAAA,CAA+B,UAAA,EAAYN,WAAS,OAAO,CAAA;AAG3D,MAAA,MAAM,WAAA,uBAAkB,GAAA,EAA+B;AAEvD,MAAA,MAAM,KAAA,GAAoB,IAAI,KAAA,CAAM,GAAA,EAAK;AAAA,QACvC,GAAA,CAAI,UAAU,IAAA,EAAM;AAClB,UAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,GAAA,CAAI,QAAA,EAAU,MAAM,QAAQ,CAAA;AAElD,UAAA,IAAI,OAAO,UAAU,UAAA,EAAY;AAC/B,YAAA,OAAO,KAAA;AAAA,UACT;AAEA,UAAA,MAAM,MAAA,GAAS,KAAA;AAEf,UAAA,MAAM,MAAA,GAAS,WAAA,CAAY,GAAA,CAAI,IAAI,CAAA;AACnC,UAAA,IAAI,MAAA,EAAQ,WAAW,MAAA,EAAQ;AAC7B,YAAA,OAAO,MAAA,CAAO,OAAA;AAAA,UAChB;AAEA,UAAA,MAAM,UAAU,gBAAA,CAAiB,QAAA,EAAU,OAAO,IAAA,EAAM,MAAA,EAAQA,WAAS,OAAO,CAAA;AAChF,UAAA,WAAA,CAAY,IAAI,IAAA,EAAM,EAAE,MAAA,EAAQ,MAAA,EAAQ,SAAS,CAAA;AACjD,UAAA,OAAO,OAAA;AAAA,QACT;AAAA,OACD,CAAA;AAED,MAAA,OAAO,KAAA;AAAA,IACT;AAAA,GACD,CAAA;AACH;;;;"}
|
|
1
|
+
{"version":3,"file":"instrumentWorkerEntrypoint.js","sources":["../../../src/instrumentations/instrumentWorkerEntrypoint.ts"],"sourcesContent":["import type { RpcStub, WorkerEntrypoint } from 'cloudflare:workers';\nimport { setAsyncLocalStorageAsyncContextStrategy } from '../async';\nimport type { CloudflareOptions } from '../client';\nimport { markAsInstrumented } from '../instrument';\nimport { getFinalOptions } from '../options';\nimport { instrumentContext } from '../utils/instrumentContext';\nimport { extractRpcMeta } from '../utils/rpcMeta';\nimport { type UncheckedMethod, wrapMethodWithSentry } from '../wrapMethodWithSentry';\nimport { instrumentEnv } from './worker/instrumentEnv';\nimport { instrumentWorkerEntrypointFetch } from './worker/instrumentFetch';\nimport { instrumentWorkerEntrypointQueue } from './worker/instrumentQueue';\nimport { instrumentWorkerEntrypointScheduled } from './worker/instrumentScheduled';\nimport { instrumentWorkerEntrypointTail } from './worker/instrumentTail';\n\nconst WORKER_ENTRYPOINT_ORIGIN = 'auto.faas.cloudflare.worker_entrypoint';\n\ntype ReservedMethod =\n | Exclude<Extract<keyof WorkerEntrypoint, string>, '__WORKER_ENTRYPOINT_BRAND'>\n | Extract<keyof ExportedHandler, string>\n | Exclude<Extract<keyof RpcStub<(...args: unknown[]) => unknown>, string>, '__RPC_STUB_BRAND'>\n | 'constructor';\n\nconst RESERVED_METHODS = new Set<string>([\n 'connect',\n 'constructor',\n 'dup',\n 'email',\n 'fetch',\n 'queue',\n 'scheduled',\n 'tail',\n 'tailStream',\n 'test',\n 'trace',\n] satisfies ReservedMethod[]);\n\ninterface CachedMethod {\n source: UncheckedMethod;\n wrapped: UncheckedMethod;\n}\n\nexport type WorkerEntrypointConstructor<Env = unknown, Props = {}> = new (\n ctx: ExecutionContext,\n // WorkerEntrypoint subclasses can define different `env` shapes, so this constructor must accept all of them.\n // oxlint-disable-next-line typescript/no-explicit-any\n env: any,\n) => InstanceType<typeof WorkerEntrypoint<Env, Props>>;\n\nfunction isPrototypeMethod(instance: object, prop: string, method: UncheckedMethod): boolean {\n let prototype = Object.getPrototypeOf(instance);\n\n while (prototype && prototype !== Object.prototype) {\n const descriptor = Object.getOwnPropertyDescriptor(prototype, prop);\n if (descriptor) {\n return descriptor.value === method;\n }\n prototype = Object.getPrototypeOf(prototype);\n }\n\n return false;\n}\n\nfunction bindToInstance(instance: object, proxy: object, method: UncheckedMethod): UncheckedMethod {\n return new Proxy(method, {\n apply(target, thisArg, args) {\n return Reflect.apply(target, thisArg === proxy ? instance : thisArg, args);\n },\n });\n}\n\nfunction instrumentMethod(\n instance: object,\n proxy: object,\n prop: PropertyKey,\n method: UncheckedMethod,\n options: CloudflareOptions,\n context: ExecutionContext,\n): UncheckedMethod {\n const ownDescriptor = Object.getOwnPropertyDescriptor(instance, prop);\n if (ownDescriptor && 'value' in ownDescriptor && !ownDescriptor.configurable && !ownDescriptor.writable) {\n return method;\n }\n\n const boundMethod = bindToInstance(instance, proxy, method);\n\n if (typeof prop !== 'string' || RESERVED_METHODS.has(prop) || !isPrototypeMethod(instance, prop, method)) {\n return boundMethod;\n }\n\n const captureMethod = wrapMethodWithSentry(\n { options, context, spanOp: 'rpc', origin: WORKER_ENTRYPOINT_ORIGIN },\n boundMethod,\n undefined,\n true,\n );\n\n // eslint-disable-next-line typescript/no-deprecated\n if (!options.enableRpcTracePropagation) {\n return captureMethod;\n }\n\n const tracedMethod = wrapMethodWithSentry(\n { options, context, spanName: prop, spanOp: 'rpc', origin: WORKER_ENTRYPOINT_ORIGIN },\n boundMethod,\n undefined,\n true,\n );\n\n return (...args: unknown[]) => {\n const { rpcMeta } = extractRpcMeta(args);\n return rpcMeta ? tracedMethod.call(proxy, ...args) : captureMethod.call(proxy, ...args);\n };\n}\n\n/**\n * Instruments a WorkerEntrypoint class to capture errors and performance data.\n *\n * Instruments the following methods (same as `withSentry` for ExportedHandler):\n * - fetch (HTTP requests)\n * - scheduled (cron triggers)\n * - queue (queue consumers)\n * - email (email handlers)\n * - tail (tail workers)\n *\n * as well as any other public RPC methods on the WorkerEntrypoint instance.\n *\n * @param optionsCallback Function that returns the options for the SDK initialization.\n * @param WorkerEntrypointClass The WorkerEntrypoint class to instrument.\n * @returns The instrumented WorkerEntrypoint class.\n *\n * @example\n * ```ts\n * class MyWorkerBase extends WorkerEntrypoint<Env> {\n * async fetch(request: Request): Promise<Response> {\n * return new Response('Hello World!');\n * }\n *\n * async myRpcMethod(arg: string): Promise<string> {\n * return `Hello ${arg}!`;\n * }\n * }\n *\n * export default instrumentWorkerEntrypoint(\n * env => ({\n * dsn: env.SENTRY_DSN,\n * tracesSampleRate: 1.0,\n * }),\n * MyWorkerBase,\n * );\n * ```\n */\nexport function instrumentWorkerEntrypoint<\n Env,\n Props,\n T extends InstanceType<typeof WorkerEntrypoint<Env, Props>>,\n C extends new (ctx: ExecutionContext, env: Env) => T,\n>(optionsCallback: (env: Env) => CloudflareOptions | undefined, WorkerEntrypointClass: C): C {\n // Set up AsyncLocalStorage strategy ONCE at instrumentation time, not per-request\n // This is critical - calling this per-request would create a new AsyncLocalStorage\n // each time, breaking scope isolation for concurrent requests\n setAsyncLocalStorageAsyncContextStrategy();\n\n const InstrumentedClass = new Proxy(WorkerEntrypointClass, {\n construct(target, [ctx, env]) {\n const context = instrumentContext(ctx);\n const options = getFinalOptions(optionsCallback(env), env);\n const instrumentedEnv = instrumentEnv(env, options);\n const obj = new target(context, instrumentedEnv);\n\n // Override this.ctx to ensure the instrumented context is used\n // This is necessary because the base WorkerEntrypoint class sets this.ctx\n // from the constructor parameter, but we want users accessing this.ctx\n // to get the instrumented version\n if ('ctx' in obj) {\n Object.defineProperty(obj, 'ctx', {\n value: context,\n writable: true,\n enumerable: true,\n configurable: true,\n });\n }\n\n if ('env' in obj) {\n Object.defineProperty(obj, 'env', {\n value: instrumentedEnv,\n writable: true,\n enumerable: true,\n configurable: true,\n });\n }\n\n Object.defineProperty(obj, '__SENTRY_CONTEXT__', {\n value: context,\n enumerable: false,\n writable: false,\n configurable: false,\n });\n\n Object.defineProperty(obj, '__SENTRY_OPTIONS__', {\n value: options,\n enumerable: false,\n writable: false,\n configurable: false,\n });\n\n const entrypoint = obj as WorkerEntrypoint;\n instrumentWorkerEntrypointFetch(entrypoint, options, context);\n instrumentWorkerEntrypointScheduled(entrypoint, options, context);\n instrumentWorkerEntrypointQueue(entrypoint, options, context);\n instrumentWorkerEntrypointTail(entrypoint, options, context);\n\n // workerd resolves RPC methods through property access.\n const methodCache = new Map<PropertyKey, CachedMethod>();\n\n const proxy: typeof obj = new Proxy(obj, {\n get(instance, prop) {\n const value = Reflect.get(instance, prop, instance);\n\n if (typeof value !== 'function') {\n return value;\n }\n\n const method = value as UncheckedMethod;\n\n const cached = methodCache.get(prop);\n if (cached?.source === method) {\n return cached.wrapped;\n }\n\n const wrapped = instrumentMethod(instance, proxy, prop, method, options, context);\n methodCache.set(prop, { source: method, wrapped });\n return wrapped;\n },\n });\n\n return proxy;\n },\n });\n // Recognizable for `_INTERNAL_wrapUnlessInstrumented`, so auto-instrumentation never nests wrappers.\n markAsInstrumented(InstrumentedClass);\n return InstrumentedClass;\n}\n"],"names":["wrapMethodWithSentry","rpcMeta","extractRpcMeta","setAsyncLocalStorageAsyncContextStrategy","instrumentContext","options","getFinalOptions","instrumentEnv","instrumentWorkerEntrypointFetch","instrumentWorkerEntrypointScheduled","instrumentWorkerEntrypointQueue","instrumentWorkerEntrypointTail","markAsInstrumented"],"mappings":"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"}
|
|
@@ -4,6 +4,7 @@ const serverUtils = require('@sentry/server-utils');
|
|
|
4
4
|
const vercelai = require('./integrations/tracing/vercelai.js');
|
|
5
5
|
const client = require('../client.js');
|
|
6
6
|
const core = require('@sentry/core');
|
|
7
|
+
const instrument = require('../instrument.js');
|
|
7
8
|
const defineCloudflareOptions = require('../defineCloudflareOptions.js');
|
|
8
9
|
const fetch = require('../integrations/fetch.js');
|
|
9
10
|
const sdk = require('../sdk.js');
|
|
@@ -113,6 +114,7 @@ exports.withScope = core.withScope;
|
|
|
113
114
|
exports.withStreamedSpan = core.withStreamedSpan;
|
|
114
115
|
exports.wrapMcpServerWithSentry = core.wrapMcpServerWithSentry;
|
|
115
116
|
exports.zodErrorsIntegration = core.zodErrorsIntegration;
|
|
117
|
+
exports._INTERNAL_wrapUnlessInstrumented = instrument._INTERNAL_wrapUnlessInstrumented;
|
|
116
118
|
exports.defineCloudflareOptions = defineCloudflareOptions.defineCloudflareOptions;
|
|
117
119
|
exports.fetchIntegration = fetch.fetchIntegration;
|
|
118
120
|
exports.getDefaultIntegrations = sdk.getDefaultIntegrations;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -23,12 +23,13 @@ async function detectAgentClasses(ast, entryId, candidates, resolver) {
|
|
|
23
23
|
function collectAgentCandidates(ast, configuredNames) {
|
|
24
24
|
const shape = moduleShape.shapeFromAst(ast);
|
|
25
25
|
const candidates = /* @__PURE__ */ new Set();
|
|
26
|
+
const isResolvable = (name) => shape.classes.has(name) || shape.imports.has(name) || shape.reexports.has(name);
|
|
26
27
|
for (const configured of configuredNames) {
|
|
27
|
-
if (
|
|
28
|
+
if (isResolvable(configured)) {
|
|
28
29
|
candidates.add(configured);
|
|
29
30
|
}
|
|
30
31
|
const local = shape.localExports.get(configured);
|
|
31
|
-
if (local &&
|
|
32
|
+
if (local && isResolvable(local)) {
|
|
32
33
|
candidates.add(local);
|
|
33
34
|
}
|
|
34
35
|
}
|