@goharvest/simforge 0.4.7 → 0.5.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/dist/client.js CHANGED
@@ -1,57 +1,93 @@
1
1
  /**
2
2
  * Simforge client for provider-based API calls.
3
3
  */
4
+ import { AsyncLocalStorage } from "node:async_hooks";
4
5
  import { runFunctionWithBaml, } from "./baml.js";
5
- import { __version__, DEFAULT_SERVICE_URL } from "./constants.js";
6
+ import { DEFAULT_SERVICE_URL } from "./constants.js";
7
+ import { HttpClient, SimforgeError } from "./http.js";
8
+ import { serializeValue } from "./serialize.js";
6
9
  import { SimforgeOpenAITracingProcessor } from "./tracing.js";
7
- // Global set to track pending trace creation promises
8
- // This prevents promises from being garbage collected before they complete
9
- const pendingTracePromises = new Set();
10
+ // Zone.js property key for storing span stack
11
+ const SPAN_STACK_KEY = "simforgeSpanStack";
10
12
  /**
11
- * Track a promise to prevent it from being garbage collected.
12
- * The promise will be removed from tracking when it completes (success or failure).
13
- * Useful for fire-and-forget operations that need to complete before process exit.
13
+ * Async context propagation for nested spans.
14
14
  *
15
- * @param promise - The promise to track
16
- * @returns The same promise (for chaining)
15
+ * Uses AsyncLocalStorage in Node.js (preferred) or Zone.js in browsers
16
+ * to track parent-child relationships across concurrent async operations.
17
17
  */
