@sentry/cloudflare 10.68.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/durableobject.js +131 -96
- package/build/cjs/durableobject.js.map +1 -1
- package/build/cjs/index.js +3 -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/nodejs_compat/index.js +5 -2
- package/build/cjs/nodejs_compat/index.js.map +1 -1
- package/build/cjs/options.js +14 -1
- package/build/cjs/options.js.map +1 -1
- package/build/cjs/sdk.js +8 -0
- 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 +17 -4
- package/build/cjs/vite/autoInstrument.js.map +1 -1
- package/build/cjs/vite/index.js +1 -1
- package/build/cjs/vite/index.js.map +1 -1
- package/build/cjs/vite/moduleShape.js +231 -0
- package/build/cjs/vite/moduleShape.js.map +1 -0
- package/build/cjs/vite/transform.js +6 -1
- package/build/cjs/vite/transform.js.map +1 -1
- package/build/cjs/vite/wranglerConfig.js +1 -1
- package/build/cjs/vite/wranglerConfig.js.map +1 -1
- package/build/esm/client.js +6 -1
- package/build/esm/client.js.map +1 -1
- package/build/esm/durableobject.js +129 -97
- package/build/esm/durableobject.js.map +1 -1
- package/build/esm/index.js +2 -1
- 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/nodejs_compat/index.js +2 -1
- package/build/esm/nodejs_compat/index.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 +8 -0
- 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 +17 -4
- package/build/esm/vite/autoInstrument.js.map +1 -1
- package/build/esm/vite/index.js +1 -1
- package/build/esm/vite/index.js.map +1 -1
- package/build/esm/vite/moduleShape.js +227 -0
- package/build/esm/vite/moduleShape.js.map +1 -0
- package/build/esm/vite/transform.js +6 -1
- package/build/esm/vite/transform.js.map +1 -1
- package/build/esm/vite/wranglerConfig.js +1 -1
- package/build/esm/vite/wranglerConfig.js.map +1 -1
- package/build/types/client.d.ts +37 -0
- package/build/types/client.d.ts.map +1 -1
- package/build/types/durableobject.d.ts +81 -0
- package/build/types/durableobject.d.ts.map +1 -1
- package/build/types/index.d.ts +2 -1
- 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/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 +7 -6
- package/build/types/vite/autoInstrument.d.ts.map +1 -1
- package/build/types/vite/index.d.ts +14 -4
- package/build/types/vite/index.d.ts.map +1 -1
- 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 +6 -1
- package/build/types/vite/transform.d.ts.map +1 -1
- package/build/types-ts3.8/client.d.ts +37 -0
- package/build/types-ts3.8/durableobject.d.ts +81 -0
- package/build/types-ts3.8/index.d.ts +2 -1
- 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/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 +7 -6
- package/build/types-ts3.8/vite/index.d.ts +14 -4
- package/build/types-ts3.8/vite/moduleShape.d.ts +52 -0
- package/build/types-ts3.8/vite/transform.d.ts +6 -1
- package/package.json +3 -3
|
@@ -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;;;;;"}
|
|
@@ -9,11 +9,12 @@ const fetch = require('../integrations/fetch.js');
|
|
|
9
9
|
const sdk = require('../sdk.js');
|
|
10
10
|
const hono = require('../integrations/hono.js');
|
|
11
11
|
const httpServer = require('../integrations/httpServer.js');
|
|
12
|
-
const instrumentD1 = require('../instrumentations/worker/instrumentD1.js');
|
|
13
12
|
const durableobject = require('../durableobject.js');
|
|
13
|
+
const instrumentD1 = require('../instrumentations/worker/instrumentD1.js');
|
|
14
14
|
const workflows = require('../workflows.js');
|
|
15
15
|
const pagesPlugin = require('../pages-plugin.js');
|
|
16
16
|
const async = require('../async.js');
|
|
17
|
+
const spotlight = require('../integrations/spotlight.js');
|
|
17
18
|
const withSentry = require('../withSentry.js');
|
|
18
19
|
const request = require('../request.js');
|
|
19
20
|
|
|
@@ -117,11 +118,13 @@ exports.fetchIntegration = fetch.fetchIntegration;
|
|
|
117
118
|
exports.getDefaultIntegrations = sdk.getDefaultIntegrations;
|
|
118
119
|
exports.honoIntegration = hono.honoIntegration;
|
|
119
120
|
exports.httpServerIntegration = httpServer.httpServerIntegration;
|
|
120
|
-
exports.
|
|
121
|
+
exports.instrumentAgentWithSentry = durableobject.instrumentAgentWithSentry;
|
|
121
122
|
exports.instrumentDurableObjectWithSentry = durableobject.instrumentDurableObjectWithSentry;
|
|
123
|
+
exports.instrumentD1WithSentry = instrumentD1.instrumentD1WithSentry;
|
|
122
124
|
exports.instrumentWorkflowWithSentry = workflows.instrumentWorkflowWithSentry;
|
|
123
125
|
exports.sentryPagesPlugin = pagesPlugin.sentryPagesPlugin;
|
|
124
126
|
exports.setAsyncLocalStorageAsyncContextStrategy = async.setAsyncLocalStorageAsyncContextStrategy;
|
|
127
|
+
exports.spotlightIntegration = spotlight.spotlightIntegration;
|
|
125
128
|
exports.withSentry = withSentry.withSentry;
|
|
126
129
|
exports.wrapRequestHandler = request.wrapRequestHandler;
|
|
127
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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
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,6 +6,7 @@ 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');
|
|
@@ -54,6 +55,13 @@ function init(options) {
|
|
|
54
55
|
transport: options.transport || transport.makeCloudflareTransport,
|
|
55
56
|
flushLock
|
|
56
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
|
+
}
|
|
57
65
|
if (!options.skipOpenTelemetrySetup) {
|
|
58
66
|
tracer.setupOpenTelemetryTracer();
|
|
59
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 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 { 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 /**\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","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;;;;;"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
|
|
3
|
+
const node_fs = require('node:fs');
|
|
4
|
+
const moduleShape = require('./moduleShape.js');
|
|
5
|
+
|
|
6
|
+
const AGENT_BASE_CLASSES = {
|
|
7
|
+
agents: /* @__PURE__ */ new Set(["Agent"]),
|
|
8
|
+
"agents/mcp": /* @__PURE__ */ new Set(["McpAgent"]),
|
|
9
|
+
"@cloudflare/ai-chat": /* @__PURE__ */ new Set(["AIChatAgent"]),
|
|
10
|
+
"@cloudflare/think": /* @__PURE__ */ new Set(["Think"])
|
|
11
|
+
};
|
|
12
|
+
const MAX_DEPTH = 8;
|
|
13
|
+
async function detectAgentClasses(ast, entryId, candidates, resolver) {
|
|
14
|
+
const ctx = { resolver, modules: /* @__PURE__ */ new Map([[entryId, moduleShape.shapeFromAst(ast)]]) };
|
|
15
|
+
const detected = /* @__PURE__ */ new Set();
|
|
16
|
+
for (const name of candidates) {
|
|
17
|
+
if (await isAgentBinding(ctx, entryId, name, 0, /* @__PURE__ */ new Set())) {
|
|
18
|
+
detected.add(name);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return detected;
|
|
22
|
+
}
|
|
23
|
+
function collectAgentCandidates(ast, configuredNames) {
|
|
24
|
+
const shape = moduleShape.shapeFromAst(ast);
|
|
25
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
26
|
+
for (const configured of configuredNames) {
|
|
27
|
+
if (shape.classes.has(configured)) {
|
|
28
|
+
candidates.add(configured);
|
|
29
|
+
}
|
|
30
|
+
const local = shape.localExports.get(configured);
|
|
31
|
+
if (local && shape.classes.has(local)) {
|
|
32
|
+
candidates.add(local);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return candidates;
|
|
36
|
+
}
|
|
37
|
+
async function isAgentBinding(ctx, moduleId, name, depth, visited) {
|
|
38
|
+
if (depth > MAX_DEPTH) return false;
|
|
39
|
+
const key = `${moduleId} ${name}`;
|
|
40
|
+
if (visited.has(key)) return false;
|
|
41
|
+
visited.add(key);
|
|
42
|
+
const shape = await getModuleShape(ctx, moduleId);
|
|
43
|
+
if (!shape) return false;
|
|
44
|
+
if (shape.classes.has(name)) {
|
|
45
|
+
return extendsAgent(ctx, shape, moduleId, shape.classes.get(name), depth, visited);
|
|
46
|
+
}
|
|
47
|
+
const imported = shape.imports.get(name);
|
|
48
|
+
if (imported) {
|
|
49
|
+
return followImport(ctx, moduleId, imported.source, imported.imported, depth, visited);
|
|
50
|
+
}
|
|
51
|
+
if (name === moduleShape.DEFAULT_EXPORT) {
|
|
52
|
+
if (shape.defaultExportIsClass) {
|
|
53
|
+
return extendsAgent(ctx, shape, moduleId, shape.defaultExportSuper, depth, visited);
|
|
54
|
+
}
|
|
55
|
+
if (shape.defaultExportName) {
|
|
56
|
+
return isAgentBinding(ctx, moduleId, shape.defaultExportName, depth + 1, visited);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const localName = shape.localExports.get(name);
|
|
60
|
+
if (localName) {
|
|
61
|
+
return isAgentBinding(ctx, moduleId, localName, depth + 1, visited);
|
|
62
|
+
}
|
|
63
|
+
const reexport = shape.reexports.get(name);
|
|
64
|
+
if (reexport) {
|
|
65
|
+
return followImport(ctx, moduleId, reexport.source, reexport.imported, depth, visited);
|
|
66
|
+
}
|
|
67
|
+
for (const source of shape.starExports) {
|
|
68
|
+
if (await followImport(ctx, moduleId, source, name, depth, visited)) {
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
async function extendsAgent(ctx, shape, moduleId, superClass, depth, visited) {
|
|
75
|
+
if (!superClass) return false;
|
|
76
|
+
if (superClass.kind === "identifier") {
|
|
77
|
+
return isAgentBinding(ctx, moduleId, superClass.name, depth + 1, visited);
|
|
78
|
+
}
|
|
79
|
+
const source = shape.namespaces.get(superClass.object);
|
|
80
|
+
if (source) {
|
|
81
|
+
return followImport(ctx, moduleId, source, superClass.property, depth, visited);
|
|
82
|
+
}
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
async function followImport(ctx, importerId, source, importedName, depth, visited) {
|
|
86
|
+
if (AGENT_BASE_CLASSES[source]?.has(importedName)) return true;
|
|
87
|
+
if (!ctx.resolver.resolve) return false;
|
|
88
|
+
let resolved;
|
|
89
|
+
try {
|
|
90
|
+
resolved = await ctx.resolver.resolve(source, importerId);
|
|
91
|
+
} catch {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
if (!resolved?.id) return false;
|
|
95
|
+
if (resolved.id.includes("/node_modules/") || resolved.id.includes("\\node_modules\\")) return false;
|
|
96
|
+
return isAgentBinding(ctx, resolved.id, importedName, depth + 1, visited);
|
|
97
|
+
}
|
|
98
|
+
async function getModuleShape(ctx, moduleId) {
|
|
99
|
+
if (ctx.modules.has(moduleId)) return ctx.modules.get(moduleId);
|
|
100
|
+
let shape;
|
|
101
|
+
try {
|
|
102
|
+
const code = ctx.resolver.readFile ? ctx.resolver.readFile(moduleId) : node_fs.readFileSync(moduleId.replace(/[?#].*$/, ""), "utf8");
|
|
103
|
+
if (typeof code === "string") {
|
|
104
|
+
shape = moduleShape.shapeFromSource(code);
|
|
105
|
+
}
|
|
106
|
+
} catch {
|
|
107
|
+
}
|
|
108
|
+
ctx.modules.set(moduleId, shape);
|
|
109
|
+
return shape;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
exports.collectAgentCandidates = collectAgentCandidates;
|
|
113
|
+
exports.detectAgentClasses = detectAgentClasses;
|
|
114
|
+
//# sourceMappingURL=agentClass.js.map
|
|
@@ -0,0 +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":["shapeFromAst","DEFAULT_EXPORT","readFileSync","shapeFromSource"],"mappings":"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"}
|