diff --git a/.changeset/honest-buckets-grab.md b/.changeset/honest-buckets-grab.md new file mode 100644 index 000000000..6cea279a1 --- /dev/null +++ b/.changeset/honest-buckets-grab.md @@ -0,0 +1,5 @@ +--- +"braintrust": minor +--- + +feat: Add `braintrust/apply-instrumentation` entrypoint for CJS/TS patching diff --git a/js/package.json b/js/package.json index 545baf20a..47f641ef9 100644 --- a/js/package.json +++ b/js/package.json @@ -11,6 +11,7 @@ "main": "./dist/index.js", "module": "./dist/index.mjs", "types": "./dist/index.d.ts", + "sideEffects": true, "browser": { "./dist/index.js": "./dist/browser.js", "./dist/index.d.ts": "./dist/browser.d.ts", @@ -50,6 +51,19 @@ "require": "./dist/browser.js", "default": "./dist/browser.mjs" }, + "./apply-instrumentation": { + "types": "./dist/apply-instrumentation.d.ts", + "edge-light": "./dist/apply-instrumentation.browser.mjs", + "workerd": "./dist/apply-instrumentation.browser.mjs", + "node": { + "import": "./dist/apply-instrumentation.mjs", + "require": "./dist/apply-instrumentation.js" + }, + "browser": "./dist/apply-instrumentation.browser.mjs", + "import": "./dist/apply-instrumentation.mjs", + "require": "./dist/apply-instrumentation.js", + "default": "./dist/apply-instrumentation.mjs" + }, "./node": { "types": "./dist/index.d.ts", "import": "./dist/index.mjs", diff --git a/js/src/auto-instrumentations/configs/all.ts b/js/src/auto-instrumentations/configs/all.ts index d26c446eb..2df69ef49 100644 --- a/js/src/auto-instrumentations/configs/all.ts +++ b/js/src/auto-instrumentations/configs/all.ts @@ -1,4 +1,9 @@ import type { InstrumentationConfig } from "@apm-js-collab/code-transformer"; +import { + isInstrumentationIntegrationDisabled, + readDisabledInstrumentationEnvConfig, + type InstrumentationIntegrationsConfig, +} from "../../instrumentation/config"; import { aiSDKConfigs } from "./ai-sdk"; import { anthropicConfigs } from "./anthropic"; import { claudeAgentSDKConfigs } from "./claude-agent-sdk"; @@ -18,70 +23,87 @@ import { openRouterConfigs } from "./openrouter"; import { openRouterAgentConfigs } from "./openrouter-agent"; interface InstrumentationConfigGroup { - disabledNames: readonly string[]; + integrations: readonly (keyof InstrumentationIntegrationsConfig)[]; configs: readonly InstrumentationConfig[]; } const defaultInstrumentationConfigGroups: readonly InstrumentationConfigGroup[] = [ - { disabledNames: ["openai"], configs: openaiConfigs }, + { integrations: ["openai"], configs: openaiConfigs }, { - disabledNames: ["openai-codex", "openai-codex-sdk", "codex", "codex-sdk"], + integrations: ["openaiCodexSDK"], configs: openAICodexConfigs, }, - { disabledNames: ["anthropic"], configs: anthropicConfigs }, + { integrations: ["anthropic"], configs: anthropicConfigs }, { - disabledNames: ["aisdk", "ai-sdk", "vercel-ai"], + integrations: ["aisdk", "vercel"], configs: aiSDKConfigs, }, { - disabledNames: ["claudeagentsdk", "claude-agent-sdk"], + integrations: ["claudeAgentSDK"], configs: claudeAgentSDKConfigs, }, - { disabledNames: ["cursor", "cursor-sdk"], configs: cursorSDKConfigs }, + { integrations: ["cursor", "cursorSDK"], configs: cursorSDKConfigs }, { - disabledNames: ["openai-agents", "openaiagents", "openai-agents-core"], + integrations: ["openAIAgents"], configs: openAIAgentsCoreConfigs, }, { - disabledNames: ["google", "google-genai"], + integrations: ["google", "googleGenAI"], configs: googleGenAIConfigs, }, - { disabledNames: ["huggingface"], configs: huggingFaceConfigs }, - { disabledNames: ["openrouter"], configs: openRouterConfigs }, + { integrations: ["huggingface"], configs: huggingFaceConfigs }, + { integrations: ["openrouter"], configs: openRouterConfigs }, { - disabledNames: ["openrouteragent", "openrouter-agent"], + integrations: ["openrouterAgent"], configs: openRouterAgentConfigs, }, - { disabledNames: ["mistral"], configs: mistralConfigs }, - { disabledNames: ["googleadk", "google-adk"], configs: googleADKConfigs }, - { disabledNames: ["cohere"], configs: cohereConfigs }, - { disabledNames: ["groq", "groq-sdk"], configs: groqConfigs }, + { integrations: ["mistral"], configs: mistralConfigs }, + { integrations: ["googleADK"], configs: googleADKConfigs }, + { integrations: ["cohere"], configs: cohereConfigs }, + { integrations: ["groq"], configs: groqConfigs }, { - disabledNames: ["genkit", "firebase-genkit"], + integrations: ["genkit"], configs: genkitConfigs, }, { - disabledNames: ["githubcopilot", "github-copilot", "copilot-sdk"], + integrations: ["gitHubCopilot"], configs: gitHubCopilotConfigs, }, ]; export function getDefaultInstrumentationConfigs({ additionalInstrumentations, + disabledIntegrationConfig, disabledIntegrations, }: { additionalInstrumentations?: readonly InstrumentationConfig[]; + disabledIntegrationConfig?: InstrumentationIntegrationsConfig; disabledIntegrations?: ReadonlySet; } = {}): InstrumentationConfig[] { + const disabledConfig = + disabledIntegrationConfig ?? + (disabledIntegrations + ? readDisabledInstrumentationEnvConfig( + [...disabledIntegrations].join(","), + ).integrations + : undefined); + return [ ...defaultInstrumentationConfigGroups.flatMap( - ({ configs, disabledNames }) => - disabledIntegrations && - disabledNames.some((name) => disabledIntegrations.has(name)) + ({ configs, integrations }) => + isInstrumentationIntegrationDisabled(disabledConfig, ...integrations) ? [] : configs, ), ...(additionalInstrumentations ?? []), ]; } + +export function getDefaultAutoInstrumentationConfigs(): InstrumentationConfig[] { + return getDefaultInstrumentationConfigs({ + disabledIntegrationConfig: readDisabledInstrumentationEnvConfig( + process.env.BRAINTRUST_DISABLE_INSTRUMENTATION, + ).integrations, + }); +} diff --git a/js/src/auto-instrumentations/hook.mts b/js/src/auto-instrumentations/hook.mts index 00f20ade3..050edf8f2 100644 --- a/js/src/auto-instrumentations/hook.mts +++ b/js/src/auto-instrumentations/hook.mts @@ -14,62 +14,54 @@ */ import { register } from "node:module"; -import { getDefaultInstrumentationConfigs } from "./configs/all.js"; +import { getDefaultAutoInstrumentationConfigs } from "./configs/all.js"; import { ModulePatch } from "./loader/cjs-patch.js"; import { patchTracingChannel } from "./patch-tracing-channel.js"; +const state = ((globalThis as any)[ + Symbol.for("braintrust.applyInstrumentation") +] ??= {}) as { applied?: boolean }; +const alreadyApplied = state.applied; + // Patch diagnostics_channel.tracePromise to handle APIPromise correctly. // MUST be done here (before any SDK code runs) to fix Anthropic APIPromise incompatibility. // Construct the module path dynamically to prevent build from stripping "node:" prefix. -const dcPath = ["node", "diagnostics_channel"].join(":"); -const dc: any = await import(/* @vite-ignore */ dcPath as any); -patchTracingChannel(dc.tracingChannel); - -function readDisabledIntegrations(): Set { - const raw = process.env.BRAINTRUST_DISABLE_INSTRUMENTATION; - if (!raw) { - return new Set(); - } - - return new Set( - raw - .split(",") - .map((value) => value.trim().toLowerCase()) - .filter((value) => value.length > 0), - ); +if (!alreadyApplied) { + const dcPath = ["node", "diagnostics_channel"].join(":"); + const dc: any = await import(/* @vite-ignore */ dcPath as any); + patchTracingChannel(dc.tracingChannel); } -// Combine all instrumentation configs. -// Respect BRAINTRUST_DISABLE_INSTRUMENTATION here too so load-time -// transformation and runtime plugins stay aligned. -const allConfigs = getDefaultInstrumentationConfigs({ - disabledIntegrations: readDisabledIntegrations(), -}); +if (!alreadyApplied) { + const allConfigs = getDefaultAutoInstrumentationConfigs(); -// 1. Register ESM loader for ESM modules -register("./loader/esm-hook.mjs", { - parentURL: import.meta.url, - data: { instrumentations: allConfigs }, -} as any); + // 1. Register ESM loader for ESM modules + register("./loader/esm-hook.mjs", { + parentURL: import.meta.url, + data: { instrumentations: allConfigs }, + } as any); -// 2. Also load CJS register for CJS modules (many apps use mixed ESM/CJS) -try { - const patch = new ModulePatch({ instrumentations: allConfigs }); - patch.patch(); + state.applied = true; - if (process.env.DEBUG === "@braintrust*" || process.env.DEBUG === "*") { - console.log( - "[Braintrust] Auto-instrumentation active (ESM + CJS) for:", - allConfigs.map((c) => c.channelName).join(", "), - ); - } -} catch (err) { - // CJS patch failed, but ESM hook is still active - if (process.env.DEBUG === "@braintrust*" || process.env.DEBUG === "*") { - console.log( - "[Braintrust] Auto-instrumentation active (ESM only) for:", - allConfigs.map((c) => c.channelName).join(", "), - ); - console.error("[Braintrust] CJS patch failed:", err); + // 2. Also load CJS register for CJS modules (many apps use mixed ESM/CJS) + try { + const patch = new ModulePatch({ instrumentations: allConfigs }); + patch.patch(); + + if (process.env.DEBUG === "@braintrust*" || process.env.DEBUG === "*") { + console.log( + "[Braintrust] Auto-instrumentation active (ESM + CJS) for:", + allConfigs.map((c) => c.channelName).join(", "), + ); + } + } catch (err) { + // CJS patch failed, but ESM hook is still active + if (process.env.DEBUG === "@braintrust*" || process.env.DEBUG === "*") { + console.log( + "[Braintrust] Auto-instrumentation active (ESM only) for:", + allConfigs.map((c) => c.channelName).join(", "), + ); + console.error("[Braintrust] CJS patch failed:", err); + } } } diff --git a/js/src/instrumentation/braintrust-plugin.ts b/js/src/instrumentation/braintrust-plugin.ts index 8b3948297..c8488df4c 100644 --- a/js/src/instrumentation/braintrust-plugin.ts +++ b/js/src/instrumentation/braintrust-plugin.ts @@ -16,30 +16,10 @@ import { CoherePlugin } from "./plugins/cohere-plugin"; import { GroqPlugin } from "./plugins/groq-plugin"; import { GenkitPlugin } from "./plugins/genkit-plugin"; import { GitHubCopilotPlugin } from "./plugins/github-copilot-plugin"; +import type { InstrumentationIntegrationsConfig } from "./config"; export interface BraintrustPluginConfig { - integrations?: { - openai?: boolean; - anthropic?: boolean; - vercel?: boolean; - aisdk?: boolean; - google?: boolean; - googleGenAI?: boolean; - huggingface?: boolean; - claudeAgentSDK?: boolean; - cursor?: boolean; - cursorSDK?: boolean; - openrouter?: boolean; - openrouterAgent?: boolean; - mistral?: boolean; - googleADK?: boolean; - cohere?: boolean; - groq?: boolean; - genkit?: boolean; - gitHubCopilot?: boolean; - openaiCodexSDK?: boolean; - openAIAgents?: boolean; - }; + integrations?: InstrumentationIntegrationsConfig; } function getIntegrationConfig( diff --git a/js/src/instrumentation/config.ts b/js/src/instrumentation/config.ts new file mode 100644 index 000000000..d6cfb9b33 --- /dev/null +++ b/js/src/instrumentation/config.ts @@ -0,0 +1,126 @@ +export interface InstrumentationIntegrationsConfig { + openai?: boolean; + anthropic?: boolean; + vercel?: boolean; + aisdk?: boolean; + google?: boolean; + googleGenAI?: boolean; + googleADK?: boolean; + huggingface?: boolean; + claudeAgentSDK?: boolean; + cursor?: boolean; + cursorSDK?: boolean; + openAIAgents?: boolean; + openrouter?: boolean; + openrouterAgent?: boolean; + mistral?: boolean; + cohere?: boolean; + groq?: boolean; + genkit?: boolean; + gitHubCopilot?: boolean; + openaiCodexSDK?: boolean; +} + +export interface InstrumentationConfig { + /** + * Configuration for individual SDK integrations. + * Set to false to disable instrumentation for that SDK. + */ + integrations?: InstrumentationIntegrationsConfig; +} + +const envIntegrationAliases: Record< + string, + keyof InstrumentationIntegrationsConfig +> = { + openai: "openai", + "openai-codex": "openaiCodexSDK", + "openai-codex-sdk": "openaiCodexSDK", + openaicodexsdk: "openaiCodexSDK", + codex: "openaiCodexSDK", + "codex-sdk": "openaiCodexSDK", + anthropic: "anthropic", + aisdk: "aisdk", + "ai-sdk": "aisdk", + "vercel-ai": "aisdk", + vercel: "vercel", + claudeagentsdk: "claudeAgentSDK", + "claude-agent-sdk": "claudeAgentSDK", + cursor: "cursor", + "cursor-sdk": "cursorSDK", + cursorsdk: "cursorSDK", + "openai-agents": "openAIAgents", + openaiagents: "openAIAgents", + "openai-agents-core": "openAIAgents", + openaiagentscore: "openAIAgents", + google: "google", + "google-genai": "googleGenAI", + googlegenai: "googleGenAI", + huggingface: "huggingface", + openrouter: "openrouter", + openrouteragent: "openrouterAgent", + "openrouter-agent": "openrouterAgent", + mistral: "mistral", + googleadk: "googleADK", + "google-adk": "googleADK", + cohere: "cohere", + groq: "groq", + "groq-sdk": "groq", + genkit: "genkit", + "firebase-genkit": "genkit", + githubcopilot: "gitHubCopilot", + "github-copilot": "gitHubCopilot", + "copilot-sdk": "gitHubCopilot", +}; + +export function getDefaultInstrumentationIntegrations(): Record< + keyof InstrumentationIntegrationsConfig, + boolean +> { + return { + openai: true, + openaiCodexSDK: true, + anthropic: true, + vercel: true, + aisdk: true, + google: true, + googleGenAI: true, + googleADK: true, + huggingface: true, + claudeAgentSDK: true, + cursor: true, + cursorSDK: true, + openAIAgents: true, + openrouter: true, + openrouterAgent: true, + mistral: true, + cohere: true, + groq: true, + genkit: true, + gitHubCopilot: true, + }; +} + +export function readDisabledInstrumentationEnvConfig( + disabledList: string | undefined, +): InstrumentationConfig { + const integrations: Record = {}; + + if (disabledList) { + for (const value of disabledList.split(",")) { + const sdk = value.trim().toLowerCase(); + if (sdk.length > 0) { + integrations[envIntegrationAliases[sdk] ?? sdk] = false; + } + } + } + + return { integrations }; +} + +export function isInstrumentationIntegrationDisabled( + integrations: InstrumentationIntegrationsConfig | undefined, + ...names: (keyof InstrumentationIntegrationsConfig)[] +): boolean { + return names.some((name) => integrations?.[name] === false); +} diff --git a/js/src/instrumentation/registry.ts b/js/src/instrumentation/registry.ts index 5daf35fe5..a28f7964a 100644 --- a/js/src/instrumentation/registry.ts +++ b/js/src/instrumentation/registry.ts @@ -7,6 +7,13 @@ import { BraintrustPlugin } from "./braintrust-plugin"; import iso from "../isomorph"; +import { + getDefaultInstrumentationIntegrations, + readDisabledInstrumentationEnvConfig, + type InstrumentationConfig, +} from "./config"; + +export type { InstrumentationConfig } from "./config"; // Key used to stamp the active PluginRegistry instance onto the shared // braintrust state object (globalThis[Symbol.for("braintrust-state")]). @@ -33,35 +40,6 @@ function getSharedState(): Record | undefined { : undefined; } -export interface InstrumentationConfig { - /** - * Configuration for individual SDK integrations. - * Set to false to disable instrumentation for that SDK. - */ - integrations?: { - openai?: boolean; - anthropic?: boolean; - vercel?: boolean; - aisdk?: boolean; - google?: boolean; - googleGenAI?: boolean; - googleADK?: boolean; - huggingface?: boolean; - claudeAgentSDK?: boolean; - cursor?: boolean; - cursorSDK?: boolean; - openrouter?: boolean; - openrouterAgent?: boolean; - mistral?: boolean; - cohere?: boolean; - groq?: boolean; - genkit?: boolean; - gitHubCopilot?: boolean; - openaiCodexSDK?: boolean; - openAIAgents?: boolean; - }; -} - class PluginRegistry { private braintrustPlugin: BraintrustPlugin | null = null; private config: InstrumentationConfig = {}; @@ -152,28 +130,7 @@ class PluginRegistry { * Get default configuration (all integrations enabled). */ private getDefaultConfig(): Record { - return { - openai: true, - openaiCodexSDK: true, - anthropic: true, - vercel: true, - aisdk: true, - google: true, - googleGenAI: true, - googleADK: true, - huggingface: true, - claudeAgentSDK: true, - cursor: true, - cursorSDK: true, - openAIAgents: true, - openrouter: true, - openrouterAgent: true, - mistral: true, - cohere: true, - groq: true, - genkit: true, - gitHubCopilot: true, - }; + return getDefaultInstrumentationIntegrations(); } /** @@ -181,33 +138,9 @@ class PluginRegistry { * Supports: BRAINTRUST_DISABLE_INSTRUMENTATION=openai,anthropic,... */ private readEnvConfig(): InstrumentationConfig { - const integrations: Record = {}; - - const disabledList = iso.getEnv("BRAINTRUST_DISABLE_INSTRUMENTATION"); - if (disabledList) { - const disabled = disabledList - .split(",") - .map((s) => s.trim().toLowerCase()) - .filter((s) => s.length > 0); - - for (const sdk of disabled) { - if (sdk === "cursor-sdk") { - integrations.cursorSDK = false; - } else if ( - sdk === "githubcopilot" || - sdk === "github-copilot" || - sdk === "copilot-sdk" - ) { - integrations.gitHubCopilot = false; - } else if (sdk === "openai-codex-sdk") { - integrations.openaiCodexSDK = false; - } else { - integrations[sdk] = false; - } - } - } - - return { integrations }; + return readDisabledInstrumentationEnvConfig( + iso.getEnv("BRAINTRUST_DISABLE_INSTRUMENTATION"), + ); } } diff --git a/js/src/node/apply-instrumentation-entry.ts b/js/src/node/apply-instrumentation-entry.ts new file mode 100644 index 000000000..650048fe2 --- /dev/null +++ b/js/src/node/apply-instrumentation-entry.ts @@ -0,0 +1,74 @@ +import * as diagnostics_channel from "node:diagnostics_channel"; +import { register } from "node:module"; +import { pathToFileURL } from "node:url"; +import { getDefaultAutoInstrumentationConfigs } from "../auto-instrumentations/configs/all"; +import { ModulePatch } from "../auto-instrumentations/loader/cjs-patch"; +import { patchTracingChannel } from "../auto-instrumentations/patch-tracing-channel"; + +interface ApplyInstrumentationState { + applied?: boolean; +} + +const stateKey = Symbol.for("braintrust.applyInstrumentation"); +const existingState = Object.getOwnPropertyDescriptor( + globalThis, + stateKey, +)?.value; +const state: ApplyInstrumentationState = isApplyInstrumentationState( + existingState, +) + ? existingState + : {}; + +if (state !== existingState) { + Object.defineProperty(globalThis, stateKey, { + configurable: false, + enumerable: false, + value: state, + writable: false, + }); +} + +if (!state.applied) { + patchTracingChannel(diagnostics_channel.tracingChannel); + + const allConfigs = getDefaultAutoInstrumentationConfigs(); + + const currentModuleUrl = getCurrentModuleUrl(); + register("./auto-instrumentations/loader/esm-hook.mjs", { + parentURL: currentModuleUrl, + data: { instrumentations: allConfigs }, + }); + + state.applied = true; + + try { + const patch = new ModulePatch({ instrumentations: allConfigs }); + patch.patch(); + } catch { + // ESM instrumentation is already active; keep user code running if CJS patching fails. + } +} + +function isApplyInstrumentationState( + value: unknown, +): value is ApplyInstrumentationState { + return typeof value === "object" && value !== null; +} + +function getCurrentModuleUrl(): string { + if (typeof __filename !== "undefined") { + return pathToFileURL(__filename).href; + } + + const stack = new Error().stack ?? ""; + const match = + stack.match(/\((file:\/\/[^)]+)\)/) ?? stack.match(/\s(file:\/\/\S+)/); + if (match) { + return match[1].replace(/:\d+:\d+$/, ""); + } + + return pathToFileURL(process.argv[1] ?? process.cwd()).href; +} + +export {}; diff --git a/js/src/non-node/apply-instrumentation-entry.ts b/js/src/non-node/apply-instrumentation-entry.ts new file mode 100644 index 000000000..cb0ff5c3b --- /dev/null +++ b/js/src/non-node/apply-instrumentation-entry.ts @@ -0,0 +1 @@ +export {}; diff --git a/js/tests/auto-instrumentations/fixtures/runtime-apply-side-effect-cjs.cjs b/js/tests/auto-instrumentations/fixtures/runtime-apply-side-effect-cjs.cjs new file mode 100644 index 000000000..a11da5a16 --- /dev/null +++ b/js/tests/auto-instrumentations/fixtures/runtime-apply-side-effect-cjs.cjs @@ -0,0 +1,6 @@ +async function main() { + require("braintrust/apply-instrumentation"); + await import("./test-app-esm.mjs"); +} + +main(); diff --git a/js/tests/auto-instrumentations/fixtures/runtime-apply-side-effect-esm.mjs b/js/tests/auto-instrumentations/fixtures/runtime-apply-side-effect-esm.mjs new file mode 100644 index 000000000..6d65c53db --- /dev/null +++ b/js/tests/auto-instrumentations/fixtures/runtime-apply-side-effect-esm.mjs @@ -0,0 +1,3 @@ +import "braintrust/apply-instrumentation"; + +await import("./test-app-esm.mjs"); diff --git a/js/tests/auto-instrumentations/loader-hook.test.ts b/js/tests/auto-instrumentations/loader-hook.test.ts index 54809fef0..451e68cda 100644 --- a/js/tests/auto-instrumentations/loader-hook.test.ts +++ b/js/tests/auto-instrumentations/loader-hook.test.ts @@ -20,6 +20,14 @@ const helperPromisePath = path.join( fixturesDir, "test-api-promise-preservation.mjs", ); +const runtimeApplySideEffectEsmPath = path.join( + fixturesDir, + "runtime-apply-side-effect-esm.mjs", +); +const runtimeApplySideEffectCjsPath = path.join( + fixturesDir, + "runtime-apply-side-effect-cjs.cjs", +); interface TestResult { events: { start: any[]; end: any[]; error: any[] }; @@ -76,9 +84,53 @@ describe("Unified Loader Hook Integration Tests", () => { expect(result.constructorName).toBe("HelperPromise"); }); }); + + describe("apply-instrumentation side-effect runtime setup", () => { + it("should apply instrumentation through the side-effect ESM export", async () => { + const result = await runWithWorker({ + execArgv: ["--import", listenerPath], + script: runtimeApplySideEffectEsmPath, + }); + + expect(result.events.start.length).toBe(1); + expect(result.events.end.length).toBe(1); + }); + + it("should apply instrumentation through the side-effect CJS export", async () => { + const result = await runWithWorker({ + execArgv: ["--import", listenerPath], + script: runtimeApplySideEffectCjsPath, + }); + + expect(result.events.start.length).toBe(1); + expect(result.events.end.length).toBe(1); + }); + + it("should respect BRAINTRUST_DISABLE_INSTRUMENTATION", async () => { + const result = await runWithWorker({ + env: { BRAINTRUST_DISABLE_INSTRUMENTATION: "openai" }, + execArgv: ["--import", listenerPath], + script: runtimeApplySideEffectEsmPath, + }); + + expect(result.events.start.length).toBe(0); + expect(result.events.end.length).toBe(0); + }); + + it("should not double-apply when import hook and side-effect export both run", async () => { + const result = await runWithWorker({ + execArgv: ["--import", listenerPath, "--import", hookPath], + script: runtimeApplySideEffectEsmPath, + }); + + expect(result.events.start.length).toBe(1); + expect(result.events.end.length).toBe(1); + }); + }); }); async function runWithWorker(options: { + env?: NodeJS.ProcessEnv; execArgv: string[]; script: string; }): Promise { @@ -89,6 +141,7 @@ async function runWithWorker(options: { } async function runWithWorkerMessage(options: { + env?: NodeJS.ProcessEnv; execArgv: string[]; messageType: string; script: string; @@ -123,7 +176,7 @@ async function runWithWorkerMessage(options: { const worker = new Worker(scriptUrl, { execArgv, - env: { ...process.env, NODE_OPTIONS: "" }, + env: { ...process.env, ...options.env, NODE_OPTIONS: "" }, }); worker.on("message", (msg) => { diff --git a/js/tsup.config.ts b/js/tsup.config.ts index ed1425524..6ca5e2fce 100644 --- a/js/tsup.config.ts +++ b/js/tsup.config.ts @@ -3,7 +3,10 @@ import { defineConfig } from "tsup"; export default defineConfig([ // Node.js entrypoint { - entry: ["src/node/index.ts"], + entry: { + index: "src/node/index.ts", + "apply-instrumentation": "src/node/apply-instrumentation-entry.ts", + }, format: ["cjs", "esm"], outDir: "dist", external: ["zod"], @@ -63,6 +66,8 @@ export default defineConfig([ browser: "src/browser/index.ts", "edge-light": "src/edge-light/index.ts", workerd: "src/workerd/index.ts", + "apply-instrumentation.browser": + "src/non-node/apply-instrumentation-entry.ts", }, format: ["cjs", "esm"], outDir: "dist",