18
- function awaitOnExit(promise) {
19
- pendingTracePromises.add(promise);
20
- promise.finally(() => {
21
- pendingTracePromises.delete(promise);
22
- });
23
- return promise;
18
+ let asyncLocalStorage = null;
19
+ // Try to use AsyncLocalStorage (Node.js)
20
+ try {
21
+ asyncLocalStorage = new AsyncLocalStorage();
24
22
  }
25
- // Register beforeExit handler to wait for pending traces (Node.js only)
26
- // This ensures traces are sent before the process exits (for scripts)
27
- if (typeof process !== "undefined" &&
28
- process.versions != null &&
29
- process.versions.node != null) {
30
- let isFlushing = false;
31
- process.on("beforeExit", () => {
32
- if (pendingTracePromises.size > 0 && !isFlushing) {
33
- isFlushing = true;
34
- // Wait for all pending traces to complete
35
- // This keeps the event loop alive until promises resolve
36
- Promise.allSettled(Array.from(pendingTracePromises).map((p) => p.catch(() => {
37
- // Silently ignore individual trace failures
38
- })))
39
- .then(() => {
40
- isFlushing = false;
41
- })
42
- .catch(() => {
43
- isFlushing = false;
44
- });
45
- }
23
+ catch {
24
+ // Not in Node.js - Zone.js will be used via globalThis.Zone if available
25
+ import("zone.js").catch(() => {
26
+ // zone.js not available, will fall back to no context tracking
46
27
  });
47
28
  }
48
- export class SimforgeError extends Error {
49
- constructor(message, url) {
50
- super(message);
51
- this.url = url;
52
- this.name = "SimforgeError";
29
+ /**
30
+ * Get the current span stack from async context.
31
+ * Uses AsyncLocalStorage in Node.js, Zone.js in browsers.
32
+ */
33
+ function getSpanStack() {
34
+ // Node.js: use AsyncLocalStorage
35
+ if (asyncLocalStorage) {
36
+ return asyncLocalStorage.getStore() ?? [];
53
37
  }
38
+ // Browser: use Zone.js
39
+ const zone = globalThis.Zone;
40
+ if (zone) {
41
+ return zone.current.get(SPAN_STACK_KEY) ?? [];
42
+ }
43
+ return [];
54
44
  }
45
+ /**
46
+ * Run a function with a new span stack context.
47
+ * Uses AsyncLocalStorage in Node.js, Zone.js in browsers.
48
+ */
49
+ function runWithSpanStack(stack, fn) {
50
+ // Node.js: use AsyncLocalStorage
51
+ if (asyncLocalStorage) {
52
+ return asyncLocalStorage.run(stack, fn);
53
+ }
54
+ // Browser: use Zone.js
55
+ const zone = globalThis.Zone;
56
+ if (zone) {
57
+ const childZone = zone.current.fork({
58
+ name: "simforgeSpan",
59
+ properties: { [SPAN_STACK_KEY]: stack },
60
+ });
61
+ return childZone.run(fn);
62
+ }
63
+ // Fallback (should not reach here if zone.js is loaded)
64
+ return fn();
65
+ }
66
+ /**
67
+ * Get a handle to the current active span.
68
+ *
69
+ * Call this from inside a traced function (wrapped with `withSpan`) to get
70
+ * a span handle that allows setting metadata at runtime.
71
+ *
72
+ * Returns `null` if called outside of a span context.
73
+ */
74
+ export function getCurrentSpan() {
75
+ const stack = getSpanStack();
76
+ const current = stack[stack.length - 1];
77
+ if (!current) {
78
+ return null;
79
+ }
80
+ return {
81
+ setMetadata(metadata) {
82
+ if (typeof metadata !== "object" || metadata === null) {
83
+ return;
84
+ }
85
+ current.metadata = { ...current.metadata, ...metadata };
86
+ },
87
+ };
88
+ }
89
+ // Re-export SimforgeError for backwards compatibility
90
+ export { SimforgeError };
55
91
  /**
56
92
  * Client for making provider-based API calls via BAML.
57
93
  */
@@ -66,7 +102,19 @@ export class Simforge {
66
102
  this.serviceUrl = config.serviceUrl ?? DEFAULT_SERVICE_URL;
67
103
  this.timeout = config.timeout ?? 120000;
68
104
  this.envVars = config.envVars ?? {};
69
- this.executeLocally = config.executeLocally ?? true;
105
+ const enabled = config.enabled ?? true;
106
+ if (enabled && (!config.apiKey || config.apiKey.trim() === "")) {
107
+ console.warn("Simforge: apiKey is empty — tracing is disabled. Provide a valid API key to enable tracing.");
108
+ this.enabled = false;
109
+ }
110
+ else {
111
+ this.enabled = enabled;
112
+ }
113
+ this.httpClient = new HttpClient({
114
+ apiKey: this.apiKey,
115
+ serviceUrl: this.serviceUrl,
116
+ timeout: this.timeout,
117
+ });
70
118
  }
71
119
  /**
72
120
  * Fetch the function with its current version and BAML prompt from the server.
@@ -76,57 +124,16 @@ export class Simforge {
76
124
  * @throws {SimforgeError} If the function is not found or an error occurs
77
125
  */
78
126
  async fetchFunctionVersion(methodName) {
79
- const url = `${this.serviceUrl}/api/sdk/functions/lookup`;
80
- const payload = { name: methodName };
81
- const controller = new AbortController();
82
- const timeoutId = setTimeout(() => controller.abort(), this.timeout);
83
- try {
84
- const response = await fetch(url, {
85
- method: "POST",
86
- headers: {
87
- "Content-Type": "application/json",
88
- Authorization: `Bearer ${this.apiKey}`,
89
- },
90
- body: JSON.stringify(payload),
91
- signal: controller.signal,
92
- });
93
- if (!response.ok) {
94
- const errorText = await response.text();
95
- throw new SimforgeError(`HTTP ${response.status}: ${errorText.slice(0, 500)}`);
96
- }
97
- const result = await response.json();
98
- // Check if function was not found
99
- if (result.id === null) {
100
- throw new SimforgeError(`Function "${methodName}" not found. Create it at: ${this.serviceUrl}/functions`, "/functions");
101
- }
102
- // Check if function has no prompt
103
- if (!result.prompt) {
104
- throw new SimforgeError(`Function "${methodName}" has no prompt configured. Add one at: ${this.serviceUrl}/functions/${result.id}`, `/functions/${result.id}`);
105
- }
106
- // Check for errors in the response
107
- if (result.error) {
108
- if (result.url) {
109
- throw new SimforgeError(`${result.error} Configure it at: ${this.serviceUrl}${result.url}`, result.url);
110
- }
111
- throw new SimforgeError(result.error);
112
- }
113
- return result;
127
+ const result = await this.httpClient.lookupFunction(methodName);
128
+ // Check if function was not found
129
+ if (result.id === null) {
130
+ throw new SimforgeError(`Function "${methodName}" not found. Create it at: ${this.serviceUrl}/functions`, "/functions");
114
131
  }
115
- catch (error) {
116
- if (error instanceof SimforgeError) {
117
- throw error;
118
- }
119
- if (error instanceof Error) {
120
- if (error.name === "AbortError") {
121
- throw new SimforgeError(`Request timed out after ${this.timeout}ms`);
122
- }
123
- throw new SimforgeError(error.message);
124
- }
125
- throw new SimforgeError("Unknown error occurred");
126
- }
127
- finally {
128
- clearTimeout(timeoutId);
132
+ // Check if function has no prompt
133
+ if (!result.prompt) {
134
+ throw new SimforgeError(`Function "${methodName}" has no prompt configured. Add one at: ${this.serviceUrl}/functions/${result.id}`, `/functions/${result.id}`);
129
135
  }
136
+ return result;
130
137
  }
131
138
  /**
132
139
  * Call a method with the given named arguments via BAML execution.
@@ -137,144 +144,32 @@ export class Simforge {
137
144
  * @throws {SimforgeError} If service_url is not set, or if an error occurs
138
145
  */
139
146
  async call(methodName, inputs = {}) {
140
- // If executeLocally is true, fetch the BAML and execute it locally
141
- if (this.executeLocally) {
142
- try {
143
- const functionVersion = await this.fetchFunctionVersion(methodName);
144
- const executionResult = await runFunctionWithBaml(functionVersion.prompt, inputs, functionVersion.providers, this.envVars);
145
- // Create trace for the local execution
146
- const resultStr = typeof executionResult.result === "string"
147
- ? executionResult.result
148
- : JSON.stringify(executionResult.result);
149
- // Create trace in background so user doesn't have to wait
150
- // Track the promise to prevent garbage collection
151
- void awaitOnExit(this.createTrace({
152
- functionId: functionVersion.id,
153
- result: resultStr,
154
- inputs: Object.keys(inputs).length > 0 ? inputs : undefined,
155
- rawCollector: executionResult.rawCollector,
156
- })).catch(() => {
157
- console.error("Failed to create trace");
158
- // Silently ignore trace creation failures
159
- });
160
- return executionResult.result;
161
- }
162
- catch (error) {
163
- if (error instanceof SimforgeError) {
164
- throw error;
165
- }
166
- if (error instanceof Error) {
167
- throw new SimforgeError(error.message);
168
- }
169
- throw new SimforgeError("Unknown error occurred during local execution");
170
- }
171
- }
172
- // Otherwise, fall back to server-side execution
173
- const url = `${this.serviceUrl}/api/sdk/call`;
174
- const payload = {
175
- name: methodName,
176
- inputs,
177
- sdkVersion: __version__,
178
- };
179
- const controller = new AbortController();
180
- const timeoutId = setTimeout(() => controller.abort(), this.timeout);
181
147
  try {
182
- const response = await fetch(url, {
183
- method: "POST",
184
- headers: {
185
- "Content-Type": "application/json",
186
- Authorization: `Bearer ${this.apiKey}`,
187
- },
188
- body: JSON.stringify(payload),
189
- signal: controller.signal,
190
- });
191
- if (!response.ok) {
192
- const errorText = await response.text();
193
- throw new SimforgeError(`HTTP ${response.status}: ${errorText.slice(0, 500)}`);
194
- }
195
- const result = await response.json();
196
- // Check for errors in the response
197
- if (result.error) {
198
- if (result.url) {
199
- throw new SimforgeError(`${result.error} Configure it at: ${this.serviceUrl}${result.url}`, result.url);
200
- }
201
- throw new SimforgeError(result.error);
202
- }
203
- return result.result;
204
- }
205
- catch (error) {
206
- if (error instanceof SimforgeError) {
207
- throw error;
208
- }
209
- if (error instanceof Error) {
210
- if (error.name === "AbortError") {
211
- throw new SimforgeError(`Request timed out after ${this.timeout}ms`);
212
- }
213
- throw new SimforgeError(error.message);
214
- }
215
- throw new SimforgeError("Unknown error occurred");
216
- }
217
- finally {
218
- clearTimeout(timeoutId);
219
- }
220
- }
221
- /**
222
- * Create a trace from a local execution result.
223
- * Internal method to record traces when executing BAML locally.
224
- */
225
- async createTrace(params) {
226
- const url = `${this.serviceUrl}/api/sdk/functions/${params.functionId}/traces`;
227
- const payload = {
228
- result: params.result,
229
- source: params.source ?? "typescript-sdk",
230
- sdkVersion: __version__,
231
- };
232
- if (params.inputs !== undefined) {
233
- payload.inputs = params.inputs;
234
- }
235
- if (params.rawCollector !== undefined && params.rawCollector !== null) {
236
- payload.rawCollector = params.rawCollector;
237
- }
238
- const controller = new AbortController();
239
- const timeoutId = setTimeout(() => controller.abort(), 30000);
240
- try {
241
- const response = await fetch(url, {
242
- method: "POST",
243
- headers: {
244
- "Content-Type": "application/json",
245
- Authorization: `Bearer ${this.apiKey}`,
246
- },
247
- body: JSON.stringify(payload),
248
- signal: controller.signal,
148
+ const functionVersion = await this.fetchFunctionVersion(methodName);
149
+ const executionResult = await runFunctionWithBaml(functionVersion.prompt, inputs, functionVersion.providers, this.envVars);
150
+ // Create trace for the local execution
151
+ const resultStr = typeof executionResult.result === "string"
152
+ ? executionResult.result
153
+ : JSON.stringify(executionResult.result);
154
+ // Create trace in background so user doesn't have to wait
155
+ this.httpClient.sendInternalTrace(functionVersion.id, {
156
+ result: resultStr,
157
+ source: "typescript-sdk",
158
+ ...(Object.keys(inputs).length > 0 && { inputs }),
159
+ ...(executionResult.rawCollector != null && {
160
+ rawCollector: executionResult.rawCollector,
161
+ }),
249
162
  });
250
- if (!response.ok) {
251
- const errorText = await response.text();
252
- throw new SimforgeError(`HTTP ${response.status}: ${errorText.slice(0, 500)}`);
253
- }
254
- const result = await response.json();
255
- // Check for errors in the response
256
- if (result.error) {
257
- if (result.url) {
258
- throw new SimforgeError(`${result.error} Configure it at: ${this.serviceUrl}${result.url}`, result.url);
259
- }
260
- throw new SimforgeError(result.error);
261
- }
262
- return result;
163
+ return executionResult.result;
263
164
  }
264
165
  catch (error) {
265
166
  if (error instanceof SimforgeError) {
266
167
  throw error;
267
168
  }
268
169
  if (error instanceof Error) {
269
- if (error.name === "AbortError") {
270
- throw new SimforgeError("Request timed out after 30000ms");
271
- }
272
170
  throw new SimforgeError(error.message);
273
171
  }
274
- throw new SimforgeError("Unknown error occurred");
275
- }
276
- finally {
277
- clearTimeout(timeoutId);
172
+ throw new SimforgeError("Unknown error occurred during local execution");
278
173
  }
279
174
  }
280
175
  /**
@@ -300,5 +195,233 @@ export class Simforge {
300
195
  serviceUrl: this.serviceUrl,
301
196
  });
302
197
  }
198
+ /**
199
+ * Wrap a function to automatically create a span for its inputs and outputs.
200
+ *
201
+ * The wrapped function behaves identically to the original, but sends
202
+ * span data to Simforge in the background after each call.
203
+ *
204
+ * Example usage:
205
+ * ```typescript
206
+ * const client = new Simforge({ apiKey: 'your-api-key' });
207
+ *
208
+ * async function processOrder(orderId: string, items: string[]): Promise<{ total: number }> {
209
+ * // ... process order
210
+ * return { total: 100 };
211
+ * }
212
+ *
213
+ * // Basic usage (defaults to "custom" span type)
214
+ * const tracedProcessOrder = client.withSpan('order-processing', processOrder);
215
+ *
216
+ * // With explicit span type
217
+ * const tracedProcessOrder = client.withSpan('order-processing', { type: 'function' }, processOrder);
218
+ *
219
+ * // Call the wrapped function normally
220
+ * const result = await tracedProcessOrder('order-123', ['item-1', 'item-2']);
221
+ * // Span is automatically sent to Simforge
222
+ * ```
223
+ *
224
+ * @param traceFunctionKey - A string identifier for grouping spans (e.g., 'order-processing', 'user-auth')
225
+ * @param optionsOrFn - Either SpanOptions or the function to wrap
226
+ * @param maybeFn - The function to wrap if options were provided
227
+ * @returns A wrapped function with the same signature that creates spans for inputs and outputs
228
+ */
229
+ withSpan(traceFunctionKey, optionsOrFn, maybeFn) {
230
+ if (!this.enabled) {
231
+ const fn = typeof optionsOrFn === "function" ? optionsOrFn : maybeFn;
232
+ return fn;
233
+ }
234
+ // Handle overloaded signature
235
+ const options = typeof optionsOrFn === "function" ? {} : optionsOrFn;
236
+ const fn = typeof optionsOrFn === "function" ? optionsOrFn : maybeFn;
237
+ const self = this;
238
+ return function (...args) {
239
+ // Get current span stack to determine trace context
240
+ const currentStack = getSpanStack();
241
+ const parentContext = currentStack[currentStack.length - 1];
242
+ // Generate trace ID (new if root, inherit if nested)
243
+ const traceId = parentContext?.traceId ?? crypto.randomUUID();
244
+ const spanId = crypto.randomUUID();
245
+ const parentSpanId = parentContext?.spanId ?? null;
246
+ // Create new context for this span
247
+ const newContext = { traceId, spanId };
248
+ const newStack = [...currentStack, newContext];
249
+ // Capture inputs and start time
250
+ const inputs = args;
251
+ const startedAt = new Date().toISOString();
252
+ // Shared span parameters
253
+ const functionName = fn.name !== "" ? fn.name : undefined;
254
+ const baseSpanParams = {
255
+ traceFunctionKey,
256
+ functionName,
257
+ spanName: options.name ?? functionName ?? traceFunctionKey,
258
+ traceId,
259
+ spanId,
260
+ parentSpanId,
261
+ inputs,
262
+ startedAt,
263
+ spanType: options.type ?? "custom",
264
+ metadata: options.metadata,
265
+ };
266
+ // Helper to send span in background (fire-and-forget).
267
+ // Wrapped in try/catch so span errors never crash the host app.
268
+ const sendSpan = (params) => {
269
+ try {
270
+ // Merge runtime metadata (from setSpanMetadata) with definition-time metadata
271
+ const runtimeMetadata = newContext.metadata;
272
+ const mergedMetadata = runtimeMetadata
273
+ ? { ...options.metadata, ...runtimeMetadata }
274
+ : options.metadata;
275
+ self.sendWrapperSpan({
276
+ ...baseSpanParams,
277
+ ...params,
278
+ metadata: mergedMetadata,
279
+ endedAt: new Date().toISOString(),
280
+ });
281
+ }
282
+ catch {
283
+ // Silently ignore — user's result/exception takes priority
284
+ }
285
+ };
286
+ // Execute function within new span context
287
+ const executeWithContext = () => {
288
+ const result = fn(...args);
289
+ // Check if result is a Promise (async function)
290
+ if (result instanceof Promise) {
291
+ return result
292
+ .then((resolvedResult) => {
293
+ sendSpan({ result: resolvedResult });
294
+ return resolvedResult;
295
+ })
296
+ .catch((error) => {
297
+ sendSpan({
298
+ result: undefined,
299
+ error: error instanceof Error ? error.message : String(error),
300
+ });
301
+ throw error;
302
+ });
303
+ }
304
+ // Sync function - create span in background
305
+ sendSpan({ result });
306
+ return result;
307
+ };
308
+ return runWithSpanStack(newStack, executeWithContext);
309
+ };
310
+ }
311
+ /**
312
+ * Get a function wrapper for a specific trace function key.
313
+ *
314
+ * This provides a fluent API alternative to calling withSpan directly,
315
+ * allowing you to bind the traceFunctionKey once and wrap multiple functions.
316
+ *
317
+ * Example usage:
318
+ * ```typescript
319
+ * const client = new Simforge({ apiKey: 'your-api-key' });
320
+ *
321
+ * const orderFunc = client.getFunction('order-processing');
322
+ * const tracedProcessOrder = orderFunc.withSpan(processOrder);
323
+ * const tracedValidateOrder = orderFunc.withSpan(validateOrder);
324
+ * ```
325
+ *
326
+ * @param traceFunctionKey - A string identifier for grouping spans
327
+ * @returns A SimforgeFunction instance for wrapping functions
328
+ */
329
+ getFunction(traceFunctionKey) {
330
+ return new SimforgeFunction(this, traceFunctionKey);
331
+ }
332
+ /**
333
+ * Send a wrapper span from function execution.
334
+ * Internal method to record spans when using withSpan.
335
+ * Fire-and-forget - sends to externalSpans endpoint via httpClient.
336
+ */
337
+ sendWrapperSpan(params) {
338
+ // Serialize inputs and result with superjson for type preservation
339
+ const serializedInputs = serializeValue(params.inputs);
340
+ const serializedResult = serializeValue(params.result);
341
+ // Format as an external span with the wrapper format
342
+ const externalSpan = {
343
+ id: params.spanId,
344
+ trace_id: params.traceId,
345
+ started_at: params.startedAt,
346
+ ended_at: params.endedAt,
347
+ span_data: {
348
+ name: params.spanName,
349
+ type: params.spanType,
350
+ input: serializedInputs.json,
351
+ output: serializedResult.json,
352
+ // Include superjson meta for type preservation
353
+ ...(serializedInputs.meta !== undefined && {
354
+ input_meta: serializedInputs.meta,
355
+ }),
356
+ ...(serializedResult.meta !== undefined && {
357
+ output_meta: serializedResult.meta,
358
+ }),
359
+ ...(params.functionName !== undefined && {
360
+ function_name: params.functionName,
361
+ }),
362
+ ...(params.error !== undefined && { error: params.error }),
363
+ ...(params.metadata !== undefined && { metadata: params.metadata }),
364
+ },
365
+ };
366
+ // Add parent_id for nested spans
367
+ if (params.parentSpanId) {
368
+ externalSpan.parent_id = params.parentSpanId;
369
+ }
370
+ this.httpClient.sendExternalSpan({
371
+ type: "sdk-function",
372
+ source: "typescript-sdk-function",
373
+ sourceTraceId: params.traceId,
374
+ traceFunctionKey: params.traceFunctionKey,
375
+ rawSpan: externalSpan,
376
+ });
377
+ }
378
+ }
379
+ /**
380
+ * Represents a Simforge function that can wrap user functions for tracing.
381
+ *
382
+ * This provides a fluent API for binding a traceFunctionKey once and
383
+ * then wrapping multiple functions with that key.
384
+ *
385
+ * Example usage:
386
+ * ```typescript
387
+ * const client = new Simforge({ apiKey: 'your-api-key' });
388
+ *
389
+ * const orderFunc = client.getFunction('order-processing');
390
+ * const tracedProcessOrder = orderFunc.withSpan(processOrder);
391
+ * const tracedValidateOrder = orderFunc.withSpan(validateOrder);
392
+ * ```
393
+ */
394
+ export class SimforgeFunction {
395
+ constructor(client, traceFunctionKey) {
396
+ this.client = client;
397
+ this.traceFunctionKey = traceFunctionKey;
398
+ }
399
+ /**
400
+ * Wrap a function to automatically create a span for its inputs and outputs.
401
+ *
402
+ * The wrapped function behaves identically to the original, but sends
403
+ * span data to Simforge in the background after each call.
404
+ *
405
+ * Example usage:
406
+ * ```typescript
407
+ * const orderFunc = client.getFunction('order-processing');
408
+ *
409
+ * // Basic usage (defaults to "custom" span type)
410
+ * const tracedProcessOrder = orderFunc.withSpan(processOrder);
411
+ *
412
+ * // With explicit span type
413
+ * const tracedProcessOrder = orderFunc.withSpan({ type: 'function' }, processOrder);
414
+ * ```
415
+ *
416
+ * @param optionsOrFn - Either SpanOptions or the function to wrap
417
+ * @param maybeFn - The function to wrap if options were provided
418
+ * @returns A wrapped function with the same signature that creates spans
419
+ */
420
+ withSpan(optionsOrFn, maybeFn) {
421
+ // Handle overloaded signature
422
+ const options = typeof optionsOrFn === "function" ? {} : optionsOrFn;
423
+ const fn = typeof optionsOrFn === "function" ? optionsOrFn : maybeFn;
424
+ return this.client.withSpan(this.traceFunctionKey, options, fn);
425
+ }
303
426
  }
304
427
  //# sourceMappingURL=client.js.map