@@ -36,13 +36,16 @@ type MethodWrapperOptions = {
3636 /**
3737 * If true, stores the current span context and links to the previous invocation's span.
3838 * Requires `startNewTrace` to be true. Uses Durable Object storage to persist the link.
39+ *
40+ * WARNING: Enabling this option causes the wrapped method to always return a Promise,
41+ * even if the original method was synchronous. Only use this for methods that are
42+ * inherently async (e.g., Cloudflare's `alarm()` handler).
43+ *
3944 * @default false
4045 */
4146 linkPreviousTrace ?: boolean ;
4247} ;
4348
44- type SpanLink = ReturnType < typeof buildSpanLinks > [ number ] ;
45-
4649// eslint-disable-next-line @typescript-eslint/no-explicit-any
4750export type UncheckedMethod = ( ...args : any [ ] ) => any ;
4851type OriginalMethod = UncheckedMethod ;
@@ -80,7 +83,7 @@ export function wrapMethodWithSentry<T extends OriginalMethod>(
8083 const currentClient = getClient ( ) ;
8184 const sentryWithScope = startNewTrace ? withIsolationScope : currentClient ? withScope : withIsolationScope ;
8285
83- const wrappedFunction = async ( scope : Scope ) : Promise < unknown > => {
86+ const wrappedFunction = ( scope : Scope ) : unknown | Promise < unknown > => {
8487 // In certain situations, the passed context can become undefined.
8588 // For example, for Astro while prerendering pages at build time.
8689 // see: https://github.com/getsentry/sentry-javascript/issues/13217
@@ -100,21 +103,13 @@ export function wrapMethodWithSentry<T extends OriginalMethod>(
100103 }
101104 }
102105
103- let links : SpanLink [ ] | undefined ;
104- let storedContext : StoredSpanContext | undefined ;
105106 const methodName = wrapperOptions . spanName || 'unknown' ;
106107
107- if ( linkPreviousTrace && storage ) {
108- storedContext = await getStoredSpanContext ( storage , methodName ) ;
109- if ( storedContext ) {
110- links = buildSpanLinks ( storedContext ) ;
111- }
112- }
113-
114- const storeContextIfNeeded = async ( ) : Promise < void > => {
108+ const teardown = async ( ) : Promise < void > => {
115109 if ( linkPreviousTrace && storage ) {
116110 await storeSpanContext ( storage , methodName ) ;
117111 }
112+ await flush ( 2000 ) ;
118113 } ;
119114
120115 if ( ! wrapperOptions . spanName ) {
@@ -126,26 +121,23 @@ export function wrapMethodWithSentry<T extends OriginalMethod>(
126121
127122 if ( isThenable ( result ) ) {
128123 return result . then (
129- async ( res : unknown ) => {
130- await storeContextIfNeeded ( ) ;
131- waitUntil ?.( flush ( 2000 ) ) ;
124+ ( res : unknown ) => {
125+ waitUntil ?.( teardown ( ) ) ;
132126 return res ;
133127 } ,
134- async ( e : unknown ) => {
128+ ( e : unknown ) => {
135129 captureException ( e , {
136130 mechanism : {
137131 type : 'auto.faas.cloudflare.durable_object' ,
138132 handled : false ,
139133 } ,
140134 } ) ;
141- await storeContextIfNeeded ( ) ;
142- waitUntil ?.( flush ( 2000 ) ) ;
135+ waitUntil ?.( teardown ( ) ) ;
143136 throw e ;
144137 } ,
145138 ) ;
146139 } else {
147- await storeContextIfNeeded ( ) ;
148- waitUntil ?.( flush ( 2000 ) ) ;
140+ waitUntil ?.( teardown ( ) ) ;
149141 return result ;
150142 }
151143 } catch ( e ) {
@@ -155,8 +147,7 @@ export function wrapMethodWithSentry<T extends OriginalMethod>(
155147 handled : false ,
156148 } ,
157149 } ) ;
158- await storeContextIfNeeded ( ) ;
159- waitUntil ?.( flush ( 2000 ) ) ;
150+ waitUntil ?.( teardown ( ) ) ;
160151 throw e ;
161152 }
162153 }
@@ -169,8 +160,10 @@ export function wrapMethodWithSentry<T extends OriginalMethod>(
169160 }
170161 : { } ;
171162
172- const executeSpan = ( ) : unknown => {
173- return startSpan ( { name : spanName , attributes, links } , async span => {
163+ const executeSpan = ( storedContext ?: StoredSpanContext ) : unknown => {
164+ const links = storedContext ? buildSpanLinks ( storedContext ) : undefined ;
165+
166+ return startSpan ( { name : spanName , attributes, links } , span => {
174167 // TODO: Remove this once EAP can store span links. We currently only set this attribute so that we
175168 // can obtain the previous trace information from the EAP store. Long-term, EAP will handle
176169 // span links and then we should remove this again. Also throwing in a TODO(v11), to remind us
@@ -188,26 +181,23 @@ export function wrapMethodWithSentry<T extends OriginalMethod>(
188181
189182 if ( isThenable ( result ) ) {
190183 return result . then (
191- async ( res : unknown ) => {
192- await storeContextIfNeeded ( ) ;
193- waitUntil ?.( flush ( 2000 ) ) ;
184+ ( res : unknown ) => {
185+ waitUntil ?.( teardown ( ) ) ;
194186 return res ;
195187 } ,
196- async ( e : unknown ) => {
188+ ( e : unknown ) => {
197189 captureException ( e , {
198190 mechanism : {
199191 type : 'auto.faas.cloudflare.durable_object' ,
200192 handled : false ,
201193 } ,
202194 } ) ;
203- await storeContextIfNeeded ( ) ;
204- waitUntil ?.( flush ( 2000 ) ) ;
195+ waitUntil ?.( teardown ( ) ) ;
205196 throw e ;
206197 } ,
207198 ) ;
208199 } else {
209- await storeContextIfNeeded ( ) ;
210- waitUntil ?.( flush ( 2000 ) ) ;
200+ waitUntil ?.( teardown ( ) ) ;
211201 return result ;
212202 }
213203 } catch ( e ) {
@@ -217,15 +207,25 @@ export function wrapMethodWithSentry<T extends OriginalMethod>(
217207 handled : false ,
218208 } ,
219209 } ) ;
220- await storeContextIfNeeded ( ) ;
221- waitUntil ?.( flush ( 2000 ) ) ;
210+ waitUntil ?.( teardown ( ) ) ;
222211 throw e ;
223212 }
224213 } ) ;
225214 } ;
226215
216+ // When linking to previous trace, we need to fetch the stored context first
217+ // We chain this with the span execution to avoid making the outer function async
218+ if ( linkPreviousTrace && storage ) {
219+ const storedContextPromise = getStoredSpanContext ( storage , methodName ) ;
220+
221+ if ( startNewTrace ) {
222+ return storedContextPromise . then ( storedContext => startNewTraceCore ( ( ) => executeSpan ( storedContext ) ) ) ;
223+ }
224+ return storedContextPromise . then ( storedContext => executeSpan ( storedContext ) ) ;
225+ }
226+
227227 if ( startNewTrace ) {
228- return startNewTraceCore ( executeSpan ) ;
228+ return startNewTraceCore ( ( ) => executeSpan ( ) ) ;
229229 }
230230
231231 return executeSpan ( ) ;
0 commit comments