Skip to content

Commit 224a1d6

Browse files
author
cod1k
committed
Integrate copyExecutionContext across handlers and workflows
Replaced direct usage of `ExecutionContext` with `copyExecutionContext` for consistency and improved flexibility. Applied changes across handlers, durable objects, and workflows to enhance context management and method overriding.
1 parent 7121b65 commit 224a1d6

File tree

4 files changed

+38
-14
lines changed

4 files changed

+38
-14
lines changed

packages/cloudflare/src/durableobject.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { isInstrumented, markAsInstrumented } from './instrument';
1818
import { getFinalOptions } from './options';
1919
import { wrapRequestHandler } from './request';
2020
import { init } from './sdk';
21+
import { copyExecutionContext } from './utils/copyExecutionContext';
2122

2223
type MethodWrapperOptions = {
2324
spanName?: string;
@@ -192,8 +193,9 @@ export function instrumentDurableObjectWithSentry<
192193
C extends new (state: DurableObjectState, env: E) => T,
193194
>(optionsCallback: (env: E) => CloudflareOptions, DurableObjectClass: C): C {
194195
return new Proxy(DurableObjectClass, {
195-
construct(target, [context, env]) {
196+
construct(target, [ctx, env]) {
196197
setAsyncLocalStorageAsyncContextStrategy();
198+
const context = copyExecutionContext(ctx);
197199

198200
const options = getFinalOptions(optionsCallback(env), env);
199201

packages/cloudflare/src/handler.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { getFinalOptions } from './options';
1515
import { wrapRequestHandler } from './request';
1616
import { addCloudResourceContext } from './scope-utils';
1717
import { init } from './sdk';
18+
import { copyExecutionContext } from './utils/copyExecutionContext';
1819

1920
/**
2021
* Wrapper for Cloudflare handlers.
@@ -38,7 +39,9 @@ export function withSentry<Env = unknown, QueueHandlerMessage = unknown, CfHostM
3839
if ('fetch' in handler && typeof handler.fetch === 'function' && !isInstrumented(handler.fetch)) {
3940
handler.fetch = new Proxy(handler.fetch, {
4041
apply(target, thisArg, args: Parameters<ExportedHandlerFetchHandler<Env, CfHostMetadata>>) {
41-
const [request, env, context] = args;
42+
const [request, env, ctx] = args;
43+
const context = copyExecutionContext(ctx);
44+
args[2] = context;
4245

4346
const options = getFinalOptions(optionsCallback(env), env);
4447

@@ -72,7 +75,10 @@ export function withSentry<Env = unknown, QueueHandlerMessage = unknown, CfHostM
7275
if ('scheduled' in handler && typeof handler.scheduled === 'function' && !isInstrumented(handler.scheduled)) {
7376
handler.scheduled = new Proxy(handler.scheduled, {
7477
apply(target, thisArg, args: Parameters<ExportedHandlerScheduledHandler<Env>>) {
75-
const [event, env, context] = args;
78+
const [event, env, ctx] = args;
79+
const context = copyExecutionContext(ctx);
80+
args[2] = context;
81+
7682
return withIsolationScope(isolationScope => {
7783
const options = getFinalOptions(optionsCallback(env), env);
7884
const waitUntil = context.waitUntil.bind(context);
@@ -115,7 +121,10 @@ export function withSentry<Env = unknown, QueueHandlerMessage = unknown, CfHostM
115121
if ('email' in handler && typeof handler.email === 'function' && !isInstrumented(handler.email)) {
116122
handler.email = new Proxy(handler.email, {
117123
apply(target, thisArg, args: Parameters<EmailExportedHandler<Env>>) {
118-
const [emailMessage, env, context] = args;
124+
const [emailMessage, env, ctx] = args;
125+
const context = copyExecutionContext(ctx);
126+
args[2] = context;
127+
119128
return withIsolationScope(isolationScope => {
120129
const options = getFinalOptions(optionsCallback(env), env);
121130
const waitUntil = context.waitUntil.bind(context);
@@ -156,7 +165,9 @@ export function withSentry<Env = unknown, QueueHandlerMessage = unknown, CfHostM
156165
if ('queue' in handler && typeof handler.queue === 'function' && !isInstrumented(handler.queue)) {
157166
handler.queue = new Proxy(handler.queue, {
158167
apply(target, thisArg, args: Parameters<ExportedHandlerQueueHandler<Env, QueueHandlerMessage>>) {
159-
const [batch, env, context] = args;
168+
const [batch, env, ctx] = args;
169+
const context = copyExecutionContext(ctx);
170+
args[2] = context;
160171

161172
return withIsolationScope(isolationScope => {
162173
const options = getFinalOptions(optionsCallback(env), env);
@@ -206,7 +217,9 @@ export function withSentry<Env = unknown, QueueHandlerMessage = unknown, CfHostM
206217
if ('tail' in handler && typeof handler.tail === 'function' && !isInstrumented(handler.tail)) {
207218
handler.tail = new Proxy(handler.tail, {
208219
apply(target, thisArg, args: Parameters<ExportedHandlerTailHandler<Env>>) {
209-
const [, env, context] = args;
220+
const [, env, ctx] = args;
221+
const context = copyExecutionContext(ctx);
222+
args[2] = context;
210223

211224
return withIsolationScope(async isolationScope => {
212225
const options = getFinalOptions(optionsCallback(env), env);

packages/cloudflare/src/utils/copyExecutionContext.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,13 @@ export function copyExecutionContext<T extends ContextType>(ctx: T): T {
1414

1515
const overrides: OverridesStore<T> = new Map();
1616
const contextPrototype = Object.getPrototypeOf(ctx);
17-
const methodNames = Object.getOwnPropertyNames(contextPrototype) as unknown as (keyof T)[];
18-
const descriptors = methodNames.reduce((prevDescriptors, methodName) => {
19-
if (methodName === 'constructor') return prevDescriptors;
17+
const prototypeMethodNames = Object.getOwnPropertyNames(contextPrototype) as unknown as (keyof T)[];
18+
const ownPropertyNames = Object.getOwnPropertyNames(ctx) as unknown as (keyof T)[];
19+
const instrumented = new Set<unknown>(['constructor']);
20+
const descriptors = [...ownPropertyNames, ...prototypeMethodNames].reduce((prevDescriptors, methodName) => {
21+
if (instrumented.has(methodName)) return prevDescriptors;
2022
if (typeof ctx[methodName] !== 'function') return prevDescriptors;
23+
instrumented.add(methodName);
2124
const overridableDescriptor = makeOverridableDescriptor(overrides, ctx, methodName);
2225
return {
2326
...prevDescriptors,
@@ -48,9 +51,11 @@ function makeOverridableDescriptor<T extends ContextType>(
4851
configurable: true,
4952
enumerable: true,
5053
set: newValue => {
51-
if (typeof newValue !== 'function') throw new Error('Cannot override non-function');
52-
store.set(method, newValue);
53-
return true;
54+
if (typeof newValue == 'function') {
55+
store.set(method, newValue);
56+
return;
57+
}
58+
Reflect.set(ctx, method, newValue);
5459
},
5560

5661
get: () => {

packages/cloudflare/src/workflows.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { setAsyncLocalStorageAsyncContextStrategy } from './async';
2222
import type { CloudflareOptions } from './client';
2323
import { addCloudResourceContext } from './scope-utils';
2424
import { init } from './sdk';
25+
import { copyExecutionContext } from './utils/copyExecutionContext';
2526

2627
const UUID_REGEX = /^[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}$/i;
2728

@@ -157,6 +158,9 @@ export function instrumentWorkflowWithSentry<
157158
return new Proxy(WorkFlowClass, {
158159
construct(target: C, args: [ctx: ExecutionContext, env: E], newTarget) {
159160
const [ctx, env] = args;
161+
const context = copyExecutionContext(ctx);
162+
args[0] = context;
163+
160164
const options = optionsCallback(env);
161165
const instance = Reflect.construct(target, args, newTarget) as T;
162166
return new Proxy(instance, {
@@ -179,10 +183,10 @@ export function instrumentWorkflowWithSentry<
179183
return await obj.run.call(
180184
obj,
181185
event,
182-
new WrappedWorkflowStep(event.instanceId, ctx, options, step),
186+
new WrappedWorkflowStep(event.instanceId, context, options, step),
183187
);
184188
} finally {
185-
ctx.waitUntil(flush(2000));
189+
context.waitUntil(flush(2000));
186190
}
187191
});
188192
});

0 commit comments

Comments
 (0)