@sentry/cloudflare 10.67.0 → 10.69.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/client.js +5 -0
- package/build/cjs/client.js.map +1 -1
- package/build/cjs/defineCloudflareOptions.js +11 -0
- package/build/cjs/defineCloudflareOptions.js.map +1 -0
- package/build/cjs/durableobject.js +131 -96
- package/build/cjs/durableobject.js.map +1 -1
- package/build/cjs/index.js +6 -0
- package/build/cjs/index.js.map +1 -1
- package/build/cjs/instrumentations/agents/index.js +14 -0
- package/build/cjs/instrumentations/agents/index.js.map +1 -0
- package/build/cjs/instrumentations/agents/instrumentAgentCallableRpc.js +52 -0
- package/build/cjs/instrumentations/agents/instrumentAgentCallableRpc.js.map +1 -0
- package/build/cjs/instrumentations/agents/instrumentChatAgentConversation.js +31 -0
- package/build/cjs/instrumentations/agents/instrumentChatAgentConversation.js.map +1 -0
- package/build/cjs/instrumentations/agents/types.js +32 -0
- package/build/cjs/instrumentations/agents/types.js.map +1 -0
- package/build/cjs/instrumentations/instrumentDurableObjectStorage.js +6 -0
- package/build/cjs/instrumentations/instrumentDurableObjectStorage.js.map +1 -1
- package/build/cjs/instrumentations/instrumentSqlStorage.js +1 -1
- package/build/cjs/instrumentations/instrumentSqlStorage.js.map +1 -1
- package/build/cjs/integrations/spotlight.js +64 -0
- package/build/cjs/integrations/spotlight.js.map +1 -0
- package/build/cjs/integrations/tracing/vercelai.js.map +1 -1
- package/build/cjs/nodejs_compat/index.js +10 -4
- package/build/cjs/nodejs_compat/index.js.map +1 -1
- package/build/cjs/nodejs_compat/integrations/tracing/vercelai.js.map +1 -1
- package/build/cjs/options.js +14 -1
- package/build/cjs/options.js.map +1 -1
- package/build/cjs/sdk.js +20 -1
- package/build/cjs/sdk.js.map +1 -1
- package/build/cjs/utils/internalSqlQuery.js +13 -7
- package/build/cjs/utils/internalSqlQuery.js.map +1 -1
- package/build/cjs/utils/internalStorageKey.js +44 -0
- package/build/cjs/utils/internalStorageKey.js.map +1 -0
- package/build/cjs/vite/agentClass.js +114 -0
- package/build/cjs/vite/agentClass.js.map +1 -0
- package/build/cjs/vite/autoInstrument.js +94 -0
- package/build/cjs/vite/autoInstrument.js.map +1 -0
- package/build/cjs/vite/index.js +14 -0
- package/build/cjs/vite/index.js.map +1 -0
- package/build/cjs/vite/instrumentFile.js +31 -0
- package/build/cjs/vite/instrumentFile.js.map +1 -0
- package/build/cjs/vite/moduleShape.js +231 -0
- package/build/cjs/vite/moduleShape.js.map +1 -0
- package/build/cjs/vite/transform.js +149 -0
- package/build/cjs/vite/transform.js.map +1 -0
- package/build/cjs/vite/workerEntrypoint.js +66 -0
- package/build/cjs/vite/workerEntrypoint.js.map +1 -0
- package/build/cjs/vite/wranglerConfig.js +54 -0
- package/build/cjs/vite/wranglerConfig.js.map +1 -0
- package/build/esm/client.js +6 -1
- package/build/esm/client.js.map +1 -1
- package/build/esm/defineCloudflareOptions.js +9 -0
- package/build/esm/defineCloudflareOptions.js.map +1 -0
- package/build/esm/durableobject.js +129 -97
- package/build/esm/durableobject.js.map +1 -1
- package/build/esm/index.js +4 -2
- package/build/esm/index.js.map +1 -1
- package/build/esm/instrumentations/agents/index.js +12 -0
- package/build/esm/instrumentations/agents/index.js.map +1 -0
- package/build/esm/instrumentations/agents/instrumentAgentCallableRpc.js +50 -0
- package/build/esm/instrumentations/agents/instrumentAgentCallableRpc.js.map +1 -0
- package/build/esm/instrumentations/agents/instrumentChatAgentConversation.js +29 -0
- package/build/esm/instrumentations/agents/instrumentChatAgentConversation.js.map +1 -0
- package/build/esm/instrumentations/agents/types.js +26 -0
- package/build/esm/instrumentations/agents/types.js.map +1 -0
- package/build/esm/instrumentations/instrumentDurableObjectStorage.js +7 -1
- package/build/esm/instrumentations/instrumentDurableObjectStorage.js.map +1 -1
- package/build/esm/instrumentations/instrumentSqlStorage.js +1 -1
- package/build/esm/instrumentations/instrumentSqlStorage.js.map +1 -1
- package/build/esm/integrations/spotlight.js +61 -0
- package/build/esm/integrations/spotlight.js.map +1 -0
- package/build/esm/integrations/tracing/vercelai.js.map +1 -1
- package/build/esm/nodejs_compat/index.js +5 -3
- package/build/esm/nodejs_compat/index.js.map +1 -1
- package/build/esm/nodejs_compat/integrations/tracing/vercelai.js.map +1 -1
- package/build/esm/options.js +14 -1
- package/build/esm/options.js.map +1 -1
- package/build/esm/package.json +1 -1
- package/build/esm/sdk.js +21 -2
- package/build/esm/sdk.js.map +1 -1
- package/build/esm/utils/internalSqlQuery.js +13 -7
- package/build/esm/utils/internalSqlQuery.js.map +1 -1
- package/build/esm/utils/internalStorageKey.js +41 -0
- package/build/esm/utils/internalStorageKey.js.map +1 -0
- package/build/esm/utils/traceLinks.js +1 -1
- package/build/esm/vite/agentClass.js +111 -0
- package/build/esm/vite/agentClass.js.map +1 -0
- package/build/esm/vite/autoInstrument.js +92 -0
- package/build/esm/vite/autoInstrument.js.map +1 -0
- package/build/esm/vite/index.js +12 -0
- package/build/esm/vite/index.js.map +1 -0
- package/build/esm/vite/instrumentFile.js +27 -0
- package/build/esm/vite/instrumentFile.js.map +1 -0
- package/build/esm/vite/moduleShape.js +227 -0
- package/build/esm/vite/moduleShape.js.map +1 -0
- package/build/esm/vite/transform.js +147 -0
- package/build/esm/vite/transform.js.map +1 -0
- package/build/esm/vite/workerEntrypoint.js +64 -0
- package/build/esm/vite/workerEntrypoint.js.map +1 -0
- package/build/esm/vite/wranglerConfig.js +52 -0
- package/build/esm/vite/wranglerConfig.js.map +1 -0
- package/build/types/client.d.ts +37 -0
- package/build/types/client.d.ts.map +1 -1
- package/build/types/defineCloudflareOptions.d.ts +38 -0
- package/build/types/defineCloudflareOptions.d.ts.map +1 -0
- package/build/types/durableobject.d.ts +81 -0
- 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/instrumentations/agents/index.d.ts +24 -0
- package/build/types/instrumentations/agents/index.d.ts.map +1 -0
- package/build/types/instrumentations/agents/instrumentAgentCallableRpc.d.ts +11 -0
- package/build/types/instrumentations/agents/instrumentAgentCallableRpc.d.ts.map +1 -0
- package/build/types/instrumentations/agents/instrumentChatAgentConversation.d.ts +22 -0
- package/build/types/instrumentations/agents/instrumentChatAgentConversation.d.ts.map +1 -0
- package/build/types/instrumentations/agents/types.d.ts +49 -0
- package/build/types/instrumentations/agents/types.d.ts.map +1 -0
- package/build/types/instrumentations/instrumentDurableObjectStorage.d.ts.map +1 -1
- package/build/types/integrations/spotlight.d.ts +22 -0
- package/build/types/integrations/spotlight.d.ts.map +1 -0
- package/build/types/integrations/tracing/vercelai.d.ts.map +1 -1
- package/build/types/nodejs_compat/index.d.ts +1 -1
- package/build/types/nodejs_compat/index.d.ts.map +1 -1
- package/build/types/nodejs_compat/integrations/tracing/vercelai.d.ts +1 -8
- package/build/types/nodejs_compat/integrations/tracing/vercelai.d.ts.map +1 -1
- package/build/types/options.d.ts.map +1 -1
- package/build/types/sdk.d.ts.map +1 -1
- package/build/types/utils/internalSqlQuery.d.ts +4 -2
- package/build/types/utils/internalSqlQuery.d.ts.map +1 -1
- package/build/types/utils/internalStorageKey.d.ts +22 -0
- package/build/types/utils/internalStorageKey.d.ts.map +1 -0
- package/build/types/vite/agentClass.d.ts +41 -0
- package/build/types/vite/agentClass.d.ts.map +1 -0
- package/build/types/vite/autoInstrument.d.ts +22 -0
- package/build/types/vite/autoInstrument.d.ts.map +1 -0
- package/build/types/vite/index.d.ts +98 -0
- package/build/types/vite/index.d.ts.map +1 -0
- package/build/types/vite/instrumentFile.d.ts +22 -0
- package/build/types/vite/instrumentFile.d.ts.map +1 -0
- package/build/types/vite/moduleShape.d.ts +52 -0
- package/build/types/vite/moduleShape.d.ts.map +1 -0
- package/build/types/vite/transform.d.ts +65 -0
- package/build/types/vite/transform.d.ts.map +1 -0
- package/build/types/vite/workerEntrypoint.d.ts +12 -0
- package/build/types/vite/workerEntrypoint.d.ts.map +1 -0
- package/build/types/vite/wranglerConfig.d.ts +43 -0
- package/build/types/vite/wranglerConfig.d.ts.map +1 -0
- package/build/types-ts3.8/client.d.ts +37 -0
- package/build/types-ts3.8/defineCloudflareOptions.d.ts +38 -0
- package/build/types-ts3.8/durableobject.d.ts +81 -0
- package/build/types-ts3.8/index.d.ts +4 -2
- package/build/types-ts3.8/instrumentations/agents/index.d.ts +24 -0
- package/build/types-ts3.8/instrumentations/agents/instrumentAgentCallableRpc.d.ts +11 -0
- package/build/types-ts3.8/instrumentations/agents/instrumentChatAgentConversation.d.ts +22 -0
- package/build/types-ts3.8/instrumentations/agents/types.d.ts +49 -0
- package/build/types-ts3.8/integrations/spotlight.d.ts +22 -0
- package/build/types-ts3.8/nodejs_compat/index.d.ts +1 -1
- package/build/types-ts3.8/nodejs_compat/integrations/tracing/vercelai.d.ts +7 -14
- package/build/types-ts3.8/utils/internalSqlQuery.d.ts +4 -2
- package/build/types-ts3.8/utils/internalStorageKey.d.ts +22 -0
- package/build/types-ts3.8/vite/agentClass.d.ts +41 -0
- package/build/types-ts3.8/vite/autoInstrument.d.ts +22 -0
- package/build/types-ts3.8/vite/index.d.ts +98 -0
- package/build/types-ts3.8/vite/instrumentFile.d.ts +22 -0
- package/build/types-ts3.8/vite/moduleShape.d.ts +52 -0
- package/build/types-ts3.8/vite/transform.d.ts +65 -0
- package/build/types-ts3.8/vite/workerEntrypoint.d.ts +12 -0
- package/build/types-ts3.8/vite/wranglerConfig.d.ts +43 -0
- package/package.json +19 -5
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
|
|
3
|
+
const core = require('@sentry/core');
|
|
4
|
+
|
|
5
|
+
const AGENT_SPAN_ORIGIN = "auto.faas.cloudflare.agents";
|
|
6
|
+
const AGENT_CLASS_ATTRIBUTE = "cloudflare.agent.class";
|
|
7
|
+
const AGENT_NAME_ATTRIBUTE = "cloudflare.agent.name";
|
|
8
|
+
function getAgentAttributes(instance) {
|
|
9
|
+
const attributes = {};
|
|
10
|
+
const agentClass = instance._ParentClass?.name;
|
|
11
|
+
if (typeof agentClass === "string" && agentClass) {
|
|
12
|
+
attributes[AGENT_CLASS_ATTRIBUTE] = agentClass;
|
|
13
|
+
}
|
|
14
|
+
const agentName = instance.name;
|
|
15
|
+
if (typeof agentName === "string" && agentName) {
|
|
16
|
+
attributes[AGENT_NAME_ATTRIBUTE] = agentName;
|
|
17
|
+
}
|
|
18
|
+
return attributes;
|
|
19
|
+
}
|
|
20
|
+
function setAgentConversationId(instance) {
|
|
21
|
+
const conversationId = instance.__sentryConversationId ?? instance.name;
|
|
22
|
+
if (typeof conversationId === "string" && conversationId) {
|
|
23
|
+
core.getCurrentScope().setConversationId(conversationId);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
exports.AGENT_CLASS_ATTRIBUTE = AGENT_CLASS_ATTRIBUTE;
|
|
28
|
+
exports.AGENT_NAME_ATTRIBUTE = AGENT_NAME_ATTRIBUTE;
|
|
29
|
+
exports.AGENT_SPAN_ORIGIN = AGENT_SPAN_ORIGIN;
|
|
30
|
+
exports.getAgentAttributes = getAgentAttributes;
|
|
31
|
+
exports.setAgentConversationId = setAgentConversationId;
|
|
32
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sources":["../../../../src/instrumentations/agents/types.ts"],"sourcesContent":["import { getCurrentScope } from '@sentry/core';\n\nexport const AGENT_SPAN_ORIGIN = 'auto.faas.cloudflare.agents';\nexport const AGENT_CLASS_ATTRIBUTE = 'cloudflare.agent.class';\nexport const AGENT_NAME_ATTRIBUTE = 'cloudflare.agent.name';\n\n/**\n * The subset of the `agents` `Agent` instance internals that we instrument. These are runtime\n * implementation details of the `agents` package (v0.13.x) rather than part of its public type\n * surface, so every access is guarded and wrapping degrades gracefully if a name changes upstream.\n */\nexport interface AgentInternals {\n /** Central choke-point through which all `agents:*` observability events are published. */\n _emit?: (type: string, payload?: Record<string, unknown>) => void;\n /** WebSocket message handler; dispatches `@callable()` RPC requests. */\n onMessage?: (...args: unknown[]) => unknown;\n /**\n * Chat-turn handler. Only `AIChatAgent` (from `@cloudflare/ai-chat`) defines this; a plain `Agent`\n * does not, so its presence discriminates a chat agent.\n */\n onChatMessage?: (...args: unknown[]) => unknown;\n /** The user's Agent class (used by the SDK for the observability event `agent` field). */\n _ParentClass?: { name?: string };\n /** The Agent instance name, which in the Agents model identifies the conversation/thread. */\n name?: string;\n /**\n * Internal: the active conversation id, rotated when the chat is cleared (the `message:clear`\n * observability event) so a reset chat groups as a fresh conversation while the instance (and its\n * MCP/OAuth state) stays put. Set by `instrumentChatAgentConversation`; falls back to `name`\n * before the first clear.\n */\n __sentryConversationId?: string;\n}\n\n/** Reads best-effort agent identity attributes from the instance, tolerating missing internals. */\nexport function getAgentAttributes(instance: AgentInternals): Record<string, string> {\n const attributes: Record<string, string> = {};\n\n const agentClass = instance._ParentClass?.name;\n if (typeof agentClass === 'string' && agentClass) {\n attributes[AGENT_CLASS_ATTRIBUTE] = agentClass;\n }\n\n const agentName = instance.name;\n if (typeof agentName === 'string' && agentName) {\n attributes[AGENT_NAME_ATTRIBUTE] = agentName;\n }\n\n return attributes;\n}\n\n/**\n * Sets the agent instance's conversation id on the current scope for the duration of the\n * surrounding unit of work (chat turn, callable RPC call). In the Agents model one instance is one\n * conversation, so the instance `name` is the natural conversation id — for chat and plain agents\n * alike, since plain agents run LLM calls too (e.g. inside `@callable()` methods). Once the chat\n * has been cleared, the rotated `__sentryConversationId` takes precedence so LLM calls from any\n * unit of work group under the fresh conversation.\n *\n * `conversationIdIntegration` reads the id off the scope at `spanStart` and stamps\n * `gen_ai.conversation.id` onto AI spans created within the unit of work, correlating its model\n * and tool calls. Callers run inside a per-event forked scope (`wrapMethodWithSentry`), so the id\n * does not leak into unrelated events.\n */\nexport function setAgentConversationId(instance: AgentInternals): void {\n const conversationId = instance.__sentryConversationId ?? instance.name;\n\n if (typeof conversationId === 'string' && conversationId) {\n getCurrentScope().setConversationId(conversationId);\n }\n}\n"],"names":["getCurrentScope"],"mappings":";;;;AAEO,MAAM,iBAAA,GAAoB;AAC1B,MAAM,qBAAA,GAAwB;AAC9B,MAAM,oBAAA,GAAuB;AA+B7B,SAAS,mBAAmB,QAAA,EAAkD;AACnF,EAAA,MAAM,aAAqC,EAAC;AAE5C,EAAA,MAAM,UAAA,GAAa,SAAS,YAAA,EAAc,IAAA;AAC1C,EAAA,IAAI,OAAO,UAAA,KAAe,QAAA,IAAY,UAAA,EAAY;AAChD,IAAA,UAAA,CAAW,qBAAqB,CAAA,GAAI,UAAA;AAAA,EACtC;AAEA,EAAA,MAAM,YAAY,QAAA,CAAS,IAAA;AAC3B,EAAA,IAAI,OAAO,SAAA,KAAc,QAAA,IAAY,SAAA,EAAW;AAC9C,IAAA,UAAA,CAAW,oBAAoB,CAAA,GAAI,SAAA;AAAA,EACrC;AAEA,EAAA,OAAO,UAAA;AACT;AAeO,SAAS,uBAAuB,QAAA,EAAgC;AACrE,EAAA,MAAM,cAAA,GAAiB,QAAA,CAAS,sBAAA,IAA0B,QAAA,CAAS,IAAA;AAEnE,EAAA,IAAI,OAAO,cAAA,KAAmB,QAAA,IAAY,cAAA,EAAgB;AACxD,IAAAA,oBAAA,EAAgB,CAAE,kBAAkB,cAAc,CAAA;AAAA,EACpD;AACF;;;;;;;;"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
|
|
3
3
|
const core = require('@sentry/core');
|
|
4
|
+
const internalStorageKey = require('../utils/internalStorageKey.js');
|
|
4
5
|
const traceLinks = require('../utils/traceLinks.js');
|
|
5
6
|
const instrumentDurableObjectSyncKvStorage = require('./instrumentDurableObjectSyncKvStorage.js');
|
|
6
7
|
const instrumentSqlStorage = require('./instrumentSqlStorage.js');
|
|
@@ -24,6 +25,11 @@ function instrumentDurableObjectStorage(storage, waitUntil) {
|
|
|
24
25
|
return original.bind(target);
|
|
25
26
|
}
|
|
26
27
|
return function(...args) {
|
|
28
|
+
const allowlist = core.getClient()?.getOptions()?.durableObjectStorageSpanAllowlist;
|
|
29
|
+
const keys = internalStorageKey.getStorageKeys(methodName, args);
|
|
30
|
+
if (keys && keys.length > 0 && keys.every((key) => internalStorageKey.targetsCloudflareInternalKey(key, allowlist))) {
|
|
31
|
+
return original.apply(target, args);
|
|
32
|
+
}
|
|
27
33
|
return core.startSpan(
|
|
28
34
|
{
|
|
29
35
|
// Use underscore naming to match Cloudflare's native instrumentation (e.g., "durable_object_storage_get")
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrumentDurableObjectStorage.js","sources":["../../../src/instrumentations/instrumentDurableObjectStorage.ts"],"sourcesContent":["import type { DurableObjectStorage, SyncKvStorage, SqlStorage } from '@cloudflare/workers-types';\nimport { isThenable, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startSpan } from '@sentry/core';\nimport { storeSpanContext } from '../utils/traceLinks';\nimport { instrumentDurableObjectSyncKvStorage } from './instrumentDurableObjectSyncKvStorage';\nimport { instrumentSqlStorage } from './instrumentSqlStorage';\n\nconst STORAGE_METHODS_TO_INSTRUMENT = ['get', 'put', 'delete', 'list', 'setAlarm', 'getAlarm', 'deleteAlarm'] as const;\n\ntype StorageMethod = (typeof STORAGE_METHODS_TO_INSTRUMENT)[number];\n\ntype WaitUntil = (promise: Promise<unknown>) => void;\n\n/**\n * Instruments DurableObjectStorage methods with Sentry spans.\n *\n * Wraps the following async methods:\n * - get, put, delete, list (KV API)\n * - setAlarm, getAlarm, deleteAlarm (Alarm API)\n *\n * When setAlarm is called, it also stores the current span context so that when\n * the alarm fires later, it can link back to the trace that called setAlarm.\n *\n * @param storage - The DurableObjectStorage instance to instrument\n * @param waitUntil - Optional waitUntil function to defer span context storage\n * @returns An instrumented DurableObjectStorage instance\n */\nexport function instrumentDurableObjectStorage(\n storage: DurableObjectStorage,\n waitUntil?: WaitUntil,\n): DurableObjectStorage {\n return new Proxy(storage, {\n get(target, prop, _receiver) {\n // Use `target` as the receiver instead of the proxy (`_receiver`).\n // Native workerd getters (e.g., `storage.sql`) validate `this` via\n // internal slots. Passing the proxy as receiver breaks that check,\n // causing \"Illegal invocation: function called with incorrect `this`\n // reference\" errors.\n const original = Reflect.get(target, prop, target);\n\n if (prop === 'kv' && original != null && 'get' in original && 'put' in original) {\n return instrumentDurableObjectSyncKvStorage(original as SyncKvStorage);\n }\n\n if (prop === 'sql' && original != null && 'databaseSize' in original && 'exec' in original) {\n return instrumentSqlStorage(original as SqlStorage);\n }\n\n if (typeof original !== 'function') {\n return original;\n }\n\n const methodName = prop as string;\n if (!STORAGE_METHODS_TO_INSTRUMENT.includes(methodName as StorageMethod)) {\n return (original as (...args: unknown[]) => unknown).bind(target);\n }\n\n return function (this: unknown, ...args: unknown[]) {\n return startSpan(\n {\n // Use underscore naming to match Cloudflare's native instrumentation (e.g., \"durable_object_storage_get\")\n name: `durable_object_storage_${methodName}`,\n op: 'db',\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.db.cloudflare.durable_object',\n 'db.system.name': 'cloudflare.durable_object.storage',\n 'db.operation.name': methodName,\n },\n },\n () => {\n const teardown = async (): Promise<void> => {\n // When setAlarm is called, store the current span context so that when the alarm\n // fires later, it can link back to the trace that called setAlarm.\n // We use the original (uninstrumented) storage (target) to avoid creating a span\n // for this internal operation. The storage is deferred via waitUntil to not block.\n if (methodName === 'setAlarm') {\n storeSpanContext(target, 'alarm');\n }\n };\n\n const result = (original as (...args: unknown[]) => unknown).apply(target, args);\n\n if (!isThenable(result)) {\n waitUntil?.(teardown());\n\n return result;\n }\n\n return result.then(\n res => {\n waitUntil?.(teardown());\n return res;\n },\n e => {\n throw e;\n },\n );\n },\n );\n };\n },\n });\n}\n"],"names":["instrumentDurableObjectSyncKvStorage","instrumentSqlStorage","startSpan","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","storeSpanContext","isThenable"],"mappings":"
|
|
1
|
+
{"version":3,"file":"instrumentDurableObjectStorage.js","sources":["../../../src/instrumentations/instrumentDurableObjectStorage.ts"],"sourcesContent":["import type { DurableObjectStorage, SyncKvStorage, SqlStorage } from '@cloudflare/workers-types';\nimport { getClient, isThenable, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startSpan } from '@sentry/core';\nimport type { CloudflareClientOptions } from '../client';\nimport { getStorageKeys, targetsCloudflareInternalKey } from '../utils/internalStorageKey';\nimport { storeSpanContext } from '../utils/traceLinks';\nimport { instrumentDurableObjectSyncKvStorage } from './instrumentDurableObjectSyncKvStorage';\nimport { instrumentSqlStorage } from './instrumentSqlStorage';\n\nconst STORAGE_METHODS_TO_INSTRUMENT = ['get', 'put', 'delete', 'list', 'setAlarm', 'getAlarm', 'deleteAlarm'] as const;\n\ntype StorageMethod = (typeof STORAGE_METHODS_TO_INSTRUMENT)[number];\n\ntype WaitUntil = (promise: Promise<unknown>) => void;\n\n/**\n * Instruments DurableObjectStorage methods with Sentry spans.\n *\n * Wraps the following async methods:\n * - get, put, delete, list (KV API)\n * - setAlarm, getAlarm, deleteAlarm (Alarm API)\n *\n * When setAlarm is called, it also stores the current span context so that when\n * the alarm fires later, it can link back to the trace that called setAlarm.\n *\n * @param storage - The DurableObjectStorage instance to instrument\n * @param waitUntil - Optional waitUntil function to defer span context storage\n * @returns An instrumented DurableObjectStorage instance\n */\nexport function instrumentDurableObjectStorage(\n storage: DurableObjectStorage,\n waitUntil?: WaitUntil,\n): DurableObjectStorage {\n return new Proxy(storage, {\n get(target, prop, _receiver) {\n // Use `target` as the receiver instead of the proxy (`_receiver`).\n // Native workerd getters (e.g., `storage.sql`) validate `this` via\n // internal slots. Passing the proxy as receiver breaks that check,\n // causing \"Illegal invocation: function called with incorrect `this`\n // reference\" errors.\n const original = Reflect.get(target, prop, target);\n\n if (prop === 'kv' && original != null && 'get' in original && 'put' in original) {\n return instrumentDurableObjectSyncKvStorage(original as SyncKvStorage);\n }\n\n if (prop === 'sql' && original != null && 'databaseSize' in original && 'exec' in original) {\n return instrumentSqlStorage(original as SqlStorage);\n }\n\n if (typeof original !== 'function') {\n return original;\n }\n\n const methodName = prop as string;\n if (!STORAGE_METHODS_TO_INSTRUMENT.includes(methodName as StorageMethod)) {\n return (original as (...args: unknown[]) => unknown).bind(target);\n }\n\n return function (this: unknown, ...args: unknown[]) {\n // KV entries managed by the DO framework itself (agents/partyserver state) are bookkeeping\n // rather than user work — skip the span, mirroring how `cf_` SQL tables are treated.\n const allowlist = (getClient()?.getOptions() as CloudflareClientOptions | undefined)\n ?.durableObjectStorageSpanAllowlist;\n const keys = getStorageKeys(methodName, args);\n if (keys && keys.length > 0 && keys.every(key => targetsCloudflareInternalKey(key, allowlist))) {\n return (original as (...a: unknown[]) => unknown).apply(target, args);\n }\n\n return startSpan(\n {\n // Use underscore naming to match Cloudflare's native instrumentation (e.g., \"durable_object_storage_get\")\n name: `durable_object_storage_${methodName}`,\n op: 'db',\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.db.cloudflare.durable_object',\n 'db.system.name': 'cloudflare.durable_object.storage',\n 'db.operation.name': methodName,\n },\n },\n () => {\n const teardown = async (): Promise<void> => {\n // When setAlarm is called, store the current span context so that when the alarm\n // fires later, it can link back to the trace that called setAlarm.\n // We use the original (uninstrumented) storage (target) to avoid creating a span\n // for this internal operation. The storage is deferred via waitUntil to not block.\n if (methodName === 'setAlarm') {\n storeSpanContext(target, 'alarm');\n }\n };\n\n const result = (original as (...args: unknown[]) => unknown).apply(target, args);\n\n if (!isThenable(result)) {\n waitUntil?.(teardown());\n\n return result;\n }\n\n return result.then(\n res => {\n waitUntil?.(teardown());\n return res;\n },\n e => {\n throw e;\n },\n );\n },\n );\n };\n },\n });\n}\n"],"names":["instrumentDurableObjectSyncKvStorage","instrumentSqlStorage","getClient","getStorageKeys","targetsCloudflareInternalKey","startSpan","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","storeSpanContext","isThenable"],"mappings":";;;;;;;;AAQA,MAAM,6BAAA,GAAgC,CAAC,KAAA,EAAO,KAAA,EAAO,UAAU,MAAA,EAAQ,UAAA,EAAY,YAAY,aAAa,CAAA;AAoBrG,SAAS,8BAAA,CACd,SACA,SAAA,EACsB;AACtB,EAAA,OAAO,IAAI,MAAM,OAAA,EAAS;AAAA,IACxB,GAAA,CAAI,MAAA,EAAQ,IAAA,EAAM,SAAA,EAAW;AAM3B,MAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,GAAA,CAAI,MAAA,EAAQ,MAAM,MAAM,CAAA;AAEjD,MAAA,IAAI,SAAS,IAAA,IAAQ,QAAA,IAAY,QAAQ,KAAA,IAAS,QAAA,IAAY,SAAS,QAAA,EAAU;AAC/E,QAAA,OAAOA,0EAAqC,QAAyB,CAAA;AAAA,MACvE;AAEA,MAAA,IAAI,SAAS,KAAA,IAAS,QAAA,IAAY,QAAQ,cAAA,IAAkB,QAAA,IAAY,UAAU,QAAA,EAAU;AAC1F,QAAA,OAAOC,0CAAqB,QAAsB,CAAA;AAAA,MACpD;AAEA,MAAA,IAAI,OAAO,aAAa,UAAA,EAAY;AAClC,QAAA,OAAO,QAAA;AAAA,MACT;AAEA,MAAA,MAAM,UAAA,GAAa,IAAA;AACnB,MAAA,IAAI,CAAC,6BAAA,CAA8B,QAAA,CAAS,UAA2B,CAAA,EAAG;AACxE,QAAA,OAAQ,QAAA,CAA6C,KAAK,MAAM,CAAA;AAAA,MAClE;AAEA,MAAA,OAAO,YAA4B,IAAA,EAAiB;AAGlD,QAAA,MAAM,SAAA,GAAaC,cAAA,EAAU,EAAG,UAAA,EAAW,EACvC,iCAAA;AACJ,QAAA,MAAM,IAAA,GAAOC,iCAAA,CAAe,UAAA,EAAY,IAAI,CAAA;AAC5C,QAAA,IAAI,IAAA,IAAQ,IAAA,CAAK,MAAA,GAAS,CAAA,IAAK,IAAA,CAAK,KAAA,CAAM,CAAA,GAAA,KAAOC,+CAAA,CAA6B,GAAA,EAAK,SAAS,CAAC,CAAA,EAAG;AAC9F,UAAA,OAAQ,QAAA,CAA0C,KAAA,CAAM,MAAA,EAAQ,IAAI,CAAA;AAAA,QACtE;AAEA,QAAA,OAAOC,cAAA;AAAA,UACL;AAAA;AAAA,YAEE,IAAA,EAAM,0BAA0B,UAAU,CAAA,CAAA;AAAA,YAC1C,EAAA,EAAI,IAAA;AAAA,YACJ,UAAA,EAAY;AAAA,cACV,CAACC,qCAAgC,GAAG,mCAAA;AAAA,cACpC,gBAAA,EAAkB,mCAAA;AAAA,cAClB,mBAAA,EAAqB;AAAA;AACvB,WACF;AAAA,UACA,MAAM;AACJ,YAAA,MAAM,WAAW,YAA2B;AAK1C,cAAA,IAAI,eAAe,UAAA,EAAY;AAC7B,gBAAAC,2BAAA,CAAiB,QAAQ,OAAO,CAAA;AAAA,cAClC;AAAA,YACF,CAAA;AAEA,YAAA,MAAM,MAAA,GAAU,QAAA,CAA6C,KAAA,CAAM,MAAA,EAAQ,IAAI,CAAA;AAE/E,YAAA,IAAI,CAACC,eAAA,CAAW,MAAM,CAAA,EAAG;AACvB,cAAA,SAAA,GAAY,UAAU,CAAA;AAEtB,cAAA,OAAO,MAAA;AAAA,YACT;AAEA,YAAA,OAAO,MAAA,CAAO,IAAA;AAAA,cACZ,CAAA,GAAA,KAAO;AACL,gBAAA,SAAA,GAAY,UAAU,CAAA;AACtB,gBAAA,OAAO,GAAA;AAAA,cACT,CAAA;AAAA,cACA,CAAA,CAAA,KAAK;AACH,gBAAA,MAAM,CAAA;AAAA,cACR;AAAA,aACF;AAAA,UACF;AAAA,SACF;AAAA,MACF,CAAA;AAAA,IACF;AAAA,GACD,CAAA;AACH;;;;"}
|
|
@@ -15,7 +15,7 @@ function instrumentSqlStorage(sql) {
|
|
|
15
15
|
const sanitizedQuery = core._INTERNAL_sanitizeSqlQuery(query);
|
|
16
16
|
const querySummary = core._INTERNAL_getSqlQuerySummary(sanitizedQuery);
|
|
17
17
|
const allowlist = core.getClient()?.getOptions()?.durableObjectSqlSpanAllowlist;
|
|
18
|
-
if (internalSqlQuery.targetsCloudflareInternalTable(querySummary, allowlist)) {
|
|
18
|
+
if (internalSqlQuery.targetsCloudflareInternalTable(querySummary, allowlist, sanitizedQuery)) {
|
|
19
19
|
return original.apply(target, args);
|
|
20
20
|
}
|
|
21
21
|
return core.startSpan(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrumentSqlStorage.js","sources":["../../../src/instrumentations/instrumentSqlStorage.ts"],"sourcesContent":["import type { SqlStorage } from '@cloudflare/workers-types';\nimport {\n _INTERNAL_getSqlQuerySummary,\n _INTERNAL_sanitizeSqlQuery,\n getClient,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n startSpan,\n} from '@sentry/core';\nimport type { CloudflareClientOptions } from '../client';\nimport { targetsCloudflareInternalTable } from '../utils/internalSqlQuery';\n\n/**\n * Instruments the Durable Object SqlStorage `exec` method with Sentry spans.\n *\n * @param sql - The SqlStorage instance to instrument\n * @returns An instrumented SqlStorage instance\n */\nexport function instrumentSqlStorage(sql: SqlStorage): SqlStorage {\n return new Proxy(sql, {\n get(target, prop, _receiver) {\n const original = Reflect.get(target, prop, target);\n\n if (prop !== 'exec' || typeof original !== 'function') {\n return original;\n }\n\n return function (this: unknown, ...args: unknown[]) {\n const [query, ...bindings] = args as [string, ...unknown[]];\n\n const sanitizedQuery = _INTERNAL_sanitizeSqlQuery(query);\n const querySummary = _INTERNAL_getSqlQuerySummary(sanitizedQuery);\n\n const allowlist = (getClient()?.getOptions() as CloudflareClientOptions | undefined)\n ?.durableObjectSqlSpanAllowlist;\n\n if (targetsCloudflareInternalTable(querySummary, allowlist)) {\n return (original as (...a: unknown[]) => ReturnType<SqlStorage['exec']>).apply(target, args);\n }\n\n return startSpan(\n {\n op: 'db.query',\n name: querySummary || sanitizedQuery,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.db.cloudflare.durable_object.sql',\n 'db.system.name': 'cloudflare-durable-object-sql',\n 'db.operation.name': 'exec',\n 'db.query.text': sanitizedQuery,\n 'db.query.summary': querySummary,\n 'cloudflare.durable_object.query.bindings': bindings.length,\n },\n },\n () => (original as (...a: unknown[]) => ReturnType<SqlStorage['exec']>).apply(target, args),\n );\n };\n },\n });\n}\n"],"names":["_INTERNAL_sanitizeSqlQuery","_INTERNAL_getSqlQuerySummary","getClient","targetsCloudflareInternalTable","startSpan","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN"],"mappings":";;;;;AAiBO,SAAS,qBAAqB,GAAA,EAA6B;AAChE,EAAA,OAAO,IAAI,MAAM,GAAA,EAAK;AAAA,IACpB,GAAA,CAAI,MAAA,EAAQ,IAAA,EAAM,SAAA,EAAW;AAC3B,MAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,GAAA,CAAI,MAAA,EAAQ,MAAM,MAAM,CAAA;AAEjD,MAAA,IAAI,IAAA,KAAS,MAAA,IAAU,OAAO,QAAA,KAAa,UAAA,EAAY;AACrD,QAAA,OAAO,QAAA;AAAA,MACT;AAEA,MAAA,OAAO,YAA4B,IAAA,EAAiB;AAClD,QAAA,MAAM,CAAC,KAAA,EAAO,GAAG,QAAQ,CAAA,GAAI,IAAA;AAE7B,QAAA,MAAM,cAAA,GAAiBA,gCAA2B,KAAK,CAAA;AACvD,QAAA,MAAM,YAAA,GAAeC,kCAA6B,cAAc,CAAA;AAEhE,QAAA,MAAM,SAAA,GAAaC,cAAA,EAAU,EAAG,UAAA,EAAW,EACvC,6BAAA;AAEJ,QAAA,IAAIC,+CAAA,CAA+B,YAAA,EAAc,
|
|
1
|
+
{"version":3,"file":"instrumentSqlStorage.js","sources":["../../../src/instrumentations/instrumentSqlStorage.ts"],"sourcesContent":["import type { SqlStorage } from '@cloudflare/workers-types';\nimport {\n _INTERNAL_getSqlQuerySummary,\n _INTERNAL_sanitizeSqlQuery,\n getClient,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n startSpan,\n} from '@sentry/core';\nimport type { CloudflareClientOptions } from '../client';\nimport { targetsCloudflareInternalTable } from '../utils/internalSqlQuery';\n\n/**\n * Instruments the Durable Object SqlStorage `exec` method with Sentry spans.\n *\n * @param sql - The SqlStorage instance to instrument\n * @returns An instrumented SqlStorage instance\n */\nexport function instrumentSqlStorage(sql: SqlStorage): SqlStorage {\n return new Proxy(sql, {\n get(target, prop, _receiver) {\n const original = Reflect.get(target, prop, target);\n\n if (prop !== 'exec' || typeof original !== 'function') {\n return original;\n }\n\n return function (this: unknown, ...args: unknown[]) {\n const [query, ...bindings] = args as [string, ...unknown[]];\n\n const sanitizedQuery = _INTERNAL_sanitizeSqlQuery(query);\n const querySummary = _INTERNAL_getSqlQuerySummary(sanitizedQuery);\n\n const allowlist = (getClient()?.getOptions() as CloudflareClientOptions | undefined)\n ?.durableObjectSqlSpanAllowlist;\n\n if (targetsCloudflareInternalTable(querySummary, allowlist, sanitizedQuery)) {\n return (original as (...a: unknown[]) => ReturnType<SqlStorage['exec']>).apply(target, args);\n }\n\n return startSpan(\n {\n op: 'db.query',\n name: querySummary || sanitizedQuery,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.db.cloudflare.durable_object.sql',\n 'db.system.name': 'cloudflare-durable-object-sql',\n 'db.operation.name': 'exec',\n 'db.query.text': sanitizedQuery,\n 'db.query.summary': querySummary,\n 'cloudflare.durable_object.query.bindings': bindings.length,\n },\n },\n () => (original as (...a: unknown[]) => ReturnType<SqlStorage['exec']>).apply(target, args),\n );\n };\n },\n });\n}\n"],"names":["_INTERNAL_sanitizeSqlQuery","_INTERNAL_getSqlQuerySummary","getClient","targetsCloudflareInternalTable","startSpan","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN"],"mappings":";;;;;AAiBO,SAAS,qBAAqB,GAAA,EAA6B;AAChE,EAAA,OAAO,IAAI,MAAM,GAAA,EAAK;AAAA,IACpB,GAAA,CAAI,MAAA,EAAQ,IAAA,EAAM,SAAA,EAAW;AAC3B,MAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,GAAA,CAAI,MAAA,EAAQ,MAAM,MAAM,CAAA;AAEjD,MAAA,IAAI,IAAA,KAAS,MAAA,IAAU,OAAO,QAAA,KAAa,UAAA,EAAY;AACrD,QAAA,OAAO,QAAA;AAAA,MACT;AAEA,MAAA,OAAO,YAA4B,IAAA,EAAiB;AAClD,QAAA,MAAM,CAAC,KAAA,EAAO,GAAG,QAAQ,CAAA,GAAI,IAAA;AAE7B,QAAA,MAAM,cAAA,GAAiBA,gCAA2B,KAAK,CAAA;AACvD,QAAA,MAAM,YAAA,GAAeC,kCAA6B,cAAc,CAAA;AAEhE,QAAA,MAAM,SAAA,GAAaC,cAAA,EAAU,EAAG,UAAA,EAAW,EACvC,6BAAA;AAEJ,QAAA,IAAIC,+CAAA,CAA+B,YAAA,EAAc,SAAA,EAAW,cAAc,CAAA,EAAG;AAC3E,UAAA,OAAQ,QAAA,CAAiE,KAAA,CAAM,MAAA,EAAQ,IAAI,CAAA;AAAA,QAC7F;AAEA,QAAA,OAAOC,cAAA;AAAA,UACL;AAAA,YACE,EAAA,EAAI,UAAA;AAAA,YACJ,MAAM,YAAA,IAAgB,cAAA;AAAA,YACtB,UAAA,EAAY;AAAA,cACV,CAACC,qCAAgC,GAAG,uCAAA;AAAA,cACpC,gBAAA,EAAkB,+BAAA;AAAA,cAClB,mBAAA,EAAqB,MAAA;AAAA,cACrB,eAAA,EAAiB,cAAA;AAAA,cACjB,kBAAA,EAAoB,YAAA;AAAA,cACpB,4CAA4C,QAAA,CAAS;AAAA;AACvD,WACF;AAAA,UACA,MAAO,QAAA,CAAiE,KAAA,CAAM,MAAA,EAAQ,IAAI;AAAA,SAC5F;AAAA,MACF,CAAA;AAAA,IACF;AAAA,GACD,CAAA;AACH;;;;"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
|
|
3
|
+
const core = require('@sentry/core');
|
|
4
|
+
const debugBuild = require('../debug-build.js');
|
|
5
|
+
|
|
6
|
+
const INTEGRATION_NAME = "Spotlight";
|
|
7
|
+
const _spotlightIntegration = ((options = {}) => {
|
|
8
|
+
const sidecarUrl = options.sidecarUrl || "http://localhost:8969/stream";
|
|
9
|
+
return {
|
|
10
|
+
name: INTEGRATION_NAME,
|
|
11
|
+
setup(client) {
|
|
12
|
+
debugBuild.DEBUG_BUILD && core.debug.log("[Spotlight] Using Sidecar URL", sidecarUrl);
|
|
13
|
+
setupSidecarForwarding(client, sidecarUrl);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
});
|
|
17
|
+
const spotlightIntegration = core.defineIntegration(_spotlightIntegration);
|
|
18
|
+
function setupSidecarForwarding(client, sidecarUrl) {
|
|
19
|
+
const parsedUrl = parseSidecarUrl(sidecarUrl);
|
|
20
|
+
if (!parsedUrl) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
let failCount = 0;
|
|
24
|
+
client.on("beforeEnvelope", (envelope) => {
|
|
25
|
+
if (failCount > 3) {
|
|
26
|
+
debugBuild.DEBUG_BUILD && core.debug.warn("[Spotlight] Disabled Sentry -> Spotlight forwarding due to too many failed requests");
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const body = core.serializeEnvelope(envelope);
|
|
30
|
+
core.suppressTracing(() => {
|
|
31
|
+
fetch(parsedUrl.href, {
|
|
32
|
+
method: "POST",
|
|
33
|
+
body,
|
|
34
|
+
headers: {
|
|
35
|
+
"Content-Type": "application/x-sentry-envelope"
|
|
36
|
+
}
|
|
37
|
+
}).then(
|
|
38
|
+
(res) => {
|
|
39
|
+
res.text().catch(() => {
|
|
40
|
+
});
|
|
41
|
+
if (res.status >= 200 && res.status < 400) {
|
|
42
|
+
failCount = 0;
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
() => {
|
|
46
|
+
failCount++;
|
|
47
|
+
debugBuild.DEBUG_BUILD && core.debug.warn("[Spotlight] Failed to send envelope to Spotlight Sidecar");
|
|
48
|
+
}
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
function parseSidecarUrl(url) {
|
|
54
|
+
try {
|
|
55
|
+
return new URL(url);
|
|
56
|
+
} catch {
|
|
57
|
+
debugBuild.DEBUG_BUILD && core.debug.warn(`[Spotlight] Invalid sidecar URL: ${url}`);
|
|
58
|
+
return void 0;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
exports.INTEGRATION_NAME = INTEGRATION_NAME;
|
|
63
|
+
exports.spotlightIntegration = spotlightIntegration;
|
|
64
|
+
//# sourceMappingURL=spotlight.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spotlight.js","sources":["../../../src/integrations/spotlight.ts"],"sourcesContent":["import type { Client, Envelope, IntegrationFn } from '@sentry/core';\nimport { debug, defineIntegration, serializeEnvelope, suppressTracing } from '@sentry/core';\nimport { DEBUG_BUILD } from '../debug-build';\n\ntype SpotlightConnectionOptions = {\n /**\n * Set this if the Spotlight Sidecar is not running on localhost:8969.\n * By default, the URL is set to http://localhost:8969/stream\n */\n sidecarUrl?: string;\n};\n\nexport const INTEGRATION_NAME = 'Spotlight' as const;\n\nconst _spotlightIntegration = ((options: Partial<SpotlightConnectionOptions> = {}) => {\n const sidecarUrl = options.sidecarUrl || 'http://localhost:8969/stream';\n\n return {\n name: INTEGRATION_NAME,\n setup(client) {\n DEBUG_BUILD && debug.log('[Spotlight] Using Sidecar URL', sidecarUrl);\n setupSidecarForwarding(client, sidecarUrl);\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Use this integration to send errors and transactions to Spotlight.\n *\n * Learn more about spotlight at https://spotlightjs.com\n *\n * Important: This integration is intended for local development only.\n * Each forwarded envelope counts as a Worker subrequest (50 free / 1000 paid\n * per invocation), so it should not be enabled in production.\n */\nexport const spotlightIntegration = defineIntegration(_spotlightIntegration);\n\nfunction setupSidecarForwarding(client: Client, sidecarUrl: string): void {\n const parsedUrl = parseSidecarUrl(sidecarUrl);\n if (!parsedUrl) {\n return;\n }\n\n let failCount = 0;\n\n client.on('beforeEnvelope', (envelope: Envelope) => {\n if (failCount > 3) {\n DEBUG_BUILD && debug.warn('[Spotlight] Disabled Sentry -> Spotlight forwarding due to too many failed requests');\n return;\n }\n\n const body = serializeEnvelope(envelope);\n\n suppressTracing(() => {\n fetch(parsedUrl.href, {\n method: 'POST',\n body,\n headers: {\n 'Content-Type': 'application/x-sentry-envelope',\n },\n }).then(\n res => {\n // Consume the response body to satisfy Cloudflare Workers' requirement\n // that all fetch response bodies are read or cancelled.\n res.text().catch(() => {\n // no-op\n });\n\n if (res.status >= 200 && res.status < 400) {\n failCount = 0;\n }\n },\n () => {\n failCount++;\n DEBUG_BUILD && debug.warn('[Spotlight] Failed to send envelope to Spotlight Sidecar');\n },\n );\n });\n });\n}\n\nfunction parseSidecarUrl(url: string): URL | undefined {\n try {\n return new URL(url);\n } catch {\n DEBUG_BUILD && debug.warn(`[Spotlight] Invalid sidecar URL: ${url}`);\n return undefined;\n }\n}\n"],"names":["DEBUG_BUILD","debug","defineIntegration","serializeEnvelope","suppressTracing"],"mappings":";;;;;AAYO,MAAM,gBAAA,GAAmB;AAEhC,MAAM,qBAAA,IAAyB,CAAC,OAAA,GAA+C,EAAC,KAAM;AACpF,EAAA,MAAM,UAAA,GAAa,QAAQ,UAAA,IAAc,8BAAA;AAEzC,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,MAAM,MAAA,EAAQ;AACZ,MAAAA,sBAAA,IAAeC,UAAA,CAAM,GAAA,CAAI,+BAAA,EAAiC,UAAU,CAAA;AACpE,MAAA,sBAAA,CAAuB,QAAQ,UAAU,CAAA;AAAA,IAC3C;AAAA,GACF;AACF,CAAA,CAAA;AAWO,MAAM,oBAAA,GAAuBC,uBAAkB,qBAAqB;AAE3E,SAAS,sBAAA,CAAuB,QAAgB,UAAA,EAA0B;AACxE,EAAA,MAAM,SAAA,GAAY,gBAAgB,UAAU,CAAA;AAC5C,EAAA,IAAI,CAAC,SAAA,EAAW;AACd,IAAA;AAAA,EACF;AAEA,EAAA,IAAI,SAAA,GAAY,CAAA;AAEhB,EAAA,MAAA,CAAO,EAAA,CAAG,gBAAA,EAAkB,CAAC,QAAA,KAAuB;AAClD,IAAA,IAAI,YAAY,CAAA,EAAG;AACjB,MAAAF,sBAAA,IAAeC,UAAA,CAAM,KAAK,qFAAqF,CAAA;AAC/G,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,IAAA,GAAOE,uBAAkB,QAAQ,CAAA;AAEvC,IAAAC,oBAAA,CAAgB,MAAM;AACpB,MAAA,KAAA,CAAM,UAAU,IAAA,EAAM;AAAA,QACpB,MAAA,EAAQ,MAAA;AAAA,QACR,IAAA;AAAA,QACA,OAAA,EAAS;AAAA,UACP,cAAA,EAAgB;AAAA;AAClB,OACD,CAAA,CAAE,IAAA;AAAA,QACD,CAAA,GAAA,KAAO;AAGL,UAAA,GAAA,CAAI,IAAA,EAAK,CAAE,KAAA,CAAM,MAAM;AAAA,UAEvB,CAAC,CAAA;AAED,UAAA,IAAI,GAAA,CAAI,MAAA,IAAU,GAAA,IAAO,GAAA,CAAI,SAAS,GAAA,EAAK;AACzC,YAAA,SAAA,GAAY,CAAA;AAAA,UACd;AAAA,QACF,CAAA;AAAA,QACA,MAAM;AACJ,UAAA,SAAA,EAAA;AACA,UAAAJ,sBAAA,IAAeC,UAAA,CAAM,KAAK,0DAA0D,CAAA;AAAA,QACtF;AAAA,OACF;AAAA,IACF,CAAC,CAAA;AAAA,EACH,CAAC,CAAA;AACH;AAEA,SAAS,gBAAgB,GAAA,EAA8B;AACrD,EAAA,IAAI;AACF,IAAA,OAAO,IAAI,IAAI,GAAG,CAAA;AAAA,EACpB,CAAA,CAAA,MAAQ;AACN,IAAAD,sBAAA,IAAeC,UAAA,CAAM,IAAA,CAAK,CAAA,iCAAA,EAAoC,GAAG,CAAA,CAAE,CAAA;AACnE,IAAA,OAAO,MAAA;AAAA,EACT;AACF;;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vercelai.js","sources":["../../../../src/integrations/tracing/vercelai.ts"],"sourcesContent":["/**\n * This is a copy of the Vercel AI integration from the node SDK.\n *\n * The only difference is that it does not use `@opentelemetry/instrumentation`\n * because Cloudflare Workers do not support it.\n *\n * Therefore, we cannot automatically patch setting `experimental_telemetry: { isEnabled: true }`\n * and users have to manually set this to get spans.\n */\n\nimport type { IntegrationFn } from '@sentry/core';\nimport { addVercelAiProcessors, defineIntegration } from '@sentry/core';\n\nconst INTEGRATION_NAME = 'VercelAI';\n\ninterface VercelAiOptions {\n /**\n * Enable or disable truncation of recorded input messages.\n * Defaults to `true`.\n */\n enableTruncation?: boolean;\n}\n\nconst _vercelAIIntegration = ((options: VercelAiOptions = {}) => {\n return {\n name: INTEGRATION_NAME,\n options,\n setup(client) {\n addVercelAiProcessors(client);\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Adds Sentry tracing instrumentation for the [ai](https://www.npmjs.com/package/ai) library.\n * This integration is not enabled by default, you need to manually add it.\n *\n * For more information, see the [`ai` documentation](https://sdk.vercel.ai/docs/ai-sdk-core/telemetry).\n *\n * You need to enable collecting spans for a specific call by setting\n * `experimental_telemetry.isEnabled` to `true` in the first argument of the function call.\n *\n * ```javascript\n * const result = await generateText({\n * model: openai('gpt-4-turbo'),\n * experimental_telemetry: { isEnabled: true },\n * });\n * ```\n *\n * If you want to collect inputs and outputs for a specific call, you must specifically opt-in to each\n * function call by setting `experimental_telemetry.recordInputs` and `experimental_telemetry.recordOutputs`\n * to `true`.\n *\n * ```javascript\n * const result = await generateText({\n * model: openai('gpt-4-turbo'),\n * experimental_telemetry: { isEnabled: true, recordInputs: true, recordOutputs: true },\n * });\n */\nexport const vercelAIIntegration = defineIntegration(_vercelAIIntegration);\n"],"names":["addVercelAiProcessors","defineIntegration"],"mappings":";;;;AAaA,MAAM,gBAAA,GAAmB,UAAA;
|
|
1
|
+
{"version":3,"file":"vercelai.js","sources":["../../../../src/integrations/tracing/vercelai.ts"],"sourcesContent":["/**\n * This is a copy of the Vercel AI integration from the node SDK.\n *\n * The only difference is that it does not use `@opentelemetry/instrumentation`\n * because Cloudflare Workers do not support it.\n *\n * Therefore, we cannot automatically patch setting `experimental_telemetry: { isEnabled: true }`\n * and users have to manually set this to get spans.\n */\n\nimport type { IntegrationFn } from '@sentry/core';\nimport { addVercelAiProcessors, defineIntegration } from '@sentry/core';\n\nconst INTEGRATION_NAME = 'VercelAI';\n\ninterface VercelAiOptions {\n /**\n * Enable or disable truncation of recorded input messages.\n * Defaults to `true`.\n */\n enableTruncation?: boolean;\n\n // `recordInputs`/`recordOutputs` are intentionally omitted: this entrypoint only post-processes\n // spans the AI SDK already emitted, so it cannot decide whether inputs/outputs are recorded.\n // Control this per call via `experimental_telemetry.recordInputs`/`recordOutputs`, or use the\n // `@sentry/cloudflare/nodejs_compat` entrypoint for integration-level control on ai >= 7.\n}\n\nconst _vercelAIIntegration = ((options: VercelAiOptions = {}) => {\n return {\n name: INTEGRATION_NAME,\n options,\n setup(client) {\n addVercelAiProcessors(client);\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Adds Sentry tracing instrumentation for the [ai](https://www.npmjs.com/package/ai) library.\n * This integration is not enabled by default, you need to manually add it.\n *\n * For more information, see the [`ai` documentation](https://sdk.vercel.ai/docs/ai-sdk-core/telemetry).\n *\n * You need to enable collecting spans for a specific call by setting\n * `experimental_telemetry.isEnabled` to `true` in the first argument of the function call.\n *\n * ```javascript\n * const result = await generateText({\n * model: openai('gpt-4-turbo'),\n * experimental_telemetry: { isEnabled: true },\n * });\n * ```\n *\n * If you want to collect inputs and outputs for a specific call, you must specifically opt-in to each\n * function call by setting `experimental_telemetry.recordInputs` and `experimental_telemetry.recordOutputs`\n * to `true`.\n *\n * ```javascript\n * const result = await generateText({\n * model: openai('gpt-4-turbo'),\n * experimental_telemetry: { isEnabled: true, recordInputs: true, recordOutputs: true },\n * });\n */\nexport const vercelAIIntegration = defineIntegration(_vercelAIIntegration);\n"],"names":["addVercelAiProcessors","defineIntegration"],"mappings":";;;;AAaA,MAAM,gBAAA,GAAmB,UAAA;AAezB,MAAM,oBAAA,IAAwB,CAAC,OAAA,GAA2B,EAAC,KAAM;AAC/D,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,OAAA;AAAA,IACA,MAAM,MAAA,EAAQ;AACZ,MAAAA,0BAAA,CAAsB,MAAM,CAAA;AAAA,IAC9B;AAAA,GACF;AACF,CAAA,CAAA;AA4BO,MAAM,mBAAA,GAAsBC,uBAAkB,oBAAoB;;;;"}
|
|
@@ -1,24 +1,26 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const serverUtils = require('@sentry/server-utils');
|
|
4
4
|
const vercelai = require('./integrations/tracing/vercelai.js');
|
|
5
5
|
const client = require('../client.js');
|
|
6
6
|
const core = require('@sentry/core');
|
|
7
|
+
const defineCloudflareOptions = require('../defineCloudflareOptions.js');
|
|
7
8
|
const fetch = require('../integrations/fetch.js');
|
|
8
9
|
const sdk = require('../sdk.js');
|
|
9
10
|
const hono = require('../integrations/hono.js');
|
|
10
11
|
const httpServer = require('../integrations/httpServer.js');
|
|
11
|
-
const instrumentD1 = require('../instrumentations/worker/instrumentD1.js');
|
|
12
12
|
const durableobject = require('../durableobject.js');
|
|
13
|
+
const instrumentD1 = require('../instrumentations/worker/instrumentD1.js');
|
|
13
14
|
const workflows = require('../workflows.js');
|
|
14
15
|
const pagesPlugin = require('../pages-plugin.js');
|
|
15
16
|
const async = require('../async.js');
|
|
17
|
+
const spotlight = require('../integrations/spotlight.js');
|
|
16
18
|
const withSentry = require('../withSentry.js');
|
|
17
19
|
const request = require('../request.js');
|
|
18
20
|
|
|
19
21
|
|
|
20
22
|
|
|
21
|
-
exports.prismaIntegration =
|
|
23
|
+
exports.prismaIntegration = serverUtils.prismaIntegration;
|
|
22
24
|
exports.vercelAIIntegration = vercelai.vercelAIIntegration;
|
|
23
25
|
exports.CloudflareClient = client.CloudflareClient;
|
|
24
26
|
exports.SDK_VERSION = core.SDK_VERSION;
|
|
@@ -69,6 +71,7 @@ exports.instrumentLangChainEmbeddings = core.instrumentLangChainEmbeddings;
|
|
|
69
71
|
exports.instrumentLangGraph = core.instrumentLangGraph;
|
|
70
72
|
exports.instrumentOpenAiClient = core.instrumentOpenAiClient;
|
|
71
73
|
exports.instrumentPostgresJsSql = core.instrumentPostgresJsSql;
|
|
74
|
+
exports.instrumentStateGraph = core.instrumentStateGraph;
|
|
72
75
|
exports.instrumentSupabaseClient = core.instrumentSupabaseClient;
|
|
73
76
|
exports.isEnabled = core.isEnabled;
|
|
74
77
|
exports.isInitialized = core.isInitialized;
|
|
@@ -110,15 +113,18 @@ exports.withScope = core.withScope;
|
|
|
110
113
|
exports.withStreamedSpan = core.withStreamedSpan;
|
|
111
114
|
exports.wrapMcpServerWithSentry = core.wrapMcpServerWithSentry;
|
|
112
115
|
exports.zodErrorsIntegration = core.zodErrorsIntegration;
|
|
116
|
+
exports.defineCloudflareOptions = defineCloudflareOptions.defineCloudflareOptions;
|
|
113
117
|
exports.fetchIntegration = fetch.fetchIntegration;
|
|
114
118
|
exports.getDefaultIntegrations = sdk.getDefaultIntegrations;
|
|
115
119
|
exports.honoIntegration = hono.honoIntegration;
|
|
116
120
|
exports.httpServerIntegration = httpServer.httpServerIntegration;
|
|
117
|
-
exports.
|
|
121
|
+
exports.instrumentAgentWithSentry = durableobject.instrumentAgentWithSentry;
|
|
118
122
|
exports.instrumentDurableObjectWithSentry = durableobject.instrumentDurableObjectWithSentry;
|
|
123
|
+
exports.instrumentD1WithSentry = instrumentD1.instrumentD1WithSentry;
|
|
119
124
|
exports.instrumentWorkflowWithSentry = workflows.instrumentWorkflowWithSentry;
|
|
120
125
|
exports.sentryPagesPlugin = pagesPlugin.sentryPagesPlugin;
|
|
121
126
|
exports.setAsyncLocalStorageAsyncContextStrategy = async.setAsyncLocalStorageAsyncContextStrategy;
|
|
127
|
+
exports.spotlightIntegration = spotlight.spotlightIntegration;
|
|
122
128
|
exports.withSentry = withSentry.withSentry;
|
|
123
129
|
exports.wrapRequestHandler = request.wrapRequestHandler;
|
|
124
130
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vercelai.js","sources":["../../../../../src/nodejs_compat/integrations/tracing/vercelai.ts"],"sourcesContent":["/**\n * This is a copy of the Vercel AI integration from the Cloudflare SDK (src/integrations/tracing/vercelai.ts).\n *\n * The only difference is that it includes tracing channel support\n * because the default Vercel AI integration needs `nodejs_als`, while this one uses `nodejs_compat`.\n */\n\nimport type { IntegrationFn } from '@sentry/core';\nimport { defineIntegration } from '@sentry/core';\nimport { vercelAiIntegration as serverUtilsVercelAiIntegration } from '@sentry/server-utils';\nimport { vercelAIIntegration as cloudflareVercelAIIntegration } from '../../../integrations/tracing/vercelai';\n\
|
|
1
|
+
{"version":3,"file":"vercelai.js","sources":["../../../../../src/nodejs_compat/integrations/tracing/vercelai.ts"],"sourcesContent":["/**\n * This is a copy of the Vercel AI integration from the Cloudflare SDK (src/integrations/tracing/vercelai.ts).\n *\n * The only difference is that it includes tracing channel support\n * because the default Vercel AI integration needs `nodejs_als`, while this one uses `nodejs_compat`.\n */\n\nimport type { IntegrationFn } from '@sentry/core';\nimport { defineIntegration } from '@sentry/core';\nimport { vercelAiIntegration as serverUtilsVercelAiIntegration, type VercelAiOptions } from '@sentry/server-utils';\nimport { vercelAIIntegration as cloudflareVercelAIIntegration } from '../../../integrations/tracing/vercelai';\n\nconst _vercelAIIntegration = ((options: VercelAiOptions = {}) => {\n const inner = serverUtilsVercelAiIntegration(options);\n const instrumentation = cloudflareVercelAIIntegration(options);\n\n return {\n ...inner,\n ...instrumentation,\n };\n}) satisfies IntegrationFn;\n\n/**\n * Adds Sentry tracing instrumentation for the [ai](https://www.npmjs.com/package/ai) library.\n * This integration is not enabled by default, you need to manually add it.\n *\n * For more information, see the [`ai` documentation](https://sdk.vercel.ai/docs/ai-sdk-core/telemetry).\n *\n * You need to enable collecting spans for a specific call by setting\n * `experimental_telemetry.isEnabled` to `true` in the first argument of the function call.\n *\n * ```javascript\n * const result = await generateText({\n * model: openai('gpt-4-turbo'),\n * experimental_telemetry: { isEnabled: true },\n * });\n * ```\n *\n * If you want to collect inputs and outputs for a specific call, you must specifically opt-in to each\n * function call by setting `experimental_telemetry.recordInputs` and `experimental_telemetry.recordOutputs`\n * to `true`.\n *\n * ```javascript\n * const result = await generateText({\n * model: openai('gpt-4-turbo'),\n * experimental_telemetry: { isEnabled: true, recordInputs: true, recordOutputs: true },\n * });\n */\nexport const vercelAIIntegration = defineIntegration(_vercelAIIntegration);\n"],"names":["serverUtilsVercelAiIntegration","cloudflareVercelAIIntegration","defineIntegration"],"mappings":";;;;;;AAYA,MAAM,oBAAA,IAAwB,CAAC,OAAA,GAA2B,EAAC,KAAM;AAC/D,EAAA,MAAM,KAAA,GAAQA,gCAA+B,OAAO,CAAA;AACpD,EAAA,MAAM,eAAA,GAAkBC,6BAA8B,OAAO,CAAA;AAE7D,EAAA,OAAO;AAAA,IACL,GAAG,KAAA;AAAA,IACH,GAAG;AAAA,GACL;AACF,CAAA,CAAA;AA4BO,MAAM,mBAAA,GAAsBC,uBAAkB,oBAAoB;;;;"}
|
package/build/cjs/options.js
CHANGED
|
@@ -14,6 +14,7 @@ function getFinalOptions(userOptions = {}, env) {
|
|
|
14
14
|
}
|
|
15
15
|
const release = "SENTRY_RELEASE" in env && typeof env.SENTRY_RELEASE === "string" ? env.SENTRY_RELEASE : "CF_VERSION_METADATA" in env && isVersionMetadata(env.CF_VERSION_METADATA) ? env.CF_VERSION_METADATA.id : void 0;
|
|
16
16
|
const tracesSampleRate = userOptions.tracesSampleRate ?? parseFloat(getEnvVar(env, "SENTRY_TRACES_SAMPLE_RATE") ?? "");
|
|
17
|
+
const spotlight = getSpotlightFromEnv(userOptions.spotlight, getEnvVar(env, "SENTRY_SPOTLIGHT"));
|
|
17
18
|
return {
|
|
18
19
|
release,
|
|
19
20
|
...userOptions,
|
|
@@ -21,9 +22,21 @@ function getFinalOptions(userOptions = {}, env) {
|
|
|
21
22
|
environment: userOptions.environment ?? getEnvVar(env, "SENTRY_ENVIRONMENT"),
|
|
22
23
|
tracesSampleRate: isFinite(tracesSampleRate) ? tracesSampleRate : void 0,
|
|
23
24
|
debug: userOptions.debug ?? core.envToBool(getEnvVar(env, "SENTRY_DEBUG")),
|
|
24
|
-
tunnel: userOptions.tunnel ?? getEnvVar(env, "SENTRY_TUNNEL")
|
|
25
|
+
tunnel: userOptions.tunnel ?? getEnvVar(env, "SENTRY_TUNNEL"),
|
|
26
|
+
spotlight
|
|
25
27
|
};
|
|
26
28
|
}
|
|
29
|
+
function getSpotlightFromEnv(optionsSpotlight, envVar) {
|
|
30
|
+
if (optionsSpotlight === false) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
if (typeof optionsSpotlight === "string") {
|
|
34
|
+
return optionsSpotlight;
|
|
35
|
+
}
|
|
36
|
+
const envBool = core.envToBool(envVar, { strict: true });
|
|
37
|
+
const envUrl = envBool === null && envVar ? envVar : void 0;
|
|
38
|
+
return optionsSpotlight === true ? envUrl ?? true : envBool ?? envUrl;
|
|
39
|
+
}
|
|
27
40
|
|
|
28
41
|
exports.getFinalOptions = getFinalOptions;
|
|
29
42
|
//# sourceMappingURL=options.js.map
|
package/build/cjs/options.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"options.js","sources":["../../src/options.ts"],"sourcesContent":["import { isObjectLike, envToBool } from '@sentry/core';\nimport type { CloudflareOptions } from './client';\n\n/**\n * Cloudflare's version metadata binding structure.\n * @see https://developers.cloudflare.com/workers/runtime-apis/bindings/version-metadata/\n */\ninterface CfVersionMetadata {\n id: string;\n tag?: string;\n timestamp?: string;\n}\n\n/**\n * Checks if the value is a valid CF_VERSION_METADATA binding.\n */\nfunction isVersionMetadata(value: unknown): value is CfVersionMetadata {\n return isObjectLike(value) && 'id' in value && typeof value.id === 'string';\n}\n\nfunction getEnvVar<T extends Record<string, unknown>>(env: unknown, varName: keyof T): string | undefined {\n return isObjectLike(env) && varName in env && typeof (env as T)[varName] === 'string'\n ? ((env as T)[varName] as string)\n : undefined;\n}\n\n/**\n * Merges the options passed in from the user with the options we read from\n * the Cloudflare `env` environment variable object.\n *\n * Release is determined with the following priority (highest to lowest):\n * 1. User-provided release option\n * 2. SENTRY_RELEASE environment variable\n * 3. CF_VERSION_METADATA.id binding (if configured in the wrangler config)\n *\n * @param userOptions - The options passed in from the user.\n * @param env - The environment variables.\n *\n * @returns The final options.\n */\nexport function getFinalOptions(userOptions: CloudflareOptions = {}, env: unknown): CloudflareOptions {\n if (typeof env !== 'object' || env === null) {\n return userOptions;\n }\n\n // Priority: userOptions.release > SENTRY_RELEASE > CF_VERSION_METADATA.id\n const release =\n 'SENTRY_RELEASE' in env && typeof env.SENTRY_RELEASE === 'string'\n ? env.SENTRY_RELEASE\n : 'CF_VERSION_METADATA' in env && isVersionMetadata(env.CF_VERSION_METADATA)\n ? env.CF_VERSION_METADATA.id\n : undefined;\n\n const tracesSampleRate =\n userOptions.tracesSampleRate ?? parseFloat(getEnvVar(env, 'SENTRY_TRACES_SAMPLE_RATE') ?? '');\n\n return {\n release,\n ...userOptions,\n dsn: userOptions.dsn ?? getEnvVar(env, 'SENTRY_DSN'),\n environment: userOptions.environment ?? getEnvVar(env, 'SENTRY_ENVIRONMENT'),\n tracesSampleRate: isFinite(tracesSampleRate) ? tracesSampleRate : undefined,\n debug: userOptions.debug ?? envToBool(getEnvVar(env, 'SENTRY_DEBUG')),\n tunnel: userOptions.tunnel ?? getEnvVar(env, 'SENTRY_TUNNEL'),\n };\n}\n"],"names":["isObjectLike","envToBool"],"mappings":";;;;AAgBA,SAAS,kBAAkB,KAAA,EAA4C;AACrE,EAAA,OAAOA,kBAAa,KAAK,CAAA,IAAK,QAAQ,KAAA,IAAS,OAAO,MAAM,EAAA,KAAO,QAAA;AACrE;AAEA,SAAS,SAAA,CAA6C,KAAc,OAAA,EAAsC;AACxG,EAAA,OAAOA,iBAAA,CAAa,GAAG,CAAA,IAAK,OAAA,IAAW,GAAA,IAAO,OAAQ,GAAA,CAAU,OAAO,CAAA,KAAM,QAAA,GACvE,GAAA,CAAU,OAAO,CAAA,GACnB,MAAA;AACN;AAgBO,SAAS,eAAA,CAAgB,WAAA,GAAiC,EAAC,EAAG,GAAA,EAAiC;AACpG,EAAA,IAAI,OAAO,GAAA,KAAQ,QAAA,IAAY,GAAA,KAAQ,IAAA,EAAM;AAC3C,IAAA,OAAO,WAAA;AAAA,EACT;AAGA,EAAA,MAAM,UACJ,gBAAA,IAAoB,GAAA,IAAO,OAAO,GAAA,CAAI,mBAAmB,QAAA,GACrD,GAAA,CAAI,cAAA,GACJ,qBAAA,IAAyB,OAAO,iBAAA,CAAkB,GAAA,CAAI,mBAAmB,CAAA,GACvE,GAAA,CAAI,oBAAoB,EAAA,GACxB,MAAA;AAER,EAAA,MAAM,gBAAA,GACJ,YAAY,gBAAA,IAAoB,UAAA,CAAW,UAAU,GAAA,EAAK,2BAA2B,KAAK,EAAE,CAAA;
|
|
1
|
+
{"version":3,"file":"options.js","sources":["../../src/options.ts"],"sourcesContent":["import { isObjectLike, envToBool } from '@sentry/core';\nimport type { CloudflareOptions } from './client';\n\n/**\n * Cloudflare's version metadata binding structure.\n * @see https://developers.cloudflare.com/workers/runtime-apis/bindings/version-metadata/\n */\ninterface CfVersionMetadata {\n id: string;\n tag?: string;\n timestamp?: string;\n}\n\n/**\n * Checks if the value is a valid CF_VERSION_METADATA binding.\n */\nfunction isVersionMetadata(value: unknown): value is CfVersionMetadata {\n return isObjectLike(value) && 'id' in value && typeof value.id === 'string';\n}\n\nfunction getEnvVar<T extends Record<string, unknown>>(env: unknown, varName: keyof T): string | undefined {\n return isObjectLike(env) && varName in env && typeof (env as T)[varName] === 'string'\n ? ((env as T)[varName] as string)\n : undefined;\n}\n\n/**\n * Merges the options passed in from the user with the options we read from\n * the Cloudflare `env` environment variable object.\n *\n * Release is determined with the following priority (highest to lowest):\n * 1. User-provided release option\n * 2. SENTRY_RELEASE environment variable\n * 3. CF_VERSION_METADATA.id binding (if configured in the wrangler config)\n *\n * @param userOptions - The options passed in from the user.\n * @param env - The environment variables.\n *\n * @returns The final options.\n */\nexport function getFinalOptions(userOptions: CloudflareOptions = {}, env: unknown): CloudflareOptions {\n if (typeof env !== 'object' || env === null) {\n return userOptions;\n }\n\n // Priority: userOptions.release > SENTRY_RELEASE > CF_VERSION_METADATA.id\n const release =\n 'SENTRY_RELEASE' in env && typeof env.SENTRY_RELEASE === 'string'\n ? env.SENTRY_RELEASE\n : 'CF_VERSION_METADATA' in env && isVersionMetadata(env.CF_VERSION_METADATA)\n ? env.CF_VERSION_METADATA.id\n : undefined;\n\n const tracesSampleRate =\n userOptions.tracesSampleRate ?? parseFloat(getEnvVar(env, 'SENTRY_TRACES_SAMPLE_RATE') ?? '');\n\n // Spotlight precedence (mirrors node-core's getSpotlightConfig):\n // - false or explicit string from options: use as-is\n // - true: enable, but prefer a custom URL from the env var if set\n // - undefined: defer entirely to the env var (bool or URL)\n const spotlight = getSpotlightFromEnv(userOptions.spotlight, getEnvVar(env, 'SENTRY_SPOTLIGHT'));\n\n return {\n release,\n ...userOptions,\n dsn: userOptions.dsn ?? getEnvVar(env, 'SENTRY_DSN'),\n environment: userOptions.environment ?? getEnvVar(env, 'SENTRY_ENVIRONMENT'),\n tracesSampleRate: isFinite(tracesSampleRate) ? tracesSampleRate : undefined,\n debug: userOptions.debug ?? envToBool(getEnvVar(env, 'SENTRY_DEBUG')),\n tunnel: userOptions.tunnel ?? getEnvVar(env, 'SENTRY_TUNNEL'),\n spotlight,\n };\n}\n\n/**\n * Resolve the spotlight option from a user-supplied value and an env binding string.\n * Mirrors node-core's `getSpotlightConfig` precedence:\n * - `false` or explicit string from options → use as-is\n * - `true` → enable, but prefer a custom URL from the env var if set\n * - `undefined` → defer entirely to the env var (bool or URL)\n */\nfunction getSpotlightFromEnv(\n optionsSpotlight: boolean | string | undefined,\n envVar: string | undefined,\n): boolean | string | undefined {\n if (optionsSpotlight === false) {\n return false;\n }\n if (typeof optionsSpotlight === 'string') {\n return optionsSpotlight;\n }\n\n // optionsSpotlight is true or undefined\n const envBool = envToBool(envVar, { strict: true });\n const envUrl = envBool === null && envVar ? envVar : undefined;\n\n return optionsSpotlight === true\n ? (envUrl ?? true) // true: use env URL if present, otherwise true\n : (envBool ?? envUrl); // undefined: use env var (bool or URL)\n}\n"],"names":["isObjectLike","envToBool"],"mappings":";;;;AAgBA,SAAS,kBAAkB,KAAA,EAA4C;AACrE,EAAA,OAAOA,kBAAa,KAAK,CAAA,IAAK,QAAQ,KAAA,IAAS,OAAO,MAAM,EAAA,KAAO,QAAA;AACrE;AAEA,SAAS,SAAA,CAA6C,KAAc,OAAA,EAAsC;AACxG,EAAA,OAAOA,iBAAA,CAAa,GAAG,CAAA,IAAK,OAAA,IAAW,GAAA,IAAO,OAAQ,GAAA,CAAU,OAAO,CAAA,KAAM,QAAA,GACvE,GAAA,CAAU,OAAO,CAAA,GACnB,MAAA;AACN;AAgBO,SAAS,eAAA,CAAgB,WAAA,GAAiC,EAAC,EAAG,GAAA,EAAiC;AACpG,EAAA,IAAI,OAAO,GAAA,KAAQ,QAAA,IAAY,GAAA,KAAQ,IAAA,EAAM;AAC3C,IAAA,OAAO,WAAA;AAAA,EACT;AAGA,EAAA,MAAM,UACJ,gBAAA,IAAoB,GAAA,IAAO,OAAO,GAAA,CAAI,mBAAmB,QAAA,GACrD,GAAA,CAAI,cAAA,GACJ,qBAAA,IAAyB,OAAO,iBAAA,CAAkB,GAAA,CAAI,mBAAmB,CAAA,GACvE,GAAA,CAAI,oBAAoB,EAAA,GACxB,MAAA;AAER,EAAA,MAAM,gBAAA,GACJ,YAAY,gBAAA,IAAoB,UAAA,CAAW,UAAU,GAAA,EAAK,2BAA2B,KAAK,EAAE,CAAA;AAM9F,EAAA,MAAM,YAAY,mBAAA,CAAoB,WAAA,CAAY,WAAW,SAAA,CAAU,GAAA,EAAK,kBAAkB,CAAC,CAAA;AAE/F,EAAA,OAAO;AAAA,IACL,OAAA;AAAA,IACA,GAAG,WAAA;AAAA,IACH,GAAA,EAAK,WAAA,CAAY,GAAA,IAAO,SAAA,CAAU,KAAK,YAAY,CAAA;AAAA,IACnD,WAAA,EAAa,WAAA,CAAY,WAAA,IAAe,SAAA,CAAU,KAAK,oBAAoB,CAAA;AAAA,IAC3E,gBAAA,EAAkB,QAAA,CAAS,gBAAgB,CAAA,GAAI,gBAAA,GAAmB,MAAA;AAAA,IAClE,OAAO,WAAA,CAAY,KAAA,IAASC,eAAU,SAAA,CAAU,GAAA,EAAK,cAAc,CAAC,CAAA;AAAA,IACpE,MAAA,EAAQ,WAAA,CAAY,MAAA,IAAU,SAAA,CAAU,KAAK,eAAe,CAAA;AAAA,IAC5D;AAAA,GACF;AACF;AASA,SAAS,mBAAA,CACP,kBACA,MAAA,EAC8B;AAC9B,EAAA,IAAI,qBAAqB,KAAA,EAAO;AAC9B,IAAA,OAAO,KAAA;AAAA,EACT;AACA,EAAA,IAAI,OAAO,qBAAqB,QAAA,EAAU;AACxC,IAAA,OAAO,gBAAA;AAAA,EACT;AAGA,EAAA,MAAM,UAAUA,cAAA,CAAU,MAAA,EAAQ,EAAE,MAAA,EAAQ,MAAM,CAAA;AAClD,EAAA,MAAM,MAAA,GAAS,OAAA,KAAY,IAAA,IAAQ,MAAA,GAAS,MAAA,GAAS,MAAA;AAErD,EAAA,OAAO,gBAAA,KAAqB,IAAA,GACvB,MAAA,IAAU,IAAA,GACV,OAAA,IAAW,MAAA;AAClB;;;;"}
|
package/build/cjs/sdk.js
CHANGED
|
@@ -6,10 +6,15 @@ const flush = require('./flush.js');
|
|
|
6
6
|
const httpServer = require('./integrations/httpServer.js');
|
|
7
7
|
const fetch = require('./integrations/fetch.js');
|
|
8
8
|
const hono = require('./integrations/hono.js');
|
|
9
|
+
const spotlight = require('./integrations/spotlight.js');
|
|
9
10
|
const tracer = require('./opentelemetry/tracer.js');
|
|
10
11
|
const transport = require('./transport.js');
|
|
11
12
|
const stacktrace = require('./vendor/stacktrace.js');
|
|
12
13
|
|
|
14
|
+
function getRegisteredChannelIntegrations() {
|
|
15
|
+
const registered = core.GLOBAL_OBJ.__SENTRY_ORCHESTRION__?.integrations;
|
|
16
|
+
return registered ? [...registered.values()].map((factory) => factory()) : [];
|
|
17
|
+
}
|
|
13
18
|
function getDefaultIntegrations(options) {
|
|
14
19
|
const cookiesEnabled = options.sendDefaultPii || options.dataCollection?.cookies != null;
|
|
15
20
|
return [
|
|
@@ -27,7 +32,14 @@ function getDefaultIntegrations(options) {
|
|
|
27
32
|
hono.honoIntegration(),
|
|
28
33
|
httpServer.httpServerIntegration(),
|
|
29
34
|
core.requestDataIntegration(cookiesEnabled ? void 0 : { include: { cookies: false } }),
|
|
30
|
-
core.consoleIntegration()
|
|
35
|
+
core.consoleIntegration(),
|
|
36
|
+
// The orchestrion diagnostics-channel subscribers (mysql, pg, …). The
|
|
37
|
+
// `@sentry/cloudflare/vite` plugin injects the channels at build time and,
|
|
38
|
+
// next to each, a snippet that registers the matching subscriber factory on
|
|
39
|
+
// the global marker. Read from there instead of importing them so bundles
|
|
40
|
+
// built without the plugin — where the channels would never fire — don't
|
|
41
|
+
// ship the code.
|
|
42
|
+
...getRegisteredChannelIntegrations()
|
|
31
43
|
];
|
|
32
44
|
}
|
|
33
45
|
function init(options) {
|
|
@@ -43,6 +55,13 @@ function init(options) {
|
|
|
43
55
|
transport: options.transport || transport.makeCloudflareTransport,
|
|
44
56
|
flushLock
|
|
45
57
|
};
|
|
58
|
+
if (options.spotlight && !clientOptions.integrations.some(({ name }) => name === spotlight.INTEGRATION_NAME)) {
|
|
59
|
+
clientOptions.integrations.push(
|
|
60
|
+
spotlight.spotlightIntegration({
|
|
61
|
+
sidecarUrl: typeof options.spotlight === "string" ? options.spotlight : void 0
|
|
62
|
+
})
|
|
63
|
+
);
|
|
64
|
+
}
|
|
46
65
|
if (!options.skipOpenTelemetrySetup) {
|
|
47
66
|
tracer.setupOpenTelemetryTracer();
|
|
48
67
|
}
|
package/build/cjs/sdk.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sdk.js","sources":["../../src/sdk.ts"],"sourcesContent":["import type { Integration } from '@sentry/core';\nimport {\n consoleIntegration,\n conversationIdIntegration,\n dedupeIntegration,\n functionToStringIntegration,\n getIntegrationsToSetup,\n inboundFiltersIntegration,\n initAndBind,\n linkedErrorsIntegration,\n requestDataIntegration,\n stackParserFromStackParserOptions,\n} from '@sentry/core';\nimport type { CloudflareClientOptions, CloudflareOptions } from './client';\nimport { CloudflareClient } from './client';\nimport { makeFlushLock } from './flush';\nimport { httpServerIntegration } from './integrations/httpServer';\nimport { fetchIntegration } from './integrations/fetch';\nimport { honoIntegration } from './integrations/hono';\nimport { setupOpenTelemetryTracer } from './opentelemetry/tracer';\nimport { makeCloudflareTransport } from './transport';\nimport { defaultStackParser } from './vendor/stacktrace';\n\n/** Get the default integrations for the Cloudflare SDK. */\nexport function getDefaultIntegrations(options: CloudflareOptions): Integration[] {\n // TODO(v11): Drop this transitional gating and let `requestDataIntegration` rely on the resolved\n // `dataCollection` defaults directly. Until then, preserve the historical Cloudflare behavior of not\n // attaching cookies unless the user explicitly opts in via `sendDefaultPii` or `dataCollection.cookies`.\n // eslint-disable-next-line typescript/no-deprecated\n const cookiesEnabled = options.sendDefaultPii || options.dataCollection?.cookies != null;\n return [\n // The Dedupe integration should not be used in workflows because we want to\n // capture all step failures, even if they are the same error.\n ...(options.enableDedupe === false ? [] : [dedupeIntegration()]),\n // TODO(v11): Replace with `eventFiltersIntegration` once we remove the deprecated `inboundFiltersIntegration`\n // eslint-disable-next-line typescript/no-deprecated\n inboundFiltersIntegration(),\n functionToStringIntegration(),\n conversationIdIntegration(),\n linkedErrorsIntegration(),\n fetchIntegration(),\n // eslint-disable-next-line typescript/no-deprecated\n honoIntegration(),\n httpServerIntegration(),\n requestDataIntegration(cookiesEnabled ? undefined : { include: { cookies: false } }),\n consoleIntegration(),\n ];\n}\n\n/**\n * Initializes the cloudflare SDK.\n */\nexport function init(options: CloudflareOptions): CloudflareClient | undefined {\n if (options.defaultIntegrations === undefined) {\n options.defaultIntegrations = getDefaultIntegrations(options);\n }\n\n const flushLock = options.ctx ? makeFlushLock(options.ctx) : undefined;\n delete options.ctx;\n\n const clientOptions: CloudflareClientOptions = {\n ...options,\n stackParser: stackParserFromStackParserOptions(options.stackParser || defaultStackParser),\n integrations: getIntegrationsToSetup(options),\n transport: options.transport || makeCloudflareTransport,\n flushLock,\n };\n\n /**\n * The Cloudflare SDK is not OpenTelemetry native, however, we set up some OpenTelemetry compatibility\n * via a custom trace provider.\n * This ensures that any spans emitted via `@opentelemetry/api` will be captured by Sentry.\n * HOWEVER, big caveat: This does not handle custom context handling, it will always work off the current scope.\n * This should be good enough for many, but not all integrations.\n */\n if (!options.skipOpenTelemetrySetup) {\n setupOpenTelemetryTracer();\n }\n\n return initAndBind(CloudflareClient, clientOptions) as CloudflareClient;\n}\n"],"names":["dedupeIntegration","inboundFiltersIntegration","functionToStringIntegration","conversationIdIntegration","linkedErrorsIntegration","fetchIntegration","honoIntegration","httpServerIntegration","requestDataIntegration","consoleIntegration","makeFlushLock","stackParserFromStackParserOptions","defaultStackParser","getIntegrationsToSetup","makeCloudflareTransport","setupOpenTelemetryTracer","initAndBind","CloudflareClient"],"mappings":"
|
|
1
|
+
{"version":3,"file":"sdk.js","sources":["../../src/sdk.ts"],"sourcesContent":["import type { Integration } from '@sentry/core';\nimport {\n consoleIntegration,\n conversationIdIntegration,\n dedupeIntegration,\n functionToStringIntegration,\n getIntegrationsToSetup,\n GLOBAL_OBJ,\n inboundFiltersIntegration,\n initAndBind,\n linkedErrorsIntegration,\n requestDataIntegration,\n stackParserFromStackParserOptions,\n} from '@sentry/core';\nimport type { CloudflareClientOptions, CloudflareOptions } from './client';\nimport { CloudflareClient } from './client';\nimport { makeFlushLock } from './flush';\nimport { httpServerIntegration } from './integrations/httpServer';\nimport { fetchIntegration } from './integrations/fetch';\nimport { honoIntegration } from './integrations/hono';\nimport { INTEGRATION_NAME as SPOTLIGHT_INTEGRATION_NAME, spotlightIntegration } from './integrations/spotlight';\nimport { setupOpenTelemetryTracer } from './opentelemetry/tracer';\nimport { makeCloudflareTransport } from './transport';\nimport { defaultStackParser } from './vendor/stacktrace';\n\n/**\n * Instantiate the channel-subscriber factories the `@sentry/cloudflare/vite`\n * plugin registered on the global marker. The plugin splices a small snippet\n * into each instrumented module that `.set`s its factory here (keyed by export\n * name), so the marker holds one factory per package actually bundled.\n *\n * The marker is read directly instead of importing the factories, so a worker\n * built without the plugin — where the channels never fire — ships none of this\n * code.\n * TODO(v11): Use `@sentry/server-utils/orchestrion` once we move to `nodejs_compat` by default.\n */\nfunction getRegisteredChannelIntegrations(): Integration[] {\n const registered = GLOBAL_OBJ.__SENTRY_ORCHESTRION__?.integrations;\n\n return registered ? [...registered.values()].map(factory => factory()) : [];\n}\n\n/** Get the default integrations for the Cloudflare SDK. */\nexport function getDefaultIntegrations(options: CloudflareOptions): Integration[] {\n // TODO(v11): Drop this transitional gating and let `requestDataIntegration` rely on the resolved\n // `dataCollection` defaults directly. Until then, preserve the historical Cloudflare behavior of not\n // attaching cookies unless the user explicitly opts in via `sendDefaultPii` or `dataCollection.cookies`.\n // eslint-disable-next-line typescript/no-deprecated\n const cookiesEnabled = options.sendDefaultPii || options.dataCollection?.cookies != null;\n return [\n // The Dedupe integration should not be used in workflows because we want to\n // capture all step failures, even if they are the same error.\n ...(options.enableDedupe === false ? [] : [dedupeIntegration()]),\n // TODO(v11): Replace with `eventFiltersIntegration` once we remove the deprecated `inboundFiltersIntegration`\n // eslint-disable-next-line typescript/no-deprecated\n inboundFiltersIntegration(),\n functionToStringIntegration(),\n conversationIdIntegration(),\n linkedErrorsIntegration(),\n fetchIntegration(),\n // eslint-disable-next-line typescript/no-deprecated\n honoIntegration(),\n httpServerIntegration(),\n requestDataIntegration(cookiesEnabled ? undefined : { include: { cookies: false } }),\n consoleIntegration(),\n // The orchestrion diagnostics-channel subscribers (mysql, pg, …). The\n // `@sentry/cloudflare/vite` plugin injects the channels at build time and,\n // next to each, a snippet that registers the matching subscriber factory on\n // the global marker. Read from there instead of importing them so bundles\n // built without the plugin — where the channels would never fire — don't\n // ship the code.\n ...getRegisteredChannelIntegrations(),\n ];\n}\n\n/**\n * Initializes the cloudflare SDK.\n */\nexport function init(options: CloudflareOptions): CloudflareClient | undefined {\n if (options.defaultIntegrations === undefined) {\n options.defaultIntegrations = getDefaultIntegrations(options);\n }\n\n const flushLock = options.ctx ? makeFlushLock(options.ctx) : undefined;\n delete options.ctx;\n\n const clientOptions: CloudflareClientOptions = {\n ...options,\n stackParser: stackParserFromStackParserOptions(options.stackParser || defaultStackParser),\n integrations: getIntegrationsToSetup(options),\n transport: options.transport || makeCloudflareTransport,\n flushLock,\n };\n\n if (options.spotlight && !clientOptions.integrations.some(({ name }) => name === SPOTLIGHT_INTEGRATION_NAME)) {\n clientOptions.integrations.push(\n spotlightIntegration({\n sidecarUrl: typeof options.spotlight === 'string' ? options.spotlight : undefined,\n }),\n );\n }\n\n /**\n * The Cloudflare SDK is not OpenTelemetry native, however, we set up some OpenTelemetry compatibility\n * via a custom trace provider.\n * This ensures that any spans emitted via `@opentelemetry/api` will be captured by Sentry.\n * HOWEVER, big caveat: This does not handle custom context handling, it will always work off the current scope.\n * This should be good enough for many, but not all integrations.\n */\n if (!options.skipOpenTelemetrySetup) {\n setupOpenTelemetryTracer();\n }\n\n return initAndBind(CloudflareClient, clientOptions) as CloudflareClient;\n}\n"],"names":["GLOBAL_OBJ","dedupeIntegration","inboundFiltersIntegration","functionToStringIntegration","conversationIdIntegration","linkedErrorsIntegration","fetchIntegration","honoIntegration","httpServerIntegration","requestDataIntegration","consoleIntegration","makeFlushLock","stackParserFromStackParserOptions","defaultStackParser","getIntegrationsToSetup","makeCloudflareTransport","SPOTLIGHT_INTEGRATION_NAME","spotlightIntegration","setupOpenTelemetryTracer","initAndBind","CloudflareClient"],"mappings":";;;;;;;;;;;;;AAoCA,SAAS,gCAAA,GAAkD;AACzD,EAAA,MAAM,UAAA,GAAaA,gBAAW,sBAAA,EAAwB,YAAA;AAEtD,EAAA,OAAO,UAAA,GAAa,CAAC,GAAG,UAAA,CAAW,MAAA,EAAQ,CAAA,CAAE,GAAA,CAAI,CAAA,OAAA,KAAW,OAAA,EAAS,CAAA,GAAI,EAAC;AAC5E;AAGO,SAAS,uBAAuB,OAAA,EAA2C;AAKhF,EAAA,MAAM,cAAA,GAAiB,OAAA,CAAQ,cAAA,IAAkB,OAAA,CAAQ,gBAAgB,OAAA,IAAW,IAAA;AACpF,EAAA,OAAO;AAAA;AAAA;AAAA,IAGL,GAAI,QAAQ,YAAA,KAAiB,KAAA,GAAQ,EAAC,GAAI,CAACC,wBAAmB,CAAA;AAAA;AAAA;AAAA,IAG9DC,8BAAA,EAA0B;AAAA,IAC1BC,gCAAA,EAA4B;AAAA,IAC5BC,8BAAA,EAA0B;AAAA,IAC1BC,4BAAA,EAAwB;AAAA,IACxBC,sBAAA,EAAiB;AAAA;AAAA,IAEjBC,oBAAA,EAAgB;AAAA,IAChBC,gCAAA,EAAsB;AAAA,IACtBC,2BAAA,CAAuB,iBAAiB,MAAA,GAAY,EAAE,SAAS,EAAE,OAAA,EAAS,KAAA,EAAM,EAAG,CAAA;AAAA,IACnFC,uBAAA,EAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOnB,GAAG,gCAAA;AAAiC,GACtC;AACF;AAKO,SAAS,KAAK,OAAA,EAA0D;AAC7E,EAAA,IAAI,OAAA,CAAQ,wBAAwB,MAAA,EAAW;AAC7C,IAAA,OAAA,CAAQ,mBAAA,GAAsB,uBAAuB,OAAO,CAAA;AAAA,EAC9D;AAEA,EAAA,MAAM,YAAY,OAAA,CAAQ,GAAA,GAAMC,mBAAA,CAAc,OAAA,CAAQ,GAAG,CAAA,GAAI,MAAA;AAC7D,EAAA,OAAO,OAAA,CAAQ,GAAA;AAEf,EAAA,MAAM,aAAA,GAAyC;AAAA,IAC7C,GAAG,OAAA;AAAA,IACH,WAAA,EAAaC,sCAAA,CAAkC,OAAA,CAAQ,WAAA,IAAeC,6BAAkB,CAAA;AAAA,IACxF,YAAA,EAAcC,4BAAuB,OAAO,CAAA;AAAA,IAC5C,SAAA,EAAW,QAAQ,SAAA,IAAaC,iCAAA;AAAA,IAChC;AAAA,GACF;AAEA,EAAA,IAAI,OAAA,CAAQ,SAAA,IAAa,CAAC,aAAA,CAAc,YAAA,CAAa,IAAA,CAAK,CAAC,EAAE,IAAA,EAAK,KAAM,IAAA,KAASC,0BAA0B,CAAA,EAAG;AAC5G,IAAA,aAAA,CAAc,YAAA,CAAa,IAAA;AAAA,MACzBC,8BAAA,CAAqB;AAAA,QACnB,YAAY,OAAO,OAAA,CAAQ,SAAA,KAAc,QAAA,GAAW,QAAQ,SAAA,GAAY;AAAA,OACzE;AAAA,KACH;AAAA,EACF;AASA,EAAA,IAAI,CAAC,QAAQ,sBAAA,EAAwB;AACnC,IAAAC,+BAAA,EAAyB;AAAA,EAC3B;AAEA,EAAA,OAAOC,gBAAA,CAAYC,yBAAkB,aAAa,CAAA;AACpD;;;;;"}
|
|
@@ -2,17 +2,23 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
2
2
|
|
|
3
3
|
const core = require('@sentry/core');
|
|
4
4
|
|
|
5
|
-
function targetsCloudflareInternalTable(querySummary, allowlist) {
|
|
5
|
+
function targetsCloudflareInternalTable(querySummary, allowlist, queryText) {
|
|
6
6
|
if (!querySummary) {
|
|
7
7
|
return false;
|
|
8
8
|
}
|
|
9
|
+
const indexedTable = queryText ? CREATE_INDEX_TABLE_RE.exec(queryText)?.groups?.["table"] : void 0;
|
|
10
|
+
if (indexedTable) {
|
|
11
|
+
return isCloudflareInternalTable(indexedTable, allowlist);
|
|
12
|
+
}
|
|
9
13
|
const [, ...tables] = querySummary.split(" ");
|
|
10
|
-
return tables.some((table) =>
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
return tables.some((table) => isCloudflareInternalTable(table, allowlist));
|
|
15
|
+
}
|
|
16
|
+
const CREATE_INDEX_TABLE_RE = /^\s*CREATE\s+(?:UNIQUE\s+)?INDEX(?:\s+IF\s+(?:NOT\s+)?EXISTS)?\s+[^\s(,;)]+\s+ON\s+(?<table>[^\s(,;)]+)/i;
|
|
17
|
+
function isCloudflareInternalTable(table, allowlist) {
|
|
18
|
+
if (!table.toLowerCase().startsWith("cf_")) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
return !allowlist?.length || !core.stringMatchesSomePattern(table, allowlist, true);
|
|
16
22
|
}
|
|
17
23
|
|
|
18
24
|
exports.targetsCloudflareInternalTable = targetsCloudflareInternalTable;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"internalSqlQuery.js","sources":["../../../src/utils/internalSqlQuery.ts"],"sourcesContent":["import { stringMatchesSomePattern } from '@sentry/core';\n\n/**\n * Cloudflare frameworks that build on Durable Objects (`agents`, `partyserver`, ...) manage their\n * own internal SQLite tables, all namespaced with a `cf_` prefix — e.g. `cf_agents_schedules`,\n * `cf_agent_state`, `cf_ai_chat_stream_chunks`. Queries against them (schedule polling, chat-stream\n * persistence, state bookkeeping) are framework implementation details that otherwise flood traces\n * with dozens of zero-signal `db.query` spans per request. The exact set of tables even varies\n * between framework versions, so we match the reserved prefix rather than an enumerated list.\n *\n * The `cf_` prefix is a reserved convention for framework-managed tables, so user tables should not\n * use it. In case a user table does collide with the prefix, the `durableObjectSqlSpanAllowlist`\n * option lets them opt those tables back into instrumentation.\n *\n * The check operates on the query summary produced by `getSqlQuerySummary` (`{operation} {table} ...`,\n * the same value used as the span name), so table targets are already isolated from the rest of the\n * query.\n */\nexport function targetsCloudflareInternalTable(\n querySummary: string | undefined,\n allowlist?: Array<string | RegExp>,\n): boolean {\n if (!querySummary) {\n return false;\n }\n\n const [, ...tables] = querySummary.split(' ');\n\n return tables.some(table => {\n
|
|
1
|
+
{"version":3,"file":"internalSqlQuery.js","sources":["../../../src/utils/internalSqlQuery.ts"],"sourcesContent":["import { stringMatchesSomePattern } from '@sentry/core';\n\n/**\n * Cloudflare frameworks that build on Durable Objects (`agents`, `partyserver`, ...) manage their\n * own internal SQLite tables, all namespaced with a `cf_` prefix — e.g. `cf_agents_schedules`,\n * `cf_agent_state`, `cf_ai_chat_stream_chunks`. Queries against them (schedule polling, chat-stream\n * persistence, state bookkeeping) are framework implementation details that otherwise flood traces\n * with dozens of zero-signal `db.query` spans per request. The exact set of tables even varies\n * between framework versions, so we match the reserved prefix rather than an enumerated list.\n *\n * The `cf_` prefix is a reserved convention for framework-managed tables, so user tables should not\n * use it. In case a user table does collide with the prefix, the `durableObjectSqlSpanAllowlist`\n * option lets them opt those tables back into instrumentation.\n *\n * The check operates on the query summary produced by `getSqlQuerySummary` (`{operation} {table} ...`,\n * the same value used as the span name), so table targets are already isolated from the rest of the\n * query. The one exception is `CREATE INDEX`: its summary carries the index name, not the indexed\n * table (upstream OTel convention), so the `cf_` target only appears in the ON clause of the full\n * statement, which `queryText` is needed for.\n */\nexport function targetsCloudflareInternalTable(\n querySummary: string | undefined,\n allowlist?: Array<string | RegExp>,\n queryText?: string,\n): boolean {\n if (!querySummary) {\n return false;\n }\n\n const indexedTable = queryText ? CREATE_INDEX_TABLE_RE.exec(queryText)?.groups?.['table'] : undefined;\n if (indexedTable) {\n return isCloudflareInternalTable(indexedTable, allowlist);\n }\n\n const [, ...tables] = querySummary.split(' ');\n\n return tables.some(table => isCloudflareInternalTable(table, allowlist));\n}\n\n// `CREATE [UNIQUE] INDEX [IF NOT EXISTS] <name> ON <table>` — the IF EXISTS shape mirrors DDL_RE\n// in @sentry/core.\nconst CREATE_INDEX_TABLE_RE =\n /^\\s*CREATE\\s+(?:UNIQUE\\s+)?INDEX(?:\\s+IF\\s+(?:NOT\\s+)?EXISTS)?\\s+[^\\s(,;)]+\\s+ON\\s+(?<table>[^\\s(,;)]+)/i;\n\nfunction isCloudflareInternalTable(table: string, allowlist?: Array<string | RegExp>): boolean {\n if (!table.toLowerCase().startsWith('cf_')) {\n return false;\n }\n\n // A table on the allowlist is treated as a user table and stays instrumented, even though it\n // matches the reserved prefix.\n return !allowlist?.length || !stringMatchesSomePattern(table, allowlist, true);\n}\n"],"names":["stringMatchesSomePattern"],"mappings":";;;;AAoBO,SAAS,8BAAA,CACd,YAAA,EACA,SAAA,EACA,SAAA,EACS;AACT,EAAA,IAAI,CAAC,YAAA,EAAc;AACjB,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,MAAM,YAAA,GAAe,YAAY,qBAAA,CAAsB,IAAA,CAAK,SAAS,CAAA,EAAG,MAAA,GAAS,OAAO,CAAA,GAAI,MAAA;AAC5F,EAAA,IAAI,YAAA,EAAc;AAChB,IAAA,OAAO,yBAAA,CAA0B,cAAc,SAAS,CAAA;AAAA,EAC1D;AAEA,EAAA,MAAM,GAAG,GAAG,MAAM,CAAA,GAAI,YAAA,CAAa,MAAM,GAAG,CAAA;AAE5C,EAAA,OAAO,OAAO,IAAA,CAAK,CAAA,KAAA,KAAS,yBAAA,CAA0B,KAAA,EAAO,SAAS,CAAC,CAAA;AACzE;AAIA,MAAM,qBAAA,GACJ,0GAAA;AAEF,SAAS,yBAAA,CAA0B,OAAe,SAAA,EAA6C;AAC7F,EAAA,IAAI,CAAC,KAAA,CAAM,WAAA,EAAY,CAAE,UAAA,CAAW,KAAK,CAAA,EAAG;AAC1C,IAAA,OAAO,KAAA;AAAA,EACT;AAIA,EAAA,OAAO,CAAC,SAAA,EAAW,MAAA,IAAU,CAACA,6BAAA,CAAyB,KAAA,EAAO,WAAW,IAAI,CAAA;AAC/E;;;;"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
|
|
3
|
+
const core = require('@sentry/core');
|
|
4
|
+
|
|
5
|
+
function targetsCloudflareInternalKey(key, allowlist) {
|
|
6
|
+
if (!key) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
const isFrameworkKey = key.startsWith("cf_") || key.startsWith("cf:") || key.startsWith("__ps_") || key.startsWith("/");
|
|
10
|
+
if (!isFrameworkKey) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
return !allowlist?.length || !core.stringMatchesSomePattern(key, allowlist, true);
|
|
14
|
+
}
|
|
15
|
+
function getStorageKeys(methodName, args) {
|
|
16
|
+
const [first] = args;
|
|
17
|
+
if (methodName === "get" || methodName === "delete") {
|
|
18
|
+
if (typeof first === "string") {
|
|
19
|
+
return [first];
|
|
20
|
+
}
|
|
21
|
+
if (Array.isArray(first)) {
|
|
22
|
+
return first.filter((k) => typeof k === "string");
|
|
23
|
+
}
|
|
24
|
+
return void 0;
|
|
25
|
+
}
|
|
26
|
+
if (methodName === "put") {
|
|
27
|
+
if (typeof first === "string") {
|
|
28
|
+
return [first];
|
|
29
|
+
}
|
|
30
|
+
if (first && typeof first === "object" && !Array.isArray(first)) {
|
|
31
|
+
return Object.keys(first);
|
|
32
|
+
}
|
|
33
|
+
return void 0;
|
|
34
|
+
}
|
|
35
|
+
if (methodName === "list") {
|
|
36
|
+
const prefix = first && typeof first === "object" ? first.prefix : void 0;
|
|
37
|
+
return typeof prefix === "string" ? [prefix] : void 0;
|
|
38
|
+
}
|
|
39
|
+
return void 0;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
exports.getStorageKeys = getStorageKeys;
|
|
43
|
+
exports.targetsCloudflareInternalKey = targetsCloudflareInternalKey;
|
|
44
|
+
//# sourceMappingURL=internalStorageKey.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"internalStorageKey.js","sources":["../../../src/utils/internalStorageKey.ts"],"sourcesContent":["import { stringMatchesSomePattern } from '@sentry/core';\n\n/**\n * Cloudflare frameworks that build on Durable Objects (`agents`, `partyserver`, ...) also manage\n * their own internal KV entries alongside their internal SQLite tables, namespaced with a reserved\n * prefix — e.g. `cf_agents_state`, `cf_agents_mcp_servers`, `__ps_name`. Reads/writes of these\n * (message persistence, MCP connection bookkeeping, name hydration) are framework implementation\n * details that otherwise flood traces with dozens of zero-signal `durable_object_storage_*` spans\n * per request, so we match the reserved prefixes rather than an enumerated list. This mirrors the\n * `cf_` convention used for internal SQL tables (see `targetsCloudflareInternalTable`).\n *\n * The prefixes are a reserved convention for framework-managed entries, so user keys should not use\n * them. In case a user key does collide, the `durableObjectStorageSpanAllowlist` option lets them\n * opt those keys back into instrumentation.\n */\nexport function targetsCloudflareInternalKey(key: string | undefined, allowlist?: Array<string | RegExp>): boolean {\n if (!key) {\n return false;\n }\n\n // Framework-managed KV namespaces:\n // - `cf_` — agents / ai-chat internal state (mirrors the internal SQL table convention)\n // - `cf:` — agents chat-recovery entries (`cf:chat-recovery:*`, `cf:chat:*`); the same reserved\n // `cf` namespace, but colon-separated instead of underscore-separated\n // - `__ps_` — partyserver internals (e.g. `__ps_name`)\n // - `/` — MCP OAuth client state (`/<clientName>/<serverId>/{token,client_info,state,...}`),\n // read on every MCP tool call. User keys on an Agent rarely use a leading slash; if one does,\n // the allowlist opts it back in.\n const isFrameworkKey =\n key.startsWith('cf_') || key.startsWith('cf:') || key.startsWith('__ps_') || key.startsWith('/');\n if (!isFrameworkKey) {\n return false;\n }\n\n // A key on the allowlist is treated as a user key and stays instrumented, even though it matches\n // a reserved prefix.\n return !allowlist?.length || !stringMatchesSomePattern(key, allowlist, true);\n}\n\n/**\n * Extracts the KV keys a Durable Object storage call targets, so the caller can decide whether the\n * operation only touches framework-internal entries. Returns `undefined` when the keys can't be\n * determined from the arguments (e.g. `list()` without a prefix), in which case the call is treated\n * as user work and stays instrumented.\n */\nexport function getStorageKeys(methodName: string, args: unknown[]): string[] | undefined {\n const [first] = args;\n\n if (methodName === 'get' || methodName === 'delete') {\n // get(key) / get(keys[]) / delete(key) / delete(keys[])\n if (typeof first === 'string') {\n return [first];\n }\n if (Array.isArray(first)) {\n return first.filter((k): k is string => typeof k === 'string');\n }\n return undefined;\n }\n\n if (methodName === 'put') {\n // put(key, value) or put({ key: value, ... })\n if (typeof first === 'string') {\n return [first];\n }\n if (first && typeof first === 'object' && !Array.isArray(first)) {\n return Object.keys(first);\n }\n return undefined;\n }\n\n if (methodName === 'list') {\n // list({ prefix })\n const prefix = first && typeof first === 'object' ? (first as { prefix?: unknown }).prefix : undefined;\n return typeof prefix === 'string' ? [prefix] : undefined;\n }\n\n return undefined;\n}\n"],"names":["stringMatchesSomePattern"],"mappings":";;;;AAeO,SAAS,4BAAA,CAA6B,KAAyB,SAAA,EAA6C;AACjH,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA,OAAO,KAAA;AAAA,EACT;AAUA,EAAA,MAAM,cAAA,GACJ,GAAA,CAAI,UAAA,CAAW,KAAK,KAAK,GAAA,CAAI,UAAA,CAAW,KAAK,CAAA,IAAK,IAAI,UAAA,CAAW,OAAO,CAAA,IAAK,GAAA,CAAI,WAAW,GAAG,CAAA;AACjG,EAAA,IAAI,CAAC,cAAA,EAAgB;AACnB,IAAA,OAAO,KAAA;AAAA,EACT;AAIA,EAAA,OAAO,CAAC,SAAA,EAAW,MAAA,IAAU,CAACA,6BAAA,CAAyB,GAAA,EAAK,WAAW,IAAI,CAAA;AAC7E;AAQO,SAAS,cAAA,CAAe,YAAoB,IAAA,EAAuC;AACxF,EAAA,MAAM,CAAC,KAAK,CAAA,GAAI,IAAA;AAEhB,EAAA,IAAI,UAAA,KAAe,KAAA,IAAS,UAAA,KAAe,QAAA,EAAU;AAEnD,IAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC7B,MAAA,OAAO,CAAC,KAAK,CAAA;AAAA,IACf;AACA,IAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACxB,MAAA,OAAO,MAAM,MAAA,CAAO,CAAC,CAAA,KAAmB,OAAO,MAAM,QAAQ,CAAA;AAAA,IAC/D;AACA,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,IAAI,eAAe,KAAA,EAAO;AAExB,IAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC7B,MAAA,OAAO,CAAC,KAAK,CAAA;AAAA,IACf;AACA,IAAA,IAAI,KAAA,IAAS,OAAO,KAAA,KAAU,QAAA,IAAY,CAAC,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AAC/D,MAAA,OAAO,MAAA,CAAO,KAAK,KAAK,CAAA;AAAA,IAC1B;AACA,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,IAAI,eAAe,MAAA,EAAQ;AAEzB,IAAA,MAAM,SAAS,KAAA,IAAS,OAAO,KAAA,KAAU,QAAA,GAAY,MAA+B,MAAA,GAAS,MAAA;AAC7F,IAAA,OAAO,OAAO,MAAA,KAAW,QAAA,GAAW,CAAC,MAAM,CAAA,GAAI,MAAA;AAAA,EACjD;AAEA,EAAA,OAAO,MAAA;AACT;;;;;"}
|