@sentry/cloudflare 11.0.0-beta.0 → 11.0.0-beta.2
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/dev/durableobject.js +6 -2
- package/build/cjs/dev/durableobject.js.map +1 -1
- package/build/cjs/dev/index.js +15 -13
- package/build/cjs/dev/index.js.map +1 -1
- package/build/cjs/dev/instrument.js +4 -0
- package/build/cjs/dev/instrument.js.map +1 -1
- package/build/cjs/dev/instrumentations/instrumentSqlStorage.js +3 -3
- package/build/cjs/dev/instrumentations/instrumentSqlStorage.js.map +1 -1
- package/build/cjs/dev/instrumentations/instrumentWorkerEntrypoint.js +4 -1
- package/build/cjs/dev/instrumentations/instrumentWorkerEntrypoint.js.map +1 -1
- package/build/cjs/dev/instrumentations/worker/instrumentD1.js +2 -2
- package/build/cjs/dev/instrumentations/worker/instrumentD1.js.map +1 -1
- package/build/cjs/dev/vite/agentClass.js +3 -2
- package/build/cjs/dev/vite/agentClass.js.map +1 -1
- package/build/cjs/dev/vite/autoInstrument.js +1 -1
- package/build/cjs/dev/vite/autoInstrument.js.map +1 -1
- package/build/cjs/dev/vite/transform.js +79 -16
- package/build/cjs/dev/vite/transform.js.map +1 -1
- package/build/cjs/dev/workflows.js +4 -1
- package/build/cjs/dev/workflows.js.map +1 -1
- package/build/cjs/prod/durableobject.js +6 -2
- package/build/cjs/prod/durableobject.js.map +1 -1
- package/build/cjs/prod/index.js +15 -13
- package/build/cjs/prod/index.js.map +1 -1
- package/build/cjs/prod/instrument.js +4 -0
- package/build/cjs/prod/instrument.js.map +1 -1
- package/build/cjs/prod/instrumentations/instrumentSqlStorage.js +3 -3
- package/build/cjs/prod/instrumentations/instrumentSqlStorage.js.map +1 -1
- package/build/cjs/prod/instrumentations/instrumentWorkerEntrypoint.js +4 -1
- package/build/cjs/prod/instrumentations/instrumentWorkerEntrypoint.js.map +1 -1
- package/build/cjs/prod/instrumentations/worker/instrumentD1.js +2 -2
- package/build/cjs/prod/instrumentations/worker/instrumentD1.js.map +1 -1
- package/build/cjs/prod/vite/agentClass.js +3 -2
- package/build/cjs/prod/vite/agentClass.js.map +1 -1
- package/build/cjs/prod/vite/autoInstrument.js +1 -1
- package/build/cjs/prod/vite/autoInstrument.js.map +1 -1
- package/build/cjs/prod/vite/transform.js +79 -16
- package/build/cjs/prod/vite/transform.js.map +1 -1
- package/build/cjs/prod/workflows.js +4 -1
- package/build/cjs/prod/workflows.js.map +1 -1
- package/build/esm/dev/durableobject.js +6 -2
- package/build/esm/dev/durableobject.js.map +1 -1
- package/build/esm/dev/index.js +3 -2
- package/build/esm/dev/index.js.map +1 -1
- package/build/esm/dev/instrument.js +4 -1
- package/build/esm/dev/instrument.js.map +1 -1
- package/build/esm/dev/instrumentations/instrumentSqlStorage.js +3 -3
- package/build/esm/dev/instrumentations/instrumentSqlStorage.js.map +1 -1
- package/build/esm/dev/instrumentations/instrumentWorkerEntrypoint.js +4 -1
- package/build/esm/dev/instrumentations/instrumentWorkerEntrypoint.js.map +1 -1
- package/build/esm/dev/instrumentations/worker/instrumentD1.js +2 -2
- package/build/esm/dev/instrumentations/worker/instrumentD1.js.map +1 -1
- package/build/esm/dev/package.json +1 -1
- package/build/esm/dev/vite/agentClass.js +3 -2
- package/build/esm/dev/vite/agentClass.js.map +1 -1
- package/build/esm/dev/vite/autoInstrument.js +1 -1
- package/build/esm/dev/vite/autoInstrument.js.map +1 -1
- package/build/esm/dev/vite/transform.js +79 -16
- package/build/esm/dev/vite/transform.js.map +1 -1
- package/build/esm/dev/workflows.js +4 -1
- package/build/esm/dev/workflows.js.map +1 -1
- package/build/esm/prod/durableobject.js +6 -2
- package/build/esm/prod/durableobject.js.map +1 -1
- package/build/esm/prod/index.js +3 -2
- package/build/esm/prod/index.js.map +1 -1
- package/build/esm/prod/instrument.js +4 -1
- package/build/esm/prod/instrument.js.map +1 -1
- package/build/esm/prod/instrumentations/instrumentSqlStorage.js +3 -3
- package/build/esm/prod/instrumentations/instrumentSqlStorage.js.map +1 -1
- package/build/esm/prod/instrumentations/instrumentWorkerEntrypoint.js +4 -1
- package/build/esm/prod/instrumentations/instrumentWorkerEntrypoint.js.map +1 -1
- package/build/esm/prod/instrumentations/worker/instrumentD1.js +2 -2
- package/build/esm/prod/instrumentations/worker/instrumentD1.js.map +1 -1
- package/build/esm/prod/package.json +1 -1
- package/build/esm/prod/vite/agentClass.js +3 -2
- package/build/esm/prod/vite/agentClass.js.map +1 -1
- package/build/esm/prod/vite/autoInstrument.js +1 -1
- package/build/esm/prod/vite/autoInstrument.js.map +1 -1
- package/build/esm/prod/vite/transform.js +79 -16
- package/build/esm/prod/vite/transform.js.map +1 -1
- package/build/esm/prod/workflows.js +4 -1
- package/build/esm/prod/workflows.js.map +1 -1
- package/build/types/durableobject.d.ts.map +1 -1
- package/build/types/index.d.ts +3 -1
- 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/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/package.json +4 -4
|
@@ -140,7 +140,7 @@ function createRpcPrototypeWrapper(methodName, originalMethod) {
|
|
|
140
140
|
return wrapper;
|
|
141
141
|
}
|
|
142
142
|
function instrumentDurableObjectWithSentry(optionsCallback, DurableObjectClass) {
|
|
143
|
-
|
|
143
|
+
const InstrumentedClass = new Proxy(DurableObjectClass, {
|
|
144
144
|
construct(target, [ctx, env], newTarget) {
|
|
145
145
|
const { obj, options, context, frameworkManagedMethods } = constructInstrumentedDurableObject(
|
|
146
146
|
target,
|
|
@@ -152,9 +152,11 @@ function instrumentDurableObjectWithSentry(optionsCallback, DurableObjectClass)
|
|
|
152
152
|
return finalizeWithRpcInstrumentation(obj, options, context, frameworkManagedMethods);
|
|
153
153
|
}
|
|
154
154
|
});
|
|
155
|
+
instrument.markAsInstrumented(InstrumentedClass);
|
|
156
|
+
return InstrumentedClass;
|
|
155
157
|
}
|
|
156
158
|
function instrumentAgentWithSentry(optionsCallback, AgentClass) {
|
|
157
|
-
|
|
159
|
+
const InstrumentedClass = new Proxy(AgentClass, {
|
|
158
160
|
construct(target, [ctx, env], newTarget) {
|
|
159
161
|
const { obj, options, context, frameworkManagedMethods } = constructInstrumentedDurableObject(
|
|
160
162
|
target,
|
|
@@ -167,6 +169,8 @@ function instrumentAgentWithSentry(optionsCallback, AgentClass) {
|
|
|
167
169
|
return finalizeWithRpcInstrumentation(obj, options, context, frameworkManagedMethods);
|
|
168
170
|
}
|
|
169
171
|
});
|
|
172
|
+
instrument.markAsInstrumented(InstrumentedClass);
|
|
173
|
+
return InstrumentedClass;
|
|
170
174
|
}
|
|
171
175
|
|
|
172
176
|
exports.constructInstrumentedDurableObject = constructInstrumentedDurableObject;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"durableobject.js","sources":["../../../src/durableobject.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/unbound-method */\nimport { RPC } from '@sentry/conventions/op';\nimport { isObjectLike } from '@sentry/core';\nimport type { DurableObject } from 'cloudflare:workers';\nimport { setAsyncLocalStorageAsyncContextStrategy } from '@sentry/server-utils/no-diagnostic-channels';\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 { instrumentCloudflareAgent } from './instrumentations/agents';\nimport type { DefaultEnv, ResolveEnv, StrictCloudflareOptions } from './types';\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 /** 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.\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 rpcInstanceStates.set(obj, { options, context });\n\n instrumentPrototypeRpcMethods(obj, excludedMethods);\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): PropertyDescriptor | undefined {\n if (\n RESERVED_RPC_METHOD_NAMES.has(methodName) ||\n Object.prototype.hasOwnProperty.call(obj, methodName) ||\n excludedMethods?.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 */\nfunction instrumentPrototypeRpcMethods(obj: object, excludedMethods?: ReadonlySet<string>): 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);\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 RPC metadata or instance state use the original method directly. The\n * original function name and 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 // Untraced calls are the common case — every internal call the instance makes to one of its\n // own methods lands here too — so check the arguments before touching per-instance state.\n if (!hasRpcMeta(args)) {\n return Reflect.apply(originalMethod, this, args);\n }\n\n const state = isObjectLike(this) ? rpcInstanceStates.get(this) : undefined;\n\n if (!state) {\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 * RPC methods (prototype methods) are instrumented too, so an incoming trace continues into them.\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 Env = DefaultEnv,\n // oxlint-disable-next-line typescript/no-explicit-any\n T extends DurableObject<any> = DurableObject<Env>,\n // oxlint-disable-next-line typescript/no-explicit-any\n C extends new (state: DurableObjectState, env: any) => T = new (state: DurableObjectState, env: any) => T,\n O = unknown,\n>(optionsCallback: (env: ResolveEnv<C, Env>) => StrictCloudflareOptions<O>, 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 * }),\n * MyAgentBase,\n * );\n * ```\n */\nexport function instrumentAgentWithSentry<\n Env = DefaultEnv,\n // oxlint-disable-next-line typescript/no-explicit-any\n T extends DurableObject<any> = DurableObject<Env>,\n // oxlint-disable-next-line typescript/no-explicit-any\n C extends new (state: DurableObjectState, env: any) => T = new (state: DurableObjectState, env: any) => T,\n O = unknown,\n>(optionsCallback: (env: ResolveEnv<C, Env>) => StrictCloudflareOptions<O>, 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","instrumentDurableObjectHandlers","getInstrumented","markAsInstrumented","hasRpcMeta","isObjectLike","wrapMethodWithSentry","RPC","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 { RPC } from '@sentry/conventions/op';\nimport { isObjectLike } from '@sentry/core';\nimport type { DurableObject } from 'cloudflare:workers';\nimport { setAsyncLocalStorageAsyncContextStrategy } from '@sentry/server-utils/no-diagnostic-channels';\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 { instrumentCloudflareAgent } from './instrumentations/agents';\nimport type { DefaultEnv, ResolveEnv, StrictCloudflareOptions } from './types';\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 /** 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.\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 rpcInstanceStates.set(obj, { options, context });\n\n instrumentPrototypeRpcMethods(obj, excludedMethods);\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): PropertyDescriptor | undefined {\n if (\n RESERVED_RPC_METHOD_NAMES.has(methodName) ||\n Object.prototype.hasOwnProperty.call(obj, methodName) ||\n excludedMethods?.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 */\nfunction instrumentPrototypeRpcMethods(obj: object, excludedMethods?: ReadonlySet<string>): 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);\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 RPC metadata or instance state use the original method directly. The\n * original function name and 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 // Untraced calls are the common case — every internal call the instance makes to one of its\n // own methods lands here too — so check the arguments before touching per-instance state.\n if (!hasRpcMeta(args)) {\n return Reflect.apply(originalMethod, this, args);\n }\n\n const state = isObjectLike(this) ? rpcInstanceStates.get(this) : undefined;\n\n if (!state) {\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 * RPC methods (prototype methods) are instrumented too, so an incoming trace continues into them.\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 Env = DefaultEnv,\n // oxlint-disable-next-line typescript/no-explicit-any\n T extends DurableObject<any> = DurableObject<Env>,\n // oxlint-disable-next-line typescript/no-explicit-any\n C extends new (state: DurableObjectState, env: any) => T = new (state: DurableObjectState, env: any) => T,\n O = unknown,\n>(optionsCallback: (env: ResolveEnv<C, Env>) => StrictCloudflareOptions<O>, 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 * }),\n * MyAgentBase,\n * );\n * ```\n */\nexport function instrumentAgentWithSentry<\n Env = DefaultEnv,\n // oxlint-disable-next-line typescript/no-explicit-any\n T extends DurableObject<any> = DurableObject<Env>,\n // oxlint-disable-next-line typescript/no-explicit-any\n C extends new (state: DurableObjectState, env: any) => T = new (state: DurableObjectState, env: any) => T,\n O = unknown,\n>(optionsCallback: (env: ResolveEnv<C, Env>) => StrictCloudflareOptions<O>, 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","getInstrumented","markAsInstrumented","hasRpcMeta","isObjectLike","wrapMethodWithSentry","RPC","instrumentCloudflareAgent"],"mappings":"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"}
|
package/build/cjs/dev/index.js
CHANGED
|
@@ -2,16 +2,17 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
2
2
|
|
|
3
3
|
const core = require('@sentry/core');
|
|
4
4
|
const server = require('@sentry/core/server');
|
|
5
|
+
const serverUtils = require('@sentry/server-utils');
|
|
5
6
|
const withSentry = require('./withSentry.js');
|
|
6
7
|
const defineCloudflareOptions = require('./defineCloudflareOptions.js');
|
|
7
8
|
const durableobject = require('./durableobject.js');
|
|
9
|
+
const instrument = require('./instrument.js');
|
|
8
10
|
const pagesPlugin = require('./pages-plugin.js');
|
|
9
11
|
const client = require('./client.js');
|
|
10
12
|
const sdk = require('./sdk.js');
|
|
11
13
|
const httpServer = require('./integrations/httpServer.js');
|
|
12
14
|
const fetch = require('./integrations/fetch.js');
|
|
13
15
|
const spotlight = require('./integrations/spotlight.js');
|
|
14
|
-
const serverUtils = require('@sentry/server-utils');
|
|
15
16
|
const workflows = require('./workflows.js');
|
|
16
17
|
const noDiagnosticChannels = require('@sentry/server-utils/no-diagnostic-channels');
|
|
17
18
|
|
|
@@ -97,20 +98,8 @@ exports.withScope = core.withScope;
|
|
|
97
98
|
exports.withStaticSpan = core.withStaticSpan;
|
|
98
99
|
exports.withStreamedSpan = core.withStreamedSpan;
|
|
99
100
|
exports.zodErrorsIntegration = core.zodErrorsIntegration;
|
|
100
|
-
exports.instrumentPostgresJsSql = server.instrumentPostgresJsSql;
|
|
101
101
|
exports.trpcMiddleware = server.trpcMiddleware;
|
|
102
102
|
exports.wrapMcpServerWithSentry = server.wrapMcpServerWithSentry;
|
|
103
|
-
exports.withSentry = withSentry.withSentry;
|
|
104
|
-
exports.defineCloudflareOptions = defineCloudflareOptions.defineCloudflareOptions;
|
|
105
|
-
exports.instrumentAgentWithSentry = durableobject.instrumentAgentWithSentry;
|
|
106
|
-
exports.instrumentDurableObjectWithSentry = durableobject.instrumentDurableObjectWithSentry;
|
|
107
|
-
exports.sentryPagesPlugin = pagesPlugin.sentryPagesPlugin;
|
|
108
|
-
exports.CloudflareClient = client.CloudflareClient;
|
|
109
|
-
exports._INTERNAL_wrapRequestHandler = sdk._INTERNAL_wrapRequestHandler;
|
|
110
|
-
exports.getDefaultIntegrations = sdk.getDefaultIntegrations;
|
|
111
|
-
exports.httpServerIntegration = httpServer.httpServerIntegration;
|
|
112
|
-
exports.fetchIntegration = fetch.fetchIntegration;
|
|
113
|
-
exports.spotlightIntegration = spotlight.spotlightIntegration;
|
|
114
103
|
exports.createLangChainCallbackHandler = serverUtils.createLangChainCallbackHandler;
|
|
115
104
|
exports.getOtlpTracesEndpoint = serverUtils.getOtlpTracesEndpoint;
|
|
116
105
|
exports.instrumentAnthropicAiClient = serverUtils.instrumentAnthropicAiClient;
|
|
@@ -118,11 +107,24 @@ exports.instrumentCreateReactAgent = serverUtils.instrumentCreateReactAgent;
|
|
|
118
107
|
exports.instrumentGoogleGenAIClient = serverUtils.instrumentGoogleGenAIClient;
|
|
119
108
|
exports.instrumentLangChainEmbeddings = serverUtils.instrumentLangChainEmbeddings;
|
|
120
109
|
exports.instrumentOpenAiClient = serverUtils.instrumentOpenAiClient;
|
|
110
|
+
exports.instrumentPostgresJsSql = serverUtils.instrumentPostgresJsSql;
|
|
121
111
|
exports.instrumentStateGraph = serverUtils.instrumentStateGraph;
|
|
122
112
|
exports.instrumentWorkersAiClient = serverUtils.instrumentWorkersAiClient;
|
|
123
113
|
exports.openTelemetryIntegration = serverUtils.openTelemetryIntegration;
|
|
124
114
|
exports.prismaIntegration = serverUtils.prismaIntegration;
|
|
125
115
|
exports.vercelAIIntegration = serverUtils.vercelAIIntegration;
|
|
116
|
+
exports.withSentry = withSentry.withSentry;
|
|
117
|
+
exports.defineCloudflareOptions = defineCloudflareOptions.defineCloudflareOptions;
|
|
118
|
+
exports.instrumentAgentWithSentry = durableobject.instrumentAgentWithSentry;
|
|
119
|
+
exports.instrumentDurableObjectWithSentry = durableobject.instrumentDurableObjectWithSentry;
|
|
120
|
+
exports._INTERNAL_wrapUnlessInstrumented = instrument._INTERNAL_wrapUnlessInstrumented;
|
|
121
|
+
exports.sentryPagesPlugin = pagesPlugin.sentryPagesPlugin;
|
|
122
|
+
exports.CloudflareClient = client.CloudflareClient;
|
|
123
|
+
exports._INTERNAL_wrapRequestHandler = sdk._INTERNAL_wrapRequestHandler;
|
|
124
|
+
exports.getDefaultIntegrations = sdk.getDefaultIntegrations;
|
|
125
|
+
exports.httpServerIntegration = httpServer.httpServerIntegration;
|
|
126
|
+
exports.fetchIntegration = fetch.fetchIntegration;
|
|
127
|
+
exports.spotlightIntegration = spotlight.spotlightIntegration;
|
|
126
128
|
exports.instrumentWorkflowWithSentry = workflows.instrumentWorkflowWithSentry;
|
|
127
129
|
exports.setAsyncLocalStorageAsyncContextStrategy = noDiagnosticChannels.setAsyncLocalStorageAsyncContextStrategy;
|
|
128
130
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -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;;;;;;;"}
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
3
3
|
const attributes = require('@sentry/conventions/attributes');
|
|
4
4
|
const op = require('@sentry/conventions/op');
|
|
5
5
|
const core = require('@sentry/core');
|
|
6
|
-
const
|
|
6
|
+
const serverUtils = require('@sentry/server-utils');
|
|
7
7
|
const internalSqlQuery = require('../utils/internalSqlQuery.js');
|
|
8
8
|
|
|
9
9
|
function instrumentSqlStorage(sql) {
|
|
@@ -15,8 +15,8 @@ function instrumentSqlStorage(sql) {
|
|
|
15
15
|
}
|
|
16
16
|
return function(...args) {
|
|
17
17
|
const [query, ...bindings] = args;
|
|
18
|
-
const sanitizedQuery =
|
|
19
|
-
const querySummary =
|
|
18
|
+
const sanitizedQuery = serverUtils.sanitizeSqlQuery(query);
|
|
19
|
+
const querySummary = serverUtils.getSqlQuerySummary(sanitizedQuery);
|
|
20
20
|
const allowlist = core.getClient()?.getOptions()?.durableObjectSqlSpanAllowlist;
|
|
21
21
|
if (internalSqlQuery.targetsCloudflareInternalTable(querySummary, allowlist, sanitizedQuery)) {
|
|
22
22
|
return original.apply(target, args);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrumentSqlStorage.js","sources":["../../../../src/instrumentations/instrumentSqlStorage.ts"],"sourcesContent":["import type { SqlStorage } from '@cloudflare/workers-types';\nimport { SENTRY_OP } from '@sentry/conventions/attributes';\nimport { DB_QUERY } from '@sentry/conventions/op';\nimport { getClient, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startSpan } from '@sentry/core';\nimport {
|
|
1
|
+
{"version":3,"file":"instrumentSqlStorage.js","sources":["../../../../src/instrumentations/instrumentSqlStorage.ts"],"sourcesContent":["import type { SqlStorage } from '@cloudflare/workers-types';\nimport { SENTRY_OP } from '@sentry/conventions/attributes';\nimport { DB_QUERY } from '@sentry/conventions/op';\nimport { getClient, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startSpan } from '@sentry/core';\nimport { getSqlQuerySummary, sanitizeSqlQuery } from '@sentry/server-utils';\nimport type { CloudflareClientOptions } from '../client';\nimport { targetsCloudflareInternalTable } from '../utils/internalSqlQuery';\n\n/**\n * Instruments the Durable Object SqlStorage `exec` method with Sentry spans.\n *\n * @param sql - The SqlStorage instance to instrument\n * @returns An instrumented SqlStorage instance\n */\nexport function instrumentSqlStorage(sql: SqlStorage): SqlStorage {\n return new Proxy(sql, {\n get(target, prop, _receiver) {\n const original = Reflect.get(target, prop, target);\n\n if (prop !== 'exec' || typeof original !== 'function') {\n return original;\n }\n\n return function (this: unknown, ...args: unknown[]) {\n const [query, ...bindings] = args as [string, ...unknown[]];\n\n const sanitizedQuery = sanitizeSqlQuery(query);\n const querySummary = getSqlQuerySummary(sanitizedQuery);\n\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion -- rule false positive: the cast reaches the Cloudflare-only `durableObjectSqlSpanAllowlist`; tsc errors without it\n const allowlist = (getClient()?.getOptions() as CloudflareClientOptions | undefined)\n ?.durableObjectSqlSpanAllowlist;\n\n if (targetsCloudflareInternalTable(querySummary, allowlist, sanitizedQuery)) {\n return (original as (...a: unknown[]) => ReturnType<SqlStorage['exec']>).apply(target, args);\n }\n\n return startSpan(\n {\n name: querySummary || sanitizedQuery,\n attributes: {\n [SENTRY_OP]: DB_QUERY,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.db.cloudflare.durable_object.sql',\n 'db.system.name': 'cloudflare-durable-object-sql',\n 'db.operation.name': 'exec',\n 'db.query.text': sanitizedQuery,\n 'db.query.summary': querySummary,\n 'cloudflare.durable_object.query.bindings': bindings.length,\n },\n },\n () => (original as (...a: unknown[]) => ReturnType<SqlStorage['exec']>).apply(target, args),\n );\n };\n },\n });\n}\n"],"names":["sanitizeSqlQuery","getSqlQuerySummary","getClient","targetsCloudflareInternalTable","startSpan","SENTRY_OP","DB_QUERY","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN"],"mappings":";;;;;;;;AAcO,SAAS,qBAAqB,GAAA,EAA6B;AAChE,EAAA,OAAO,IAAI,MAAM,GAAA,EAAK;AAAA,IACpB,GAAA,CAAI,MAAA,EAAQ,IAAA,EAAM,SAAA,EAAW;AAC3B,MAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,GAAA,CAAI,MAAA,EAAQ,MAAM,MAAM,CAAA;AAEjD,MAAA,IAAI,IAAA,KAAS,MAAA,IAAU,OAAO,QAAA,KAAa,UAAA,EAAY;AACrD,QAAA,OAAO,QAAA;AAAA,MACT;AAEA,MAAA,OAAO,YAA4B,IAAA,EAAiB;AAClD,QAAA,MAAM,CAAC,KAAA,EAAO,GAAG,QAAQ,CAAA,GAAI,IAAA;AAE7B,QAAA,MAAM,cAAA,GAAiBA,6BAAiB,KAAK,CAAA;AAC7C,QAAA,MAAM,YAAA,GAAeC,+BAAmB,cAAc,CAAA;AAGtD,QAAA,MAAM,SAAA,GAAaC,cAAA,EAAU,EAAG,UAAA,EAAW,EACvC,6BAAA;AAEJ,QAAA,IAAIC,+CAAA,CAA+B,YAAA,EAAc,SAAA,EAAW,cAAc,CAAA,EAAG;AAC3E,UAAA,OAAQ,QAAA,CAAiE,KAAA,CAAM,MAAA,EAAQ,IAAI,CAAA;AAAA,QAC7F;AAEA,QAAA,OAAOC,cAAA;AAAA,UACL;AAAA,YACE,MAAM,YAAA,IAAgB,cAAA;AAAA,YACtB,UAAA,EAAY;AAAA,cACV,CAACC,oBAAS,GAAGC,WAAA;AAAA,cACb,CAACC,qCAAgC,GAAG,uCAAA;AAAA,cACpC,gBAAA,EAAkB,+BAAA;AAAA,cAClB,mBAAA,EAAqB,MAAA;AAAA,cACrB,eAAA,EAAiB,cAAA;AAAA,cACjB,kBAAA,EAAoB,YAAA;AAAA,cACpB,4CAA4C,QAAA,CAAS;AAAA;AACvD,WACF;AAAA,UACA,MAAO,QAAA,CAAiE,KAAA,CAAM,MAAA,EAAQ,IAAI;AAAA,SAC5F;AAAA,MACF,CAAA;AAAA,IACF;AAAA,GACD,CAAA;AACH;;;;"}
|
|
@@ -2,6 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
2
2
|
|
|
3
3
|
const op = require('@sentry/conventions/op');
|
|
4
4
|
const noDiagnosticChannels = require('@sentry/server-utils/no-diagnostic-channels');
|
|
5
|
+
const instrument = require('../instrument.js');
|
|
5
6
|
const options = require('../options.js');
|
|
6
7
|
const instrumentContext = require('../utils/instrumentContext.js');
|
|
7
8
|
const wrapMethodWithSentry = require('../wrapMethodWithSentry.js');
|
|
@@ -67,7 +68,7 @@ function instrumentMethod(instance, proxy, prop, method, options, context) {
|
|
|
67
68
|
}
|
|
68
69
|
function instrumentWorkerEntrypoint(optionsCallback, WorkerEntrypointClass) {
|
|
69
70
|
noDiagnosticChannels.setAsyncLocalStorageAsyncContextStrategy();
|
|
70
|
-
|
|
71
|
+
const InstrumentedClass = new Proxy(WorkerEntrypointClass, {
|
|
71
72
|
construct(target, [ctx, env]) {
|
|
72
73
|
const context = instrumentContext.instrumentContext(ctx);
|
|
73
74
|
const options$1 = options.getFinalOptions(optionsCallback(env), env);
|
|
@@ -126,6 +127,8 @@ function instrumentWorkerEntrypoint(optionsCallback, WorkerEntrypointClass) {
|
|
|
126
127
|
return proxy;
|
|
127
128
|
}
|
|
128
129
|
});
|
|
130
|
+
instrument.markAsInstrumented(InstrumentedClass);
|
|
131
|
+
return InstrumentedClass;
|
|
129
132
|
}
|
|
130
133
|
|
|
131
134
|
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 { RPC } from '@sentry/conventions/op';\nimport { setAsyncLocalStorageAsyncContextStrategy } from '@sentry/server-utils/no-diagnostic-channels';\nimport type { CloudflareOptions } from '../client';\nimport { getFinalOptions } from '../options';\nimport type { DefaultEnv, ResolveEnv, StrictCloudflareOptions } from '../types';\nimport { instrumentContext } from '../utils/instrumentContext';\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 return wrapMethodWithSentry(\n {\n options,\n context,\n spanName: rpcMeta => (rpcMeta ? prop : undefined),\n spanOp: RPC,\n origin: WORKER_ENTRYPOINT_ORIGIN,\n },\n boundMethod,\n undefined,\n true,\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 = DefaultEnv,\n Props = {},\n // oxlint-disable-next-line typescript/no-explicit-any\n T extends WorkerEntrypoint<any, any> = WorkerEntrypoint<Env, Props>,\n // oxlint-disable-next-line typescript/no-explicit-any\n C extends new (ctx: ExecutionContext, env: any) => T = new (ctx: ExecutionContext, env: any) => T,\n O = unknown,\n>(optionsCallback: (env: ResolveEnv<C, Env>) => StrictCloudflareOptions<O> | 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","RPC","setAsyncLocalStorageAsyncContextStrategy","instrumentContext","options","getFinalOptions","instrumentEnv","instrumentWorkerEntrypointFetch","instrumentWorkerEntrypointScheduled","instrumentWorkerEntrypointQueue","instrumentWorkerEntrypointTail"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"instrumentWorkerEntrypoint.js","sources":["../../../../src/instrumentations/instrumentWorkerEntrypoint.ts"],"sourcesContent":["import type { RpcStub, WorkerEntrypoint } from 'cloudflare:workers';\nimport { RPC } from '@sentry/conventions/op';\nimport { setAsyncLocalStorageAsyncContextStrategy } from '@sentry/server-utils/no-diagnostic-channels';\nimport type { CloudflareOptions } from '../client';\nimport { markAsInstrumented } from '../instrument';\nimport { getFinalOptions } from '../options';\nimport type { DefaultEnv, ResolveEnv, StrictCloudflareOptions } from '../types';\nimport { instrumentContext } from '../utils/instrumentContext';\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 return wrapMethodWithSentry(\n {\n options,\n context,\n spanName: rpcMeta => (rpcMeta ? prop : undefined),\n spanOp: RPC,\n origin: WORKER_ENTRYPOINT_ORIGIN,\n },\n boundMethod,\n undefined,\n true,\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 = DefaultEnv,\n Props = {},\n // oxlint-disable-next-line typescript/no-explicit-any\n T extends WorkerEntrypoint<any, any> = WorkerEntrypoint<Env, Props>,\n // oxlint-disable-next-line typescript/no-explicit-any\n C extends new (ctx: ExecutionContext, env: any) => T = new (ctx: ExecutionContext, env: any) => T,\n O = unknown,\n>(optionsCallback: (env: ResolveEnv<C, Env>) => StrictCloudflareOptions<O> | 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","RPC","setAsyncLocalStorageAsyncContextStrategy","instrumentContext","options","getFinalOptions","instrumentEnv","instrumentWorkerEntrypointFetch","instrumentWorkerEntrypointScheduled","instrumentWorkerEntrypointQueue","instrumentWorkerEntrypointTail","markAsInstrumented"],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
3
3
|
const attributes = require('@sentry/conventions/attributes');
|
|
4
4
|
const op = require('@sentry/conventions/op');
|
|
5
5
|
const core = require('@sentry/core');
|
|
6
|
-
const
|
|
6
|
+
const serverUtils = require('@sentry/server-utils');
|
|
7
7
|
const instrument = require('../../instrument.js');
|
|
8
8
|
|
|
9
9
|
const patchedStatement = /* @__PURE__ */ new WeakSet();
|
|
@@ -84,7 +84,7 @@ function createD1Breadcrumb(query, type, d1Result) {
|
|
|
84
84
|
});
|
|
85
85
|
}
|
|
86
86
|
function createStartSpanOptions(query, type) {
|
|
87
|
-
const querySummary = query ?
|
|
87
|
+
const querySummary = query ? serverUtils.getSqlQuerySummary(serverUtils.sanitizeSqlQuery(query)) : void 0;
|
|
88
88
|
const client = core.getClient();
|
|
89
89
|
const name = client && core.hasSpanStreamingEnabled(client) ? querySummary || "cloudflare-d1" : query;
|
|
90
90
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrumentD1.js","sources":["../../../../../src/instrumentations/worker/instrumentD1.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/unbound-method */\nimport type { D1Database, D1DatabaseSession, D1PreparedStatement, D1Response } from '@cloudflare/workers-types';\nimport { SENTRY_OP } from '@sentry/conventions/attributes';\nimport { DB_QUERY } from '@sentry/conventions/op';\nimport type { Span, SpanAttributes, StartSpanOptions } from '@sentry/core';\nimport {\n addBreadcrumb,\n getClient,\n hasSpanStreamingEnabled,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_STATUS_ERROR,\n startSpan,\n} from '@sentry/core';\nimport { _INTERNAL_getSqlQuerySummary, _INTERNAL_sanitizeSqlQuery } from '@sentry/core/server';\nimport { ensureInstrumented } from '../../instrument';\n\n// Patching is based on internal Cloudflare D1 API\n// https://github.com/cloudflare/workerd/blob/cd5279e7b305003f1d9c851e73efa9d67e4b68b2/src/cloudflare/internal/d1-api.ts\n\nconst patchedStatement = new WeakSet<D1PreparedStatement>();\n\n/**\n * Patches the query methods of a Cloudflare D1 prepared statement with Sentry.\n */\nfunction instrumentD1PreparedStatementQueries(statement: D1PreparedStatement, query: string): D1PreparedStatement {\n if (patchedStatement.has(statement)) {\n return statement;\n }\n\n // eslint-disable-next-line @typescript-eslint/unbound-method\n statement.first = new Proxy(statement.first, {\n apply(target, thisArg, args: Parameters<typeof statement.first>) {\n return startSpan(createStartSpanOptions(query, 'first'), async () => {\n const res = await Reflect.apply(target, thisArg, args);\n createD1Breadcrumb(query, 'first');\n return res;\n });\n },\n });\n\n // eslint-disable-next-line @typescript-eslint/unbound-method\n statement.run = new Proxy(statement.run, {\n apply(target, thisArg, args: Parameters<typeof statement.run>) {\n return startSpan(createStartSpanOptions(query, 'run'), async span => {\n const d1Response = await Reflect.apply(target, thisArg, args);\n applyD1ReturnObjectToSpan(span, d1Response);\n createD1Breadcrumb(query, 'run', d1Response);\n return d1Response;\n });\n },\n });\n\n // eslint-disable-next-line @typescript-eslint/unbound-method\n statement.all = new Proxy(statement.all, {\n apply(target, thisArg, args: Parameters<typeof statement.all>) {\n return startSpan(createStartSpanOptions(query, 'all'), async span => {\n const d1Result = await Reflect.apply(target, thisArg, args);\n applyD1ReturnObjectToSpan(span, d1Result);\n createD1Breadcrumb(query, 'all', d1Result);\n return d1Result;\n });\n },\n });\n\n // eslint-disable-next-line @typescript-eslint/unbound-method\n statement.raw = new Proxy(statement.raw, {\n apply(target, thisArg, args: Parameters<typeof statement.raw>) {\n return startSpan(createStartSpanOptions(query, 'raw'), async () => {\n const res = await Reflect.apply(target, thisArg, args);\n createD1Breadcrumb(query, 'raw');\n return res;\n });\n },\n });\n\n patchedStatement.add(statement);\n\n return statement;\n}\n\n/**\n * Instruments a Cloudflare D1 prepared statement with Sentry.\n *\n * This is meant to be used as a top-level call, under the hood it calls `instrumentD1PreparedStatementQueries`\n * to patch the query methods. The reason for this abstraction is to ensure that the `bind` method is also patched.\n */\nfunction instrumentD1PreparedStatement(statement: D1PreparedStatement, query: string): D1PreparedStatement {\n // statement.bind() returns a new instance of D1PreparedStatement, so we have to patch it as well.\n // eslint-disable-next-line @typescript-eslint/unbound-method\n statement.bind = new Proxy(statement.bind, {\n apply(target, thisArg, args: Parameters<typeof statement.bind>) {\n return instrumentD1PreparedStatementQueries(Reflect.apply(target, thisArg, args), query);\n },\n });\n\n return instrumentD1PreparedStatementQueries(statement, query);\n}\n\n/**\n * Add D1Response meta information to a span.\n *\n * See: https://developers.cloudflare.com/d1/build-with-d1/d1-client-api/#return-object\n */\nfunction applyD1ReturnObjectToSpan(span: Span, d1Result: D1Response): void {\n if (!d1Result.success) {\n span.setStatus({ code: SPAN_STATUS_ERROR });\n }\n\n span.setAttributes(getAttributesFromD1Response(d1Result));\n}\n\nfunction getAttributesFromD1Response(d1Result: D1Response): SpanAttributes {\n return {\n 'cloudflare.d1.duration': d1Result.meta.duration,\n 'cloudflare.d1.rows_read': d1Result.meta.rows_read,\n 'cloudflare.d1.rows_written': d1Result.meta.rows_written,\n };\n}\n\ntype D1QueryType = 'first' | 'run' | 'all' | 'raw' | 'batch' | 'exec';\n\nfunction createD1Breadcrumb(query: string, type: D1QueryType, d1Result?: D1Response): void {\n addBreadcrumb({\n category: 'query',\n message: query,\n data: {\n ...(d1Result ? getAttributesFromD1Response(d1Result) : {}),\n 'db.operation.name': type,\n },\n });\n}\n\nfunction createStartSpanOptions(query: string, type: D1QueryType): StartSpanOptions {\n const querySummary = query ? _INTERNAL_getSqlQuerySummary(_INTERNAL_sanitizeSqlQuery(query)) : undefined;\n\n const client = getClient();\n const name = client && hasSpanStreamingEnabled(client) ? querySummary || 'cloudflare-d1' : query;\n\n return {\n name,\n attributes: {\n [SENTRY_OP]: DB_QUERY,\n 'db.system.name': 'cloudflare-d1',\n 'db.operation.name': type,\n 'db.query.text': query,\n 'db.query.summary': querySummary,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.db.cloudflare.d1',\n },\n };\n}\n\nfunction instrumentPrepare(\n prepare: D1Database['prepare'] | D1DatabaseSession['prepare'],\n): D1Database['prepare'] | D1DatabaseSession['prepare'] {\n return new Proxy(prepare, {\n apply(target, thisArg, args: Parameters<typeof prepare>) {\n const [query] = args;\n return instrumentD1PreparedStatement(Reflect.apply(target, thisArg, args), query);\n },\n });\n}\n\nfunction instrumentBatch(\n batch: D1Database['batch'] | D1DatabaseSession['batch'],\n): D1Database['batch'] | D1DatabaseSession['batch'] {\n return new Proxy(batch, {\n apply(target, thisArg, args: Parameters<typeof batch>) {\n const statements = args[0];\n // D1PreparedStatement exposes a `statement` property at runtime, but it's not in @cloudflare/workers-types.\n // https://github.com/cloudflare/workerd/blob/dc12d7650b4f5d4f9ba6a47aa45fad769cdf8db4/src/cloudflare/internal/d1-api.ts#L210\n const queryText = statements\n .map(statement => (statement as unknown as { statement?: string }).statement ?? '')\n .filter(Boolean)\n .join('\\n');\n\n return startSpan(\n {\n name: 'D1 batch',\n attributes: {\n [SENTRY_OP]: DB_QUERY,\n 'db.system.name': 'cloudflare-d1',\n 'db.operation.name': 'batch',\n 'db.query.text': queryText || undefined,\n 'db.operation.batch.size': statements.length,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.db.cloudflare.d1',\n },\n },\n async () => {\n const res = await Reflect.apply(target, thisArg, args);\n createD1Breadcrumb('D1 batch', 'batch');\n return res;\n },\n );\n },\n });\n}\n\nfunction instrumentD1Session(session: D1DatabaseSession): D1DatabaseSession {\n session.prepare = instrumentPrepare(session.prepare);\n session.batch = instrumentBatch(session.batch);\n return session;\n}\n\nfunction _instrumentD1(db: D1Database): D1Database {\n db.prepare = instrumentPrepare(db.prepare);\n db.batch = instrumentBatch(db.batch);\n\n db.exec = new Proxy(db.exec, {\n apply(target, thisArg, args: Parameters<typeof db.exec>) {\n const [query] = args;\n return startSpan(createStartSpanOptions(query, 'exec'), async () => {\n const res = await Reflect.apply(target, thisArg, args);\n createD1Breadcrumb(query, 'exec');\n return res;\n });\n },\n });\n\n if ('withSession' in db && typeof db.withSession === 'function') {\n db.withSession = new Proxy(db.withSession, {\n apply(target, thisArg, args: [unknown]) {\n return instrumentD1Session(Reflect.apply(target, thisArg, args) as D1DatabaseSession);\n },\n });\n }\n\n return db;\n}\n\nexport function instrumentD1(db: D1Database): D1Database {\n return ensureInstrumented(db, _instrumentD1);\n}\n"],"names":["startSpan","SPAN_STATUS_ERROR","addBreadcrumb","_INTERNAL_getSqlQuerySummary","_INTERNAL_sanitizeSqlQuery","getClient","hasSpanStreamingEnabled","SENTRY_OP","DB_QUERY","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","ensureInstrumented"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"instrumentD1.js","sources":["../../../../../src/instrumentations/worker/instrumentD1.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/unbound-method */\nimport type { D1Database, D1DatabaseSession, D1PreparedStatement, D1Response } from '@cloudflare/workers-types';\nimport { SENTRY_OP } from '@sentry/conventions/attributes';\nimport { DB_QUERY } from '@sentry/conventions/op';\nimport type { Span, SpanAttributes, StartSpanOptions } from '@sentry/core';\nimport {\n addBreadcrumb,\n getClient,\n hasSpanStreamingEnabled,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_STATUS_ERROR,\n startSpan,\n} from '@sentry/core';\nimport { getSqlQuerySummary, sanitizeSqlQuery } from '@sentry/server-utils';\nimport { ensureInstrumented } from '../../instrument';\n\n// Patching is based on internal Cloudflare D1 API\n// https://github.com/cloudflare/workerd/blob/cd5279e7b305003f1d9c851e73efa9d67e4b68b2/src/cloudflare/internal/d1-api.ts\n\nconst patchedStatement = new WeakSet<D1PreparedStatement>();\n\n/**\n * Patches the query methods of a Cloudflare D1 prepared statement with Sentry.\n */\nfunction instrumentD1PreparedStatementQueries(statement: D1PreparedStatement, query: string): D1PreparedStatement {\n if (patchedStatement.has(statement)) {\n return statement;\n }\n\n // eslint-disable-next-line @typescript-eslint/unbound-method\n statement.first = new Proxy(statement.first, {\n apply(target, thisArg, args: Parameters<typeof statement.first>) {\n return startSpan(createStartSpanOptions(query, 'first'), async () => {\n const res = await Reflect.apply(target, thisArg, args);\n createD1Breadcrumb(query, 'first');\n return res;\n });\n },\n });\n\n // eslint-disable-next-line @typescript-eslint/unbound-method\n statement.run = new Proxy(statement.run, {\n apply(target, thisArg, args: Parameters<typeof statement.run>) {\n return startSpan(createStartSpanOptions(query, 'run'), async span => {\n const d1Response = await Reflect.apply(target, thisArg, args);\n applyD1ReturnObjectToSpan(span, d1Response);\n createD1Breadcrumb(query, 'run', d1Response);\n return d1Response;\n });\n },\n });\n\n // eslint-disable-next-line @typescript-eslint/unbound-method\n statement.all = new Proxy(statement.all, {\n apply(target, thisArg, args: Parameters<typeof statement.all>) {\n return startSpan(createStartSpanOptions(query, 'all'), async span => {\n const d1Result = await Reflect.apply(target, thisArg, args);\n applyD1ReturnObjectToSpan(span, d1Result);\n createD1Breadcrumb(query, 'all', d1Result);\n return d1Result;\n });\n },\n });\n\n // eslint-disable-next-line @typescript-eslint/unbound-method\n statement.raw = new Proxy(statement.raw, {\n apply(target, thisArg, args: Parameters<typeof statement.raw>) {\n return startSpan(createStartSpanOptions(query, 'raw'), async () => {\n const res = await Reflect.apply(target, thisArg, args);\n createD1Breadcrumb(query, 'raw');\n return res;\n });\n },\n });\n\n patchedStatement.add(statement);\n\n return statement;\n}\n\n/**\n * Instruments a Cloudflare D1 prepared statement with Sentry.\n *\n * This is meant to be used as a top-level call, under the hood it calls `instrumentD1PreparedStatementQueries`\n * to patch the query methods. The reason for this abstraction is to ensure that the `bind` method is also patched.\n */\nfunction instrumentD1PreparedStatement(statement: D1PreparedStatement, query: string): D1PreparedStatement {\n // statement.bind() returns a new instance of D1PreparedStatement, so we have to patch it as well.\n // eslint-disable-next-line @typescript-eslint/unbound-method\n statement.bind = new Proxy(statement.bind, {\n apply(target, thisArg, args: Parameters<typeof statement.bind>) {\n return instrumentD1PreparedStatementQueries(Reflect.apply(target, thisArg, args), query);\n },\n });\n\n return instrumentD1PreparedStatementQueries(statement, query);\n}\n\n/**\n * Add D1Response meta information to a span.\n *\n * See: https://developers.cloudflare.com/d1/build-with-d1/d1-client-api/#return-object\n */\nfunction applyD1ReturnObjectToSpan(span: Span, d1Result: D1Response): void {\n if (!d1Result.success) {\n span.setStatus({ code: SPAN_STATUS_ERROR });\n }\n\n span.setAttributes(getAttributesFromD1Response(d1Result));\n}\n\nfunction getAttributesFromD1Response(d1Result: D1Response): SpanAttributes {\n return {\n 'cloudflare.d1.duration': d1Result.meta.duration,\n 'cloudflare.d1.rows_read': d1Result.meta.rows_read,\n 'cloudflare.d1.rows_written': d1Result.meta.rows_written,\n };\n}\n\ntype D1QueryType = 'first' | 'run' | 'all' | 'raw' | 'batch' | 'exec';\n\nfunction createD1Breadcrumb(query: string, type: D1QueryType, d1Result?: D1Response): void {\n addBreadcrumb({\n category: 'query',\n message: query,\n data: {\n ...(d1Result ? getAttributesFromD1Response(d1Result) : {}),\n 'db.operation.name': type,\n },\n });\n}\n\nfunction createStartSpanOptions(query: string, type: D1QueryType): StartSpanOptions {\n const querySummary = query ? getSqlQuerySummary(sanitizeSqlQuery(query)) : undefined;\n\n const client = getClient();\n const name = client && hasSpanStreamingEnabled(client) ? querySummary || 'cloudflare-d1' : query;\n\n return {\n name,\n attributes: {\n [SENTRY_OP]: DB_QUERY,\n 'db.system.name': 'cloudflare-d1',\n 'db.operation.name': type,\n 'db.query.text': query,\n 'db.query.summary': querySummary,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.db.cloudflare.d1',\n },\n };\n}\n\nfunction instrumentPrepare(\n prepare: D1Database['prepare'] | D1DatabaseSession['prepare'],\n): D1Database['prepare'] | D1DatabaseSession['prepare'] {\n return new Proxy(prepare, {\n apply(target, thisArg, args: Parameters<typeof prepare>) {\n const [query] = args;\n return instrumentD1PreparedStatement(Reflect.apply(target, thisArg, args), query);\n },\n });\n}\n\nfunction instrumentBatch(\n batch: D1Database['batch'] | D1DatabaseSession['batch'],\n): D1Database['batch'] | D1DatabaseSession['batch'] {\n return new Proxy(batch, {\n apply(target, thisArg, args: Parameters<typeof batch>) {\n const statements = args[0];\n // D1PreparedStatement exposes a `statement` property at runtime, but it's not in @cloudflare/workers-types.\n // https://github.com/cloudflare/workerd/blob/dc12d7650b4f5d4f9ba6a47aa45fad769cdf8db4/src/cloudflare/internal/d1-api.ts#L210\n const queryText = statements\n .map(statement => (statement as unknown as { statement?: string }).statement ?? '')\n .filter(Boolean)\n .join('\\n');\n\n return startSpan(\n {\n name: 'D1 batch',\n attributes: {\n [SENTRY_OP]: DB_QUERY,\n 'db.system.name': 'cloudflare-d1',\n 'db.operation.name': 'batch',\n 'db.query.text': queryText || undefined,\n 'db.operation.batch.size': statements.length,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.db.cloudflare.d1',\n },\n },\n async () => {\n const res = await Reflect.apply(target, thisArg, args);\n createD1Breadcrumb('D1 batch', 'batch');\n return res;\n },\n );\n },\n });\n}\n\nfunction instrumentD1Session(session: D1DatabaseSession): D1DatabaseSession {\n session.prepare = instrumentPrepare(session.prepare);\n session.batch = instrumentBatch(session.batch);\n return session;\n}\n\nfunction _instrumentD1(db: D1Database): D1Database {\n db.prepare = instrumentPrepare(db.prepare);\n db.batch = instrumentBatch(db.batch);\n\n db.exec = new Proxy(db.exec, {\n apply(target, thisArg, args: Parameters<typeof db.exec>) {\n const [query] = args;\n return startSpan(createStartSpanOptions(query, 'exec'), async () => {\n const res = await Reflect.apply(target, thisArg, args);\n createD1Breadcrumb(query, 'exec');\n return res;\n });\n },\n });\n\n if ('withSession' in db && typeof db.withSession === 'function') {\n db.withSession = new Proxy(db.withSession, {\n apply(target, thisArg, args: [unknown]) {\n return instrumentD1Session(Reflect.apply(target, thisArg, args) as D1DatabaseSession);\n },\n });\n }\n\n return db;\n}\n\nexport function instrumentD1(db: D1Database): D1Database {\n return ensureInstrumented(db, _instrumentD1);\n}\n"],"names":["startSpan","SPAN_STATUS_ERROR","addBreadcrumb","getSqlQuerySummary","sanitizeSqlQuery","getClient","hasSpanStreamingEnabled","SENTRY_OP","DB_QUERY","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","ensureInstrumented"],"mappings":"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"}
|
|
@@ -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
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agentClass.js","sources":["../../../../src/vite/agentClass.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { DEFAULT_EXPORT, type ModuleShape, shapeFromAst, shapeFromSource, type SuperRef } from './moduleShape';\nimport type { ProgramBody } from './transform';\n\n/**\n * Agent base classes, keyed by the module specifier they are imported from. A class extending any\n * of these — directly or through a chain of local/imported subclasses — is an `agents` Agent rather\n * than a plain Durable Object.\n *\n * `McpAgent`, `AIChatAgent` and `Think` all extend `Agent` themselves, but they live in packages\n * whose sources we never walk into (see {@link followImport}), so each needs its own entry.\n */\nconst AGENT_BASE_CLASSES: Record<string, ReadonlySet<string>> = {\n agents: new Set(['Agent']),\n 'agents/mcp': new Set(['McpAgent']),\n '@cloudflare/ai-chat': new Set(['AIChatAgent']),\n '@cloudflare/think': new Set(['Think']),\n};\n\n/**\n * How many modules deep the base-class chain is followed. Guards against pathological module graphs;\n * real Agent hierarchies are only a few levels deep.\n */\nconst MAX_DEPTH = 8;\n\n/**\n * The subset of the Vite/Rollup plugin context needed to walk the module graph.\n *\n * `resolve` is optional: without it detection still works, but only for base-class chains declared\n * inside the entry module itself. `readFile` is injectable for tests and defaults to reading disk.\n *\n * Deliberately **no `load`**: awaiting the plugin context's `load()` from inside a `transform` hook\n * deadlocks the build — the module cannot finish loading while its own transform is still pending\n * (Rollup documents this hazard, and Vite's Rolldown pipeline is stricter still). Sibling modules\n * are therefore read straight off disk; see {@link getModuleShape}.\n */\nexport interface ModuleResolver {\n parse(code: string): ProgramBody;\n resolve?(source: string, importer: string): Promise<{ id: string } | null | undefined>;\n readFile?(id: string): string | undefined;\n}\n\ninterface DetectContext {\n resolver: ModuleResolver;\n modules: Map<string, ModuleShape | undefined>;\n}\n\n/**\n * Find which of `candidates` (top-level class names in the entry module) are Cloudflare\n * [`agents`](https://www.npmjs.com/package/agents) Agents.\n *\n * An Agent is a Durable Object under the hood, so wrangler configures it as one and the transform\n * would otherwise reach for `instrumentDurableObjectWithSentry`. Detecting the Agent base chain lets\n * it pick `instrumentAgentWithSentry` instead, which adds the Agent-specific telemetry on top.\n *\n * Unlike `WorkerEntrypoint` detection, the chain is followed *across* modules: base classes commonly\n * live in their own file (`import { MyBase } from './base'`), and there is no runtime fallback that\n * would catch a missed one. Resolution stops at package boundaries — a base class imported from\n * `node_modules` is only recognized when it is one of {@link AGENT_BASE_CLASSES}.\n */\nexport async function detectAgentClasses(\n ast: ProgramBody,\n entryId: string,\n candidates: Iterable<string>,\n resolver: ModuleResolver,\n): Promise<Set<string>> {\n // The entry's shape comes from the AST Vite already handed us (TypeScript stripped, full\n // fidelity); only sibling modules fall back to source scanning.\n const ctx: DetectContext = { resolver, modules: new Map([[entryId, shapeFromAst(ast)]]) };\n\n const detected = new Set<string>();\n for (const name of candidates) {\n // A fresh cycle guard per candidate: a shared one would record \"not an Agent\" for bindings\n // visited while resolving an earlier candidate that turned out to be unrelated.\n if (await isAgentBinding(ctx, entryId, name, 0, new Set())) {\n detected.add(name);\n }\n }\n return detected;\n}\n\n/**\n * The local class names in the entry that a configured class name could refer to — either declared\n * under that name directly, or aliased to it by an `export { Local as Configured }` specifier.\n *\n * Keeps detection (which reads and scans other modules) off classes that no binding points at.\n */\nexport function collectAgentCandidates(ast: ProgramBody, configuredNames: Iterable<string>): Set<string> {\n const shape = shapeFromAst(ast);\n const candidates = new Set<string>();\n\n for (const configured of configuredNames) {\n if (shape.classes.has(configured)) {\n candidates.add(configured);\n }\n const local = shape.localExports.get(configured);\n if (local && shape.classes.has(local)) {\n candidates.add(local);\n }\n }\n\n return candidates;\n}\n\nasync function isAgentBinding(\n ctx: DetectContext,\n moduleId: string,\n name: string,\n depth: number,\n visited: Set<string>,\n): Promise<boolean> {\n if (depth > MAX_DEPTH) return false;\n\n const key = `${moduleId} ${name}`;\n if (visited.has(key)) return false;\n visited.add(key);\n\n const shape = await getModuleShape(ctx, moduleId);\n if (!shape) return false;\n\n if (shape.classes.has(name)) {\n return extendsAgent(ctx, shape, moduleId, shape.classes.get(name), depth, visited);\n }\n\n const imported = shape.imports.get(name);\n if (imported) {\n return followImport(ctx, moduleId, imported.source, imported.imported, depth, visited);\n }\n\n if (name === DEFAULT_EXPORT) {\n if (shape.defaultExportIsClass) {\n return extendsAgent(ctx, shape, moduleId, shape.defaultExportSuper, depth, visited);\n }\n if (shape.defaultExportName) {\n return isAgentBinding(ctx, moduleId, shape.defaultExportName, depth + 1, visited);\n }\n }\n\n // `export { Local as Requested }` — the caller asked by exported name.\n const localName = shape.localExports.get(name);\n if (localName) {\n return isAgentBinding(ctx, moduleId, localName, depth + 1, visited);\n }\n\n const reexport = shape.reexports.get(name);\n if (reexport) {\n return followImport(ctx, moduleId, reexport.source, reexport.imported, depth, visited);\n }\n\n // Barrel modules (`export * from './base'`) don't name the binding, so every star source is a\n // candidate home for it.\n for (const source of shape.starExports) {\n if (await followImport(ctx, moduleId, source, name, depth, visited)) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Whether a superclass reference resolves to an Agent base — a bare identifier (imported, or a\n * subclass declared in the same module) or `ns.Agent` off a namespace import.\n */\nasync function extendsAgent(\n ctx: DetectContext,\n shape: ModuleShape,\n moduleId: string,\n superClass: SuperRef | undefined,\n depth: number,\n visited: Set<string>,\n): Promise<boolean> {\n if (!superClass) return false;\n\n if (superClass.kind === 'identifier') {\n return isAgentBinding(ctx, moduleId, superClass.name, depth + 1, visited);\n }\n\n const source = shape.namespaces.get(superClass.object);\n if (source) {\n return followImport(ctx, moduleId, source, superClass.property, depth, visited);\n }\n\n return false;\n}\n\n/** Resolve an import to its module and continue the search there, stopping at package boundaries. */\nasync function followImport(\n ctx: DetectContext,\n importerId: string,\n source: string,\n importedName: string,\n depth: number,\n visited: Set<string>,\n): Promise<boolean> {\n if (AGENT_BASE_CLASSES[source]?.has(importedName)) return true;\n\n if (!ctx.resolver.resolve) return false;\n\n let resolved: { id: string } | null | undefined;\n try {\n resolved = await ctx.resolver.resolve(source, importerId);\n } catch {\n return false;\n }\n if (!resolved?.id) return false;\n\n // Third-party sources are not walked: they are large, may not be plain JS by the time we see\n // them, and any Agent base worth knowing about is listed in AGENT_BASE_CLASSES.\n if (resolved.id.includes('/node_modules/') || resolved.id.includes('\\\\node_modules\\\\')) return false;\n\n return isAgentBinding(ctx, resolved.id, importedName, depth + 1, visited);\n}\n\nasync function getModuleShape(ctx: DetectContext, moduleId: string): Promise<ModuleShape | undefined> {\n if (ctx.modules.has(moduleId)) return ctx.modules.get(moduleId);\n\n let shape: ModuleShape | undefined;\n try {\n const code = ctx.resolver.readFile\n ? ctx.resolver.readFile(moduleId)\n : readFileSync(moduleId.replace(/[?#].*$/, ''), 'utf8');\n if (typeof code === 'string') {\n shape = shapeFromSource(code);\n }\n } catch {\n // Unreadable or not a source file — treat as \"not an Agent\" rather than failing the build.\n }\n\n ctx.modules.set(moduleId, shape);\n return shape;\n}\n"],"names":["shapeFromAst","DEFAULT_EXPORT","readFileSync","shapeFromSource"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"agentClass.js","sources":["../../../../src/vite/agentClass.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { DEFAULT_EXPORT, type ModuleShape, shapeFromAst, shapeFromSource, type SuperRef } from './moduleShape';\nimport type { ProgramBody } from './transform';\n\n/**\n * Agent base classes, keyed by the module specifier they are imported from. A class extending any\n * of these — directly or through a chain of local/imported subclasses — is an `agents` Agent rather\n * than a plain Durable Object.\n *\n * `McpAgent`, `AIChatAgent` and `Think` all extend `Agent` themselves, but they live in packages\n * whose sources we never walk into (see {@link followImport}), so each needs its own entry.\n */\nconst AGENT_BASE_CLASSES: Record<string, ReadonlySet<string>> = {\n agents: new Set(['Agent']),\n 'agents/mcp': new Set(['McpAgent']),\n '@cloudflare/ai-chat': new Set(['AIChatAgent']),\n '@cloudflare/think': new Set(['Think']),\n};\n\n/**\n * How many modules deep the base-class chain is followed. Guards against pathological module graphs;\n * real Agent hierarchies are only a few levels deep.\n */\nconst MAX_DEPTH = 8;\n\n/**\n * The subset of the Vite/Rollup plugin context needed to walk the module graph.\n *\n * `resolve` is optional: without it detection still works, but only for base-class chains declared\n * inside the entry module itself. `readFile` is injectable for tests and defaults to reading disk.\n *\n * Deliberately **no `load`**: awaiting the plugin context's `load()` from inside a `transform` hook\n * deadlocks the build — the module cannot finish loading while its own transform is still pending\n * (Rollup documents this hazard, and Vite's Rolldown pipeline is stricter still). Sibling modules\n * are therefore read straight off disk; see {@link getModuleShape}.\n */\nexport interface ModuleResolver {\n parse(code: string): ProgramBody;\n resolve?(source: string, importer: string): Promise<{ id: string } | null | undefined>;\n readFile?(id: string): string | undefined;\n}\n\ninterface DetectContext {\n resolver: ModuleResolver;\n modules: Map<string, ModuleShape | undefined>;\n}\n\n/**\n * Find which of `candidates` (top-level class names in the entry module) are Cloudflare\n * [`agents`](https://www.npmjs.com/package/agents) Agents.\n *\n * An Agent is a Durable Object under the hood, so wrangler configures it as one and the transform\n * would otherwise reach for `instrumentDurableObjectWithSentry`. Detecting the Agent base chain lets\n * it pick `instrumentAgentWithSentry` instead, which adds the Agent-specific telemetry on top.\n *\n * Unlike `WorkerEntrypoint` detection, the chain is followed *across* modules: base classes commonly\n * live in their own file (`import { MyBase } from './base'`), and there is no runtime fallback that\n * would catch a missed one. Resolution stops at package boundaries — a base class imported from\n * `node_modules` is only recognized when it is one of {@link AGENT_BASE_CLASSES}.\n */\nexport async function detectAgentClasses(\n ast: ProgramBody,\n entryId: string,\n candidates: Iterable<string>,\n resolver: ModuleResolver,\n): Promise<Set<string>> {\n // The entry's shape comes from the AST Vite already handed us (TypeScript stripped, full\n // fidelity); only sibling modules fall back to source scanning.\n const ctx: DetectContext = { resolver, modules: new Map([[entryId, shapeFromAst(ast)]]) };\n\n const detected = new Set<string>();\n for (const name of candidates) {\n // A fresh cycle guard per candidate: a shared one would record \"not an Agent\" for bindings\n // visited while resolving an earlier candidate that turned out to be unrelated.\n if (await isAgentBinding(ctx, entryId, name, 0, new Set())) {\n detected.add(name);\n }\n }\n return detected;\n}\n\n/**\n * The entry-module binding names a configured class name could refer to — a class declared under\n * that name, an import of it from another module, a `export { X } from './x'` re-export, or the\n * local binding an `export { Local as Configured }` specifier aliases.\n *\n * Keeps detection (which reads and scans other modules) off names no binding points at.\n */\nexport function collectAgentCandidates(ast: ProgramBody, configuredNames: Iterable<string>): Set<string> {\n const shape = shapeFromAst(ast);\n const candidates = new Set<string>();\n\n const isResolvable = (name: string): boolean =>\n shape.classes.has(name) || shape.imports.has(name) || shape.reexports.has(name);\n\n for (const configured of configuredNames) {\n if (isResolvable(configured)) {\n candidates.add(configured);\n }\n const local = shape.localExports.get(configured);\n if (local && isResolvable(local)) {\n candidates.add(local);\n }\n }\n\n return candidates;\n}\n\nasync function isAgentBinding(\n ctx: DetectContext,\n moduleId: string,\n name: string,\n depth: number,\n visited: Set<string>,\n): Promise<boolean> {\n if (depth > MAX_DEPTH) return false;\n\n const key = `${moduleId} ${name}`;\n if (visited.has(key)) return false;\n visited.add(key);\n\n const shape = await getModuleShape(ctx, moduleId);\n if (!shape) return false;\n\n if (shape.classes.has(name)) {\n return extendsAgent(ctx, shape, moduleId, shape.classes.get(name), depth, visited);\n }\n\n const imported = shape.imports.get(name);\n if (imported) {\n return followImport(ctx, moduleId, imported.source, imported.imported, depth, visited);\n }\n\n if (name === DEFAULT_EXPORT) {\n if (shape.defaultExportIsClass) {\n return extendsAgent(ctx, shape, moduleId, shape.defaultExportSuper, depth, visited);\n }\n if (shape.defaultExportName) {\n return isAgentBinding(ctx, moduleId, shape.defaultExportName, depth + 1, visited);\n }\n }\n\n // `export { Local as Requested }` — the caller asked by exported name.\n const localName = shape.localExports.get(name);\n if (localName) {\n return isAgentBinding(ctx, moduleId, localName, depth + 1, visited);\n }\n\n const reexport = shape.reexports.get(name);\n if (reexport) {\n return followImport(ctx, moduleId, reexport.source, reexport.imported, depth, visited);\n }\n\n // Barrel modules (`export * from './base'`) don't name the binding, so every star source is a\n // candidate home for it.\n for (const source of shape.starExports) {\n if (await followImport(ctx, moduleId, source, name, depth, visited)) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Whether a superclass reference resolves to an Agent base — a bare identifier (imported, or a\n * subclass declared in the same module) or `ns.Agent` off a namespace import.\n */\nasync function extendsAgent(\n ctx: DetectContext,\n shape: ModuleShape,\n moduleId: string,\n superClass: SuperRef | undefined,\n depth: number,\n visited: Set<string>,\n): Promise<boolean> {\n if (!superClass) return false;\n\n if (superClass.kind === 'identifier') {\n return isAgentBinding(ctx, moduleId, superClass.name, depth + 1, visited);\n }\n\n const source = shape.namespaces.get(superClass.object);\n if (source) {\n return followImport(ctx, moduleId, source, superClass.property, depth, visited);\n }\n\n return false;\n}\n\n/** Resolve an import to its module and continue the search there, stopping at package boundaries. */\nasync function followImport(\n ctx: DetectContext,\n importerId: string,\n source: string,\n importedName: string,\n depth: number,\n visited: Set<string>,\n): Promise<boolean> {\n if (AGENT_BASE_CLASSES[source]?.has(importedName)) return true;\n\n if (!ctx.resolver.resolve) return false;\n\n let resolved: { id: string } | null | undefined;\n try {\n resolved = await ctx.resolver.resolve(source, importerId);\n } catch {\n return false;\n }\n if (!resolved?.id) return false;\n\n // Third-party sources are not walked: they are large, may not be plain JS by the time we see\n // them, and any Agent base worth knowing about is listed in AGENT_BASE_CLASSES.\n if (resolved.id.includes('/node_modules/') || resolved.id.includes('\\\\node_modules\\\\')) return false;\n\n return isAgentBinding(ctx, resolved.id, importedName, depth + 1, visited);\n}\n\nasync function getModuleShape(ctx: DetectContext, moduleId: string): Promise<ModuleShape | undefined> {\n if (ctx.modules.has(moduleId)) return ctx.modules.get(moduleId);\n\n let shape: ModuleShape | undefined;\n try {\n const code = ctx.resolver.readFile\n ? ctx.resolver.readFile(moduleId)\n : readFileSync(moduleId.replace(/[?#].*$/, ''), 'utf8');\n if (typeof code === 'string') {\n shape = shapeFromSource(code);\n }\n } catch {\n // Unreadable or not a source file — treat as \"not an Agent\" rather than failing the build.\n }\n\n ctx.modules.set(moduleId, shape);\n return shape;\n}\n"],"names":["shapeFromAst","DEFAULT_EXPORT","readFileSync","shapeFromSource"],"mappings":"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"}
|
|
@@ -83,7 +83,7 @@ function sentryCloudflareAutoInstrumentPlugin(options = {}) {
|
|
|
83
83
|
const missing = [...classWrappers.keys()].filter((name) => !wrappedClasses.has(name));
|
|
84
84
|
if (missing.length > 0) {
|
|
85
85
|
this.warn?.(
|
|
86
|
-
`[sentry] Could not auto-instrument
|
|
86
|
+
`[sentry] Could not auto-instrument ${missing.join(", ")}.The worker entry has no export matching them. Star re-exports (\`export * from "./do"\`) are not matched. Export them by name or wrap them manually with corresponding \`instrument*WithSentry\` helpers.`
|
|
87
87
|
);
|
|
88
88
|
}
|
|
89
89
|
return result ?? void 0;
|