@sentry/cloudflare 11.0.0-beta.1 → 11.0.0-rc.0
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 +19 -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 +20 -18
- package/build/cjs/dev/instrumentations/worker/instrumentD1.js.map +1 -1
- package/build/cjs/dev/instrumentations/worker/instrumentEmail.js +4 -3
- package/build/cjs/dev/instrumentations/worker/instrumentEmail.js.map +1 -1
- package/build/cjs/dev/instrumentations/worker/instrumentScheduled.js +10 -5
- package/build/cjs/dev/instrumentations/worker/instrumentScheduled.js.map +1 -1
- package/build/cjs/dev/orchestrion-deferred-channels.js +39 -0
- package/build/cjs/dev/orchestrion-deferred-channels.js.map +1 -0
- package/build/cjs/dev/orchestrion-diagnostics-channel.js +83 -0
- package/build/cjs/dev/orchestrion-diagnostics-channel.js.map +1 -0
- 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/index.js +6 -1
- package/build/cjs/dev/vite/index.js.map +1 -1
- package/build/cjs/dev/vite/mastraObservability.js +39 -0
- package/build/cjs/dev/vite/mastraObservability.js.map +1 -0
- 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/dev/wrapMethodWithSentry.js +8 -4
- package/build/cjs/dev/wrapMethodWithSentry.js.map +1 -1
- package/build/cjs/dev/wrapRequestHandlerWithInit.js +2 -0
- package/build/cjs/dev/wrapRequestHandlerWithInit.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 +19 -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 +20 -18
- package/build/cjs/prod/instrumentations/worker/instrumentD1.js.map +1 -1
- package/build/cjs/prod/instrumentations/worker/instrumentEmail.js +4 -3
- package/build/cjs/prod/instrumentations/worker/instrumentEmail.js.map +1 -1
- package/build/cjs/prod/instrumentations/worker/instrumentScheduled.js +10 -5
- package/build/cjs/prod/instrumentations/worker/instrumentScheduled.js.map +1 -1
- package/build/cjs/prod/orchestrion-deferred-channels.js +39 -0
- package/build/cjs/prod/orchestrion-deferred-channels.js.map +1 -0
- package/build/cjs/prod/orchestrion-diagnostics-channel.js +83 -0
- package/build/cjs/prod/orchestrion-diagnostics-channel.js.map +1 -0
- 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/index.js +6 -1
- package/build/cjs/prod/vite/index.js.map +1 -1
- package/build/cjs/prod/vite/mastraObservability.js +39 -0
- package/build/cjs/prod/vite/mastraObservability.js.map +1 -0
- 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/cjs/prod/wrapMethodWithSentry.js +8 -4
- package/build/cjs/prod/wrapMethodWithSentry.js.map +1 -1
- package/build/cjs/prod/wrapRequestHandlerWithInit.js +2 -0
- package/build/cjs/prod/wrapRequestHandlerWithInit.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 +20 -18
- package/build/esm/dev/instrumentations/worker/instrumentD1.js.map +1 -1
- package/build/esm/dev/instrumentations/worker/instrumentEmail.js +6 -5
- package/build/esm/dev/instrumentations/worker/instrumentEmail.js.map +1 -1
- package/build/esm/dev/instrumentations/worker/instrumentScheduled.js +12 -7
- package/build/esm/dev/instrumentations/worker/instrumentScheduled.js.map +1 -1
- package/build/esm/dev/orchestrion-deferred-channels.js +35 -0
- package/build/esm/dev/orchestrion-deferred-channels.js.map +1 -0
- package/build/esm/dev/orchestrion-diagnostics-channel.js +77 -0
- package/build/esm/dev/orchestrion-diagnostics-channel.js.map +1 -0
- 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/index.js +6 -1
- package/build/esm/dev/vite/index.js.map +1 -1
- package/build/esm/dev/vite/mastraObservability.js +36 -0
- package/build/esm/dev/vite/mastraObservability.js.map +1 -0
- 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/dev/wrapMethodWithSentry.js +7 -3
- package/build/esm/dev/wrapMethodWithSentry.js.map +1 -1
- package/build/esm/dev/wrapRequestHandlerWithInit.js +2 -0
- package/build/esm/dev/wrapRequestHandlerWithInit.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 +20 -18
- package/build/esm/prod/instrumentations/worker/instrumentD1.js.map +1 -1
- package/build/esm/prod/instrumentations/worker/instrumentEmail.js +6 -5
- package/build/esm/prod/instrumentations/worker/instrumentEmail.js.map +1 -1
- package/build/esm/prod/instrumentations/worker/instrumentScheduled.js +12 -7
- package/build/esm/prod/instrumentations/worker/instrumentScheduled.js.map +1 -1
- package/build/esm/prod/orchestrion-deferred-channels.js +35 -0
- package/build/esm/prod/orchestrion-deferred-channels.js.map +1 -0
- package/build/esm/prod/orchestrion-diagnostics-channel.js +77 -0
- package/build/esm/prod/orchestrion-diagnostics-channel.js.map +1 -0
- 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/index.js +6 -1
- package/build/esm/prod/vite/index.js.map +1 -1
- package/build/esm/prod/vite/mastraObservability.js +36 -0
- package/build/esm/prod/vite/mastraObservability.js.map +1 -0
- 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/esm/prod/wrapMethodWithSentry.js +7 -3
- package/build/esm/prod/wrapMethodWithSentry.js.map +1 -1
- package/build/esm/prod/wrapRequestHandlerWithInit.js +2 -0
- package/build/esm/prod/wrapRequestHandlerWithInit.js.map +1 -1
- package/build/types/durableobject.d.ts.map +1 -1
- package/build/types/index.d.ts +4 -2
- 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/instrumentations/worker/instrumentD1.d.ts.map +1 -1
- package/build/types/instrumentations/worker/instrumentEmail.d.ts.map +1 -1
- package/build/types/instrumentations/worker/instrumentScheduled.d.ts.map +1 -1
- package/build/types/orchestrion-deferred-channels.d.ts +26 -0
- package/build/types/orchestrion-deferred-channels.d.ts.map +1 -0
- package/build/types/orchestrion-diagnostics-channel.d.ts +44 -0
- package/build/types/orchestrion-diagnostics-channel.d.ts.map +1 -0
- 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/index.d.ts.map +1 -1
- package/build/types/vite/mastraObservability.d.ts +27 -0
- package/build/types/vite/mastraObservability.d.ts.map +1 -0
- 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/build/types/wrapMethodWithSentry.d.ts.map +1 -1
- package/build/types/wrapRequestHandlerWithInit.d.ts.map +1 -1
- package/package.json +10 -5
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { SENTRY_SEGMENT_NAME_SOURCE, SENTRY_OP } from '@sentry/conventions/attributes';
|
|
1
|
+
import { SENTRY_SEGMENT_NAME_SOURCE, SENTRY_ORIGIN, FAAS_TRIGGER, FAAS_TIME, FAAS_CRON, CODE_FUNCTION_NAME, SENTRY_DESCRIPTION, SENTRY_OP } from '@sentry/conventions/attributes';
|
|
2
2
|
import { FUNCTION } from '@sentry/conventions/op';
|
|
3
|
-
import { withIsolationScope, startSpan,
|
|
3
|
+
import { withIsolationScope, startSpan, hasSpanStreamingEnabled, captureException } from '@sentry/core';
|
|
4
4
|
import { flushAndDispose } from '../../flush.js';
|
|
5
5
|
import { ensureInstrumented } from '../../instrument.js';
|
|
6
6
|
import { getFinalOptions } from '../../options.js';
|
|
@@ -17,15 +17,20 @@ function wrapScheduledHandler(controller, options, context, fn) {
|
|
|
17
17
|
const client = init({ ...options, ctx: context });
|
|
18
18
|
isolationScope.setClient(client);
|
|
19
19
|
addCloudResourceContext(isolationScope);
|
|
20
|
+
const description = `Scheduled Cron ${controller.cron}`;
|
|
20
21
|
return startSpan(
|
|
21
22
|
{
|
|
22
|
-
name:
|
|
23
|
+
name: client && hasSpanStreamingEnabled(client) ? "scheduled" : description,
|
|
23
24
|
attributes: {
|
|
24
25
|
[SENTRY_OP]: FUNCTION,
|
|
25
|
-
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
[
|
|
26
|
+
// override description inference by Relay to preserve the original (transaction-based) description.
|
|
27
|
+
// sentry-conventions can't map the special case for the "Scheduled Cron" prefix and the chron string.
|
|
28
|
+
[SENTRY_DESCRIPTION]: description,
|
|
29
|
+
[CODE_FUNCTION_NAME]: "scheduled",
|
|
30
|
+
[FAAS_CRON]: controller.cron,
|
|
31
|
+
[FAAS_TIME]: new Date(controller.scheduledTime).toISOString(),
|
|
32
|
+
[FAAS_TRIGGER]: "timer",
|
|
33
|
+
[SENTRY_ORIGIN]: "auto.faas.cloudflare.scheduled",
|
|
29
34
|
[SENTRY_SEGMENT_NAME_SOURCE]: "task"
|
|
30
35
|
}
|
|
31
36
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrumentScheduled.js","sources":["../../../../../src/instrumentations/worker/instrumentScheduled.ts"],"sourcesContent":["import type { ScheduledController } from '@cloudflare/workers-types';\nimport type { AnyExportedHandler } from '../../types';\nimport type { env as cloudflareEnv, WorkerEntrypoint } from 'cloudflare:workers';\nimport {
|
|
1
|
+
{"version":3,"file":"instrumentScheduled.js","sources":["../../../../../src/instrumentations/worker/instrumentScheduled.ts"],"sourcesContent":["import type { ScheduledController } from '@cloudflare/workers-types';\nimport type { AnyExportedHandler } from '../../types';\nimport type { env as cloudflareEnv, WorkerEntrypoint } from 'cloudflare:workers';\nimport {\n SENTRY_SEGMENT_NAME_SOURCE,\n CODE_FUNCTION_NAME,\n SENTRY_OP,\n FAAS_CRON,\n FAAS_TIME,\n FAAS_TRIGGER,\n SENTRY_DESCRIPTION,\n SENTRY_ORIGIN,\n} from '@sentry/conventions/attributes';\nimport { FUNCTION } from '@sentry/conventions/op';\nimport { captureException, hasSpanStreamingEnabled, startSpan, withIsolationScope } from '@sentry/core';\nimport type { CloudflareOptions } from '../../client';\nimport { flushAndDispose } from '../../flush';\nimport { ensureInstrumented } from '../../instrument';\nimport { getFinalOptions } from '../../options';\nimport { addCloudResourceContext } from '../../scope-utils';\nimport { init } from '../../sdk';\nimport { instrumentContext } from '../../utils/instrumentContext';\nimport { setInvocationState } from '../../utils/invocationContext';\nimport { instrumentEnv } from './instrumentEnv';\n\nfunction wrapScheduledHandler(\n controller: ScheduledController,\n options: CloudflareOptions,\n context: ExecutionContext,\n fn: () => unknown,\n): unknown {\n return withIsolationScope(isolationScope => {\n const waitUntil = context.waitUntil.bind(context);\n\n setInvocationState(isolationScope, { ctx: context });\n\n const client = init({ ...options, ctx: context });\n isolationScope.setClient(client);\n\n addCloudResourceContext(isolationScope);\n\n const description = `Scheduled Cron ${controller.cron}`;\n\n return startSpan(\n {\n name: client && hasSpanStreamingEnabled(client) ? 'scheduled' : description,\n attributes: {\n [SENTRY_OP]: FUNCTION,\n // override description inference by Relay to preserve the original (transaction-based) description.\n // sentry-conventions can't map the special case for the \"Scheduled Cron\" prefix and the chron string.\n [SENTRY_DESCRIPTION]: description,\n [CODE_FUNCTION_NAME]: 'scheduled',\n [FAAS_CRON]: controller.cron,\n [FAAS_TIME]: new Date(controller.scheduledTime).toISOString(),\n [FAAS_TRIGGER]: 'timer',\n [SENTRY_ORIGIN]: 'auto.faas.cloudflare.scheduled',\n [SENTRY_SEGMENT_NAME_SOURCE]: 'task',\n },\n },\n async () => {\n try {\n return await fn();\n } catch (e) {\n captureException(e, { mechanism: { handled: false, type: 'auto.faas.cloudflare.scheduled' } });\n throw e;\n } finally {\n waitUntil(flushAndDispose(client));\n }\n },\n );\n });\n}\n\n/**\n * Instruments a scheduled handler for ExportedHandler (env/ctx come from args).\n */\nexport function instrumentExportedHandlerScheduled<T extends AnyExportedHandler>(\n handler: T,\n optionsCallback: (env: typeof cloudflareEnv) => CloudflareOptions | undefined,\n): void {\n if (!('scheduled' in handler) || typeof handler.scheduled !== 'function') {\n return;\n }\n\n handler.scheduled = ensureInstrumented(\n handler.scheduled,\n original =>\n new Proxy(original, {\n apply(target, thisArg, args: Parameters<NonNullable<T['scheduled']>>) {\n const [controller, env, ctx] = args;\n const context = instrumentContext(ctx);\n const options = getFinalOptions(optionsCallback(env), env);\n args[1] = instrumentEnv(env, options);\n args[2] = context;\n\n return wrapScheduledHandler(controller, options, context, () => target.apply(thisArg, args));\n },\n }),\n );\n}\n\n/**\n * Instruments a scheduled method for WorkerEntrypoint (options/context already available).\n */\nexport function instrumentWorkerEntrypointScheduled<T extends WorkerEntrypoint>(\n instance: T,\n options: CloudflareOptions,\n context: ExecutionContext,\n): void {\n if (!instance.scheduled) {\n return;\n }\n\n const original = instance.scheduled.bind(instance);\n instance.scheduled = new Proxy(original, {\n apply(target, thisArg, args: [ScheduledController]) {\n const [controller] = args;\n\n return wrapScheduledHandler(controller, options, context, () => Reflect.apply(target, thisArg, args));\n },\n });\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { debug } from '@sentry/core';
|
|
2
|
+
import { DEBUG_BUILD } from './debug-build.js';
|
|
3
|
+
|
|
4
|
+
const deferred = [];
|
|
5
|
+
const DEFERRED = /* @__PURE__ */ Symbol("sentryOrchestrionDeferred");
|
|
6
|
+
function isGlobalScopeError(error) {
|
|
7
|
+
return error instanceof Error && /within global scope/.test(error.message);
|
|
8
|
+
}
|
|
9
|
+
function deferChannelEvent(channel, ctx) {
|
|
10
|
+
if (ctx[DEFERRED]) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
ctx[DEFERRED] = true;
|
|
14
|
+
deferred.push({ channel, ctx, hadError: "error" in ctx });
|
|
15
|
+
}
|
|
16
|
+
function flushDeferredChannelEvents() {
|
|
17
|
+
if (deferred.length === 0) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const pending = deferred.splice(0, deferred.length);
|
|
21
|
+
for (const { channel, ctx, hadError } of pending) {
|
|
22
|
+
try {
|
|
23
|
+
channel.start.publish(ctx);
|
|
24
|
+
if (hadError) {
|
|
25
|
+
channel.error.publish(ctx);
|
|
26
|
+
}
|
|
27
|
+
channel.end.publish(ctx);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
DEBUG_BUILD && debug.warn("[orchestrion] failed to emit a deferred channel event", error);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export { deferChannelEvent, flushDeferredChannelEvents, isGlobalScopeError };
|
|
35
|
+
//# sourceMappingURL=orchestrion-deferred-channels.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orchestrion-deferred-channels.js","sources":["../../../src/orchestrion-deferred-channels.ts"],"sourcesContent":["/**\n * Deferral for orchestrion channel events that could not be published when they occurred.\n *\n * On workerd, `channel.publish()`/`runStores()` throw at module/global scope, so a library\n * instantiated at module scope (`const app = new Hono()`) cannot emit its wrapping event\n * there. {@link ./orchestrion-diagnostics-channel} defers those events here;\n * {@link flushDeferredChannelEvents} emits them once a request is being handled (where\n * publish works) and the SDK's `init()` has registered subscribers.\n *\n * This module imports NO `node:*` builtin, so it is safe to pull into the request wrapper on\n * runtimes without `nodejs_compat` — there the queue is always empty (no channels were ever\n * injected) and the flush is a no-op.\n */\nimport { debug } from '@sentry/core';\nimport { DEBUG_BUILD } from './debug-build';\n\ntype Ctx = Record<string, unknown>;\n\n// The minimal slice of a `node:diagnostics_channel` TracingChannel we emit on. Declared locally\n// (rather than imported from `node:diagnostics_channel`) so this module's graph carries no `node:`\n// specifier — `wrapRequestHandler` must load on runtimes without `nodejs_compat`, and the\n// module-graph test forbids any `node:` import reachable from it.\ninterface PublishableTracingChannel {\n start: { publish(ctx: Ctx): void };\n end: { publish(ctx: Ctx): void };\n error: { publish(ctx: Ctx): void };\n}\n\ninterface DeferredChannelEvent {\n channel: PublishableTracingChannel;\n ctx: Ctx;\n hadError: boolean;\n}\n\nconst deferred: DeferredChannelEvent[] = [];\nconst DEFERRED = Symbol('sentryOrchestrionDeferred');\n\n/** workerd's global-scope guard is the only error we degrade on; anything else is a real fault. */\nexport function isGlobalScopeError(error: unknown): boolean {\n return error instanceof Error && /within global scope/.test(error.message);\n}\n\n/** Queue a channel event that could not be published now, at most once per context object. */\nexport function deferChannelEvent(channel: PublishableTracingChannel, ctx: Ctx): void {\n if ((ctx as Record<symbol, unknown>)[DEFERRED]) {\n return;\n }\n (ctx as Record<symbol, unknown>)[DEFERRED] = true;\n deferred.push({ channel, ctx, hadError: 'error' in ctx });\n}\n\n/**\n * Emit every deferred channel event via the real channel. Called at the top of the request\n * handler, after `init()` has subscribed. Uses bare `start`/`end` publish (never `runStores`),\n * so no `bindStore` producer runs and no span is parented to this request — only the plain\n * subscribers (the instance patchers) fire. Idempotent: it drains the queue, so later requests\n * and re-entrant calls are no-ops.\n */\nexport function flushDeferredChannelEvents(): void {\n if (deferred.length === 0) {\n return;\n }\n const pending = deferred.splice(0, deferred.length);\n for (const { channel, ctx, hadError } of pending) {\n try {\n channel.start.publish(ctx);\n if (hadError) {\n channel.error.publish(ctx);\n }\n channel.end.publish(ctx);\n } catch (error) {\n DEBUG_BUILD && debug.warn('[orchestrion] failed to emit a deferred channel event', error);\n }\n }\n}\n"],"names":[],"mappings":";;;AAkCA,MAAM,WAAmC,EAAC;AAC1C,MAAM,QAAA,0BAAkB,2BAA2B,CAAA;AAG5C,SAAS,mBAAmB,KAAA,EAAyB;AAC1D,EAAA,OAAO,KAAA,YAAiB,KAAA,IAAS,qBAAA,CAAsB,IAAA,CAAK,MAAM,OAAO,CAAA;AAC3E;AAGO,SAAS,iBAAA,CAAkB,SAAoC,GAAA,EAAgB;AACpF,EAAA,IAAK,GAAA,CAAgC,QAAQ,CAAA,EAAG;AAC9C,IAAA;AAAA,EACF;AACA,EAAC,GAAA,CAAgC,QAAQ,CAAA,GAAI,IAAA;AAC7C,EAAA,QAAA,CAAS,KAAK,EAAE,OAAA,EAAS,KAAK,QAAA,EAAU,OAAA,IAAW,KAAK,CAAA;AAC1D;AASO,SAAS,0BAAA,GAAmC;AACjD,EAAA,IAAI,QAAA,CAAS,WAAW,CAAA,EAAG;AACzB,IAAA;AAAA,EACF;AACA,EAAA,MAAM,OAAA,GAAU,QAAA,CAAS,MAAA,CAAO,CAAA,EAAG,SAAS,MAAM,CAAA;AAClD,EAAA,KAAA,MAAW,EAAE,OAAA,EAAS,GAAA,EAAK,QAAA,MAAc,OAAA,EAAS;AAChD,IAAA,IAAI;AACF,MAAA,OAAA,CAAQ,KAAA,CAAM,QAAQ,GAAG,CAAA;AACzB,MAAA,IAAI,QAAA,EAAU;AACZ,QAAA,OAAA,CAAQ,KAAA,CAAM,QAAQ,GAAG,CAAA;AAAA,MAC3B;AACA,MAAA,OAAA,CAAQ,GAAA,CAAI,QAAQ,GAAG,CAAA;AAAA,IACzB,SAAS,KAAA,EAAO;AACd,MAAA,WAAA,IAAe,KAAA,CAAM,IAAA,CAAK,uDAAA,EAAyD,KAAK,CAAA;AAAA,IAC1F;AAAA,EACF;AACF;;;;"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import * as realDc from 'node:diagnostics_channel';
|
|
2
|
+
import { isGlobalScopeError, deferChannelEvent } from './orchestrion-deferred-channels.js';
|
|
3
|
+
|
|
4
|
+
const cache = /* @__PURE__ */ new Map();
|
|
5
|
+
function channelHasSubscribers(ch) {
|
|
6
|
+
const value = ch.hasSubscribers;
|
|
7
|
+
return typeof value === "function" ? Boolean(value.call(ch)) : Boolean(value);
|
|
8
|
+
}
|
|
9
|
+
function wrapSubChannel(tracing, real) {
|
|
10
|
+
return {
|
|
11
|
+
name: real.name,
|
|
12
|
+
// A method (not a getter) so it reads truthy as a property in the emit guard *and* is callable,
|
|
13
|
+
// matching workerd's native shape. Forced `true`: on Cloudflare a channel is only injected for a
|
|
14
|
+
// bundled integration, so the wrapper's no-subscriber short-circuit buys nothing — and disabling
|
|
15
|
+
// it is what lets the wrapper run (and reach us) at module scope before `init()` has subscribed.
|
|
16
|
+
hasSubscribers() {
|
|
17
|
+
return true;
|
|
18
|
+
},
|
|
19
|
+
publish(ctx) {
|
|
20
|
+
const hadSubscribers = channelHasSubscribers(real);
|
|
21
|
+
try {
|
|
22
|
+
real.publish(ctx);
|
|
23
|
+
} catch (error) {
|
|
24
|
+
if (!isGlobalScopeError(error)) {
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
if (!hadSubscribers) {
|
|
28
|
+
deferChannelEvent(tracing, ctx);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
runStores(ctx, fn, ...rest) {
|
|
33
|
+
try {
|
|
34
|
+
return real.runStores(ctx, fn, ...rest);
|
|
35
|
+
} catch (error) {
|
|
36
|
+
if (!isGlobalScopeError(error)) {
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
39
|
+
return fn();
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
subscribe: real.subscribe.bind(real),
|
|
43
|
+
unsubscribe: real.unsubscribe.bind(real),
|
|
44
|
+
bindStore: real.bindStore.bind(real),
|
|
45
|
+
unbindStore: real.unbindStore.bind(real)
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function tracingChannel(name) {
|
|
49
|
+
const cached = cache.get(name);
|
|
50
|
+
if (cached) {
|
|
51
|
+
return cached;
|
|
52
|
+
}
|
|
53
|
+
const real = realDc.tracingChannel(name);
|
|
54
|
+
const facade = {
|
|
55
|
+
start: wrapSubChannel(real, real.start),
|
|
56
|
+
end: wrapSubChannel(real, real.end),
|
|
57
|
+
asyncStart: wrapSubChannel(real, real.asyncStart),
|
|
58
|
+
asyncEnd: wrapSubChannel(real, real.asyncEnd),
|
|
59
|
+
error: wrapSubChannel(real, real.error),
|
|
60
|
+
// Iterator (`:next`) channels call these on the tracingChannel itself; delegate to the real
|
|
61
|
+
// implementation (they only run in-request, where publish/runStores are allowed).
|
|
62
|
+
traceSync: real.traceSync.bind(real),
|
|
63
|
+
tracePromise: real.tracePromise.bind(real),
|
|
64
|
+
traceCallback: real.traceCallback.bind(real),
|
|
65
|
+
subscribe: real.subscribe.bind(real),
|
|
66
|
+
unsubscribe: real.unsubscribe.bind(real)
|
|
67
|
+
};
|
|
68
|
+
cache.set(name, facade);
|
|
69
|
+
return facade;
|
|
70
|
+
}
|
|
71
|
+
const channel = realDc.channel;
|
|
72
|
+
const subscribe = realDc.subscribe;
|
|
73
|
+
const unsubscribe = realDc.unsubscribe;
|
|
74
|
+
const orchestrionDiagnosticsChannel = { tracingChannel, channel, subscribe, unsubscribe };
|
|
75
|
+
|
|
76
|
+
export { channel, orchestrionDiagnosticsChannel as default, subscribe, tracingChannel, unsubscribe };
|
|
77
|
+
//# sourceMappingURL=orchestrion-diagnostics-channel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orchestrion-diagnostics-channel.js","sources":["../../../src/orchestrion-diagnostics-channel.ts"],"sourcesContent":["/**\n * A workerd-safe `diagnostics_channel` façade for the orchestrion EMIT side, and the\n * `dcModule` target for the Cloudflare orchestrion build (see `vite/index.ts`): orchestrion's\n * transformed code imports `tracingChannel` from here (its default export) instead of\n * `node:diagnostics_channel`.\n *\n * On workerd, `channel.publish()` and `channel.runStores()` throw at module/global scope\n * (\"Disallowed operation called within global scope …\"); only inside a request handler do they\n * work. `subscribe` and channel creation are fine anywhere. Orchestrion's transformed code emits\n * `start.runStores(...)` / `end.publish(...)` at each wrapped call site, so a library\n * instantiated at module scope (`const app = new Hono()`) throws the moment it is wrapped — or,\n * if no subscriber exists yet, is skipped by the wrapper's `if (!hasSubscribers) return\n * __apm$traced()` short-circuit and never instrumented.\n *\n * This façade delegates to the real `node:diagnostics_channel` singletons — so subscribers, which\n * keep subscribing through `node:diagnostics_channel`, are reached unchanged — and changes two\n * things:\n *\n * 1. `hasSubscribers` always reports `true`, so the wrapper never short-circuits. On Cloudflare a\n * channel is only injected for an integration that is actually bundled, so the short-circuit\n * buys nothing — and disabling it is what lets the wrapper run (and reach us) at module scope,\n * before the per-request cached `init()` has subscribed.\n * 2. When a delegated `publish`/`runStores` throws the global-scope error, the event is queued via\n * {@link deferChannelEvent} instead. {@link flushDeferredChannelEvents}, called at the top of\n * the request handler after `init()`, emits it via the real channel — now inside a request,\n * where publish works.\n *\n * In a request handler nothing throws, so every call is a transparent pass-through to the real\n * channel: lifecycle channels (spans, context binding) behave exactly as on Node.\n */\nimport * as realDc from 'node:diagnostics_channel';\nimport { deferChannelEvent, isGlobalScopeError } from './orchestrion-deferred-channels';\n\ntype Ctx = Record<string, unknown>;\n\n// One façade per channel name (node's `tracingChannel` is itself a per-name singleton), built\n// once when the transformed module declares `const CH = tracingChannel(name)`.\nconst cache = new Map<string, realDc.TracingChannel>();\n\n// `Channel.hasSubscribers` is a getter on Node but a method on workerd — normalize both to a\n// boolean. (This shape difference is also why the emit wrapper never short-circuits on workerd:\n// its `if (!ch.start.hasSubscribers)` guard reads a truthy method reference.)\nfunction channelHasSubscribers(ch: realDc.Channel): boolean {\n const value = (ch as { hasSubscribers: unknown }).hasSubscribers;\n return typeof value === 'function' ? Boolean((value as () => unknown).call(ch)) : Boolean(value);\n}\n\nfunction wrapSubChannel(tracing: realDc.TracingChannel, real: realDc.Channel): realDc.Channel {\n return {\n name: real.name,\n // A method (not a getter) so it reads truthy as a property in the emit guard *and* is callable,\n // matching workerd's native shape. Forced `true`: on Cloudflare a channel is only injected for a\n // bundled integration, so the wrapper's no-subscriber short-circuit buys nothing — and disabling\n // it is what lets the wrapper run (and reach us) at module scope before `init()` has subscribed.\n hasSubscribers(): boolean {\n return true;\n },\n publish(ctx: Ctx): void {\n // Snapshot subscriber presence before publishing — it decides whether to defer if the\n // global-scope error fires below.\n const hadSubscribers = channelHasSubscribers(real);\n try {\n real.publish(ctx);\n } catch (error) {\n if (!isGlobalScopeError(error)) {\n throw error;\n }\n // We're at module scope (workerd throws here even with no subscribers). If someone was\n // already subscribed — top-level `Sentry.init()` — workerd delivered to them before\n // throwing, so they got it exactly once; don't defer. Otherwise — the common per-request\n // cached-init case — nobody received it, so defer for a subscriber that registers later\n // (at `init()`), which then gets it exactly once. In a request `publish` doesn't throw,\n // so this path never defers a normal in-request event.\n if (!hadSubscribers) {\n deferChannelEvent(tracing, ctx);\n }\n }\n },\n runStores(ctx: Ctx, fn: (...args: unknown[]) => unknown, ...rest: unknown[]): unknown {\n try {\n return real.runStores(ctx, fn, ...rest);\n } catch (error) {\n if (!isGlobalScopeError(error)) {\n throw error;\n }\n // publish() is gated too, so the store can't be established — just run the wrapped call\n // so its return value (e.g. the constructed instance) is produced now. Its own\n // `end.publish` runs back through this façade and is deferred.\n return fn();\n }\n },\n subscribe: real.subscribe.bind(real),\n unsubscribe: real.unsubscribe.bind(real),\n bindStore: real.bindStore.bind(real),\n unbindStore: real.unbindStore.bind(real),\n } as unknown as realDc.Channel;\n}\n\nexport function tracingChannel<T = Ctx>(name: string): realDc.TracingChannel<T> {\n const cached = cache.get(name);\n if (cached) {\n return cached;\n }\n\n const real = realDc.tracingChannel(name);\n const facade = {\n start: wrapSubChannel(real, real.start),\n end: wrapSubChannel(real, real.end),\n asyncStart: wrapSubChannel(real, real.asyncStart),\n asyncEnd: wrapSubChannel(real, real.asyncEnd),\n error: wrapSubChannel(real, real.error),\n // Iterator (`:next`) channels call these on the tracingChannel itself; delegate to the real\n // implementation (they only run in-request, where publish/runStores are allowed).\n traceSync: real.traceSync.bind(real),\n tracePromise: real.tracePromise.bind(real),\n traceCallback: real.traceCallback.bind(real),\n subscribe: real.subscribe.bind(real),\n unsubscribe: real.unsubscribe.bind(real),\n } as unknown as realDc.TracingChannel<T>;\n\n cache.set(name, facade);\n return facade;\n}\n\nexport const channel = realDc.channel;\nexport const subscribe = realDc.subscribe;\nexport const unsubscribe = realDc.unsubscribe;\n\n// Orchestrion's emit imports the default and destructures `tracingChannel`.\nexport default { tracingChannel, channel, subscribe, unsubscribe };\n"],"names":[],"mappings":";;;AAqCA,MAAM,KAAA,uBAAY,GAAA,EAAmC;AAKrD,SAAS,sBAAsB,EAAA,EAA6B;AAC1D,EAAA,MAAM,QAAS,EAAA,CAAmC,cAAA;AAClD,EAAA,OAAO,OAAO,KAAA,KAAU,UAAA,GAAa,OAAA,CAAS,KAAA,CAAwB,KAAK,EAAE,CAAC,CAAA,GAAI,OAAA,CAAQ,KAAK,CAAA;AACjG;AAEA,SAAS,cAAA,CAAe,SAAgC,IAAA,EAAsC;AAC5F,EAAA,OAAO;AAAA,IACL,MAAM,IAAA,CAAK,IAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAKX,cAAA,GAA0B;AACxB,MAAA,OAAO,IAAA;AAAA,IACT,CAAA;AAAA,IACA,QAAQ,GAAA,EAAgB;AAGtB,MAAA,MAAM,cAAA,GAAiB,sBAAsB,IAAI,CAAA;AACjD,MAAA,IAAI;AACF,QAAA,IAAA,CAAK,QAAQ,GAAG,CAAA;AAAA,MAClB,SAAS,KAAA,EAAO;AACd,QAAA,IAAI,CAAC,kBAAA,CAAmB,KAAK,CAAA,EAAG;AAC9B,UAAA,MAAM,KAAA;AAAA,QACR;AAOA,QAAA,IAAI,CAAC,cAAA,EAAgB;AACnB,UAAA,iBAAA,CAAkB,SAAS,GAAG,CAAA;AAAA,QAChC;AAAA,MACF;AAAA,IACF,CAAA;AAAA,IACA,SAAA,CAAU,GAAA,EAAU,EAAA,EAAA,GAAwC,IAAA,EAA0B;AACpF,MAAA,IAAI;AACF,QAAA,OAAO,IAAA,CAAK,SAAA,CAAU,GAAA,EAAK,EAAA,EAAI,GAAG,IAAI,CAAA;AAAA,MACxC,SAAS,KAAA,EAAO;AACd,QAAA,IAAI,CAAC,kBAAA,CAAmB,KAAK,CAAA,EAAG;AAC9B,UAAA,MAAM,KAAA;AAAA,QACR;AAIA,QAAA,OAAO,EAAA,EAAG;AAAA,MACZ;AAAA,IACF,CAAA;AAAA,IACA,SAAA,EAAW,IAAA,CAAK,SAAA,CAAU,IAAA,CAAK,IAAI,CAAA;AAAA,IACnC,WAAA,EAAa,IAAA,CAAK,WAAA,CAAY,IAAA,CAAK,IAAI,CAAA;AAAA,IACvC,SAAA,EAAW,IAAA,CAAK,SAAA,CAAU,IAAA,CAAK,IAAI,CAAA;AAAA,IACnC,WAAA,EAAa,IAAA,CAAK,WAAA,CAAY,IAAA,CAAK,IAAI;AAAA,GACzC;AACF;AAEO,SAAS,eAAwB,IAAA,EAAwC;AAC9E,EAAA,MAAM,MAAA,GAAS,KAAA,CAAM,GAAA,CAAI,IAAI,CAAA;AAC7B,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,cAAA,CAAe,IAAI,CAAA;AACvC,EAAA,MAAM,MAAA,GAAS;AAAA,IACb,KAAA,EAAO,cAAA,CAAe,IAAA,EAAM,IAAA,CAAK,KAAK,CAAA;AAAA,IACtC,GAAA,EAAK,cAAA,CAAe,IAAA,EAAM,IAAA,CAAK,GAAG,CAAA;AAAA,IAClC,UAAA,EAAY,cAAA,CAAe,IAAA,EAAM,IAAA,CAAK,UAAU,CAAA;AAAA,IAChD,QAAA,EAAU,cAAA,CAAe,IAAA,EAAM,IAAA,CAAK,QAAQ,CAAA;AAAA,IAC5C,KAAA,EAAO,cAAA,CAAe,IAAA,EAAM,IAAA,CAAK,KAAK,CAAA;AAAA;AAAA;AAAA,IAGtC,SAAA,EAAW,IAAA,CAAK,SAAA,CAAU,IAAA,CAAK,IAAI,CAAA;AAAA,IACnC,YAAA,EAAc,IAAA,CAAK,YAAA,CAAa,IAAA,CAAK,IAAI,CAAA;AAAA,IACzC,aAAA,EAAe,IAAA,CAAK,aAAA,CAAc,IAAA,CAAK,IAAI,CAAA;AAAA,IAC3C,SAAA,EAAW,IAAA,CAAK,SAAA,CAAU,IAAA,CAAK,IAAI,CAAA;AAAA,IACnC,WAAA,EAAa,IAAA,CAAK,WAAA,CAAY,IAAA,CAAK,IAAI;AAAA,GACzC;AAEA,EAAA,KAAA,CAAM,GAAA,CAAI,MAAM,MAAM,CAAA;AACtB,EAAA,OAAO,MAAA;AACT;AAEO,MAAM,UAAU,MAAA,CAAO;AACvB,MAAM,YAAY,MAAA,CAAO;AACzB,MAAM,cAAc,MAAA,CAAO;AAGlC,sCAAe,EAAE,cAAA,EAAgB,OAAA,EAAS,SAAA,EAAW,WAAA,EAAY;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"type":"module","version":"11.0.0-
|
|
1
|
+
{"type":"module","version":"11.0.0-rc.0","sideEffects":false}
|
|
@@ -21,12 +21,13 @@ async function detectAgentClasses(ast, entryId, candidates, resolver) {
|
|
|
21
21
|
function collectAgentCandidates(ast, configuredNames) {
|
|
22
22
|
const shape = shapeFromAst(ast);
|
|
23
23
|
const candidates = /* @__PURE__ */ new Set();
|
|
24
|
+
const isResolvable = (name) => shape.classes.has(name) || shape.imports.has(name) || shape.reexports.has(name);
|
|
24
25
|
for (const configured of configuredNames) {
|
|
25
|
-
if (
|
|
26
|
+
if (isResolvable(configured)) {
|
|
26
27
|
candidates.add(configured);
|
|
27
28
|
}
|
|
28
29
|
const local = shape.localExports.get(configured);
|
|
29
|
-
if (local &&
|
|
30
|
+
if (local && isResolvable(local)) {
|
|
30
31
|
candidates.add(local);
|
|
31
32
|
}
|
|
32
33
|
}
|
|
@@ -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":[],"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":[],"mappings":"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"}
|
|
@@ -81,7 +81,7 @@ function sentryCloudflareAutoInstrumentPlugin(options = {}) {
|
|
|
81
81
|
const missing = [...classWrappers.keys()].filter((name) => !wrappedClasses.has(name));
|
|
82
82
|
if (missing.length > 0) {
|
|
83
83
|
this.warn?.(
|
|
84
|
-
`[sentry] Could not auto-instrument
|
|
84
|
+
`[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.`
|
|
85
85
|
);
|
|
86
86
|
}
|
|
87
87
|
return result ?? void 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"autoInstrument.js","sources":["../../../../src/vite/autoInstrument.ts"],"sourcesContent":["import { basename } from 'node:path';\nimport { collectAgentCandidates, detectAgentClasses, type ModuleResolver } from './agentClass';\nimport { buildOptionsImport, ENV_FALLBACK_OPTIONS_FN, resolveInstrumentFile } from './instrumentFile';\nimport { applyAutoInstrumentTransforms, type ClassWrapperKind, type ProgramBody } from './transform';\nimport { resolveWranglerConfig, type WranglerConfig } from './wranglerConfig';\n\n// Vite normalizes module IDs to posix separators even on Windows, while\n// `path.resolve` yields backslashes there — normalize before comparing.\nfunction normalizePath(path: string): string {\n return path.replace(/\\\\/g, '/');\n}\n\n// Extensions the entry-module match may tolerate swapping (e.g. wrangler's\n// `main` says `.ts` but the served module is `.js`). Anything else — `.css`,\n// `.html`, … — sharing the entry's basename must never be treated as the entry.\nconst JS_EXTENSION_REGEX = /\\.[cm]?[jt]sx?$/;\n\nexport function sentryCloudflareAutoInstrumentPlugin(options: { wranglerConfigPath?: string } = {}) {\n let wranglerConfig: WranglerConfig | undefined;\n let entryFilePath: string | undefined;\n\n let optionsFn = ENV_FALLBACK_OPTIONS_FN;\n let optionsImport: string | undefined;\n\n return {\n name: 'sentry-cloudflare-auto-instrument',\n\n configResolved(config: { root: string; logger?: { warn(msg: string): void } }): void {\n const result = resolveWranglerConfig(config.root, options.wranglerConfigPath);\n if (!result) {\n // An explicit path that fails is a misconfiguration worth naming;\n // without one, hint at the option so custom-named configs (e.g. a\n // `configPath` handed to @cloudflare/vite-plugin) are discoverable.\n config.logger?.warn(\n options.wranglerConfigPath\n ? `[sentry] Could not find or parse the wrangler config \"${basename(options.wranglerConfigPath)}\" ` +\n '(resolved against the Vite root) — auto-instrumentation disabled.'\n : '[sentry] No parseable wrangler config found — auto-instrumentation disabled. ' +\n 'Set `wranglerConfigPath` if your config uses a custom name.',\n );\n return;\n }\n\n wranglerConfig = result.config;\n if (wranglerConfig.main) {\n // `main` is already absolute (wrangler resolves it); just normalize\n // separators so the entry-module comparison holds on Windows.\n entryFilePath = normalizePath(wranglerConfig.main);\n }\n\n if (entryFilePath) {\n const instrumentFilePath = resolveInstrumentFile(entryFilePath);\n if (instrumentFilePath) {\n const built = buildOptionsImport(entryFilePath, instrumentFilePath);\n optionsFn = built.optionsFn;\n optionsImport = built.importStmt;\n }\n }\n },\n\n async transform(\n this: ModuleResolver & { warn?(msg: string): void; environment?: { name?: string } },\n code: string,\n id: string,\n ): Promise<{ code: string; map: unknown } | undefined> {\n if (!wranglerConfig || !entryFilePath) return undefined;\n\n // The worker entry never belongs to the client (browser) environment.\n // Skipping it keeps a same-basename sibling (e.g. a `src/index.tsx`\n // client entry next to a `src/index.ts` worker) out of the browser bundle.\n if (this.environment?.name === 'client') return undefined;\n\n // Vite may append query/hash params to the module ID.\n const normalizedId = normalizePath(id.replace(/[?#].*$/, ''));\n if (normalizedId !== entryFilePath) {\n // Tolerate a differing JS-flavored extension (e.g. `.js` vs `.ts`).\n if (!JS_EXTENSION_REGEX.test(normalizedId) || !JS_EXTENSION_REGEX.test(entryFilePath)) return undefined;\n if (normalizedId.replace(JS_EXTENSION_REGEX, '') !== entryFilePath.replace(JS_EXTENSION_REGEX, '')) {\n return undefined;\n }\n }\n\n let ast: ProgramBody;\n try {\n ast = this.parse(code);\n } catch {\n // Raw TypeScript or syntax error — esbuild hasn't run yet (unlikely)\n // or the file is genuinely broken. Either way, skip silently.\n return undefined;\n }\n\n const classWrappers = new Map<string, ClassWrapperKind>();\n for (const { className } of wranglerConfig.durableObjects) {\n classWrappers.set(className, 'durableObject');\n }\n for (const { className } of wranglerConfig.workflows) {\n classWrappers.set(className, 'workflow');\n }\n for (const className of wranglerConfig.workerEntrypoints) {\n classWrappers.set(className, 'workerEntrypoint');\n }\n\n // An `agents` Agent is a Durable Object, so wrangler lists it among the DO bindings and only\n // its base-class chain tells the two apart. Detection walks the module graph (base classes\n // usually live in their own file), so it is limited to the configured DO classes.\n //\n // Only `resolve` is handed over — never `load`: awaiting `load()` from inside a transform\n // hook deadlocks the build, since the module can't finish loading while this transform is\n // still pending. `agentClass` reads sibling modules off disk instead.\n const agentCandidates = collectAgentCandidates(\n ast,\n [...classWrappers].filter(([, kind]) => kind === 'durableObject').map(([name]) => name),\n );\n const agentClasses =\n agentCandidates.size > 0\n ? await detectAgentClasses(ast, normalizedId, agentCandidates, {\n parse: code => this.parse(code),\n resolve: this.resolve ? (source, importer) => this.resolve!(source, importer) : undefined,\n })\n : undefined;\n\n // No registration import is injected here: the orchestrion plugin's\n // subscribe-injection makes each bundled package self-register its channel\n // subscriber on the global marker, so wrapping the entry with `withSentry`\n // is all this plugin needs to do.\n const result = applyAutoInstrumentTransforms(code, ast, {\n classWrappers,\n agentClasses,\n optionsFn,\n optionsImport,\n sameWorkerBindings: wranglerConfig.sameWorkerBindings,\n });\n\n const wrappedClasses = result?.wrappedClasses ?? new Set<string>();\n const missing = [...classWrappers.keys()].filter(name => !wrappedClasses.has(name));\n if (missing.length > 0) {\n this.warn?.(\n `[sentry] Could not auto-instrument class(es) ${missing.join(', ')}: no matching exported class ` +\n 'declaration found in the worker entry (re-exports from other modules cannot be wrapped ' +\n 'automatically). Wrap them manually with the matching `instrument*WithSentry` helper.',\n );\n }\n\n return result ?? undefined;\n },\n };\n}\n"],"names":["code"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"autoInstrument.js","sources":["../../../../src/vite/autoInstrument.ts"],"sourcesContent":["import { basename } from 'node:path';\nimport { collectAgentCandidates, detectAgentClasses, type ModuleResolver } from './agentClass';\nimport { buildOptionsImport, ENV_FALLBACK_OPTIONS_FN, resolveInstrumentFile } from './instrumentFile';\nimport { applyAutoInstrumentTransforms, type ClassWrapperKind, type ProgramBody } from './transform';\nimport { resolveWranglerConfig, type WranglerConfig } from './wranglerConfig';\n\n// Vite normalizes module IDs to posix separators even on Windows, while\n// `path.resolve` yields backslashes there — normalize before comparing.\nfunction normalizePath(path: string): string {\n return path.replace(/\\\\/g, '/');\n}\n\n// Extensions the entry-module match may tolerate swapping (e.g. wrangler's\n// `main` says `.ts` but the served module is `.js`). Anything else — `.css`,\n// `.html`, … — sharing the entry's basename must never be treated as the entry.\nconst JS_EXTENSION_REGEX = /\\.[cm]?[jt]sx?$/;\n\nexport function sentryCloudflareAutoInstrumentPlugin(options: { wranglerConfigPath?: string } = {}) {\n let wranglerConfig: WranglerConfig | undefined;\n let entryFilePath: string | undefined;\n\n let optionsFn = ENV_FALLBACK_OPTIONS_FN;\n let optionsImport: string | undefined;\n\n return {\n name: 'sentry-cloudflare-auto-instrument',\n\n configResolved(config: { root: string; logger?: { warn(msg: string): void } }): void {\n const result = resolveWranglerConfig(config.root, options.wranglerConfigPath);\n if (!result) {\n // An explicit path that fails is a misconfiguration worth naming;\n // without one, hint at the option so custom-named configs (e.g. a\n // `configPath` handed to @cloudflare/vite-plugin) are discoverable.\n config.logger?.warn(\n options.wranglerConfigPath\n ? `[sentry] Could not find or parse the wrangler config \"${basename(options.wranglerConfigPath)}\" ` +\n '(resolved against the Vite root) — auto-instrumentation disabled.'\n : '[sentry] No parseable wrangler config found — auto-instrumentation disabled. ' +\n 'Set `wranglerConfigPath` if your config uses a custom name.',\n );\n return;\n }\n\n wranglerConfig = result.config;\n if (wranglerConfig.main) {\n // `main` is already absolute (wrangler resolves it); just normalize\n // separators so the entry-module comparison holds on Windows.\n entryFilePath = normalizePath(wranglerConfig.main);\n }\n\n if (entryFilePath) {\n const instrumentFilePath = resolveInstrumentFile(entryFilePath);\n if (instrumentFilePath) {\n const built = buildOptionsImport(entryFilePath, instrumentFilePath);\n optionsFn = built.optionsFn;\n optionsImport = built.importStmt;\n }\n }\n },\n\n async transform(\n this: ModuleResolver & { warn?(msg: string): void; environment?: { name?: string } },\n code: string,\n id: string,\n ): Promise<{ code: string; map: unknown } | undefined> {\n if (!wranglerConfig || !entryFilePath) return undefined;\n\n // The worker entry never belongs to the client (browser) environment.\n // Skipping it keeps a same-basename sibling (e.g. a `src/index.tsx`\n // client entry next to a `src/index.ts` worker) out of the browser bundle.\n if (this.environment?.name === 'client') return undefined;\n\n // Vite may append query/hash params to the module ID.\n const normalizedId = normalizePath(id.replace(/[?#].*$/, ''));\n if (normalizedId !== entryFilePath) {\n // Tolerate a differing JS-flavored extension (e.g. `.js` vs `.ts`).\n if (!JS_EXTENSION_REGEX.test(normalizedId) || !JS_EXTENSION_REGEX.test(entryFilePath)) return undefined;\n if (normalizedId.replace(JS_EXTENSION_REGEX, '') !== entryFilePath.replace(JS_EXTENSION_REGEX, '')) {\n return undefined;\n }\n }\n\n let ast: ProgramBody;\n try {\n ast = this.parse(code);\n } catch {\n // Raw TypeScript or syntax error — esbuild hasn't run yet (unlikely)\n // or the file is genuinely broken. Either way, skip silently.\n return undefined;\n }\n\n const classWrappers = new Map<string, ClassWrapperKind>();\n for (const { className } of wranglerConfig.durableObjects) {\n classWrappers.set(className, 'durableObject');\n }\n for (const { className } of wranglerConfig.workflows) {\n classWrappers.set(className, 'workflow');\n }\n for (const className of wranglerConfig.workerEntrypoints) {\n classWrappers.set(className, 'workerEntrypoint');\n }\n\n // An `agents` Agent is a Durable Object, so wrangler lists it among the DO bindings and only\n // its base-class chain tells the two apart. Detection walks the module graph (base classes\n // usually live in their own file), so it is limited to the configured DO classes.\n //\n // Only `resolve` is handed over — never `load`: awaiting `load()` from inside a transform\n // hook deadlocks the build, since the module can't finish loading while this transform is\n // still pending. `agentClass` reads sibling modules off disk instead.\n const agentCandidates = collectAgentCandidates(\n ast,\n [...classWrappers].filter(([, kind]) => kind === 'durableObject').map(([name]) => name),\n );\n const agentClasses =\n agentCandidates.size > 0\n ? await detectAgentClasses(ast, normalizedId, agentCandidates, {\n parse: code => this.parse(code),\n resolve: this.resolve ? (source, importer) => this.resolve!(source, importer) : undefined,\n })\n : undefined;\n\n // No registration import is injected here: the orchestrion plugin's\n // subscribe-injection makes each bundled package self-register its channel\n // subscriber on the global marker, so wrapping the entry with `withSentry`\n // is all this plugin needs to do.\n const result = applyAutoInstrumentTransforms(code, ast, {\n classWrappers,\n agentClasses,\n optionsFn,\n optionsImport,\n sameWorkerBindings: wranglerConfig.sameWorkerBindings,\n });\n\n const wrappedClasses = result?.wrappedClasses ?? new Set<string>();\n const missing = [...classWrappers.keys()].filter(name => !wrappedClasses.has(name));\n if (missing.length > 0) {\n this.warn?.(\n `[sentry] Could not auto-instrument ${missing.join(', ')}.` +\n 'The worker entry has no export matching them. ' +\n 'Star re-exports (`export * from \"./do\"`) are not matched. ' +\n 'Export them by name or wrap them manually with corresponding `instrument*WithSentry` helpers.',\n );\n }\n\n return result ?? undefined;\n },\n };\n}\n"],"names":["code"],"mappings":"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"}
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { sentryOrchestrionPlugin } from '@sentry/server-utils/orchestrion/vite';
|
|
2
2
|
import { sentryCloudflareAutoInstrumentPlugin } from './autoInstrument.js';
|
|
3
|
+
import { sentryMastraObservabilityProviderPlugin } from './mastraObservability.js';
|
|
3
4
|
|
|
4
5
|
function sentryCloudflareVitePlugin(options = {}) {
|
|
5
6
|
return [
|
|
6
7
|
sentryOrchestrionPlugin({
|
|
7
|
-
buildTimeInstrumentation: options.buildTimeInstrumentation
|
|
8
|
+
buildTimeInstrumentation: options.buildTimeInstrumentation,
|
|
9
|
+
// Route the injected `tracingChannel` imports through the workerd-safe façade so a
|
|
10
|
+
// dependency wrapped at module scope (`const app = new Hono()`) doesn't throw on workerd.
|
|
11
|
+
dcModule: "@sentry/cloudflare/orchestrion-diagnostics-channel"
|
|
8
12
|
}),
|
|
13
|
+
sentryMastraObservabilityProviderPlugin(),
|
|
9
14
|
...options.autoInstrumentation !== false ? [sentryCloudflareAutoInstrumentPlugin({ wranglerConfigPath: options.wranglerConfigPath })] : []
|
|
10
15
|
];
|
|
11
16
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../../src/vite/index.ts"],"sourcesContent":["// Published ESM-only via the `@sentry/cloudflare/vite` subpath export:\n// `@sentry/server-utils/orchestrion/vite` exposes no `require` condition, so a\n// CJS entry here would fail at resolution time (ERR_PACKAGE_PATH_NOT_EXPORTED).\n// The CJS rollup variant still emits this file, but `package.json` doesn't\n// expose it — same setup as `@sentry/server-utils/orchestrion/vite` itself.\nimport { sentryOrchestrionPlugin } from '@sentry/server-utils/orchestrion/vite';\nimport { sentryCloudflareAutoInstrumentPlugin } from './autoInstrument';\n\n/**\n * Options for {@link sentryCloudflareVitePlugin}.\n */\nexport interface SentryCloudflareVitePluginOptions {\n /**\n * Path to the wrangler config, relative to the Vite root (or absolute).\n * Set this when your config doesn't use a default name — e.g. when you\n * pass `configPath: './wrangler.agent.jsonc'` to `@cloudflare/vite-plugin`,\n * which the Sentry plugin cannot see. When set, only this file is read\n * (no default-name probing), and a warning is emitted if it is missing or\n * unparseable.\n *\n * @default undefined (probes `wrangler.json`, `wrangler.jsonc`, `wrangler.toml` at the Vite root)\n */\n wranglerConfigPath?: string;\n /**\n * Build-time automatic instrumentation of supported dependencies (e.g.\n * database clients like `mysql`) so the Sentry Cloudflare SDK can trace them\n * without monkey-patching, which wouldn't work in workerd anyway.\n *\n * When enabled, the plugin injects `diagnostics_channel.tracingChannel` calls\n * into the bundled packages and, next to each, a snippet that registers the\n * matching Sentry channel-subscriber factory on the global marker, which the\n * SDK picks up in `Sentry.withSentry()`. Both `vite build` and `vite dev` are\n * instrumented. Set to `false` to opt out.\n *\n * @default true\n */\n buildTimeInstrumentation?: boolean;\n /**\n * Automatically wraps your Worker at build time so you don't have to edit\n * your entry: the plugin reads your wrangler config, wraps the default\n * export with `Sentry.withSentry()` (sourcing options from a co-located\n * `instrument.*` file, falling back to env), and wraps any configured\n * Durable Object class with `instrumentDurableObjectWithSentry`. Both\n * `vite build` and `vite dev` are instrumented. Already-wrapped entries are\n * left alone, so this is safe alongside manual instrumentation. Set to\n * `false` to opt out.\n *\n * The plugin also adds the bindings that resolve to the wrapped classes (this worker's own\n * Durable Objects and self service bindings) to `rpcTracePropagationBindings`. Bindings to\n * other workers stay opt-in, their receivers may not run Sentry.\n *\n * @default true\n */\n autoInstrumentation?: boolean;\n}\n\n/**\n * Sentry Vite plugin for Cloudflare Workers.\n *\n * Add this plugin to your Vite configuration to enable additional Sentry\n * instrumentation for Cloudflare Workers built with Vite.\n *\n * By default, the plugin\n * - build-time instruments supported dependencies (such as database clients) so\n * they are traced without changing your application code — opt out with\n * `buildTimeInstrumentation: false`, and\n * - wraps your Worker entry (and any Durable Object, Workflow or\n * WorkerEntrypoint class in your wrangler config) with the matching Sentry\n * helper — opt out with `autoInstrumentation: false` and call\n * `Sentry.withSentry()` yourself.\n *\n * @example\n * ```ts\n * // vite.config.ts\n * import { cloudflare } from '@cloudflare/vite-plugin';\n * import { sentryCloudflareVitePlugin } from '@sentry/cloudflare/vite';\n * import { defineConfig } from 'vite';\n *\n * export default defineConfig({\n * plugins: [cloudflare(), sentryCloudflareVitePlugin()],\n * });\n * ```\n */\nexport function sentryCloudflareVitePlugin(options: SentryCloudflareVitePluginOptions = {}): Array<{ name: string }> {\n return [\n sentryOrchestrionPlugin({\n buildTimeInstrumentation: options.buildTimeInstrumentation,\n }),\n ...(options.autoInstrumentation !== false\n ? [sentryCloudflareAutoInstrumentPlugin({ wranglerConfigPath: options.wranglerConfigPath })]\n : []),\n ];\n}\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../../src/vite/index.ts"],"sourcesContent":["// Published ESM-only via the `@sentry/cloudflare/vite` subpath export:\n// `@sentry/server-utils/orchestrion/vite` exposes no `require` condition, so a\n// CJS entry here would fail at resolution time (ERR_PACKAGE_PATH_NOT_EXPORTED).\n// The CJS rollup variant still emits this file, but `package.json` doesn't\n// expose it — same setup as `@sentry/server-utils/orchestrion/vite` itself.\nimport { sentryOrchestrionPlugin } from '@sentry/server-utils/orchestrion/vite';\nimport { sentryCloudflareAutoInstrumentPlugin } from './autoInstrument';\nimport { sentryMastraObservabilityProviderPlugin } from './mastraObservability';\n\n/**\n * Options for {@link sentryCloudflareVitePlugin}.\n */\nexport interface SentryCloudflareVitePluginOptions {\n /**\n * Path to the wrangler config, relative to the Vite root (or absolute).\n * Set this when your config doesn't use a default name — e.g. when you\n * pass `configPath: './wrangler.agent.jsonc'` to `@cloudflare/vite-plugin`,\n * which the Sentry plugin cannot see. When set, only this file is read\n * (no default-name probing), and a warning is emitted if it is missing or\n * unparseable.\n *\n * @default undefined (probes `wrangler.json`, `wrangler.jsonc`, `wrangler.toml` at the Vite root)\n */\n wranglerConfigPath?: string;\n /**\n * Build-time automatic instrumentation of supported dependencies (e.g.\n * database clients like `mysql`) so the Sentry Cloudflare SDK can trace them\n * without monkey-patching, which wouldn't work in workerd anyway.\n *\n * When enabled, the plugin injects `diagnostics_channel.tracingChannel` calls\n * into the bundled packages and, next to each, a snippet that registers the\n * matching Sentry channel-subscriber factory on the global marker, which the\n * SDK picks up in `Sentry.withSentry()`. Both `vite build` and `vite dev` are\n * instrumented. Set to `false` to opt out.\n *\n * @default true\n */\n buildTimeInstrumentation?: boolean;\n /**\n * Automatically wraps your Worker at build time so you don't have to edit\n * your entry: the plugin reads your wrangler config, wraps the default\n * export with `Sentry.withSentry()` (sourcing options from a co-located\n * `instrument.*` file, falling back to env), and wraps any configured\n * Durable Object class with `instrumentDurableObjectWithSentry`. Both\n * `vite build` and `vite dev` are instrumented. Already-wrapped entries are\n * left alone, so this is safe alongside manual instrumentation. Set to\n * `false` to opt out.\n *\n * The plugin also adds the bindings that resolve to the wrapped classes (this worker's own\n * Durable Objects and self service bindings) to `rpcTracePropagationBindings`. Bindings to\n * other workers stay opt-in, their receivers may not run Sentry.\n *\n * @default true\n */\n autoInstrumentation?: boolean;\n}\n\n/**\n * Sentry Vite plugin for Cloudflare Workers.\n *\n * Add this plugin to your Vite configuration to enable additional Sentry\n * instrumentation for Cloudflare Workers built with Vite.\n *\n * By default, the plugin\n * - build-time instruments supported dependencies (such as database clients) so\n * they are traced without changing your application code — opt out with\n * `buildTimeInstrumentation: false`, and\n * - wraps your Worker entry (and any Durable Object, Workflow or\n * WorkerEntrypoint class in your wrangler config) with the matching Sentry\n * helper — opt out with `autoInstrumentation: false` and call\n * `Sentry.withSentry()` yourself.\n *\n * @example\n * ```ts\n * // vite.config.ts\n * import { cloudflare } from '@cloudflare/vite-plugin';\n * import { sentryCloudflareVitePlugin } from '@sentry/cloudflare/vite';\n * import { defineConfig } from 'vite';\n *\n * export default defineConfig({\n * plugins: [cloudflare(), sentryCloudflareVitePlugin()],\n * });\n * ```\n */\nexport function sentryCloudflareVitePlugin(options: SentryCloudflareVitePluginOptions = {}): Array<{ name: string }> {\n return [\n sentryOrchestrionPlugin({\n buildTimeInstrumentation: options.buildTimeInstrumentation,\n // Route the injected `tracingChannel` imports through the workerd-safe façade so a\n // dependency wrapped at module scope (`const app = new Hono()`) doesn't throw on workerd.\n dcModule: '@sentry/cloudflare/orchestrion-diagnostics-channel',\n }),\n sentryMastraObservabilityProviderPlugin(),\n ...(options.autoInstrumentation !== false\n ? [sentryCloudflareAutoInstrumentPlugin({ wranglerConfigPath: options.wranglerConfigPath })]\n : []),\n ];\n}\n"],"names":[],"mappings":";;;;AAoFO,SAAS,0BAAA,CAA2B,OAAA,GAA6C,EAAC,EAA4B;AACnH,EAAA,OAAO;AAAA,IACL,uBAAA,CAAwB;AAAA,MACtB,0BAA0B,OAAA,CAAQ,wBAAA;AAAA;AAAA;AAAA,MAGlC,QAAA,EAAU;AAAA,KACX,CAAA;AAAA,IACD,uCAAA,EAAwC;AAAA,IACxC,GAAI,OAAA,CAAQ,mBAAA,KAAwB,KAAA,GAChC,CAAC,oCAAA,CAAqC,EAAE,kBAAA,EAAoB,OAAA,CAAQ,kBAAA,EAAoB,CAAC,IACzF;AAAC,GACP;AACF;;;;"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import MagicString from 'magic-string';
|
|
4
|
+
|
|
5
|
+
const PROVIDER_IDENTIFIER = "__SENTRY_MASTRA_OBSERVABILITY__";
|
|
6
|
+
const MASTRA_INTEGRATION_ID = /@sentry\/server-utils\/build\/esm\/integrations\/mastra\.js$/;
|
|
7
|
+
function isMastraIntegrationModuleId(id) {
|
|
8
|
+
const normalizedId = id.replace(/\\/g, "/").replace(/[?#].*$/, "");
|
|
9
|
+
return MASTRA_INTEGRATION_ID.test(normalizedId);
|
|
10
|
+
}
|
|
11
|
+
function sentryMastraObservabilityProviderPlugin() {
|
|
12
|
+
let providerSnippet;
|
|
13
|
+
return {
|
|
14
|
+
name: "sentry-cloudflare-mastra-observability-provider",
|
|
15
|
+
configResolved(config) {
|
|
16
|
+
try {
|
|
17
|
+
createRequire(resolve(config.root, "noop.js")).resolve("@mastra/observability");
|
|
18
|
+
} catch {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
providerSnippet = `import * as ${PROVIDER_IDENTIFIER} from '@mastra/observability';
|
|
22
|
+
(globalThis.__SENTRY_ORCHESTRION__ = globalThis.__SENTRY_ORCHESTRION__ || {});
|
|
23
|
+
(globalThis.__SENTRY_ORCHESTRION__.providedModules = globalThis.__SENTRY_ORCHESTRION__.providedModules || {})['@mastra/observability'] = ${PROVIDER_IDENTIFIER};
|
|
24
|
+
`;
|
|
25
|
+
},
|
|
26
|
+
transform(code, id) {
|
|
27
|
+
if (!providerSnippet || !isMastraIntegrationModuleId(id)) return void 0;
|
|
28
|
+
const ms = new MagicString(code);
|
|
29
|
+
ms.prepend(providerSnippet);
|
|
30
|
+
return { code: ms.toString(), map: ms.generateMap({ hires: true }) };
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export { isMastraIntegrationModuleId, sentryMastraObservabilityProviderPlugin };
|
|
36
|
+
//# sourceMappingURL=mastraObservability.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mastraObservability.js","sources":["../../../../src/vite/mastraObservability.ts"],"sourcesContent":["import { createRequire } from 'node:module';\nimport { resolve } from 'node:path';\nimport MagicString from 'magic-string';\n\n// Namespace binding the injected provider import uses; read back by the integration\n// off the global marker.\nconst PROVIDER_IDENTIFIER = '__SENTRY_MASTRA_OBSERVABILITY__';\n\n// The bundled `@sentry/server-utils` Mastra integration module (ESM build — the only\n// one a worker loads). Its `loadMastraObservability` reads `@mastra/observability` off\n// the global marker this provider populates, instead of `createRequire`, which cannot\n// resolve a package inside a bundled worker.\nconst MASTRA_INTEGRATION_ID = /@sentry\\/server-utils\\/build\\/esm\\/integrations\\/mastra\\.js$/;\n\n/** Whether `id` is the Sentry Mastra integration module the provider injects into. */\nexport function isMastraIntegrationModuleId(id: string): boolean {\n const normalizedId = id.replace(/\\\\/g, '/').replace(/[?#].*$/, '');\n return MASTRA_INTEGRATION_ID.test(normalizedId);\n}\n\n/**\n * Splices a static `import * as … from '@mastra/observability'` into Sentry's own\n * Mastra integration module and stashes the namespace on the global orchestrion\n * marker.\n *\n * On Cloudflare the integration cannot `createRequire('@mastra/observability')` to\n * bootstrap Mastra's observability pipeline — there is no on-disk `node_modules` in\n * workerd — so without this the user has to construct and wire up an `Observability`\n * themselves. The import is static (statically analyzable, no lazy `import()`), lands\n * in Sentry's module rather than the user's code, and is only emitted when the package\n * actually resolves; if it is absent, the integration keeps its Node `createRequire`\n * fallback and the marker stays empty.\n */\nexport function sentryMastraObservabilityProviderPlugin(): {\n name: string;\n configResolved(config: { root: string }): void;\n transform(code: string, id: string): { code: string; map: ReturnType<MagicString['generateMap']> } | undefined;\n} {\n let providerSnippet: string | undefined;\n\n return {\n name: 'sentry-cloudflare-mastra-observability-provider',\n\n configResolved(config: { root: string }): void {\n // Resolved at build time (Node), so this `createRequire` never ships to the worker.\n try {\n createRequire(resolve(config.root, 'noop.js')).resolve('@mastra/observability');\n } catch {\n return;\n }\n providerSnippet =\n `import * as ${PROVIDER_IDENTIFIER} from '@mastra/observability';\\n` +\n '(globalThis.__SENTRY_ORCHESTRION__ = globalThis.__SENTRY_ORCHESTRION__ || {});\\n' +\n '(globalThis.__SENTRY_ORCHESTRION__.providedModules = globalThis.__SENTRY_ORCHESTRION__.providedModules || {})' +\n `['@mastra/observability'] = ${PROVIDER_IDENTIFIER};\\n`;\n },\n\n transform(code: string, id: string): { code: string; map: ReturnType<MagicString['generateMap']> } | undefined {\n if (!providerSnippet || !isMastraIntegrationModuleId(id)) return undefined;\n\n const ms = new MagicString(code);\n ms.prepend(providerSnippet);\n return { code: ms.toString(), map: ms.generateMap({ hires: true }) };\n },\n };\n}\n"],"names":[],"mappings":";;;;AAMA,MAAM,mBAAA,GAAsB,iCAAA;AAM5B,MAAM,qBAAA,GAAwB,8DAAA;AAGvB,SAAS,4BAA4B,EAAA,EAAqB;AAC/D,EAAA,MAAM,YAAA,GAAe,GAAG,OAAA,CAAQ,KAAA,EAAO,GAAG,CAAA,CAAE,OAAA,CAAQ,WAAW,EAAE,CAAA;AACjE,EAAA,OAAO,qBAAA,CAAsB,KAAK,YAAY,CAAA;AAChD;AAeO,SAAS,uCAAA,GAId;AACA,EAAA,IAAI,eAAA;AAEJ,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,iDAAA;AAAA,IAEN,eAAe,MAAA,EAAgC;AAE7C,MAAA,IAAI;AACF,QAAA,aAAA,CAAc,QAAQ,MAAA,CAAO,IAAA,EAAM,SAAS,CAAC,CAAA,CAAE,QAAQ,uBAAuB,CAAA;AAAA,MAChF,CAAA,CAAA,MAAQ;AACN,QAAA;AAAA,MACF;AACA,MAAA,eAAA,GACE,eAAe,mBAAmB,CAAA;AAAA;AAAA,yIAAA,EAGH,mBAAmB,CAAA;AAAA,CAAA;AAAA,IACtD,CAAA;AAAA,IAEA,SAAA,CAAU,MAAc,EAAA,EAAuF;AAC7G,MAAA,IAAI,CAAC,eAAA,IAAmB,CAAC,2BAAA,CAA4B,EAAE,GAAG,OAAO,MAAA;AAEjE,MAAA,MAAM,EAAA,GAAK,IAAI,WAAA,CAAY,IAAI,CAAA;AAC/B,MAAA,EAAA,CAAG,QAAQ,eAAe,CAAA;AAC1B,MAAA,OAAO,EAAE,IAAA,EAAM,EAAA,CAAG,QAAA,EAAS,EAAG,GAAA,EAAK,EAAA,CAAG,WAAA,CAAY,EAAE,KAAA,EAAO,IAAA,EAAM,CAAA,EAAE;AAAA,IACrE;AAAA,GACF;AACF;;;;"}
|
|
@@ -10,6 +10,7 @@ function isCallToMethod(node, methodName) {
|
|
|
10
10
|
if (callee.type === "Identifier" && callee.name === methodName) return true;
|
|
11
11
|
return callee.type === "MemberExpression" && callee.property?.type === "Identifier" && callee.property.name === methodName;
|
|
12
12
|
}
|
|
13
|
+
const DEFAULT_IMPORT = "default";
|
|
13
14
|
const WRAPPER_METHODS = {
|
|
14
15
|
durableObject: "instrumentDurableObjectWithSentry",
|
|
15
16
|
agent: "instrumentAgentWithSentry",
|
|
@@ -31,7 +32,8 @@ function applyAutoInstrumentTransforms(code, ast, ctx) {
|
|
|
31
32
|
workerEntrypointClasses: detectWorkerEntrypointClasses(ast),
|
|
32
33
|
// The identifier must be chosen before wrapping, which bindings survive is only known after.
|
|
33
34
|
optionsFn: sameWorkerBindings.length > 0 ? MERGED_OPTIONS_IDENTIFIER : ctx.optionsFn,
|
|
34
|
-
autoWrapped: /* @__PURE__ */ new Set()
|
|
35
|
+
autoWrapped: /* @__PURE__ */ new Set(),
|
|
36
|
+
manuallyWrappedLocals: collectManuallyWrappedLocals(ast)
|
|
35
37
|
};
|
|
36
38
|
const { wrappedClasses } = state;
|
|
37
39
|
for (const node of ast.body) {
|
|
@@ -71,11 +73,28 @@ function buildMergedOptionsDeclaration(sameWorkerBindings, optionsFn, state) {
|
|
|
71
73
|
}
|
|
72
74
|
function resolveWrapperKind(exportedName, localName, state) {
|
|
73
75
|
const configured = state.classWrappers.get(exportedName);
|
|
74
|
-
if (configured === "durableObject" && localName && state.agentClasses.has(localName))
|
|
76
|
+
if (configured === "durableObject" && (localName && state.agentClasses.has(localName) || state.agentClasses.has(exportedName))) {
|
|
77
|
+
return "agent";
|
|
78
|
+
}
|
|
75
79
|
if (configured) return configured;
|
|
76
80
|
if (localName && state.workerEntrypointClasses.has(localName)) return "workerEntrypoint";
|
|
77
81
|
return void 0;
|
|
78
82
|
}
|
|
83
|
+
function collectManuallyWrappedLocals(ast) {
|
|
84
|
+
const wrapperMethods = Object.values(WRAPPER_METHODS);
|
|
85
|
+
const locals = /* @__PURE__ */ new Set();
|
|
86
|
+
for (const node of ast.body) {
|
|
87
|
+
if (node.type !== "VariableDeclaration") continue;
|
|
88
|
+
for (const declarator of node.declarations ?? []) {
|
|
89
|
+
const name = declarator.id?.type === "Identifier" ? declarator.id.name : void 0;
|
|
90
|
+
const init = declarator.init;
|
|
91
|
+
if (name && init && wrapperMethods.some((method) => isCallToMethod(init, method))) {
|
|
92
|
+
locals.add(name);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return locals;
|
|
97
|
+
}
|
|
79
98
|
function collectTopLevelClasses(ast) {
|
|
80
99
|
const classes = /* @__PURE__ */ new Map();
|
|
81
100
|
for (const node of ast.body) {
|
|
@@ -109,10 +128,7 @@ function handleNamedExport(node, ctx, state) {
|
|
|
109
128
|
wrapInlineClassExport(node, decl, ctx, state);
|
|
110
129
|
return;
|
|
111
130
|
}
|
|
112
|
-
|
|
113
|
-
for (const specifier of node.specifiers ?? []) {
|
|
114
|
-
wrapSpecifierExport(specifier, ctx, state);
|
|
115
|
-
}
|
|
131
|
+
wrapSpecifierExports(node, ctx, state);
|
|
116
132
|
}
|
|
117
133
|
function collectManuallyWrappedClassExports(varDecl, ctx, state) {
|
|
118
134
|
for (const declarator of varDecl.declarations ?? []) {
|
|
@@ -144,22 +160,69 @@ export const ${className} = __SENTRY__.${WRAPPER_METHODS[kind]}(${state.optionsF
|
|
|
144
160
|
state.renamedLocals.add(className);
|
|
145
161
|
state.needsImport = true;
|
|
146
162
|
}
|
|
147
|
-
function
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
163
|
+
function wrapSpecifierExports(node, ctx, state) {
|
|
164
|
+
const specifiers = node.specifiers ?? [];
|
|
165
|
+
if (specifiers.length === 0) return;
|
|
166
|
+
const sourceLiteral = node.source ? state.ms.original.slice(node.source.start, node.source.end) : void 0;
|
|
167
|
+
const prelude = [];
|
|
168
|
+
const wrappedPairs = [];
|
|
169
|
+
const kept = [];
|
|
170
|
+
for (const specifier of specifiers) {
|
|
171
|
+
const pair = wrapCrossModuleSpecifier(specifier, sourceLiteral, ctx, state, prelude);
|
|
172
|
+
if (pair) {
|
|
173
|
+
wrappedPairs.push(pair);
|
|
174
|
+
} else {
|
|
175
|
+
kept.push(state.ms.original.slice(specifier.start, specifier.end));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (wrappedPairs.length === 0) return;
|
|
179
|
+
const statements = [...prelude, `export { ${wrappedPairs.join(", ")} };`];
|
|
180
|
+
if (kept.length > 0) {
|
|
181
|
+
const clause = `export { ${kept.join(", ")} }`;
|
|
182
|
+
statements.push(sourceLiteral ? `${clause} from ${sourceLiteral};` : `${clause};`);
|
|
183
|
+
}
|
|
184
|
+
state.ms.overwrite(node.start, node.end, statements.join("\n"));
|
|
185
|
+
}
|
|
186
|
+
function wrapCrossModuleSpecifier(specifier, sourceLiteral, ctx, state, prelude) {
|
|
187
|
+
const exportedName = specifier.type === "ExportSpecifier" && specifier.exported?.type === "Identifier" ? specifier.exported.name : void 0;
|
|
151
188
|
const localName = specifier.local?.type === "Identifier" ? specifier.local.name : void 0;
|
|
152
|
-
const kind = resolveWrapperKind(exportedName, localName, state);
|
|
153
|
-
if (!kind) return;
|
|
154
|
-
|
|
155
|
-
|
|
189
|
+
const kind = exportedName ? resolveWrapperKind(exportedName, sourceLiteral ? void 0 : localName, state) : void 0;
|
|
190
|
+
if (!exportedName || !localName || !kind) return void 0;
|
|
191
|
+
if (!sourceLiteral) {
|
|
192
|
+
const localClass = state.topLevelClasses.get(localName);
|
|
193
|
+
if (localClass?.id) {
|
|
194
|
+
wrapLocalClassExport(localName, localClass, kind, ctx, state);
|
|
195
|
+
state.wrappedClasses.add(exportedName);
|
|
196
|
+
state.needsImport = true;
|
|
197
|
+
return void 0;
|
|
198
|
+
}
|
|
199
|
+
if (state.manuallyWrappedLocals.has(localName)) {
|
|
200
|
+
state.wrappedClasses.add(exportedName);
|
|
201
|
+
return void 0;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
let target = localName;
|
|
205
|
+
if (sourceLiteral) {
|
|
206
|
+
target = `__SENTRY_REEXPORT_${exportedName}__`;
|
|
207
|
+
prelude.push(
|
|
208
|
+
localName === DEFAULT_IMPORT ? `import ${target} from ${sourceLiteral};` : `import { ${localName} as ${target} } from ${sourceLiteral};`
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
const wrappedName = `__SENTRY_WRAPPED_${exportedName}__`;
|
|
212
|
+
prelude.push(
|
|
213
|
+
`const ${wrappedName} = __SENTRY__._INTERNAL_wrapUnlessInstrumented(__SENTRY__.${WRAPPER_METHODS[kind]}, ${state.optionsFn}, ${target});`
|
|
214
|
+
);
|
|
156
215
|
state.wrappedClasses.add(exportedName);
|
|
157
216
|
state.autoWrapped.add(exportedName);
|
|
158
217
|
state.needsImport = true;
|
|
159
|
-
|
|
218
|
+
return `${wrappedName} as ${exportedName}`;
|
|
219
|
+
}
|
|
220
|
+
function wrapLocalClassExport(localName, localClass, kind, ctx, state) {
|
|
221
|
+
const classId = localClass.id;
|
|
222
|
+
if (!classId || state.renamedLocals.has(localName)) return;
|
|
160
223
|
state.renamedLocals.add(localName);
|
|
161
224
|
const renamedClass = `__SENTRY_ORIGINAL_${localName}__`;
|
|
162
|
-
state.ms.overwrite(
|
|
225
|
+
state.ms.overwrite(classId.start, classId.end, renamedClass);
|
|
163
226
|
state.ms.appendLeft(
|
|
164
227
|
localClass.end,
|
|
165
228
|
`
|