@sentry/node-core
Advanced tools
+40
-44
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const replacements = [ | ||
| ['january', '1'], | ||
| ['february', '2'], | ||
| ['march', '3'], | ||
| ['april', '4'], | ||
| ['may', '5'], | ||
| ['june', '6'], | ||
| ['july', '7'], | ||
| ['august', '8'], | ||
| ['september', '9'], | ||
| ['october', '10'], | ||
| ['november', '11'], | ||
| ['december', '12'], | ||
| ['jan', '1'], | ||
| ['feb', '2'], | ||
| ['mar', '3'], | ||
| ['apr', '4'], | ||
| ['may', '5'], | ||
| ['jun', '6'], | ||
| ['jul', '7'], | ||
| ['aug', '8'], | ||
| ['sep', '9'], | ||
| ['oct', '10'], | ||
| ['nov', '11'], | ||
| ['dec', '12'], | ||
| ['sunday', '0'], | ||
| ['monday', '1'], | ||
| ['tuesday', '2'], | ||
| ['wednesday', '3'], | ||
| ['thursday', '4'], | ||
| ['friday', '5'], | ||
| ['saturday', '6'], | ||
| ['sun', '0'], | ||
| ['mon', '1'], | ||
| ['tue', '2'], | ||
| ['wed', '3'], | ||
| ['thu', '4'], | ||
| ['fri', '5'], | ||
| ['sat', '6'], | ||
| ["january", "1"], | ||
| ["february", "2"], | ||
| ["march", "3"], | ||
| ["april", "4"], | ||
| ["may", "5"], | ||
| ["june", "6"], | ||
| ["july", "7"], | ||
| ["august", "8"], | ||
| ["september", "9"], | ||
| ["october", "10"], | ||
| ["november", "11"], | ||
| ["december", "12"], | ||
| ["jan", "1"], | ||
| ["feb", "2"], | ||
| ["mar", "3"], | ||
| ["apr", "4"], | ||
| ["may", "5"], | ||
| ["jun", "6"], | ||
| ["jul", "7"], | ||
| ["aug", "8"], | ||
| ["sep", "9"], | ||
| ["oct", "10"], | ||
| ["nov", "11"], | ||
| ["dec", "12"], | ||
| ["sunday", "0"], | ||
| ["monday", "1"], | ||
| ["tuesday", "2"], | ||
| ["wednesday", "3"], | ||
| ["thursday", "4"], | ||
| ["friday", "5"], | ||
| ["saturday", "6"], | ||
| ["sun", "0"], | ||
| ["mon", "1"], | ||
| ["tue", "2"], | ||
| ["wed", "3"], | ||
| ["thu", "4"], | ||
| ["fri", "5"], | ||
| ["sat", "6"] | ||
| ]; | ||
| /** | ||
| * Replaces names in cron expressions | ||
| */ | ||
| function replaceCronNames(cronExpression) { | ||
| return replacements.reduce( | ||
| // oxlint-disable-next-line sdk/no-regexp-constructor | ||
| (acc, [name, replacement]) => acc.replace(new RegExp(name, 'gi'), replacement), | ||
| cronExpression, | ||
| (acc, [name, replacement]) => acc.replace(new RegExp(name, "gi"), replacement), | ||
| cronExpression | ||
| ); | ||
@@ -53,0 +49,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"common.js","sources":["../../../src/cron/common.ts"],"sourcesContent":["const replacements: [string, string][] = [\n ['january', '1'],\n ['february', '2'],\n ['march', '3'],\n ['april', '4'],\n ['may', '5'],\n ['june', '6'],\n ['july', '7'],\n ['august', '8'],\n ['september', '9'],\n ['october', '10'],\n ['november', '11'],\n ['december', '12'],\n ['jan', '1'],\n ['feb', '2'],\n ['mar', '3'],\n ['apr', '4'],\n ['may', '5'],\n ['jun', '6'],\n ['jul', '7'],\n ['aug', '8'],\n ['sep', '9'],\n ['oct', '10'],\n ['nov', '11'],\n ['dec', '12'],\n ['sunday', '0'],\n ['monday', '1'],\n ['tuesday', '2'],\n ['wednesday', '3'],\n ['thursday', '4'],\n ['friday', '5'],\n ['saturday', '6'],\n ['sun', '0'],\n ['mon', '1'],\n ['tue', '2'],\n ['wed', '3'],\n ['thu', '4'],\n ['fri', '5'],\n ['sat', '6'],\n];\n\n/**\n * Replaces names in cron expressions\n */\nexport function replaceCronNames(cronExpression: string): string {\n return replacements.reduce(\n // oxlint-disable-next-line sdk/no-regexp-constructor\n (acc, [name, replacement]) => acc.replace(new RegExp(name, 'gi'), replacement),\n cronExpression,\n );\n}\n"],"names":[],"mappings":";;AAAA,MAAM,YAAY,GAAuB;AACzC,EAAE,CAAC,SAAS,EAAE,GAAG,CAAC;AAClB,EAAE,CAAC,UAAU,EAAE,GAAG,CAAC;AACnB,EAAE,CAAC,OAAO,EAAE,GAAG,CAAC;AAChB,EAAE,CAAC,OAAO,EAAE,GAAG,CAAC;AAChB,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC;AACd,EAAE,CAAC,MAAM,EAAE,GAAG,CAAC;AACf,EAAE,CAAC,MAAM,EAAE,GAAG,CAAC;AACf,EAAE,CAAC,QAAQ,EAAE,GAAG,CAAC;AACjB,EAAE,CAAC,WAAW,EAAE,GAAG,CAAC;AACpB,EAAE,CAAC,SAAS,EAAE,IAAI,CAAC;AACnB,EAAE,CAAC,UAAU,EAAE,IAAI,CAAC;AACpB,EAAE,CAAC,UAAU,EAAE,IAAI,CAAC;AACpB,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC;AACd,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC;AACd,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC;AACd,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC;AACd,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC;AACd,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC;AACd,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC;AACd,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC;AACd,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC;AACd,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC;AACf,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC;AACf,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC;AACf,EAAE,CAAC,QAAQ,EAAE,GAAG,CAAC;AACjB,EAAE,CAAC,QAAQ,EAAE,GAAG,CAAC;AACjB,EAAE,CAAC,SAAS,EAAE,GAAG,CAAC;AAClB,EAAE,CAAC,WAAW,EAAE,GAAG,CAAC;AACpB,EAAE,CAAC,UAAU,EAAE,GAAG,CAAC;AACnB,EAAE,CAAC,QAAQ,EAAE,GAAG,CAAC;AACjB,EAAE,CAAC,UAAU,EAAE,GAAG,CAAC;AACnB,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC;AACd,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC;AACd,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC;AACd,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC;AACd,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC;AACd,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC;AACd,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC;AACd,CAAC;;AAED;AACA;AACA;AACO,SAAS,gBAAgB,CAAC,cAAc,EAAkB;AACjE,EAAE,OAAO,YAAY,CAAC,MAAM;AAC5B;AACA,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,WAAW,CAAC,KAAK,GAAG,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,WAAW,CAAC;AAClF,IAAI,cAAc;AAClB,GAAG;AACH;;;;"} | ||
| {"version":3,"file":"common.js","sources":["../../../src/cron/common.ts"],"sourcesContent":["const replacements: [string, string][] = [\n ['january', '1'],\n ['february', '2'],\n ['march', '3'],\n ['april', '4'],\n ['may', '5'],\n ['june', '6'],\n ['july', '7'],\n ['august', '8'],\n ['september', '9'],\n ['october', '10'],\n ['november', '11'],\n ['december', '12'],\n ['jan', '1'],\n ['feb', '2'],\n ['mar', '3'],\n ['apr', '4'],\n ['may', '5'],\n ['jun', '6'],\n ['jul', '7'],\n ['aug', '8'],\n ['sep', '9'],\n ['oct', '10'],\n ['nov', '11'],\n ['dec', '12'],\n ['sunday', '0'],\n ['monday', '1'],\n ['tuesday', '2'],\n ['wednesday', '3'],\n ['thursday', '4'],\n ['friday', '5'],\n ['saturday', '6'],\n ['sun', '0'],\n ['mon', '1'],\n ['tue', '2'],\n ['wed', '3'],\n ['thu', '4'],\n ['fri', '5'],\n ['sat', '6'],\n];\n\n/**\n * Replaces names in cron expressions\n */\nexport function replaceCronNames(cronExpression: string): string {\n return replacements.reduce(\n // oxlint-disable-next-line sdk/no-regexp-constructor\n (acc, [name, replacement]) => acc.replace(new RegExp(name, 'gi'), replacement),\n cronExpression,\n );\n}\n"],"names":[],"mappings":";;AAAA,MAAM,YAAA,GAAmC;AAAA,EACvC,CAAC,WAAW,GAAG,CAAA;AAAA,EACf,CAAC,YAAY,GAAG,CAAA;AAAA,EAChB,CAAC,SAAS,GAAG,CAAA;AAAA,EACb,CAAC,SAAS,GAAG,CAAA;AAAA,EACb,CAAC,OAAO,GAAG,CAAA;AAAA,EACX,CAAC,QAAQ,GAAG,CAAA;AAAA,EACZ,CAAC,QAAQ,GAAG,CAAA;AAAA,EACZ,CAAC,UAAU,GAAG,CAAA;AAAA,EACd,CAAC,aAAa,GAAG,CAAA;AAAA,EACjB,CAAC,WAAW,IAAI,CAAA;AAAA,EAChB,CAAC,YAAY,IAAI,CAAA;AAAA,EACjB,CAAC,YAAY,IAAI,CAAA;AAAA,EACjB,CAAC,OAAO,GAAG,CAAA;AAAA,EACX,CAAC,OAAO,GAAG,CAAA;AAAA,EACX,CAAC,OAAO,GAAG,CAAA;AAAA,EACX,CAAC,OAAO,GAAG,CAAA;AAAA,EACX,CAAC,OAAO,GAAG,CAAA;AAAA,EACX,CAAC,OAAO,GAAG,CAAA;AAAA,EACX,CAAC,OAAO,GAAG,CAAA;AAAA,EACX,CAAC,OAAO,GAAG,CAAA;AAAA,EACX,CAAC,OAAO,GAAG,CAAA;AAAA,EACX,CAAC,OAAO,IAAI,CAAA;AAAA,EACZ,CAAC,OAAO,IAAI,CAAA;AAAA,EACZ,CAAC,OAAO,IAAI,CAAA;AAAA,EACZ,CAAC,UAAU,GAAG,CAAA;AAAA,EACd,CAAC,UAAU,GAAG,CAAA;AAAA,EACd,CAAC,WAAW,GAAG,CAAA;AAAA,EACf,CAAC,aAAa,GAAG,CAAA;AAAA,EACjB,CAAC,YAAY,GAAG,CAAA;AAAA,EAChB,CAAC,UAAU,GAAG,CAAA;AAAA,EACd,CAAC,YAAY,GAAG,CAAA;AAAA,EAChB,CAAC,OAAO,GAAG,CAAA;AAAA,EACX,CAAC,OAAO,GAAG,CAAA;AAAA,EACX,CAAC,OAAO,GAAG,CAAA;AAAA,EACX,CAAC,OAAO,GAAG,CAAA;AAAA,EACX,CAAC,OAAO,GAAG,CAAA;AAAA,EACX,CAAC,OAAO,GAAG,CAAA;AAAA,EACX,CAAC,OAAO,GAAG;AACb,CAAA;AAKO,SAAS,iBAAiB,cAAA,EAAgC;AAC/D,EAAA,OAAO,YAAA,CAAa,MAAA;AAAA;AAAA,IAElB,CAAC,GAAA,EAAK,CAAC,IAAA,EAAM,WAAW,CAAA,KAAM,GAAA,CAAI,OAAA,CAAQ,IAAI,MAAA,CAAO,IAAA,EAAM,IAAI,GAAG,WAAW,CAAA;AAAA,IAC7E;AAAA,GACF;AACF;;;;"} |
+17
-51
@@ -6,44 +6,17 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const ERROR_TEXT = 'Automatic instrumentation of CronJob only supports crontab string'; | ||
| /** | ||
| * Instruments the `cron` library to send a check-in event to Sentry for each job execution. | ||
| * | ||
| * ```ts | ||
| * import * as Sentry from '@sentry/node'; | ||
| * import { CronJob } from 'cron'; | ||
| * | ||
| * const CronJobWithCheckIn = Sentry.cron.instrumentCron(CronJob, 'my-cron-job'); | ||
| * | ||
| * // use the constructor | ||
| * const job = new CronJobWithCheckIn('* * * * *', () => { | ||
| * console.log('You will see this message every minute'); | ||
| * }); | ||
| * | ||
| * // or from | ||
| * const job = CronJobWithCheckIn.from({ cronTime: '* * * * *', onTick: () => { | ||
| * console.log('You will see this message every minute'); | ||
| * }); | ||
| * ``` | ||
| */ | ||
| const ERROR_TEXT = "Automatic instrumentation of CronJob only supports crontab string"; | ||
| function instrumentCron(lib, monitorSlug) { | ||
| let jobScheduled = false; | ||
| return new Proxy(lib, { | ||
| construct(target, args) { | ||
| const [cronTime, onTick, onComplete, start, timeZone, ...rest] = args; | ||
| if (typeof cronTime !== 'string') { | ||
| if (typeof cronTime !== "string") { | ||
| throw new Error(ERROR_TEXT); | ||
| } | ||
| if (jobScheduled) { | ||
| throw new Error(`A job named '${monitorSlug}' has already been scheduled`); | ||
| } | ||
| jobScheduled = true; | ||
| const cronString = common.replaceCronNames(cronTime); | ||
| async function monitoredTick(context, onComplete) { | ||
| async function monitoredTick(context, onComplete2) { | ||
| return core.withMonitor( | ||
@@ -53,3 +26,3 @@ monitorSlug, | ||
| try { | ||
| await onTick(context, onComplete); | ||
| await onTick(context, onComplete2); | ||
| } catch (e) { | ||
@@ -59,4 +32,4 @@ core.captureException(e, { | ||
| handled: false, | ||
| type: 'auto.function.cron.instrumentCron', | ||
| }, | ||
| type: "auto.function.cron.instrumentCron" | ||
| } | ||
| }); | ||
@@ -67,27 +40,21 @@ throw e; | ||
| { | ||
| schedule: { type: 'crontab', value: cronString }, | ||
| timezone: timeZone || undefined, | ||
| }, | ||
| schedule: { type: "crontab", value: cronString }, | ||
| timezone: timeZone || void 0 | ||
| } | ||
| ); | ||
| } | ||
| return new target(cronTime, monitoredTick, onComplete, start, timeZone, ...rest); | ||
| }, | ||
| get(target, prop) { | ||
| if (prop === 'from') { | ||
| if (prop === "from") { | ||
| return (param) => { | ||
| const { cronTime, onTick, timeZone } = param; | ||
| if (typeof cronTime !== 'string') { | ||
| if (typeof cronTime !== "string") { | ||
| throw new Error(ERROR_TEXT); | ||
| } | ||
| if (jobScheduled) { | ||
| throw new Error(`A job named '${monitorSlug}' has already been scheduled`); | ||
| } | ||
| jobScheduled = true; | ||
| const cronString = common.replaceCronNames(cronTime); | ||
| param.onTick = async (context, onComplete) => { | ||
@@ -103,4 +70,4 @@ return core.withMonitor( | ||
| handled: false, | ||
| type: 'auto.function.cron.instrumentCron', | ||
| }, | ||
| type: "auto.function.cron.instrumentCron" | ||
| } | ||
| }); | ||
@@ -111,8 +78,7 @@ throw e; | ||
| { | ||
| schedule: { type: 'crontab', value: cronString }, | ||
| timezone: timeZone || undefined, | ||
| }, | ||
| schedule: { type: "crontab", value: cronString }, | ||
| timezone: timeZone || void 0 | ||
| } | ||
| ); | ||
| }; | ||
| return target.from(param); | ||
@@ -123,3 +89,3 @@ }; | ||
| } | ||
| }, | ||
| } | ||
| }); | ||
@@ -126,0 +92,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"cron.js","sources":["../../../src/cron/cron.ts"],"sourcesContent":["import { captureException, withMonitor } from '@sentry/core';\nimport { replaceCronNames } from './common';\n\nexport type CronJobParams = {\n cronTime: string | Date;\n onTick: (context: unknown, onComplete?: unknown) => void | Promise<void>;\n onComplete?: () => void | Promise<void>;\n start?: boolean | null;\n context?: unknown;\n runOnInit?: boolean | null;\n unrefTimeout?: boolean | null;\n} & (\n | {\n timeZone?: string | null;\n utcOffset?: never;\n }\n | {\n timeZone?: never;\n utcOffset?: number | null;\n }\n);\n\nexport type CronJob = {\n //\n};\n\nexport type CronJobConstructor = {\n from: (param: CronJobParams) => CronJob;\n\n new (\n cronTime: CronJobParams['cronTime'],\n onTick: CronJobParams['onTick'],\n onComplete?: CronJobParams['onComplete'],\n start?: CronJobParams['start'],\n timeZone?: CronJobParams['timeZone'],\n context?: CronJobParams['context'],\n runOnInit?: CronJobParams['runOnInit'],\n utcOffset?: null,\n unrefTimeout?: CronJobParams['unrefTimeout'],\n ): CronJob;\n new (\n cronTime: CronJobParams['cronTime'],\n onTick: CronJobParams['onTick'],\n onComplete?: CronJobParams['onComplete'],\n start?: CronJobParams['start'],\n timeZone?: null,\n context?: CronJobParams['context'],\n runOnInit?: CronJobParams['runOnInit'],\n utcOffset?: CronJobParams['utcOffset'],\n unrefTimeout?: CronJobParams['unrefTimeout'],\n ): CronJob;\n};\n\nconst ERROR_TEXT = 'Automatic instrumentation of CronJob only supports crontab string';\n\n/**\n * Instruments the `cron` library to send a check-in event to Sentry for each job execution.\n *\n * ```ts\n * import * as Sentry from '@sentry/node';\n * import { CronJob } from 'cron';\n *\n * const CronJobWithCheckIn = Sentry.cron.instrumentCron(CronJob, 'my-cron-job');\n *\n * // use the constructor\n * const job = new CronJobWithCheckIn('* * * * *', () => {\n * console.log('You will see this message every minute');\n * });\n *\n * // or from\n * const job = CronJobWithCheckIn.from({ cronTime: '* * * * *', onTick: () => {\n * console.log('You will see this message every minute');\n * });\n * ```\n */\nexport function instrumentCron<T>(lib: T & CronJobConstructor, monitorSlug: string): T {\n let jobScheduled = false;\n\n return new Proxy(lib, {\n construct(target, args: ConstructorParameters<CronJobConstructor>) {\n const [cronTime, onTick, onComplete, start, timeZone, ...rest] = args;\n\n if (typeof cronTime !== 'string') {\n throw new Error(ERROR_TEXT);\n }\n\n if (jobScheduled) {\n throw new Error(`A job named '${monitorSlug}' has already been scheduled`);\n }\n\n jobScheduled = true;\n\n const cronString = replaceCronNames(cronTime);\n\n async function monitoredTick(context: unknown, onComplete?: unknown): Promise<void> {\n return withMonitor(\n monitorSlug,\n async () => {\n try {\n await onTick(context, onComplete);\n } catch (e) {\n captureException(e, {\n mechanism: {\n handled: false,\n type: 'auto.function.cron.instrumentCron',\n },\n });\n throw e;\n }\n },\n {\n schedule: { type: 'crontab', value: cronString },\n timezone: timeZone || undefined,\n },\n );\n }\n\n return new target(cronTime, monitoredTick, onComplete, start, timeZone, ...rest);\n },\n get(target, prop: keyof CronJobConstructor) {\n if (prop === 'from') {\n return (param: CronJobParams) => {\n const { cronTime, onTick, timeZone } = param;\n\n if (typeof cronTime !== 'string') {\n throw new Error(ERROR_TEXT);\n }\n\n if (jobScheduled) {\n throw new Error(`A job named '${monitorSlug}' has already been scheduled`);\n }\n\n jobScheduled = true;\n\n const cronString = replaceCronNames(cronTime);\n\n param.onTick = async (context: unknown, onComplete?: unknown) => {\n return withMonitor(\n monitorSlug,\n async () => {\n try {\n await onTick(context, onComplete);\n } catch (e) {\n captureException(e, {\n mechanism: {\n handled: false,\n type: 'auto.function.cron.instrumentCron',\n },\n });\n throw e;\n }\n },\n {\n schedule: { type: 'crontab', value: cronString },\n timezone: timeZone || undefined,\n },\n );\n };\n\n return target.from(param);\n };\n } else {\n return target[prop];\n }\n },\n });\n}\n"],"names":["replaceCronNames","withMonitor","captureException"],"mappings":"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"} | ||
| {"version":3,"file":"cron.js","sources":["../../../src/cron/cron.ts"],"sourcesContent":["import { captureException, withMonitor } from '@sentry/core';\nimport { replaceCronNames } from './common';\n\nexport type CronJobParams = {\n cronTime: string | Date;\n onTick: (context: unknown, onComplete?: unknown) => void | Promise<void>;\n onComplete?: () => void | Promise<void>;\n start?: boolean | null;\n context?: unknown;\n runOnInit?: boolean | null;\n unrefTimeout?: boolean | null;\n} & (\n | {\n timeZone?: string | null;\n utcOffset?: never;\n }\n | {\n timeZone?: never;\n utcOffset?: number | null;\n }\n);\n\nexport type CronJob = {\n //\n};\n\nexport type CronJobConstructor = {\n from: (param: CronJobParams) => CronJob;\n\n new (\n cronTime: CronJobParams['cronTime'],\n onTick: CronJobParams['onTick'],\n onComplete?: CronJobParams['onComplete'],\n start?: CronJobParams['start'],\n timeZone?: CronJobParams['timeZone'],\n context?: CronJobParams['context'],\n runOnInit?: CronJobParams['runOnInit'],\n utcOffset?: null,\n unrefTimeout?: CronJobParams['unrefTimeout'],\n ): CronJob;\n new (\n cronTime: CronJobParams['cronTime'],\n onTick: CronJobParams['onTick'],\n onComplete?: CronJobParams['onComplete'],\n start?: CronJobParams['start'],\n timeZone?: null,\n context?: CronJobParams['context'],\n runOnInit?: CronJobParams['runOnInit'],\n utcOffset?: CronJobParams['utcOffset'],\n unrefTimeout?: CronJobParams['unrefTimeout'],\n ): CronJob;\n};\n\nconst ERROR_TEXT = 'Automatic instrumentation of CronJob only supports crontab string';\n\n/**\n * Instruments the `cron` library to send a check-in event to Sentry for each job execution.\n *\n * ```ts\n * import * as Sentry from '@sentry/node';\n * import { CronJob } from 'cron';\n *\n * const CronJobWithCheckIn = Sentry.cron.instrumentCron(CronJob, 'my-cron-job');\n *\n * // use the constructor\n * const job = new CronJobWithCheckIn('* * * * *', () => {\n * console.log('You will see this message every minute');\n * });\n *\n * // or from\n * const job = CronJobWithCheckIn.from({ cronTime: '* * * * *', onTick: () => {\n * console.log('You will see this message every minute');\n * });\n * ```\n */\nexport function instrumentCron<T>(lib: T & CronJobConstructor, monitorSlug: string): T {\n let jobScheduled = false;\n\n return new Proxy(lib, {\n construct(target, args: ConstructorParameters<CronJobConstructor>) {\n const [cronTime, onTick, onComplete, start, timeZone, ...rest] = args;\n\n if (typeof cronTime !== 'string') {\n throw new Error(ERROR_TEXT);\n }\n\n if (jobScheduled) {\n throw new Error(`A job named '${monitorSlug}' has already been scheduled`);\n }\n\n jobScheduled = true;\n\n const cronString = replaceCronNames(cronTime);\n\n async function monitoredTick(context: unknown, onComplete?: unknown): Promise<void> {\n return withMonitor(\n monitorSlug,\n async () => {\n try {\n await onTick(context, onComplete);\n } catch (e) {\n captureException(e, {\n mechanism: {\n handled: false,\n type: 'auto.function.cron.instrumentCron',\n },\n });\n throw e;\n }\n },\n {\n schedule: { type: 'crontab', value: cronString },\n timezone: timeZone || undefined,\n },\n );\n }\n\n return new target(cronTime, monitoredTick, onComplete, start, timeZone, ...rest);\n },\n get(target, prop: keyof CronJobConstructor) {\n if (prop === 'from') {\n return (param: CronJobParams) => {\n const { cronTime, onTick, timeZone } = param;\n\n if (typeof cronTime !== 'string') {\n throw new Error(ERROR_TEXT);\n }\n\n if (jobScheduled) {\n throw new Error(`A job named '${monitorSlug}' has already been scheduled`);\n }\n\n jobScheduled = true;\n\n const cronString = replaceCronNames(cronTime);\n\n param.onTick = async (context: unknown, onComplete?: unknown) => {\n return withMonitor(\n monitorSlug,\n async () => {\n try {\n await onTick(context, onComplete);\n } catch (e) {\n captureException(e, {\n mechanism: {\n handled: false,\n type: 'auto.function.cron.instrumentCron',\n },\n });\n throw e;\n }\n },\n {\n schedule: { type: 'crontab', value: cronString },\n timezone: timeZone || undefined,\n },\n );\n };\n\n return target.from(param);\n };\n } else {\n return target[prop];\n }\n },\n });\n}\n"],"names":["replaceCronNames","onComplete","withMonitor","captureException"],"mappings":"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"} |
@@ -7,7 +7,6 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| /** Methods to instrument cron libraries for Sentry check-ins */ | ||
| const cron = { | ||
| instrumentCron: cron$1.instrumentCron, | ||
| instrumentNodeCron: nodeCron.instrumentNodeCron, | ||
| instrumentNodeSchedule: nodeSchedule.instrumentNodeSchedule, | ||
| instrumentNodeSchedule: nodeSchedule.instrumentNodeSchedule | ||
| }; | ||
@@ -14,0 +13,0 @@ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.js","sources":["../../../src/cron/index.ts"],"sourcesContent":["import { instrumentCron } from './cron';\nimport { instrumentNodeCron } from './node-cron';\nimport { instrumentNodeSchedule } from './node-schedule';\n\n/** Methods to instrument cron libraries for Sentry check-ins */\nexport const cron = {\n instrumentCron,\n instrumentNodeCron,\n instrumentNodeSchedule,\n};\n"],"names":["instrumentCron","instrumentNodeCron","instrumentNodeSchedule"],"mappings":";;;;;;AAIA;AACO,MAAM,OAAO;AACpB,kBAAEA,qBAAc;AAChB,sBAAEC,2BAAkB;AACpB,0BAAEC,mCAAsB;AACxB;;;;"} | ||
| {"version":3,"file":"index.js","sources":["../../../src/cron/index.ts"],"sourcesContent":["import { instrumentCron } from './cron';\nimport { instrumentNodeCron } from './node-cron';\nimport { instrumentNodeSchedule } from './node-schedule';\n\n/** Methods to instrument cron libraries for Sentry check-ins */\nexport const cron = {\n instrumentCron,\n instrumentNodeCron,\n instrumentNodeSchedule,\n};\n"],"names":["instrumentCron","instrumentNodeCron","instrumentNodeSchedule"],"mappings":";;;;;;AAKO,MAAM,IAAA,GAAO;AAAA,kBAClBA,qBAAA;AAAA,sBACAC,2BAAA;AAAA,0BACAC;AACF;;;;"} |
@@ -6,39 +6,14 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| /** | ||
| * Wraps the `node-cron` library with check-in monitoring. | ||
| * | ||
| * ```ts | ||
| * import * as Sentry from "@sentry/node"; | ||
| * import * as cron from "node-cron"; | ||
| * | ||
| * const cronWithCheckIn = Sentry.cron.instrumentNodeCron(cron); | ||
| * | ||
| * cronWithCheckIn.schedule( | ||
| * "* * * * *", | ||
| * () => { | ||
| * console.log("running a task every minute"); | ||
| * }, | ||
| * { name: "my-cron-job" }, | ||
| * ); | ||
| * ``` | ||
| */ | ||
| function instrumentNodeCron( | ||
| lib, | ||
| monitorConfig = {}, | ||
| ) { | ||
| function instrumentNodeCron(lib, monitorConfig = {}) { | ||
| return new Proxy(lib, { | ||
| get(target, prop) { | ||
| if (prop === 'schedule' && target.schedule) { | ||
| // When 'get' is called for schedule, return a proxied version of the schedule function | ||
| if (prop === "schedule" && target.schedule) { | ||
| return new Proxy(target.schedule, { | ||
| apply(target, thisArg, argArray) { | ||
| apply(target2, thisArg, argArray) { | ||
| const [expression, callback, options] = argArray; | ||
| const name = options?.name; | ||
| const timezone = options?.timezone; | ||
| if (!name) { | ||
| throw new Error('Missing "name" for scheduled job. A name is required for Sentry check-in monitoring.'); | ||
| } | ||
| const monitoredCallback = async (...args) => { | ||
@@ -48,6 +23,3 @@ return core.withMonitor( | ||
| async () => { | ||
| // We have to manually catch here and capture the exception because node-cron swallows errors | ||
| // https://github.com/node-cron/node-cron/issues/399 | ||
| try { | ||
| // oxlint-disable-next-line typescript/await-thenable -- callback may be async at runtime | ||
| return await callback(...args); | ||
@@ -58,4 +30,4 @@ } catch (e) { | ||
| handled: false, | ||
| type: 'auto.function.node-cron.instrumentNodeCron', | ||
| }, | ||
| type: "auto.function.node-cron.instrumentNodeCron" | ||
| } | ||
| }); | ||
@@ -66,16 +38,15 @@ throw e; | ||
| { | ||
| schedule: { type: 'crontab', value: common.replaceCronNames(expression) }, | ||
| schedule: { type: "crontab", value: common.replaceCronNames(expression) }, | ||
| timezone, | ||
| ...monitorConfig, | ||
| }, | ||
| ...monitorConfig | ||
| } | ||
| ); | ||
| }; | ||
| return target.apply(thisArg, [expression, monitoredCallback, options]); | ||
| }, | ||
| return target2.apply(thisArg, [expression, monitoredCallback, options]); | ||
| } | ||
| }); | ||
| } else { | ||
| return target[prop ]; | ||
| return target[prop]; | ||
| } | ||
| }, | ||
| } | ||
| }); | ||
@@ -82,0 +53,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"node-cron.js","sources":["../../../src/cron/node-cron.ts"],"sourcesContent":["import { captureException, type MonitorConfig, withMonitor } from '@sentry/core';\nimport { replaceCronNames } from './common';\n\nexport interface NodeCronOptions {\n name: string;\n timezone?: string;\n}\n\nexport interface NodeCron {\n schedule: (\n cronExpression: string,\n callback: (context?: unknown) => void,\n options: NodeCronOptions | undefined,\n ) => unknown;\n}\n\n/**\n * Wraps the `node-cron` library with check-in monitoring.\n *\n * ```ts\n * import * as Sentry from \"@sentry/node\";\n * import * as cron from \"node-cron\";\n *\n * const cronWithCheckIn = Sentry.cron.instrumentNodeCron(cron);\n *\n * cronWithCheckIn.schedule(\n * \"* * * * *\",\n * () => {\n * console.log(\"running a task every minute\");\n * },\n * { name: \"my-cron-job\" },\n * );\n * ```\n */\nexport function instrumentNodeCron<T>(\n lib: Partial<NodeCron> & T,\n monitorConfig: Pick<MonitorConfig, 'isolateTrace'> = {},\n): T {\n return new Proxy(lib, {\n get(target, prop) {\n if (prop === 'schedule' && target.schedule) {\n // When 'get' is called for schedule, return a proxied version of the schedule function\n return new Proxy(target.schedule, {\n apply(target, thisArg, argArray: Parameters<NodeCron['schedule']>) {\n const [expression, callback, options] = argArray;\n\n const name = options?.name;\n const timezone = options?.timezone;\n\n if (!name) {\n throw new Error('Missing \"name\" for scheduled job. A name is required for Sentry check-in monitoring.');\n }\n\n const monitoredCallback = async (...args: Parameters<typeof callback>): Promise<void> => {\n return withMonitor(\n name,\n async () => {\n // We have to manually catch here and capture the exception because node-cron swallows errors\n // https://github.com/node-cron/node-cron/issues/399\n try {\n // oxlint-disable-next-line typescript/await-thenable -- callback may be async at runtime\n return await callback(...args);\n } catch (e) {\n captureException(e, {\n mechanism: {\n handled: false,\n type: 'auto.function.node-cron.instrumentNodeCron',\n },\n });\n throw e;\n }\n },\n {\n schedule: { type: 'crontab', value: replaceCronNames(expression) },\n timezone,\n ...monitorConfig,\n },\n );\n };\n\n return target.apply(thisArg, [expression, monitoredCallback, options]);\n },\n });\n } else {\n return target[prop as keyof T];\n }\n },\n });\n}\n"],"names":["withMonitor","captureException","replaceCronNames"],"mappings":";;;;;AAgBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,kBAAkB;AAClC,EAAE,GAAG;AACL,EAAE,aAAa,GAAwC,EAAE;AACzD,EAAK;AACL,EAAE,OAAO,IAAI,KAAK,CAAC,GAAG,EAAE;AACxB,IAAI,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE;AACtB,MAAM,IAAI,IAAA,KAAS,cAAc,MAAM,CAAC,QAAQ,EAAE;AAClD;AACA,QAAQ,OAAO,IAAI,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE;AAC1C,UAAU,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAoC;AAC7E,YAAY,MAAM,CAAC,UAAU,EAAE,QAAQ,EAAE,OAAO,CAAA,GAAI,QAAQ;;AAE5D,YAAY,MAAM,IAAA,GAAO,OAAO,EAAE,IAAI;AACtC,YAAY,MAAM,QAAA,GAAW,OAAO,EAAE,QAAQ;;AAE9C,YAAY,IAAI,CAAC,IAAI,EAAE;AACvB,cAAc,MAAM,IAAI,KAAK,CAAC,sFAAsF,CAAC;AACrH,YAAY;;AAEZ,YAAY,MAAM,iBAAA,GAAoB,OAAO,GAAG,IAAI,KAAiD;AACrG,cAAc,OAAOA,gBAAW;AAChC,gBAAgB,IAAI;AACpB,gBAAgB,YAAY;AAC5B;AACA;AACA,kBAAkB,IAAI;AACtB;AACA,oBAAoB,OAAO,MAAM,QAAQ,CAAC,GAAG,IAAI,CAAC;AAClD,kBAAkB,CAAA,CAAE,OAAO,CAAC,EAAE;AAC9B,oBAAoBC,qBAAgB,CAAC,CAAC,EAAE;AACxC,sBAAsB,SAAS,EAAE;AACjC,wBAAwB,OAAO,EAAE,KAAK;AACtC,wBAAwB,IAAI,EAAE,4CAA4C;AAC1E,uBAAuB;AACvB,qBAAqB,CAAC;AACtB,oBAAoB,MAAM,CAAC;AAC3B,kBAAkB;AAClB,gBAAgB,CAAC;AACjB,gBAAgB;AAChB,kBAAkB,QAAQ,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAEC,uBAAgB,CAAC,UAAU,GAAG;AACpF,kBAAkB,QAAQ;AAC1B,kBAAkB,GAAG,aAAa;AAClC,iBAAiB;AACjB,eAAe;AACf,YAAY,CAAC;;AAEb,YAAY,OAAO,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,UAAU,EAAE,iBAAiB,EAAE,OAAO,CAAC,CAAC;AAClF,UAAU,CAAC;AACX,SAAS,CAAC;AACV,MAAM,OAAO;AACb,QAAQ,OAAO,MAAM,CAAC,IAAA,EAAgB;AACtC,MAAM;AACN,IAAI,CAAC;AACL,GAAG,CAAC;AACJ;;;;"} | ||
| {"version":3,"file":"node-cron.js","sources":["../../../src/cron/node-cron.ts"],"sourcesContent":["import { captureException, type MonitorConfig, withMonitor } from '@sentry/core';\nimport { replaceCronNames } from './common';\n\nexport interface NodeCronOptions {\n name: string;\n timezone?: string;\n}\n\nexport interface NodeCron {\n schedule: (\n cronExpression: string,\n callback: (context?: unknown) => void,\n options: NodeCronOptions | undefined,\n ) => unknown;\n}\n\n/**\n * Wraps the `node-cron` library with check-in monitoring.\n *\n * ```ts\n * import * as Sentry from \"@sentry/node\";\n * import * as cron from \"node-cron\";\n *\n * const cronWithCheckIn = Sentry.cron.instrumentNodeCron(cron);\n *\n * cronWithCheckIn.schedule(\n * \"* * * * *\",\n * () => {\n * console.log(\"running a task every minute\");\n * },\n * { name: \"my-cron-job\" },\n * );\n * ```\n */\nexport function instrumentNodeCron<T>(\n lib: Partial<NodeCron> & T,\n monitorConfig: Pick<MonitorConfig, 'isolateTrace'> = {},\n): T {\n return new Proxy(lib, {\n get(target, prop) {\n if (prop === 'schedule' && target.schedule) {\n // When 'get' is called for schedule, return a proxied version of the schedule function\n return new Proxy(target.schedule, {\n apply(target, thisArg, argArray: Parameters<NodeCron['schedule']>) {\n const [expression, callback, options] = argArray;\n\n const name = options?.name;\n const timezone = options?.timezone;\n\n if (!name) {\n throw new Error('Missing \"name\" for scheduled job. A name is required for Sentry check-in monitoring.');\n }\n\n const monitoredCallback = async (...args: Parameters<typeof callback>): Promise<void> => {\n return withMonitor(\n name,\n async () => {\n // We have to manually catch here and capture the exception because node-cron swallows errors\n // https://github.com/node-cron/node-cron/issues/399\n try {\n // oxlint-disable-next-line typescript/await-thenable -- callback may be async at runtime\n return await callback(...args);\n } catch (e) {\n captureException(e, {\n mechanism: {\n handled: false,\n type: 'auto.function.node-cron.instrumentNodeCron',\n },\n });\n throw e;\n }\n },\n {\n schedule: { type: 'crontab', value: replaceCronNames(expression) },\n timezone,\n ...monitorConfig,\n },\n );\n };\n\n return target.apply(thisArg, [expression, monitoredCallback, options]);\n },\n });\n } else {\n return target[prop as keyof T];\n }\n },\n });\n}\n"],"names":["target","withMonitor","captureException","replaceCronNames"],"mappings":";;;;;AAkCO,SAAS,kBAAA,CACd,GAAA,EACA,aAAA,GAAqD,EAAC,EACnD;AACH,EAAA,OAAO,IAAI,MAAM,GAAA,EAAK;AAAA,IACpB,GAAA,CAAI,QAAQ,IAAA,EAAM;AAChB,MAAA,IAAI,IAAA,KAAS,UAAA,IAAc,MAAA,CAAO,QAAA,EAAU;AAE1C,QAAA,OAAO,IAAI,KAAA,CAAM,MAAA,CAAO,QAAA,EAAU;AAAA,UAChC,KAAA,CAAMA,OAAAA,EAAQ,OAAA,EAAS,QAAA,EAA4C;AACjE,YAAA,MAAM,CAAC,UAAA,EAAY,QAAA,EAAU,OAAO,CAAA,GAAI,QAAA;AAExC,YAAA,MAAM,OAAO,OAAA,EAAS,IAAA;AACtB,YAAA,MAAM,WAAW,OAAA,EAAS,QAAA;AAE1B,YAAA,IAAI,CAAC,IAAA,EAAM;AACT,cAAA,MAAM,IAAI,MAAM,sFAAsF,CAAA;AAAA,YACxG;AAEA,YAAA,MAAM,iBAAA,GAAoB,UAAU,IAAA,KAAqD;AACvF,cAAA,OAAOC,gBAAA;AAAA,gBACL,IAAA;AAAA,gBACA,YAAY;AAGV,kBAAA,IAAI;AAEF,oBAAA,OAAO,MAAM,QAAA,CAAS,GAAG,IAAI,CAAA;AAAA,kBAC/B,SAAS,CAAA,EAAG;AACV,oBAAAC,qBAAA,CAAiB,CAAA,EAAG;AAAA,sBAClB,SAAA,EAAW;AAAA,wBACT,OAAA,EAAS,KAAA;AAAA,wBACT,IAAA,EAAM;AAAA;AACR,qBACD,CAAA;AACD,oBAAA,MAAM,CAAA;AAAA,kBACR;AAAA,gBACF,CAAA;AAAA,gBACA;AAAA,kBACE,UAAU,EAAE,IAAA,EAAM,WAAW,KAAA,EAAOC,uBAAA,CAAiB,UAAU,CAAA,EAAE;AAAA,kBACjE,QAAA;AAAA,kBACA,GAAG;AAAA;AACL,eACF;AAAA,YACF,CAAA;AAEA,YAAA,OAAOH,QAAO,KAAA,CAAM,OAAA,EAAS,CAAC,UAAA,EAAY,iBAAA,EAAmB,OAAO,CAAC,CAAA;AAAA,UACvE;AAAA,SACD,CAAA;AAAA,MACH,CAAA,MAAO;AACL,QAAA,OAAO,OAAO,IAAe,CAAA;AAAA,MAC/B;AAAA,IACF;AAAA,GACD,CAAA;AACH;;;;"} |
@@ -6,38 +6,16 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| /** | ||
| * Instruments the `node-schedule` library to send a check-in event to Sentry for each job execution. | ||
| * | ||
| * ```ts | ||
| * import * as Sentry from '@sentry/node'; | ||
| * import * as schedule from 'node-schedule'; | ||
| * | ||
| * const scheduleWithCheckIn = Sentry.cron.instrumentNodeSchedule(schedule); | ||
| * | ||
| * const job = scheduleWithCheckIn.scheduleJob('my-cron-job', '* * * * *', () => { | ||
| * console.log('You will see this message every minute'); | ||
| * }); | ||
| * ``` | ||
| */ | ||
| function instrumentNodeSchedule(lib) { | ||
| return new Proxy(lib, { | ||
| get(target, prop) { | ||
| if (prop === 'scheduleJob') { | ||
| // eslint-disable-next-line @typescript-eslint/unbound-method | ||
| if (prop === "scheduleJob") { | ||
| return new Proxy(target.scheduleJob, { | ||
| apply(target, thisArg, argArray) { | ||
| apply(target2, thisArg, argArray) { | ||
| const [nameOrExpression, expressionOrCallback, callback] = argArray; | ||
| if ( | ||
| typeof nameOrExpression !== 'string' || | ||
| typeof expressionOrCallback !== 'string' || | ||
| typeof callback !== 'function' | ||
| ) { | ||
| if (typeof nameOrExpression !== "string" || typeof expressionOrCallback !== "string" || typeof callback !== "function") { | ||
| throw new Error( | ||
| "Automatic instrumentation of 'node-schedule' requires the first parameter of 'scheduleJob' to be a job name string and the second parameter to be a crontab string", | ||
| "Automatic instrumentation of 'node-schedule' requires the first parameter of 'scheduleJob' to be a job name string and the second parameter to be a crontab string" | ||
| ); | ||
| } | ||
| const monitorSlug = nameOrExpression; | ||
| const expression = expressionOrCallback; | ||
| async function monitoredCallback() { | ||
@@ -47,18 +25,15 @@ return core.withMonitor( | ||
| async () => { | ||
| // oxlint-disable-next-line typescript/await-thenable -- callback may be async at runtime | ||
| await callback?.(); | ||
| }, | ||
| { | ||
| schedule: { type: 'crontab', value: common.replaceCronNames(expression) }, | ||
| }, | ||
| schedule: { type: "crontab", value: common.replaceCronNames(expression) } | ||
| } | ||
| ); | ||
| } | ||
| return target.apply(thisArg, [monitorSlug, expression, monitoredCallback]); | ||
| }, | ||
| return target2.apply(thisArg, [monitorSlug, expression, monitoredCallback]); | ||
| } | ||
| }); | ||
| } | ||
| return target[prop]; | ||
| }, | ||
| } | ||
| }); | ||
@@ -65,0 +40,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"node-schedule.js","sources":["../../../src/cron/node-schedule.ts"],"sourcesContent":["import { withMonitor } from '@sentry/core';\nimport { replaceCronNames } from './common';\n\nexport interface NodeSchedule {\n scheduleJob(\n nameOrExpression: string | Date | object,\n expressionOrCallback: string | Date | object | (() => void),\n callback?: () => void,\n ): unknown;\n}\n\n/**\n * Instruments the `node-schedule` library to send a check-in event to Sentry for each job execution.\n *\n * ```ts\n * import * as Sentry from '@sentry/node';\n * import * as schedule from 'node-schedule';\n *\n * const scheduleWithCheckIn = Sentry.cron.instrumentNodeSchedule(schedule);\n *\n * const job = scheduleWithCheckIn.scheduleJob('my-cron-job', '* * * * *', () => {\n * console.log('You will see this message every minute');\n * });\n * ```\n */\nexport function instrumentNodeSchedule<T>(lib: T & NodeSchedule): T {\n return new Proxy(lib, {\n get(target, prop: keyof NodeSchedule) {\n if (prop === 'scheduleJob') {\n // eslint-disable-next-line @typescript-eslint/unbound-method\n return new Proxy(target.scheduleJob, {\n apply(target, thisArg, argArray: Parameters<NodeSchedule['scheduleJob']>) {\n const [nameOrExpression, expressionOrCallback, callback] = argArray;\n\n if (\n typeof nameOrExpression !== 'string' ||\n typeof expressionOrCallback !== 'string' ||\n typeof callback !== 'function'\n ) {\n throw new Error(\n \"Automatic instrumentation of 'node-schedule' requires the first parameter of 'scheduleJob' to be a job name string and the second parameter to be a crontab string\",\n );\n }\n\n const monitorSlug = nameOrExpression;\n const expression = expressionOrCallback;\n\n async function monitoredCallback(): Promise<void> {\n return withMonitor(\n monitorSlug,\n async () => {\n // oxlint-disable-next-line typescript/await-thenable -- callback may be async at runtime\n await callback?.();\n },\n {\n schedule: { type: 'crontab', value: replaceCronNames(expression) },\n },\n );\n }\n\n return target.apply(thisArg, [monitorSlug, expression, monitoredCallback]);\n },\n });\n }\n\n return target[prop];\n },\n });\n}\n"],"names":["withMonitor","replaceCronNames"],"mappings":";;;;;AAWA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,sBAAsB,CAAI,GAAG,EAAuB;AACpE,EAAE,OAAO,IAAI,KAAK,CAAC,GAAG,EAAE;AACxB,IAAI,GAAG,CAAC,MAAM,EAAE,IAAI,EAAsB;AAC1C,MAAM,IAAI,IAAA,KAAS,aAAa,EAAE;AAClC;AACA,QAAQ,OAAO,IAAI,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE;AAC7C,UAAU,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAA2C;AACpF,YAAY,MAAM,CAAC,gBAAgB,EAAE,oBAAoB,EAAE,QAAQ,CAAA,GAAI,QAAQ;;AAE/E,YAAY;AACZ,cAAc,OAAO,gBAAA,KAAqB,QAAA;AAC1C,cAAc,OAAO,oBAAA,KAAyB,QAAA;AAC9C,cAAc,OAAO,aAAa;AAClC,cAAc;AACd,cAAc,MAAM,IAAI,KAAK;AAC7B,gBAAgB,oKAAoK;AACpL,eAAe;AACf,YAAY;;AAEZ,YAAY,MAAM,WAAA,GAAc,gBAAgB;AAChD,YAAY,MAAM,UAAA,GAAa,oBAAoB;;AAEnD,YAAY,eAAe,iBAAiB,GAAkB;AAC9D,cAAc,OAAOA,gBAAW;AAChC,gBAAgB,WAAW;AAC3B,gBAAgB,YAAY;AAC5B;AACA,kBAAkB,MAAM,QAAQ,IAAI;AACpC,gBAAgB,CAAC;AACjB,gBAAgB;AAChB,kBAAkB,QAAQ,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAEC,uBAAgB,CAAC,UAAU,GAAG;AACpF,iBAAiB;AACjB,eAAe;AACf,YAAY;;AAEZ,YAAY,OAAO,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,WAAW,EAAE,UAAU,EAAE,iBAAiB,CAAC,CAAC;AACtF,UAAU,CAAC;AACX,SAAS,CAAC;AACV,MAAM;;AAEN,MAAM,OAAO,MAAM,CAAC,IAAI,CAAC;AACzB,IAAI,CAAC;AACL,GAAG,CAAC;AACJ;;;;"} | ||
| {"version":3,"file":"node-schedule.js","sources":["../../../src/cron/node-schedule.ts"],"sourcesContent":["import { withMonitor } from '@sentry/core';\nimport { replaceCronNames } from './common';\n\nexport interface NodeSchedule {\n scheduleJob(\n nameOrExpression: string | Date | object,\n expressionOrCallback: string | Date | object | (() => void),\n callback?: () => void,\n ): unknown;\n}\n\n/**\n * Instruments the `node-schedule` library to send a check-in event to Sentry for each job execution.\n *\n * ```ts\n * import * as Sentry from '@sentry/node';\n * import * as schedule from 'node-schedule';\n *\n * const scheduleWithCheckIn = Sentry.cron.instrumentNodeSchedule(schedule);\n *\n * const job = scheduleWithCheckIn.scheduleJob('my-cron-job', '* * * * *', () => {\n * console.log('You will see this message every minute');\n * });\n * ```\n */\nexport function instrumentNodeSchedule<T>(lib: T & NodeSchedule): T {\n return new Proxy(lib, {\n get(target, prop: keyof NodeSchedule) {\n if (prop === 'scheduleJob') {\n // eslint-disable-next-line @typescript-eslint/unbound-method\n return new Proxy(target.scheduleJob, {\n apply(target, thisArg, argArray: Parameters<NodeSchedule['scheduleJob']>) {\n const [nameOrExpression, expressionOrCallback, callback] = argArray;\n\n if (\n typeof nameOrExpression !== 'string' ||\n typeof expressionOrCallback !== 'string' ||\n typeof callback !== 'function'\n ) {\n throw new Error(\n \"Automatic instrumentation of 'node-schedule' requires the first parameter of 'scheduleJob' to be a job name string and the second parameter to be a crontab string\",\n );\n }\n\n const monitorSlug = nameOrExpression;\n const expression = expressionOrCallback;\n\n async function monitoredCallback(): Promise<void> {\n return withMonitor(\n monitorSlug,\n async () => {\n // oxlint-disable-next-line typescript/await-thenable -- callback may be async at runtime\n await callback?.();\n },\n {\n schedule: { type: 'crontab', value: replaceCronNames(expression) },\n },\n );\n }\n\n return target.apply(thisArg, [monitorSlug, expression, monitoredCallback]);\n },\n });\n }\n\n return target[prop];\n },\n });\n}\n"],"names":["target","withMonitor","replaceCronNames"],"mappings":";;;;;AAyBO,SAAS,uBAA0B,GAAA,EAA0B;AAClE,EAAA,OAAO,IAAI,MAAM,GAAA,EAAK;AAAA,IACpB,GAAA,CAAI,QAAQ,IAAA,EAA0B;AACpC,MAAA,IAAI,SAAS,aAAA,EAAe;AAE1B,QAAA,OAAO,IAAI,KAAA,CAAM,MAAA,CAAO,WAAA,EAAa;AAAA,UACnC,KAAA,CAAMA,OAAAA,EAAQ,OAAA,EAAS,QAAA,EAAmD;AACxE,YAAA,MAAM,CAAC,gBAAA,EAAkB,oBAAA,EAAsB,QAAQ,CAAA,GAAI,QAAA;AAE3D,YAAA,IACE,OAAO,qBAAqB,QAAA,IAC5B,OAAO,yBAAyB,QAAA,IAChC,OAAO,aAAa,UAAA,EACpB;AACA,cAAA,MAAM,IAAI,KAAA;AAAA,gBACR;AAAA,eACF;AAAA,YACF;AAEA,YAAA,MAAM,WAAA,GAAc,gBAAA;AACpB,YAAA,MAAM,UAAA,GAAa,oBAAA;AAEnB,YAAA,eAAe,iBAAA,GAAmC;AAChD,cAAA,OAAOC,gBAAA;AAAA,gBACL,WAAA;AAAA,gBACA,YAAY;AAEV,kBAAA,MAAM,QAAA,IAAW;AAAA,gBACnB,CAAA;AAAA,gBACA;AAAA,kBACE,UAAU,EAAE,IAAA,EAAM,WAAW,KAAA,EAAOC,uBAAA,CAAiB,UAAU,CAAA;AAAE;AACnE,eACF;AAAA,YACF;AAEA,YAAA,OAAOF,QAAO,KAAA,CAAM,OAAA,EAAS,CAAC,WAAA,EAAa,UAAA,EAAY,iBAAiB,CAAC,CAAA;AAAA,UAC3E;AAAA,SACD,CAAA;AAAA,MACH;AAEA,MAAA,OAAO,OAAO,IAAI,CAAA;AAAA,IACpB;AAAA,GACD,CAAA;AACH;;;;"} |
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| /** | ||
| * This serves as a build time flag that will be true by default, but false in non-debug builds or if users replace `__SENTRY_DEBUG__` in their generated code. | ||
| * | ||
| * ATTENTION: This constant must never cross package boundaries (i.e. be exported) to guarantee that it can be used for tree shaking. | ||
| */ | ||
| const DEBUG_BUILD = (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__); | ||
@@ -9,0 +4,0 @@ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"debug-build.js","sources":["../../src/debug-build.ts"],"sourcesContent":["declare const __DEBUG_BUILD__: boolean;\n\n/**\n * This serves as a build time flag that will be true by default, but false in non-debug builds or if users replace `__SENTRY_DEBUG__` in their generated code.\n *\n * ATTENTION: This constant must never cross package boundaries (i.e. be exported) to guarantee that it can be used for tree shaking.\n */\nexport const DEBUG_BUILD = __DEBUG_BUILD__;\n"],"names":[],"mappings":";;AAEA;AACA;AACA;AACA;AACA;AACO,MAAM,WAAA,IAAc,OAAA,gBAAA,KAAA,WAAA,IAAA,gBAAA;;;;"} | ||
| {"version":3,"file":"debug-build.js","sources":["../../src/debug-build.ts"],"sourcesContent":["declare const __DEBUG_BUILD__: boolean;\n\n/**\n * This serves as a build time flag that will be true by default, but false in non-debug builds or if users replace `__SENTRY_DEBUG__` in their generated code.\n *\n * ATTENTION: This constant must never cross package boundaries (i.e. be exported) to guarantee that it can be used for tree shaking.\n */\nexport const DEBUG_BUILD = __DEBUG_BUILD__;\n"],"names":[],"mappings":";;AAOO,MAAM,WAAA,IAAc,OAAA,gBAAA,KAAA,WAAA,IAAA,gBAAA;;;;"} |
| const index = require('./sdk/index.js'); | ||
| /** | ||
| * The @sentry/node-core/init export can be used with the node --import and --require args to initialize the SDK entirely via | ||
| * environment variables. | ||
| * | ||
| * > SENTRY_DSN=https://examplePublicKey@o0.ingest.sentry.io/0 SENTRY_TRACES_SAMPLE_RATE=1.0 node --import=@sentry/node/init app.mjs | ||
| */ | ||
| index.init(); | ||
| //# sourceMappingURL=init.js.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"init.js","sources":["../../src/init.ts"],"sourcesContent":["import { init } from './sdk';\n\n/**\n * The @sentry/node-core/init export can be used with the node --import and --require args to initialize the SDK entirely via\n * environment variables.\n *\n * > SENTRY_DSN=https://examplePublicKey@o0.ingest.sentry.io/0 SENTRY_TRACES_SAMPLE_RATE=1.0 node --import=@sentry/node/init app.mjs\n */\ninit();\n"],"names":["init"],"mappings":";;AAEA;AACA;AACA;AACA;AACA;AACA;AACAA,UAAI,EAAE;;"} | ||
| {"version":3,"file":"init.js","sources":["../../src/init.ts"],"sourcesContent":["import { init } from './sdk';\n\n/**\n * The @sentry/node-core/init export can be used with the node --import and --require args to initialize the SDK entirely via\n * environment variables.\n *\n * > SENTRY_DSN=https://examplePublicKey@o0.ingest.sentry.io/0 SENTRY_TRACES_SAMPLE_RATE=1.0 node --import=@sentry/node/init app.mjs\n */\ninit();\n"],"names":["init"],"mappings":";;AAQAA,UAAA,EAAK;;"} |
@@ -10,36 +10,20 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const { isPromise } = util.types; | ||
| // This string is a placeholder that gets overwritten with the worker code. | ||
| const base64WorkerScript = 'LyohIEBzZW50cnkvbm9kZS1jb3JlIDEwLjUzLjEgKGNkOTc0MDgpIHwgaHR0cHM6Ly9naXRodWIuY29tL2dldHNlbnRyeS9zZW50cnktamF2YXNjcmlwdCAqLwppbXBvcnR7U2Vzc2lvbiBhcyB0fWZyb20ibm9kZTppbnNwZWN0b3IiO2ltcG9ydHt3b3JrZXJEYXRhIGFzIG4scGFyZW50UG9ydCBhcyBlfWZyb20ibm9kZTp3b3JrZXJfdGhyZWFkcyI7aW1wb3J0e3Bvc2l4IGFzIHIsc2VwIGFzIG99ZnJvbSJub2RlOnBhdGgiO2ltcG9ydCphcyBpIGZyb20ibm9kZTpodHRwIjtpbXBvcnQqYXMgcyBmcm9tIm5vZGU6aHR0cHMiO2ltcG9ydHtSZWFkYWJsZSBhcyBjfWZyb20ibm9kZTpzdHJlYW0iO2ltcG9ydHtjcmVhdGVHemlwIGFzIHV9ZnJvbSJub2RlOnpsaWIiO2ltcG9ydCphcyBhIGZyb20ibm9kZTpuZXQiO2ltcG9ydCphcyBmIGZyb20ibm9kZTp0bHMiO2NvbnN0IGg9InVuZGVmaW5lZCI9PXR5cGVvZiBfX1NFTlRSWV9ERUJVR19ffHxfX1NFTlRSWV9ERUJVR19fLHA9Z2xvYmFsVGhpcyxsPSIxMC41My4xIjtmdW5jdGlvbiBkKCl7cmV0dXJuIG0ocCkscH1mdW5jdGlvbiBtKHQpe2NvbnN0IG49dC5fX1NFTlRSWV9fPXQuX19TRU5UUllfX3x8e307cmV0dXJuIG4udmVyc2lvbj1uLnZlcnNpb258fGwsbltsXT1uW2xdfHx7fX1mdW5jdGlvbiBnKHQsbixlPXApe2NvbnN0IHI9ZS5fX1NFTlRSWV9fPWUuX19TRU5UUllfX3x8e30sbz1yW2xdPXJbbF18fHt9O3JldHVybiBvW3RdfHwob1t0XT1uKCkpfWNvbnN0IHk9e307ZnVuY3Rpb24gYih0KXtpZighKCJjb25zb2xlImluIHApKXJldHVybiB0KCk7Y29uc3Qgbj1wLmNvbnNvbGUsZT17fSxyPU9iamVjdC5rZXlzKHkpO3IuZm9yRWFjaCh0PT57Y29uc3Qgcj15W3RdO2VbdF09blt0XSxuW3RdPXJ9KTt0cnl7cmV0dXJuIHQoKX1maW5hbGx5e3IuZm9yRWFjaCh0PT57blt0XT1lW3RdfSl9fWZ1bmN0aW9uIHYoKXtyZXR1cm4gdygpLmVuYWJsZWR9ZnVuY3Rpb24gXyh0LC4uLm4pe2gmJnYoKSYmYigoKT0+e3AuY29uc29sZVt0XShgU2VudHJ5IExvZ2dlciBbJHt0fV06YCwuLi5uKX0pfWZ1bmN0aW9uIHcoKXtyZXR1cm4gaD9nKCJsb2dnZXJTZXR0aW5ncyIsKCk9Pih7ZW5hYmxlZDohMX0pKTp7ZW5hYmxlZDohMX19Y29uc3QgUz17ZW5hYmxlOmZ1bmN0aW9uKCl7dygpLmVuYWJsZWQ9ITB9LGRpc2FibGU6ZnVuY3Rpb24oKXt3KCkuZW5hYmxlZD0hMX0saXNFbmFibGVkOnYsbG9nOmZ1bmN0aW9uKC4uLnQpe18oImxvZyIsLi4udCl9LHdhcm46ZnVuY3Rpb24oLi4udCl7Xygid2FybiIsLi4udCl9LGVycm9yOmZ1bmN0aW9uKC4uLnQpe18oImVycm9yIiwuLi50KX19LCQ9L2NhcHR1cmVNZXNzYWdlfGNhcHR1cmVFeGNlcHRpb24vO2Z1bmN0aW9uIEUodCl7cmV0dXJuIHRbdC5sZW5ndGgtMV18fHt9fWNvbnN0IHg9Ijxhbm9ueW1vdXM+Ijtjb25zdCBOPU9iamVjdC5wcm90b3R5cGUudG9TdHJpbmc7ZnVuY3Rpb24gaih0LG4pe3JldHVybiBOLmNhbGwodCk9PT1gW29iamVjdCAke259XWB9ZnVuY3Rpb24gQyh0KXtyZXR1cm4gaih0LCJTdHJpbmciKX1mdW5jdGlvbiBBKHQpe3JldHVybiBqKHQsIk9iamVjdCIpfWZ1bmN0aW9uIGsodCl7cmV0dXJuIEJvb2xlYW4odD8udGhlbiYmImZ1bmN0aW9uIj09dHlwZW9mIHQudGhlbil9ZnVuY3Rpb24gVCh0LG4pe3RyeXtyZXR1cm4gdCBpbnN0YW5jZW9mIG59Y2F0Y2h7cmV0dXJuITF9fWNvbnN0IEk9cDtmdW5jdGlvbiBPKHQsbil7Y29uc3QgZT10LHI9W107aWYoIWU/LnRhZ05hbWUpcmV0dXJuIiI7aWYoSS5IVE1MRWxlbWVudCYmZSBpbnN0YW5jZW9mIEhUTUxFbGVtZW50JiZlLmRhdGFzZXQpe2lmKGUuZGF0YXNldC5zZW50cnlDb21wb25lbnQpcmV0dXJuIGUuZGF0YXNldC5zZW50cnlDb21wb25lbnQ7aWYoZS5kYXRhc2V0LnNlbnRyeUVsZW1lbnQpcmV0dXJuIGUuZGF0YXNldC5zZW50cnlFbGVtZW50fXIucHVzaChlLnRhZ05hbWUudG9Mb3dlckNhc2UoKSk7Y29uc3Qgbz1uPy5sZW5ndGg/bi5maWx0ZXIodD0+ZS5nZXRBdHRyaWJ1dGUodCkpLm1hcCh0PT5bdCxlLmdldEF0dHJpYnV0ZSh0KV0pOm51bGw7aWYobz8ubGVuZ3RoKW8uZm9yRWFjaCh0PT57ci5wdXNoKGBbJHt0WzBdfT0iJHt0WzFdfSJdYCl9KTtlbHNle2UuaWQmJnIucHVzaChgIyR7ZS5pZH1gKTtjb25zdCB0PWUuY2xhc3NOYW1lO2lmKHQmJkModCkpe2NvbnN0IG49dC5zcGxpdCgvXHMrLyk7Zm9yKGNvbnN0IHQgb2YgbilyLnB1c2goYC4ke3R9YCl9fWZvcihjb25zdCB0IG9mWyJhcmlhLWxhYmVsIiwidHlwZSIsIm5hbWUiLCJ0aXRsZSIsImFsdCJdKXtjb25zdCBuPWUuZ2V0QXR0cmlidXRlKHQpO24mJnIucHVzaChgWyR7dH09IiR7bn0iXWApfXJldHVybiByLmpvaW4oIiIpfWZ1bmN0aW9uIFIodCl7aWYoZnVuY3Rpb24odCl7c3dpdGNoKE4uY2FsbCh0KSl7Y2FzZSJbb2JqZWN0IEVycm9yXSI6Y2FzZSJbb2JqZWN0IEV4Y2VwdGlvbl0iOmNhc2UiW29iamVjdCBET01FeGNlcHRpb25dIjpjYXNlIltvYmplY3QgV2ViQXNzZW1ibHkuRXhjZXB0aW9uXSI6cmV0dXJuITA7ZGVmYXVsdDpyZXR1cm4gVCh0LEVycm9yKX19KHQpKXJldHVybnttZXNzYWdlOnQubWVzc2FnZSxuYW1lOnQubmFtZSxzdGFjazp0LnN0YWNrLC4uLkQodCl9O2lmKG49dCwidW5kZWZpbmVkIiE9dHlwZW9mIEV2ZW50JiZUKG4sRXZlbnQpKXtjb25zdCBuPXt0eXBlOnQudHlwZSx0YXJnZXQ6UCh0LnRhcmdldCksY3VycmVudFRhcmdldDpQKHQuY3VycmVudFRhcmdldCksLi4uRCh0KX07cmV0dXJuInVuZGVmaW5lZCIhPXR5cGVvZiBDdXN0b21FdmVudCYmVCh0LEN1c3RvbUV2ZW50KSYmKG4uZGV0YWlsPXQuZGV0YWlsKSxufXJldHVybiB0O3ZhciBufWZ1bmN0aW9uIFAodCl7dHJ5e3JldHVybiBuPXQsInVuZGVmaW5lZCIhPXR5cGVvZiBFbGVtZW50JiZUKG4sRWxlbWVudCk/ZnVuY3Rpb24odCxuPXt9KXtpZighdClyZXR1cm4iPHVua25vd24+Ijt0cnl7bGV0IGU9dDtjb25zdCByPTUsbz1bXTtsZXQgaT0wLHM9MDtjb25zdCBjPSIgPiAiLHU9Yy5sZW5ndGg7bGV0IGE7Y29uc3QgZj1BcnJheS5pc0FycmF5KG4pP246bi5rZXlBdHRycyxoPSFBcnJheS5pc0FycmF5KG4pJiZuLm1heFN0cmluZ0xlbmd0aHx8ODA7Zm9yKDtlJiZpKys8ciYmKGE9TyhlLGYpLCEoImh0bWwiPT09YXx8aT4xJiZzK28ubGVuZ3RoKnUrYS5sZW5ndGg+PWgpKTspby5wdXNoKGEpLHMrPWEubGVuZ3RoLGU9ZS5wYXJlbnROb2RlO3JldHVybiBvLnJldmVyc2UoKS5qb2luKGMpfWNhdGNoe3JldHVybiI8dW5rbm93bj4ifX0odCk6T2JqZWN0LnByb3RvdHlwZS50b1N0cmluZy5jYWxsKHQpfWNhdGNoe3JldHVybiI8dW5rbm93bj4ifXZhciBufWZ1bmN0aW9uIEQodCl7cmV0dXJuIm9iamVjdCI9PXR5cGVvZiB0JiZudWxsIT09dD9PYmplY3QuZnJvbUVudHJpZXMoT2JqZWN0LmVudHJpZXModCkpOnt9fWxldCBVLEw7ZnVuY3Rpb24gTSh0KXtpZih2b2lkIDAhPT1VKXJldHVybiBVP1UodCk6dCgpO2NvbnN0IG49U3ltYm9sLmZvcigiX19TRU5UUllfU0FGRV9SQU5ET01fSURfV1JBUFBFUl9fIiksZT1wO3JldHVybiBuIGluIGUmJiJmdW5jdGlvbiI9PXR5cGVvZiBlW25dPyhVPWVbbl0sVSh0KSk6KFU9bnVsbCx0KCkpfWZ1bmN0aW9uIEIoKXtyZXR1cm4gTSgoKT0+TWF0aC5yYW5kb20oKSl9ZnVuY3Rpb24geigpe3JldHVybiBNKCgpPT5EYXRlLm5vdygpKX1mdW5jdGlvbiBXKHQsbj0wKXtyZXR1cm4ic3RyaW5nIiE9dHlwZW9mIHR8fDA9PT1ufHx0Lmxlbmd0aDw9bj90OmAke3Quc2xpY2UoMCxuKX0uLi5gfWZ1bmN0aW9uIEYodD1mdW5jdGlvbigpe2NvbnN0IHQ9cDtyZXR1cm4gdC5jcnlwdG98fHQubXNDcnlwdG99KCkpe3RyeXtpZih0Py5yYW5kb21VVUlEKXJldHVybiBNKCgpPT50LnJhbmRvbVVVSUQoKSkucmVwbGFjZSgvLS9nLCIiKX1jYXRjaHt9cmV0dXJuIEx8fChMPVsxZTddKzFlMys0ZTMrOGUzKzFlMTEpLEwucmVwbGFjZSgvWzAxOF0vZyx0PT4odF4oMTYqQigpJjE1KT4+dC80KS50b1N0cmluZygxNikpfWZ1bmN0aW9uIEcoKXtyZXR1cm4geigpLzFlM31sZXQgSDtmdW5jdGlvbiBKKCl7cmV0dXJuKEg/PyhIPWZ1bmN0aW9uKCl7Y29uc3R7cGVyZm9ybWFuY2U6dH09cDtpZighdD8ubm93fHwhdC50aW1lT3JpZ2luKXJldHVybiBHO2NvbnN0IG49dC50aW1lT3JpZ2luO3JldHVybigpPT4obitNKCgpPT50Lm5vdygpKSkvMWUzfSgpKSkoKX1mdW5jdGlvbiBZKHQpe2NvbnN0IG49SigpLGU9e3NpZDpGKCksaW5pdDohMCx0aW1lc3RhbXA6bixzdGFydGVkOm4sZHVyYXRpb246MCxzdGF0dXM6Im9rIixlcnJvcnM6MCxpZ25vcmVEdXJhdGlvbjohMSx0b0pTT046KCk9PmZ1bmN0aW9uKHQpe3JldHVybntzaWQ6YCR7dC5zaWR9YCxpbml0OnQuaW5pdCxzdGFydGVkOm5ldyBEYXRlKDFlMyp0LnN0YXJ0ZWQpLnRvSVNPU3RyaW5nKCksdGltZXN0YW1wOm5ldyBEYXRlKDFlMyp0LnRpbWVzdGFtcCkudG9JU09TdHJpbmcoKSxzdGF0dXM6dC5zdGF0dXMsZXJyb3JzOnQuZXJyb3JzLGRpZDoibnVtYmVyIj09dHlwZW9mIHQuZGlkfHwic3RyaW5nIj09dHlwZW9mIHQuZGlkP2Ake3QuZGlkfWA6dm9pZCAwLGR1cmF0aW9uOnQuZHVyYXRpb24sYWJub3JtYWxfbWVjaGFuaXNtOnQuYWJub3JtYWxfbWVjaGFuaXNtLGF0dHJzOntyZWxlYXNlOnQucmVsZWFzZSxlbnZpcm9ubWVudDp0LmVudmlyb25tZW50LGlwX2FkZHJlc3M6dC5pcEFkZHJlc3MsdXNlcl9hZ2VudDp0LnVzZXJBZ2VudH19fShlKX07cmV0dXJuIHQmJlYoZSx0KSxlfWZ1bmN0aW9uIFYodCxuPXt9KXtpZihuLnVzZXImJighdC5pcEFkZHJlc3MmJm4udXNlci5pcF9hZGRyZXNzJiYodC5pcEFkZHJlc3M9bi51c2VyLmlwX2FkZHJlc3MpLHQuZGlkfHxuLmRpZHx8KHQuZGlkPW4udXNlci5pZHx8bi51c2VyLmVtYWlsfHxuLnVzZXIudXNlcm5hbWUpKSx0LnRpbWVzdGFtcD1uLnRpbWVzdGFtcHx8SigpLG4uYWJub3JtYWxfbWVjaGFuaXNtJiYodC5hYm5vcm1hbF9tZWNoYW5pc209bi5hYm5vcm1hbF9tZWNoYW5pc20pLG4uaWdub3JlRHVyYXRpb24mJih0Lmlnbm9yZUR1cmF0aW9uPW4uaWdub3JlRHVyYXRpb24pLG4uc2lkJiYodC5zaWQ9MzI9PT1uLnNpZC5sZW5ndGg/bi5zaWQ6RigpKSx2b2lkIDAhPT1uLmluaXQmJih0LmluaXQ9bi5pbml0KSwhdC5kaWQmJm4uZGlkJiYodC5kaWQ9YCR7bi5kaWR9YCksIm51bWJlciI9PXR5cGVvZiBuLnN0YXJ0ZWQmJih0LnN0YXJ0ZWQ9bi5zdGFydGVkKSx0Lmlnbm9yZUR1cmF0aW9uKXQuZHVyYXRpb249dm9pZCAwO2Vsc2UgaWYoIm51bWJlciI9PXR5cGVvZiBuLmR1cmF0aW9uKXQuZHVyYXRpb249bi5kdXJhdGlvbjtlbHNle2NvbnN0IG49dC50aW1lc3RhbXAtdC5zdGFydGVkO3QuZHVyYXRpb249bj49MD9uOjB9bi5yZWxlYXNlJiYodC5yZWxlYXNlPW4ucmVsZWFzZSksbi5lbnZpcm9ubWVudCYmKHQuZW52aXJvbm1lbnQ9bi5lbnZpcm9ubWVudCksIXQuaXBBZGRyZXNzJiZuLmlwQWRkcmVzcyYmKHQuaXBBZGRyZXNzPW4uaXBBZGRyZXNzKSwhdC51c2VyQWdlbnQmJm4udXNlckFnZW50JiYodC51c2VyQWdlbnQ9bi51c2VyQWdlbnQpLCJudW1iZXIiPT10eXBlb2Ygbi5lcnJvcnMmJih0LmVycm9ycz1uLmVycm9ycyksbi5zdGF0dXMmJih0LnN0YXR1cz1uLnN0YXR1cyl9ZnVuY3Rpb24gSyh0LG4sZT0yKXtpZighbnx8Im9iamVjdCIhPXR5cGVvZiBufHxlPD0wKXJldHVybiBuO2lmKHQmJjA9PT1PYmplY3Qua2V5cyhuKS5sZW5ndGgpcmV0dXJuIHQ7Y29uc3Qgcj17Li4udH07Zm9yKGNvbnN0IHQgaW4gbilPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwobix0KSYmKHJbdF09SyhyW3RdLG5bdF0sZS0xKSk7cmV0dXJuIHJ9ZnVuY3Rpb24gWigpe3JldHVybiBGKCl9ZnVuY3Rpb24gcSgpe3JldHVybiBGKCkuc3Vic3RyaW5nKDE2KX1jb25zdCBRPSJfc2VudHJ5U3BhbiI7ZnVuY3Rpb24gWCh0LG4pe24/ZnVuY3Rpb24odCxuLGUpe3RyeXtPYmplY3QuZGVmaW5lUHJvcGVydHkodCxuLHt2YWx1ZTplLHdyaXRhYmxlOiEwLGNvbmZpZ3VyYWJsZTohMH0pfWNhdGNoe2gmJlMubG9nKGBGYWlsZWQgdG8gYWRkIG5vbi1lbnVtZXJhYmxlIHByb3BlcnR5ICIke1N0cmluZyhuKX0iIHRvIG9iamVjdGAsdCl9fSh0LFEsbik6ZGVsZXRlIHRbUV19ZnVuY3Rpb24gdHQodCl7cmV0dXJuIHRbUV19Y2xhc3MgbnR7Y29uc3RydWN0b3IoKXt0aGlzLnQ9ITEsdGhpcy5vPVtdLHRoaXMuaT1bXSx0aGlzLnU9W10sdGhpcy5oPVtdLHRoaXMucD17fSx0aGlzLmw9e30sdGhpcy5tPXt9LHRoaXMudj17fSx0aGlzLl89e30sdGhpcy5TPXt9LHRoaXMuTj17dHJhY2VJZDpaKCksc2FtcGxlUmFuZDpCKCl9fWNsb25lKCl7Y29uc3QgdD1uZXcgbnQ7cmV0dXJuIHQudT1bLi4udGhpcy51XSx0Lmw9ey4uLnRoaXMubH0sdC5tPXsuLi50aGlzLm19LHQudj17Li4udGhpcy52fSx0Ll89ey4uLnRoaXMuX30sdGhpcy5fLmZsYWdzJiYodC5fLmZsYWdzPXt2YWx1ZXM6Wy4uLnRoaXMuXy5mbGFncy52YWx1ZXNdfSksdC5wPXRoaXMucCx0Lmo9dGhpcy5qLHQuQz10aGlzLkMsdC5BPXRoaXMuQSx0Lms9dGhpcy5rLHQuaT1bLi4udGhpcy5pXSx0Lmg9Wy4uLnRoaXMuaF0sdC5TPXsuLi50aGlzLlN9LHQuTj17Li4udGhpcy5OfSx0LlQ9dGhpcy5ULHQuST10aGlzLkksdC5PPXRoaXMuTyxYKHQsdHQodGhpcykpLHR9c2V0Q2xpZW50KHQpe3RoaXMuVD10fXNldExhc3RFdmVudElkKHQpe3RoaXMuST10fWdldENsaWVudCgpe3JldHVybiB0aGlzLlR9bGFzdEV2ZW50SWQoKXtyZXR1cm4gdGhpcy5JfWFkZFNjb3BlTGlzdGVuZXIodCl7dGhpcy5vLnB1c2godCl9YWRkRXZlbnRQcm9jZXNzb3IodCl7cmV0dXJuIHRoaXMuaS5wdXNoKHQpLHRoaXN9c2V0VXNlcih0KXtyZXR1cm4gdGhpcy5wPXR8fHtlbWFpbDp2b2lkIDAsaWQ6dm9pZCAwLGlwX2FkZHJlc3M6dm9pZCAwLHVzZXJuYW1lOnZvaWQgMH0sdGhpcy5DJiZWKHRoaXMuQyx7dXNlcjp0fSksdGhpcy5SKCksdGhpc31nZXRVc2VyKCl7cmV0dXJuIHRoaXMucH1zZXRDb252ZXJzYXRpb25JZCh0KXtyZXR1cm4gdGhpcy5PPXR8fHZvaWQgMCx0aGlzLlIoKSx0aGlzfXNldFRhZ3ModCl7cmV0dXJuIHRoaXMubD17Li4udGhpcy5sLC4uLnR9LHRoaXMuUigpLHRoaXN9c2V0VGFnKHQsbil7cmV0dXJuIHRoaXMuc2V0VGFncyh7W3RdOm59KX1zZXRBdHRyaWJ1dGVzKHQpe3JldHVybiB0aGlzLm09ey4uLnRoaXMubSwuLi50fSx0aGlzLlIoKSx0aGlzfXNldEF0dHJpYnV0ZSh0LG4pe3JldHVybiB0aGlzLnNldEF0dHJpYnV0ZXMoe1t0XTpufSl9cmVtb3ZlQXR0cmlidXRlKHQpe3JldHVybiB0IGluIHRoaXMubSYmKGRlbGV0ZSB0aGlzLm1bdF0sdGhpcy5SKCkpLHRoaXN9c2V0RXh0cmFzKHQpe3JldHVybiB0aGlzLnY9ey4uLnRoaXMudiwuLi50fSx0aGlzLlIoKSx0aGlzfXNldEV4dHJhKHQsbil7cmV0dXJuIHRoaXMudj17Li4udGhpcy52LFt0XTpufSx0aGlzLlIoKSx0aGlzfXNldEZpbmdlcnByaW50KHQpe3JldHVybiB0aGlzLms9dCx0aGlzLlIoKSx0aGlzfXNldExldmVsKHQpe3JldHVybiB0aGlzLmo9dCx0aGlzLlIoKSx0aGlzfXNldFRyYW5zYWN0aW9uTmFtZSh0KXtyZXR1cm4gdGhpcy5BPXQsdGhpcy5SKCksdGhpc31zZXRDb250ZXh0KHQsbil7cmV0dXJuIG51bGw9PT1uP2RlbGV0ZSB0aGlzLl9bdF06dGhpcy5fW3RdPW4sdGhpcy5SKCksdGhpc31zZXRTZXNzaW9uKHQpe3JldHVybiB0P3RoaXMuQz10OmRlbGV0ZSB0aGlzLkMsdGhpcy5SKCksdGhpc31nZXRTZXNzaW9uKCl7cmV0dXJuIHRoaXMuQ311cGRhdGUodCl7aWYoIXQpcmV0dXJuIHRoaXM7Y29uc3Qgbj0iZnVuY3Rpb24iPT10eXBlb2YgdD90KHRoaXMpOnQsZT1uIGluc3RhbmNlb2YgbnQ/bi5nZXRTY29wZURhdGEoKTpBKG4pP3Q6dm9pZCAwLHt0YWdzOnIsYXR0cmlidXRlczpvLGV4dHJhOmksdXNlcjpzLGNvbnRleHRzOmMsbGV2ZWw6dSxmaW5nZXJwcmludDphPVtdLHByb3BhZ2F0aW9uQ29udGV4dDpmLGNvbnZlcnNhdGlvbklkOmh9PWV8fHt9O3JldHVybiB0aGlzLmw9ey4uLnRoaXMubCwuLi5yfSx0aGlzLm09ey4uLnRoaXMubSwuLi5vfSx0aGlzLnY9ey4uLnRoaXMudiwuLi5pfSx0aGlzLl89ey4uLnRoaXMuXywuLi5jfSxzJiZPYmplY3Qua2V5cyhzKS5sZW5ndGgmJih0aGlzLnA9cyksdSYmKHRoaXMuaj11KSxhLmxlbmd0aCYmKHRoaXMuaz1hKSxmJiYodGhpcy5OPWYpLGgmJih0aGlzLk89aCksdGhpc31jbGVhcigpe3JldHVybiB0aGlzLnU9W10sdGhpcy5sPXt9LHRoaXMubT17fSx0aGlzLnY9e30sdGhpcy5wPXt9LHRoaXMuXz17fSx0aGlzLmo9dm9pZCAwLHRoaXMuQT12b2lkIDAsdGhpcy5rPXZvaWQgMCx0aGlzLkM9dm9pZCAwLHRoaXMuTz12b2lkIDAsWCh0aGlzLHZvaWQgMCksdGhpcy5oPVtdLHRoaXMuc2V0UHJvcGFnYXRpb25Db250ZXh0KHt0cmFjZUlkOlooKSxzYW1wbGVSYW5kOkIoKX0pLHRoaXMuUigpLHRoaXN9YWRkQnJlYWRjcnVtYih0LG4pe2NvbnN0IGU9Im51bWJlciI9PXR5cGVvZiBuP246MTAwO2lmKGU8PTApcmV0dXJuIHRoaXM7Y29uc3Qgcj17dGltZXN0YW1wOkcoKSwuLi50LG1lc3NhZ2U6dC5tZXNzYWdlP1codC5tZXNzYWdlLDIwNDgpOnQubWVzc2FnZX07cmV0dXJuIHRoaXMudS5wdXNoKHIpLHRoaXMudS5sZW5ndGg+ZSYmKHRoaXMudT10aGlzLnUuc2xpY2UoLWUpLHRoaXMuVD8ucmVjb3JkRHJvcHBlZEV2ZW50KCJidWZmZXJfb3ZlcmZsb3ciLCJsb2dfaXRlbSIpKSx0aGlzLlIoKSx0aGlzfWdldExhc3RCcmVhZGNydW1iKCl7cmV0dXJuIHRoaXMudVt0aGlzLnUubGVuZ3RoLTFdfWNsZWFyQnJlYWRjcnVtYnMoKXtyZXR1cm4gdGhpcy51PVtdLHRoaXMuUigpLHRoaXN9YWRkQXR0YWNobWVudCh0KXtyZXR1cm4gdGhpcy5oLnB1c2godCksdGhpc31jbGVhckF0dGFjaG1lbnRzKCl7cmV0dXJuIHRoaXMuaD1bXSx0aGlzfWdldFNjb3BlRGF0YSgpe3JldHVybnticmVhZGNydW1iczp0aGlzLnUsYXR0YWNobWVudHM6dGhpcy5oLGNvbnRleHRzOnRoaXMuXyx0YWdzOnRoaXMubCxhdHRyaWJ1dGVzOnRoaXMubSxleHRyYTp0aGlzLnYsdXNlcjp0aGlzLnAsbGV2ZWw6dGhpcy5qLGZpbmdlcnByaW50OnRoaXMua3x8W10sZXZlbnRQcm9jZXNzb3JzOnRoaXMuaSxwcm9wYWdhdGlvbkNvbnRleHQ6dGhpcy5OLHNka1Byb2Nlc3NpbmdNZXRhZGF0YTp0aGlzLlMsdHJhbnNhY3Rpb25OYW1lOnRoaXMuQSxzcGFuOnR0KHRoaXMpLGNvbnZlcnNhdGlvbklkOnRoaXMuT319c2V0U0RLUHJvY2Vzc2luZ01ldGFkYXRhKHQpe3JldHVybiB0aGlzLlM9Syh0aGlzLlMsdCwyKSx0aGlzfXNldFByb3BhZ2F0aW9uQ29udGV4dCh0KXtyZXR1cm4gdGhpcy5OPXQsdGhpc31nZXRQcm9wYWdhdGlvbkNvbnRleHQoKXtyZXR1cm4gdGhpcy5OfWNhcHR1cmVFeGNlcHRpb24odCxuKXtjb25zdCBlPW4/LmV2ZW50X2lkfHxGKCk7aWYoIXRoaXMuVClyZXR1cm4gaCYmUy53YXJuKCJObyBjbGllbnQgY29uZmlndXJlZCBvbiBzY29wZSAtIHdpbGwgbm90IGNhcHR1cmUgZXhjZXB0aW9uISIpLGU7Y29uc3Qgcj1uZXcgRXJyb3IoIlNlbnRyeSBzeW50aGV0aWNFeGNlcHRpb24iKTtyZXR1cm4gdGhpcy5ULmNhcHR1cmVFeGNlcHRpb24odCx7b3JpZ2luYWxFeGNlcHRpb246dCxzeW50aGV0aWNFeGNlcHRpb246ciwuLi5uLGV2ZW50X2lkOmV9LHRoaXMpLGV9Y2FwdHVyZU1lc3NhZ2UodCxuLGUpe2NvbnN0IHI9ZT8uZXZlbnRfaWR8fEYoKTtpZighdGhpcy5UKXJldHVybiBoJiZTLndhcm4oIk5vIGNsaWVudCBjb25maWd1cmVkIG9uIHNjb3BlIC0gd2lsbCBub3QgY2FwdHVyZSBtZXNzYWdlISIpLHI7Y29uc3Qgbz1lPy5zeW50aGV0aWNFeGNlcHRpb24/P25ldyBFcnJvcih0KTtyZXR1cm4gdGhpcy5ULmNhcHR1cmVNZXNzYWdlKHQsbix7b3JpZ2luYWxFeGNlcHRpb246dCxzeW50aGV0aWNFeGNlcHRpb246bywuLi5lLGV2ZW50X2lkOnJ9LHRoaXMpLHJ9Y2FwdHVyZUV2ZW50KHQsbil7Y29uc3QgZT10LmV2ZW50X2lkfHxuPy5ldmVudF9pZHx8RigpO3JldHVybiB0aGlzLlQ/KHRoaXMuVC5jYXB0dXJlRXZlbnQodCx7Li4ubixldmVudF9pZDplfSx0aGlzKSxlKTooaCYmUy53YXJuKCJObyBjbGllbnQgY29uZmlndXJlZCBvbiBzY29wZSAtIHdpbGwgbm90IGNhcHR1cmUgZXZlbnQhIiksZSl9Uigpe3RoaXMudHx8KHRoaXMudD0hMCx0aGlzLm8uZm9yRWFjaCh0PT57dCh0aGlzKX0pLHRoaXMudD0hMSl9fWNvbnN0IGV0PXQ9PnQgaW5zdGFuY2VvZiBQcm9taXNlJiYhdFtydF0scnQ9U3ltYm9sKCJjaGFpbmVkIFByb21pc2VMaWtlIiksb3Q9KHQsbik9PntpZighbilyZXR1cm4gdDtsZXQgZT0hMTtmb3IoY29uc3QgciBpbiB0KXtpZihyIGluIG4pY29udGludWU7ZT0hMDtjb25zdCBvPXRbcl07ImZ1bmN0aW9uIj09dHlwZW9mIG8/T2JqZWN0LmRlZmluZVByb3BlcnR5KG4scix7dmFsdWU6KC4uLm4pPT5vLmFwcGx5KHQsbiksZW51bWVyYWJsZTohMCxjb25maWd1cmFibGU6ITAsd3JpdGFibGU6ITB9KTpuW3JdPW99cmV0dXJuIGUmJk9iamVjdC5hc3NpZ24obix7W3J0XTohMH0pLG59O2NsYXNzIGl0e2NvbnN0cnVjdG9yKHQsbil7bGV0IGUscjtlPXR8fG5ldyBudCxyPW58fG5ldyBudCx0aGlzLlA9W3tzY29wZTplfV0sdGhpcy5EPXJ9d2l0aFNjb3BlKHQpe2NvbnN0IG49dGhpcy5VKCk7bGV0IGU7dHJ5e2U9dChuKX1jYXRjaCh0KXt0aHJvdyB0aGlzLkwoKSx0fXJldHVybiBrKGUpPygodCxuLGUpPT57Y29uc3Qgcj10LnRoZW4odD0+KG4odCksdCksdD0+e3Rocm93IGUodCksdH0pO3JldHVybiBldChyKSYmZXQodCk/cjpvdCh0LHIpfSkoZSwoKT0+dGhpcy5MKCksKCk9PnRoaXMuTCgpKToodGhpcy5MKCksZSl9Z2V0Q2xpZW50KCl7cmV0dXJuIHRoaXMuZ2V0U3RhY2tUb3AoKS5jbGllbnR9Z2V0U2NvcGUoKXtyZXR1cm4gdGhpcy5nZXRTdGFja1RvcCgpLnNjb3BlfWdldElzb2xhdGlvblNjb3BlKCl7cmV0dXJuIHRoaXMuRH1nZXRTdGFja1RvcCgpe3JldHVybiB0aGlzLlBbdGhpcy5QLmxlbmd0aC0xXX1VKCl7Y29uc3QgdD10aGlzLmdldFNjb3BlKCkuY2xvbmUoKTtyZXR1cm4gdGhpcy5QLnB1c2goe2NsaWVudDp0aGlzLmdldENsaWVudCgpLHNjb3BlOnR9KSx0fUwoKXtyZXR1cm4hKHRoaXMuUC5sZW5ndGg8PTEpJiYhIXRoaXMuUC5wb3AoKX19ZnVuY3Rpb24gc3QoKXtjb25zdCB0PW0oZCgpKTtyZXR1cm4gdC5zdGFjaz10LnN0YWNrfHxuZXcgaXQoZygiZGVmYXVsdEN1cnJlbnRTY29wZSIsKCk9Pm5ldyBudCksZygiZGVmYXVsdElzb2xhdGlvblNjb3BlIiwoKT0+bmV3IG50KSl9ZnVuY3Rpb24gY3QodCl7cmV0dXJuIHN0KCkud2l0aFNjb3BlKHQpfWZ1bmN0aW9uIHV0KHQsbil7Y29uc3QgZT1zdCgpO3JldHVybiBlLndpdGhTY29wZSgoKT0+KGUuZ2V0U3RhY2tUb3AoKS5zY29wZT10LG4odCkpKX1mdW5jdGlvbiBhdCh0KXtyZXR1cm4gc3QoKS53aXRoU2NvcGUoKCk9PnQoc3QoKS5nZXRJc29sYXRpb25TY29wZSgpKSl9ZnVuY3Rpb24gZnQodCl7Y29uc3Qgbj1tKHQpO3JldHVybiBuLmFjcz9uLmFjczp7d2l0aElzb2xhdGlvblNjb3BlOmF0LHdpdGhTY29wZTpjdCx3aXRoU2V0U2NvcGU6dXQsd2l0aFNldElzb2xhdGlvblNjb3BlOih0LG4pPT5hdChuKSxnZXRDdXJyZW50U2NvcGU6KCk9PnN0KCkuZ2V0U2NvcGUoKSxnZXRJc29sYXRpb25TY29wZTooKT0+c3QoKS5nZXRJc29sYXRpb25TY29wZSgpfX1mdW5jdGlvbiBodCgpe3JldHVybiBmdChkKCkpLmdldEN1cnJlbnRTY29wZSgpLmdldENsaWVudCgpfWZ1bmN0aW9uIHB0KHQpe2lmKHQpe2lmKCJvYmplY3QiPT10eXBlb2YgdCYmImRlcmVmImluIHQmJiJmdW5jdGlvbiI9PXR5cGVvZiB0LmRlcmVmKXRyeXtyZXR1cm4gdC5kZXJlZigpfWNhdGNoe3JldHVybn1yZXR1cm4gdH19ZnVuY3Rpb24gbHQodCl7Y29uc3Qgbj10O3JldHVybntzY29wZTpuLl9zZW50cnlTY29wZSxpc29sYXRpb25TY29wZTpwdChuLl9zZW50cnlJc29sYXRpb25TY29wZSl9fWNvbnN0IGR0PSJzZW50cnktIjtmdW5jdGlvbiBtdCh0KXtjb25zdCBuPWZ1bmN0aW9uKHQpe2lmKCF0fHwhQyh0KSYmIUFycmF5LmlzQXJyYXkodCkpcmV0dXJuO2lmKEFycmF5LmlzQXJyYXkodCkpcmV0dXJuIHQucmVkdWNlKCh0LG4pPT57Y29uc3QgZT1ndChuKTtyZXR1cm4gT2JqZWN0LmVudHJpZXMoZSkuZm9yRWFjaCgoW24sZV0pPT57dFtuXT1lfSksdH0se30pO3JldHVybiBndCh0KX0odCk7aWYoIW4pcmV0dXJuO2NvbnN0IGU9T2JqZWN0LmVudHJpZXMobikucmVkdWNlKCh0LFtuLGVdKT0+e2lmKG4uc3RhcnRzV2l0aChkdCkpe3Rbbi5zbGljZSg3KV09ZX1yZXR1cm4gdH0se30pO3JldHVybiBPYmplY3Qua2V5cyhlKS5sZW5ndGg+MD9lOnZvaWQgMH1mdW5jdGlvbiBndCh0KXtyZXR1cm4gdC5zcGxpdCgiLCIpLm1hcCh0PT57Y29uc3Qgbj10LmluZGV4T2YoIj0iKTtpZigtMT09PW4pcmV0dXJuW107cmV0dXJuW3Quc2xpY2UoMCxuKSx0LnNsaWNlKG4rMSldLm1hcCh0PT57dHJ5e3JldHVybiBkZWNvZGVVUklDb21wb25lbnQodC50cmltKCkpfWNhdGNoe3JldHVybn19KX0pLnJlZHVjZSgodCxbbixlXSk9PihuJiZlJiYodFtuXT1lKSx0KSx7fSl9Y29uc3QgeXQ9L15vKFxkKylcLi87ZnVuY3Rpb24gYnQodCxuPSExKXtjb25zdHtob3N0OmUscGF0aDpyLHBhc3M6byxwb3J0OmkscHJvamVjdElkOnMscHJvdG9jb2w6YyxwdWJsaWNLZXk6dX09dDtyZXR1cm5gJHtjfTovLyR7dX0ke24mJm8/YDoke299YDoiIn1AJHtlfSR7aT9gOiR7aX1gOiIifS8ke3I/YCR7cn0vYDpyfSR7c31gfWZ1bmN0aW9uIHZ0KHQpe2NvbnN0IG49dC5nZXRPcHRpb25zKCkse2hvc3Q6ZX09dC5nZXREc24oKXx8e307bGV0IHI7cmV0dXJuIG4ub3JnSWQ/cj1TdHJpbmcobi5vcmdJZCk6ZSYmKHI9ZnVuY3Rpb24odCl7Y29uc3Qgbj10Lm1hdGNoKHl0KTtyZXR1cm4gbj8uWzFdfShlKSkscn1mdW5jdGlvbiBfdCh0KXtjb25zdHtzcGFuSWQ6bix0cmFjZUlkOmUsaXNSZW1vdGU6cn09dC5zcGFuQ29udGV4dCgpLG89cj9uOkV0KHQpLnBhcmVudF9zcGFuX2lkLGk9bHQodCkuc2NvcGU7cmV0dXJue3BhcmVudF9zcGFuX2lkOm8sc3Bhbl9pZDpyP2k/LmdldFByb3BhZ2F0aW9uQ29udGV4dCgpLnByb3BhZ2F0aW9uU3BhbklkfHxxKCk6bix0cmFjZV9pZDplfX1mdW5jdGlvbiB3dCh0KXtyZXR1cm4gdCYmdC5sZW5ndGg+MD90Lm1hcCgoe2NvbnRleHQ6e3NwYW5JZDp0LHRyYWNlSWQ6bix0cmFjZUZsYWdzOmUsLi4ucn0sYXR0cmlidXRlczpvfSk9Pih7c3Bhbl9pZDp0LHRyYWNlX2lkOm4sc2FtcGxlZDoxPT09ZSxhdHRyaWJ1dGVzOm8sLi4ucn0pKTp2b2lkIDB9ZnVuY3Rpb24gU3QodCl7cmV0dXJuIm51bWJlciI9PXR5cGVvZiB0PyR0KHQpOkFycmF5LmlzQXJyYXkodCk/dFswXSt0WzFdLzFlOTp0IGluc3RhbmNlb2YgRGF0ZT8kdCh0LmdldFRpbWUoKSk6SigpfWZ1bmN0aW9uICR0KHQpe3JldHVybiB0Pjk5OTk5OTk5OTk/dC8xZTM6dH1mdW5jdGlvbiBFdCh0KXtpZihmdW5jdGlvbih0KXtyZXR1cm4iZnVuY3Rpb24iPT10eXBlb2YgdC5nZXRTcGFuSlNPTn0odCkpcmV0dXJuIHQuZ2V0U3BhbkpTT04oKTtjb25zdHtzcGFuSWQ6bix0cmFjZUlkOmV9PXQuc3BhbkNvbnRleHQoKTtpZihmdW5jdGlvbih0KXtjb25zdCBuPXQ7cmV0dXJuISEobi5hdHRyaWJ1dGVzJiZuLnN0YXJ0VGltZSYmbi5uYW1lJiZuLmVuZFRpbWUmJm4uc3RhdHVzKX0odCkpe2NvbnN0e2F0dHJpYnV0ZXM6cixzdGFydFRpbWU6byxuYW1lOmksZW5kVGltZTpzLHN0YXR1czpjLGxpbmtzOnV9PXQ7cmV0dXJue3NwYW5faWQ6bix0cmFjZV9pZDplLGRhdGE6cixkZXNjcmlwdGlvbjppLHBhcmVudF9zcGFuX2lkOnh0KHQpLHN0YXJ0X3RpbWVzdGFtcDpTdChvKSx0aW1lc3RhbXA6U3Qocyl8fHZvaWQgMCxzdGF0dXM6TnQoYyksb3A6clsic2VudHJ5Lm9wIl0sb3JpZ2luOnJbInNlbnRyeS5vcmlnaW4iXSxsaW5rczp3dCh1KX19cmV0dXJue3NwYW5faWQ6bix0cmFjZV9pZDplLHN0YXJ0X3RpbWVzdGFtcDowLGRhdGE6e319fWZ1bmN0aW9uIHh0KHQpe3JldHVybiJwYXJlbnRTcGFuSWQiaW4gdD90LnBhcmVudFNwYW5JZDoicGFyZW50U3BhbkNvbnRleHQiaW4gdD90LnBhcmVudFNwYW5Db250ZXh0Py5zcGFuSWQ6dm9pZCAwfWZ1bmN0aW9uIE50KHQpe2lmKHQmJjAhPT10LmNvZGUpcmV0dXJuIDE9PT10LmNvZGU/Im9rIjp0Lm1lc3NhZ2V8fCJpbnRlcm5hbF9lcnJvciJ9Y29uc3QganQ9ZnVuY3Rpb24odCl7cmV0dXJuIHQuX3NlbnRyeVJvb3RTcGFufHx0fTtmdW5jdGlvbiBDdCh0KXtjb25zdCBuPWh0KCk7aWYoIW4pcmV0dXJue307Y29uc3QgZT1qdCh0KSxyPUV0KGUpLG89ci5kYXRhLGk9ZS5zcGFuQ29udGV4dCgpLnRyYWNlU3RhdGUscz1pPy5nZXQoInNlbnRyeS5zYW1wbGVfcmF0ZSIpPz9vWyJzZW50cnkuc2FtcGxlX3JhdGUiXT8/b1sic2VudHJ5LnByZXZpb3VzX3RyYWNlX3NhbXBsZV9yYXRlIl07ZnVuY3Rpb24gYyh0KXtyZXR1cm4ibnVtYmVyIiE9dHlwZW9mIHMmJiJzdHJpbmciIT10eXBlb2Ygc3x8KHQuc2FtcGxlX3JhdGU9YCR7c31gKSx0fWNvbnN0IHU9ZS5fZnJvemVuRHNjO2lmKHUpcmV0dXJuIGModSk7Y29uc3QgYT1pPy5nZXQoInNlbnRyeS5kc2MiKSxmPWEmJm10KGEpO2lmKGYpcmV0dXJuIGMoZik7Y29uc3QgaD1mdW5jdGlvbih0LG4pe2NvbnN0IGU9bi5nZXRPcHRpb25zKCkse3B1YmxpY0tleTpyfT1uLmdldERzbigpfHx7fSxvPXtlbnZpcm9ubWVudDplLmVudmlyb25tZW50fHwicHJvZHVjdGlvbiIscmVsZWFzZTplLnJlbGVhc2UscHVibGljX2tleTpyLHRyYWNlX2lkOnQsb3JnX2lkOnZ0KG4pfTtyZXR1cm4gbi5lbWl0KCJjcmVhdGVEc2MiLG8pLG99KHQuc3BhbkNvbnRleHQoKS50cmFjZUlkLG4pLHA9b1sic2VudHJ5LnNvdXJjZSJdPz9vWyJzZW50cnkuc3Bhbi5zb3VyY2UiXSxsPXIuZGVzY3JpcHRpb247cmV0dXJuInVybCIhPT1wJiZsJiYoaC50cmFuc2FjdGlvbj1sKSxmdW5jdGlvbigpe2lmKCJib29sZWFuIj09dHlwZW9mIF9fU0VOVFJZX1RSQUNJTkdfXyYmIV9fU0VOVFJZX1RSQUNJTkdfXylyZXR1cm4hMTtjb25zdCB0PWh0KCk/LmdldE9wdGlvbnMoKTtyZXR1cm4hKCF0fHxudWxsPT10LnRyYWNlc1NhbXBsZVJhdGUmJiF0LnRyYWNlc1NhbXBsZXIpfSgpJiYoaC5zYW1wbGVkPVN0cmluZyhmdW5jdGlvbih0KXtjb25zdHt0cmFjZUZsYWdzOm59PXQuc3BhbkNvbnRleHQoKTtyZXR1cm4gMT09PW59KGUpKSxoLnNhbXBsZV9yYW5kPWk/LmdldCgic2VudHJ5LnNhbXBsZV9yYW5kIik/P2x0KGUpLnNjb3BlPy5nZXRQcm9wYWdhdGlvbkNvbnRleHQoKS5zYW1wbGVSYW5kLnRvU3RyaW5nKCkpLGMoaCksbi5lbWl0KCJjcmVhdGVEc2MiLGgsZSksaH1jb25zdCBBdD1TeW1ib2wuZm9yKCJzZW50cnkuc2tpcE5vcm1hbGl6YXRpb24iKSxrdD1TeW1ib2wuZm9yKCJzZW50cnkub3ZlcnJpZGVOb3JtYWxpemF0aW9uRGVwdGgiKTtmdW5jdGlvbiBUdCh0LG49MTAwLGU9MS8wKXt0cnl7cmV0dXJuIEl0KCIiLHQsbixlKX1jYXRjaCh0KXtyZXR1cm57RVJST1I6YCoqbm9uLXNlcmlhbGl6YWJsZSoqICgke3R9KWB9fX1mdW5jdGlvbiBJdCh0LG4sZT0xLzAscj0xLzAsbz1mdW5jdGlvbigpe2NvbnN0IHQ9bmV3IFdlYWtTZXQ7ZnVuY3Rpb24gbihuKXtyZXR1cm4hIXQuaGFzKG4pfHwodC5hZGQobiksITEpfWZ1bmN0aW9uIGUobil7dC5kZWxldGUobil9cmV0dXJuW24sZV19KCkpe2NvbnN0W2ksc109bztpZihudWxsPT1ufHxbImJvb2xlYW4iLCJzdHJpbmciXS5pbmNsdWRlcyh0eXBlb2Ygbil8fCJudW1iZXIiPT10eXBlb2YgbiYmTnVtYmVyLmlzRmluaXRlKG4pKXJldHVybiBuO2NvbnN0IGM9ZnVuY3Rpb24odCxuKXt0cnl7aWYoImRvbWFpbiI9PT10JiZuJiYib2JqZWN0Ij09dHlwZW9mIG4mJm4uTSlyZXR1cm4iW0RvbWFpbl0iO2lmKCJkb21haW5FbWl0dGVyIj09PXQpcmV0dXJuIltEb21haW5FbWl0dGVyXSI7aWYoInVuZGVmaW5lZCIhPXR5cGVvZiBnbG9iYWwmJm49PT1nbG9iYWwpcmV0dXJuIltHbG9iYWxdIjtpZigidW5kZWZpbmVkIiE9dHlwZW9mIHdpbmRvdyYmbj09PXdpbmRvdylyZXR1cm4iW1dpbmRvd10iO2lmKCJ1bmRlZmluZWQiIT10eXBlb2YgZG9jdW1lbnQmJm49PT1kb2N1bWVudClyZXR1cm4iW0RvY3VtZW50XSI7aWYoIm9iamVjdCI9PXR5cGVvZihlPW4pJiZudWxsIT09ZSYmKGUuX19pc1Z1ZXx8ZS5CfHxlLl9fdl9pc1ZOb2RlKSlyZXR1cm4gZnVuY3Rpb24odCl7cmV0dXJuIl9fdl9pc1ZOb2RlImluIHQmJnQuX192X2lzVk5vZGU/IltWdWVWTm9kZV0iOiJbVnVlVmlld01vZGVsXSJ9KG4pO2lmKGZ1bmN0aW9uKHQpe3JldHVybiBBKHQpJiYibmF0aXZlRXZlbnQiaW4gdCYmInByZXZlbnREZWZhdWx0ImluIHQmJiJzdG9wUHJvcGFnYXRpb24iaW4gdH0obikpcmV0dXJuIltTeW50aGV0aWNFdmVudF0iO2lmKCJudW1iZXIiPT10eXBlb2YgbiYmIU51bWJlci5pc0Zpbml0ZShuKSlyZXR1cm5gWyR7bn1dYDtpZigiZnVuY3Rpb24iPT10eXBlb2YgbilyZXR1cm5gW0Z1bmN0aW9uOiAke2Z1bmN0aW9uKHQpe3RyeXtyZXR1cm4gdCYmImZ1bmN0aW9uIj09dHlwZW9mIHQmJnQubmFtZXx8eH1jYXRjaHtyZXR1cm4geH19KG4pfV1gO2lmKCJzeW1ib2wiPT10eXBlb2YgbilyZXR1cm5gWyR7U3RyaW5nKG4pfV1gO2lmKCJiaWdpbnQiPT10eXBlb2YgbilyZXR1cm5gW0JpZ0ludDogJHtTdHJpbmcobil9XWA7Y29uc3Qgcj1mdW5jdGlvbih0KXtjb25zdCBuPU9iamVjdC5nZXRQcm90b3R5cGVPZih0KTtyZXR1cm4gbj8uY29uc3RydWN0b3I/bi5jb25zdHJ1Y3Rvci5uYW1lOiJudWxsIHByb3RvdHlwZSJ9KG4pO3JldHVybi9eSFRNTChcdyopRWxlbWVudCQvLnRlc3Qocik/YFtIVE1MRWxlbWVudDogJHtyfV1gOmBbb2JqZWN0ICR7cn1dYH1jYXRjaCh0KXtyZXR1cm5gKipub24tc2VyaWFsaXphYmxlKiogKCR7dH0pYH12YXIgZX0odCxuKTtpZighYy5zdGFydHNXaXRoKCJbb2JqZWN0ICIpKXJldHVybiBjO2lmKGZ1bmN0aW9uKHQpe3JldHVybiBCb29sZWFuKHRbQXRdKX0obikpcmV0dXJuIG47Y29uc3QgdT1mdW5jdGlvbih0KXtjb25zdCBuPXRba3RdO3JldHVybiJudW1iZXIiPT10eXBlb2Ygbj9uOnZvaWQgMH0obiksYT12b2lkIDAhPT11P3U6ZTtpZigwPT09YSlyZXR1cm4gYy5yZXBsYWNlKCJvYmplY3QgIiwiIik7aWYoaShuKSlyZXR1cm4iW0NpcmN1bGFyIH5dIjtjb25zdCBmPW47aWYoZiYmImZ1bmN0aW9uIj09dHlwZW9mIGYudG9KU09OKXRyeXtyZXR1cm4gSXQoIiIsZi50b0pTT04oKSxhLTEscixvKX1jYXRjaHt9Y29uc3QgaD1BcnJheS5pc0FycmF5KG4pP1tdOnt9O2xldCBwPTA7Y29uc3QgbD1SKG4pO2Zvcihjb25zdCB0IGluIGwpe2lmKCFPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwobCx0KSljb250aW51ZTtpZihwPj1yKXtoW3RdPSJbTWF4UHJvcGVydGllcyB+XSI7YnJlYWt9Y29uc3Qgbj1sW3RdO2hbdF09SXQodCxuLGEtMSxyLG8pLHArK31yZXR1cm4gcyhuKSxofWZ1bmN0aW9uIE90KHQsbil7Y29uc3QgZT1uLnJlcGxhY2UoL1xcL2csIi8iKS5yZXBsYWNlKC9bfFxce30oKVtcXV4kKyo/Ll0vZywiXFwkJiIpO2xldCByPXQ7dHJ5e3I9ZGVjb2RlVVJJKHQpfWNhdGNoe31yZXR1cm4gci5yZXBsYWNlKC9cXC9nLCIvIikucmVwbGFjZSgvd2VicGFjazpcLz8vZywiIikucmVwbGFjZShuZXcgUmVnRXhwKGAoZmlsZTovLyk/Lyoke2V9LypgLCJpZyIpLCJhcHA6Ly8vIil9ZnVuY3Rpb24gUnQodCxuPVtdKXtyZXR1cm5bdCxuXX1mdW5jdGlvbiBQdCh0LG4pe2NvbnN0IGU9dFsxXTtmb3IoY29uc3QgdCBvZiBlKXtpZihuKHQsdFswXS50eXBlKSlyZXR1cm4hMH1yZXR1cm4hMX1mdW5jdGlvbiBEdCh0KXtjb25zdCBuPW0ocCk7cmV0dXJuIG4uZW5jb2RlUG9seWZpbGw/bi5lbmNvZGVQb2x5ZmlsbCh0KToobmV3IFRleHRFbmNvZGVyKS5lbmNvZGUodCl9ZnVuY3Rpb24gVXQodCl7Y29uc3RbbixlXT10O2xldCByPUpTT04uc3RyaW5naWZ5KG4pO2Z1bmN0aW9uIG8odCl7InN0cmluZyI9PXR5cGVvZiByP3I9InN0cmluZyI9PXR5cGVvZiB0P3IrdDpbRHQociksdF06ci5wdXNoKCJzdHJpbmciPT10eXBlb2YgdD9EdCh0KTp0KX1mb3IoY29uc3QgdCBvZiBlKXtjb25zdFtuLGVdPXQ7aWYobyhgXG4ke0pTT04uc3RyaW5naWZ5KG4pfVxuYCksInN0cmluZyI9PXR5cGVvZiBlfHxlIGluc3RhbmNlb2YgVWludDhBcnJheSlvKGUpO2Vsc2V7bGV0IHQ7dHJ5e3Q9SlNPTi5zdHJpbmdpZnkoZSl9Y2F0Y2h7dD1KU09OLnN0cmluZ2lmeShUdChlKSl9byh0KX19cmV0dXJuInN0cmluZyI9PXR5cGVvZiByP3I6ZnVuY3Rpb24odCl7Y29uc3Qgbj10LnJlZHVjZSgodCxuKT0+dCtuLmxlbmd0aCwwKSxlPW5ldyBVaW50OEFycmF5KG4pO2xldCByPTA7Zm9yKGNvbnN0IG4gb2YgdCllLnNldChuLHIpLHIrPW4ubGVuZ3RoO3JldHVybiBlfShyKX1jb25zdCBMdD17c2Vzc2lvbnM6InNlc3Npb24iLGV2ZW50OiJlcnJvciIsY2xpZW50X3JlcG9ydDoiaW50ZXJuYWwiLHVzZXJfcmVwb3J0OiJkZWZhdWx0Iixwcm9maWxlX2NodW5rOiJwcm9maWxlIixyZXBsYXlfZXZlbnQ6InJlcGxheSIscmVwbGF5X3JlY29yZGluZzoicmVwbGF5IixjaGVja19pbjoibW9uaXRvciIscmF3X3NlY3VyaXR5OiJzZWN1cml0eSIsbG9nOiJsb2dfaXRlbSIsdHJhY2VfbWV0cmljOiJtZXRyaWMifTtmdW5jdGlvbiBNdCh0KXtyZXR1cm4gZnVuY3Rpb24odCl7cmV0dXJuIHQgaW4gTHR9KHQpP0x0W3RdOnR9ZnVuY3Rpb24gQnQodCl7aWYoIXQ/LnNkaylyZXR1cm47Y29uc3R7bmFtZTpuLHZlcnNpb246ZX09dC5zZGs7cmV0dXJue25hbWU6bix2ZXJzaW9uOmV9fWZ1bmN0aW9uIHp0KHQsbixlLHIpe2NvbnN0IG89QnQoZSksaT10LnR5cGUmJiJyZXBsYXlfZXZlbnQiIT09dC50eXBlP3QudHlwZToiZXZlbnQiOyFmdW5jdGlvbih0LG4pe2lmKCFuKXJldHVybiB0O2NvbnN0IGU9dC5zZGt8fHt9O3Quc2RrPXsuLi5lLG5hbWU6ZS5uYW1lfHxuLm5hbWUsdmVyc2lvbjplLnZlcnNpb258fG4udmVyc2lvbixpbnRlZ3JhdGlvbnM6Wy4uLnQuc2RrPy5pbnRlZ3JhdGlvbnN8fFtdLC4uLm4uaW50ZWdyYXRpb25zfHxbXV0scGFja2FnZXM6Wy4uLnQuc2RrPy5wYWNrYWdlc3x8W10sLi4ubi5wYWNrYWdlc3x8W11dLHNldHRpbmdzOnQuc2RrPy5zZXR0aW5nc3x8bi5zZXR0aW5ncz97Li4udC5zZGs/LnNldHRpbmdzLC4uLm4uc2V0dGluZ3N9OnZvaWQgMH19KHQsZT8uc2RrKTtjb25zdCBzPWZ1bmN0aW9uKHQsbixlLHIpe2NvbnN0IG89dC5zZGtQcm9jZXNzaW5nTWV0YWRhdGE/LmR5bmFtaWNTYW1wbGluZ0NvbnRleHQ7cmV0dXJue2V2ZW50X2lkOnQuZXZlbnRfaWQsc2VudF9hdDoobmV3IERhdGUpLnRvSVNPU3RyaW5nKCksLi4ubiYme3NkazpufSwuLi4hIWUmJnImJntkc246YnQocil9LC4uLm8mJnt0cmFjZTpvfX19KHQsbyxyLG4pO2RlbGV0ZSB0LnNka1Byb2Nlc3NpbmdNZXRhZGF0YTtyZXR1cm4gUnQocyxbW3t0eXBlOml9LHRdXSl9Y29uc3QgV3Q9Il9fU0VOVFJZX1NVUFBSRVNTX1RSQUNJTkdfXyI7ZnVuY3Rpb24gRnQodCl7Y29uc3Qgbj1mdChkKCkpO3JldHVybiBuLnN1cHByZXNzVHJhY2luZz9uLnN1cHByZXNzVHJhY2luZyh0KTpmdW5jdGlvbiguLi50KXtjb25zdCBuPWZ0KGQoKSk7aWYoMj09PXQubGVuZ3RoKXtjb25zdFtlLHJdPXQ7cmV0dXJuIGU/bi53aXRoU2V0U2NvcGUoZSxyKTpuLndpdGhTY29wZShyKX1yZXR1cm4gbi53aXRoU2NvcGUodFswXSl9KG49PntuLnNldFNES1Byb2Nlc3NpbmdNZXRhZGF0YSh7W1d0XTohMH0pO2NvbnN0IGU9dCgpO3JldHVybiBuLnNldFNES1Byb2Nlc3NpbmdNZXRhZGF0YSh7W1d0XTp2b2lkIDB9KSxlfSl9ZnVuY3Rpb24gR3QodCxuKXtjb25zdHtmaW5nZXJwcmludDplLHNwYW46cixicmVhZGNydW1iczpvLHNka1Byb2Nlc3NpbmdNZXRhZGF0YTppfT1uOyFmdW5jdGlvbih0LG4pe2NvbnN0e2V4dHJhOmUsdGFnczpyLHVzZXI6byxjb250ZXh0czppLGxldmVsOnMsdHJhbnNhY3Rpb25OYW1lOmN9PW47T2JqZWN0LmtleXMoZSkubGVuZ3RoJiYodC5leHRyYT17Li4uZSwuLi50LmV4dHJhfSk7T2JqZWN0LmtleXMocikubGVuZ3RoJiYodC50YWdzPXsuLi5yLC4uLnQudGFnc30pO09iamVjdC5rZXlzKG8pLmxlbmd0aCYmKHQudXNlcj17Li4ubywuLi50LnVzZXJ9KTtPYmplY3Qua2V5cyhpKS5sZW5ndGgmJih0LmNvbnRleHRzPXsuLi5pLC4uLnQuY29udGV4dHN9KTtzJiYodC5sZXZlbD1zKTtjJiYidHJhbnNhY3Rpb24iIT09dC50eXBlJiYodC50cmFuc2FjdGlvbj1jKX0odCxuKSxyJiZmdW5jdGlvbih0LG4pe3QuY29udGV4dHM9e3RyYWNlOl90KG4pLC4uLnQuY29udGV4dHN9LHQuc2RrUHJvY2Vzc2luZ01ldGFkYXRhPXtkeW5hbWljU2FtcGxpbmdDb250ZXh0OkN0KG4pLC4uLnQuc2RrUHJvY2Vzc2luZ01ldGFkYXRhfTtjb25zdCBlPWp0KG4pLHI9RXQoZSkuZGVzY3JpcHRpb247ciYmIXQudHJhbnNhY3Rpb24mJiJ0cmFuc2FjdGlvbiI9PT10LnR5cGUmJih0LnRyYW5zYWN0aW9uPXIpfSh0LHIpLGZ1bmN0aW9uKHQsbil7dC5maW5nZXJwcmludD10LmZpbmdlcnByaW50P0FycmF5LmlzQXJyYXkodC5maW5nZXJwcmludCk/dC5maW5nZXJwcmludDpbdC5maW5nZXJwcmludF06W10sbiYmKHQuZmluZ2VycHJpbnQ9dC5maW5nZXJwcmludC5jb25jYXQobikpO3QuZmluZ2VycHJpbnQubGVuZ3RofHxkZWxldGUgdC5maW5nZXJwcmludH0odCxlKSxmdW5jdGlvbih0LG4pe2NvbnN0IGU9Wy4uLnQuYnJlYWRjcnVtYnN8fFtdLC4uLm5dO3QuYnJlYWRjcnVtYnM9ZS5sZW5ndGg/ZTp2b2lkIDB9KHQsbyksZnVuY3Rpb24odCxuKXt0LnNka1Byb2Nlc3NpbmdNZXRhZGF0YT17Li4udC5zZGtQcm9jZXNzaW5nTWV0YWRhdGEsLi4ubn19KHQsaSl9Y2xhc3MgSHR7Y29uc3RydWN0b3IodCl7dGhpcy5XPTAsdGhpcy5GPVtdLHRoaXMuRyh0KX10aGVuKHQsbil7cmV0dXJuIG5ldyBIdCgoZSxyKT0+e3RoaXMuRi5wdXNoKFshMSxuPT57aWYodCl0cnl7ZSh0KG4pKX1jYXRjaCh0KXtyKHQpfWVsc2UgZShuKX0sdD0+e2lmKG4pdHJ5e2Uobih0KSl9Y2F0Y2godCl7cih0KX1lbHNlIHIodCl9XSksdGhpcy5IKCl9KX1jYXRjaCh0KXtyZXR1cm4gdGhpcy50aGVuKHQ9PnQsdCl9ZmluYWxseSh0KXtyZXR1cm4gbmV3IEh0KChuLGUpPT57bGV0IHIsbztyZXR1cm4gdGhpcy50aGVuKG49PntvPSExLHI9bix0JiZ0KCl9LG49PntvPSEwLHI9bix0JiZ0KCl9KS50aGVuKCgpPT57bz9lKHIpOm4ocil9KX0pfUgoKXtpZigwPT09dGhpcy5XKXJldHVybjtjb25zdCB0PXRoaXMuRi5zbGljZSgpO3RoaXMuRj1bXSx0LmZvckVhY2godD0+e3RbMF18fCgxPT09dGhpcy5XJiZ0WzFdKHRoaXMuSiksMj09PXRoaXMuVyYmdFsyXSh0aGlzLkopLHRbMF09ITApfSl9Ryh0KXtjb25zdCBuPSh0LG4pPT57MD09PXRoaXMuVyYmKGsobik/bi50aGVuKGUscik6KHRoaXMuVz10LHRoaXMuSj1uLHRoaXMuSCgpKSl9LGU9dD0+e24oMSx0KX0scj10PT57bigyLHQpfTt0cnl7dChlLHIpfWNhdGNoKHQpe3IodCl9fX1jb25zdCBKdD1TeW1ib2wuZm9yKCJTZW50cnlCdWZmZXJGdWxsRXJyb3IiKTtmdW5jdGlvbiBZdCh0PTEwMCl7Y29uc3Qgbj1uZXcgU2V0O2Z1bmN0aW9uIGUodCl7bi5kZWxldGUodCl9cmV0dXJue2dldCAkKCl7cmV0dXJuIEFycmF5LmZyb20obil9LGFkZDpmdW5jdGlvbihyKXtpZighKG4uc2l6ZTx0KSlyZXR1cm4gbz1KdCxuZXcgSHQoKHQsbik9PntuKG8pfSk7dmFyIG87Y29uc3QgaT1yKCk7cmV0dXJuIG4uYWRkKGkpLGkudGhlbigoKT0+ZShpKSwoKT0+ZShpKSksaX0sZHJhaW46ZnVuY3Rpb24odCl7aWYoIW4uc2l6ZSlyZXR1cm4gZT0hMCxuZXcgSHQodD0+e3QoZSl9KTt2YXIgZTtjb25zdCByPVByb21pc2UuYWxsU2V0dGxlZChBcnJheS5mcm9tKG4pKS50aGVuKCgpPT4hMCk7aWYoIXQpcmV0dXJuIHI7Y29uc3Qgbz1bcixuZXcgUHJvbWlzZShuPT57cmV0dXJuIm9iamVjdCI9PXR5cGVvZihlPXNldFRpbWVvdXQoKCk9Pm4oITEpLHQpKSYmImZ1bmN0aW9uIj09dHlwZW9mIGUudW5yZWYmJmUudW5yZWYoKSxlO3ZhciBlfSldO3JldHVybiBQcm9taXNlLnJhY2Uobyl9fX1mdW5jdGlvbiBWdCh0LHtzdGF0dXNDb2RlOm4saGVhZGVyczplfSxyPXooKSl7Y29uc3Qgbz17Li4udH0saT1lPy5bIngtc2VudHJ5LXJhdGUtbGltaXRzIl0scz1lPy5bInJldHJ5LWFmdGVyIl07aWYoaSlmb3IoY29uc3QgdCBvZiBpLnRyaW0oKS5zcGxpdCgiLCIpKXtjb25zdFtuLGUsLCxpXT10LnNwbGl0KCI6Iiw1KSxzPXBhcnNlSW50KG4sMTApLGM9MWUzKihpc05hTihzKT82MDpzKTtpZihlKWZvcihjb25zdCB0IG9mIGUuc3BsaXQoIjsiKSkibWV0cmljX2J1Y2tldCI9PT10JiZpJiYhaS5zcGxpdCgiOyIpLmluY2x1ZGVzKCJjdXN0b20iKXx8KG9bdF09citjKTtlbHNlIG8uYWxsPXIrY31lbHNlIHM/by5hbGw9citmdW5jdGlvbih0LG49eigpKXtjb25zdCBlPXBhcnNlSW50KGAke3R9YCwxMCk7aWYoIWlzTmFOKGUpKXJldHVybiAxZTMqZTtjb25zdCByPURhdGUucGFyc2UoYCR7dH1gKTtyZXR1cm4gaXNOYU4ocik/NmU0OnItbn0ocyxyKTo0Mjk9PT1uJiYoby5hbGw9cis2ZTQpO3JldHVybiBvfWZ1bmN0aW9uIEt0KHQsbixlPVl0KHQuYnVmZmVyU2l6ZXx8NjQpKXtsZXQgcj17fTtyZXR1cm57c2VuZDpmdW5jdGlvbih0KXtjb25zdCBvPVtdO2lmKFB0KHQsKHQsbik9Pntjb25zdCBlPU10KG4pOyhmdW5jdGlvbih0LG4sZT16KCkpe3JldHVybiBmdW5jdGlvbih0LG4pe3JldHVybiB0W25dfHx0LmFsbHx8MH0odCxuKT5lfSkocixlKXx8by5wdXNoKHQpfSksMD09PW8ubGVuZ3RoKXJldHVybiBQcm9taXNlLnJlc29sdmUoe30pO2NvbnN0IGk9UnQodFswXSxvKSxzPXQ9PnshZnVuY3Rpb24odCxuKXtyZXR1cm4gUHQodCwodCxlKT0+bi5pbmNsdWRlcyhlKSl9KGksWyJjbGllbnRfcmVwb3J0Il0pP1B0KGksKHQsbik9Pnt9KTpoJiZTLndhcm4oYERyb3BwaW5nIGNsaWVudCByZXBvcnQuIFdpbGwgbm90IHNlbmQgb3V0Y29tZXMgKHJlYXNvbjogJHt0fSkuYCl9O3JldHVybiBlLmFkZCgoKT0+bih7Ym9keTpVdChpKX0pLnRoZW4odD0+NDEzPT09dC5zdGF0dXNDb2RlPyhoJiZTLmVycm9yKCJTZW50cnkgcmVzcG9uZGVkIHdpdGggc3RhdHVzIGNvZGUgNDEzLiBFbnZlbG9wZSB3YXMgZGlzY2FyZGVkIGR1ZSB0byBleGNlZWRpbmcgc2l6ZSBsaW1pdHMuIikscygic2VuZF9lcnJvciIpLHQpOihoJiZ2b2lkIDAhPT10LnN0YXR1c0NvZGUmJih0LnN0YXR1c0NvZGU8MjAwfHx0LnN0YXR1c0NvZGU+PTMwMCkmJlMud2FybihgU2VudHJ5IHJlc3BvbmRlZCB3aXRoIHN0YXR1cyBjb2RlICR7dC5zdGF0dXNDb2RlfSB0byBzZW50IGV2ZW50LmApLHI9VnQocix0KSx0KSx0PT57dGhyb3cgcygibmV0d29ya19lcnJvciIpLGgmJlMuZXJyb3IoIkVuY291bnRlcmVkIGVycm9yIHJ1bm5pbmcgdHJhbnNwb3J0IHJlcXVlc3Q6Iix0KSx0fSkpLnRoZW4odD0+dCx0PT57aWYodD09PUp0KXJldHVybiBoJiZTLmVycm9yKCJTa2lwcGVkIHNlbmRpbmcgZXZlbnQgYmVjYXVzZSBidWZmZXIgaXMgZnVsbC4iKSxzKCJxdWV1ZV9vdmVyZmxvdyIpLFByb21pc2UucmVzb2x2ZSh7fSk7dGhyb3cgdH0pfSxmbHVzaDp0PT5lLmRyYWluKHQpfX1jb25zdCBadD0vXihcUys6XFx8XC8/KShbXHNcU10qPykoKD86XC57MSwyfXxbXi9cXF0rP3wpKFwuW14uL1xcXSp8KSkoPzpbL1xcXSopJC87ZnVuY3Rpb24gcXQodCl7Y29uc3Qgbj1mdW5jdGlvbih0KXtjb25zdCBuPXQubGVuZ3RoPjEwMjQ/YDx0cnVuY2F0ZWQ+JHt0LnNsaWNlKC0xMDI0KX1gOnQsZT1adC5leGVjKG4pO3JldHVybiBlP2Uuc2xpY2UoMSk6W119KHQpLGU9blswXXx8IiI7bGV0IHI9blsxXTtyZXR1cm4gZXx8cj8ociYmKHI9ci5zbGljZSgwLHIubGVuZ3RoLTEpKSxlK3IpOiIuIn1mdW5jdGlvbiBRdCh0LG49ITEpe3JldHVybiEobnx8dCYmIXQuc3RhcnRzV2l0aCgiLyIpJiYhdC5tYXRjaCgvXltBLVpdOi8pJiYhdC5zdGFydHNXaXRoKCIuIikmJiF0Lm1hdGNoKC9eW2EtekEtWl0oW2EtekEtWjAtOS5cLStdKSo6XC9cLy8pKSYmdm9pZCAwIT09dCYmIXQuaW5jbHVkZXMoIm5vZGVfbW9kdWxlcy8iKX1jb25zdCBYdD1TeW1ib2woIkFnZW50QmFzZUludGVybmFsU3RhdGUiKTtjbGFzcyB0biBleHRlbmRzIGkuQWdlbnR7Y29uc3RydWN0b3IodCl7c3VwZXIodCksdGhpc1tYdF09e319aXNTZWN1cmVFbmRwb2ludCh0KXtpZih0KXtpZigiYm9vbGVhbiI9PXR5cGVvZiB0LnNlY3VyZUVuZHBvaW50KXJldHVybiB0LnNlY3VyZUVuZHBvaW50O2lmKCJzdHJpbmciPT10eXBlb2YgdC5wcm90b2NvbClyZXR1cm4iaHR0cHM6Ij09PXQucHJvdG9jb2x9Y29uc3R7c3RhY2s6bn09bmV3IEVycm9yO3JldHVybiJzdHJpbmciPT10eXBlb2YgbiYmbi5zcGxpdCgiXG4iKS5zb21lKHQ9Pi0xIT09dC5pbmRleE9mKCIoaHR0cHMuanM6Iil8fC0xIT09dC5pbmRleE9mKCJub2RlOmh0dHBzOiIpKX1jcmVhdGVTb2NrZXQodCxuLGUpe2NvbnN0IHI9ey4uLm4sc2VjdXJlRW5kcG9pbnQ6dGhpcy5pc1NlY3VyZUVuZHBvaW50KG4pfTtQcm9taXNlLnJlc29sdmUoKS50aGVuKCgpPT50aGlzLmNvbm5lY3QodCxyKSkudGhlbihvPT57aWYobyBpbnN0YW5jZW9mIGkuQWdlbnQpcmV0dXJuIG8uYWRkUmVxdWVzdCh0LHIpO3RoaXNbWHRdLmN1cnJlbnRTb2NrZXQ9byxzdXBlci5jcmVhdGVTb2NrZXQodCxuLGUpfSxlKX1jcmVhdGVDb25uZWN0aW9uKCl7Y29uc3QgdD10aGlzW1h0XS5jdXJyZW50U29ja2V0O2lmKHRoaXNbWHRdLmN1cnJlbnRTb2NrZXQ9dm9pZCAwLCF0KXRocm93IG5ldyBFcnJvcigiTm8gc29ja2V0IHdhcyByZXR1cm5lZCBpbiB0aGUgYGNvbm5lY3QoKWAgZnVuY3Rpb24iKTtyZXR1cm4gdH1nZXQgZGVmYXVsdFBvcnQoKXtyZXR1cm4gdGhpc1tYdF0uZGVmYXVsdFBvcnQ/PygiaHR0cHM6Ij09PXRoaXMucHJvdG9jb2w/NDQzOjgwKX1zZXQgZGVmYXVsdFBvcnQodCl7dGhpc1tYdF0mJih0aGlzW1h0XS5kZWZhdWx0UG9ydD10KX1nZXQgcHJvdG9jb2woKXtyZXR1cm4gdGhpc1tYdF0ucHJvdG9jb2w/Pyh0aGlzLmlzU2VjdXJlRW5kcG9pbnQoKT8iaHR0cHM6IjoiaHR0cDoiKX1zZXQgcHJvdG9jb2wodCl7dGhpc1tYdF0mJih0aGlzW1h0XS5wcm90b2NvbD10KX19ZnVuY3Rpb24gbm4oLi4udCl7Uy5sb2coIltodHRwcy1wcm94eS1hZ2VudDpwYXJzZS1wcm94eS1yZXNwb25zZV0iLC4uLnQpfWZ1bmN0aW9uIGVuKHQpe3JldHVybiBuZXcgUHJvbWlzZSgobixlKT0+e2xldCByPTA7Y29uc3Qgbz1bXTtmdW5jdGlvbiBpKCl7Y29uc3QgYz10LnJlYWQoKTtjP2Z1bmN0aW9uKGMpe28ucHVzaChjKSxyKz1jLmxlbmd0aDtjb25zdCB1PUJ1ZmZlci5jb25jYXQobyxyKSxhPXUuaW5kZXhPZigiXHJcblxyXG4iKTtpZigtMT09PWEpcmV0dXJuIG5uKCJoYXZlIG5vdCByZWNlaXZlZCBlbmQgb2YgSFRUUCBoZWFkZXJzIHlldC4uLiIpLHZvaWQgaSgpO2NvbnN0IGY9dS5zdWJhcnJheSgwLGEpLnRvU3RyaW5nKCJhc2NpaSIpLnNwbGl0KCJcclxuIiksaD1mLnNoaWZ0KCk7aWYoIWgpcmV0dXJuIHQuZGVzdHJveSgpLGUobmV3IEVycm9yKCJObyBoZWFkZXIgcmVjZWl2ZWQgZnJvbSBwcm94eSBDT05ORUNUIHJlc3BvbnNlIikpO2NvbnN0IHA9aC5zcGxpdCgiICIpLGw9KyhwWzFdfHwwKSxkPXAuc2xpY2UoMikuam9pbigiICIpLG09e307Zm9yKGNvbnN0IG4gb2YgZil7aWYoIW4pY29udGludWU7Y29uc3Qgcj1uLmluZGV4T2YoIjoiKTtpZigtMT09PXIpcmV0dXJuIHQuZGVzdHJveSgpLGUobmV3IEVycm9yKGBJbnZhbGlkIGhlYWRlciBmcm9tIHByb3h5IENPTk5FQ1QgcmVzcG9uc2U6ICIke259ImApKTtjb25zdCBvPW4uc2xpY2UoMCxyKS50b0xvd2VyQ2FzZSgpLGk9bi5zbGljZShyKzEpLnRyaW1TdGFydCgpLHM9bVtvXTsic3RyaW5nIj09dHlwZW9mIHM/bVtvXT1bcyxpXTpBcnJheS5pc0FycmF5KHMpP3MucHVzaChpKTptW29dPWl9bm4oImdvdCBwcm94eSBzZXJ2ZXIgcmVzcG9uc2U6ICVvICVvIixoLG0pLHMoKSxuKHtjb25uZWN0OntzdGF0dXNDb2RlOmwsc3RhdHVzVGV4dDpkLGhlYWRlcnM6bX0sYnVmZmVyZWQ6dX0pfShjKTp0Lm9uY2UoInJlYWRhYmxlIixpKX1mdW5jdGlvbiBzKCl7dC5yZW1vdmVMaXN0ZW5lcigiZW5kIixjKSx0LnJlbW92ZUxpc3RlbmVyKCJlcnJvciIsdSksdC5yZW1vdmVMaXN0ZW5lcigicmVhZGFibGUiLGkpfWZ1bmN0aW9uIGMoKXtzKCksbm4oIm9uZW5kIiksZShuZXcgRXJyb3IoIlByb3h5IGNvbm5lY3Rpb24gZW5kZWQgYmVmb3JlIHJlY2VpdmluZyBDT05ORUNUIHJlc3BvbnNlIikpfWZ1bmN0aW9uIHUodCl7cygpLG5uKCJvbmVycm9yICVvIix0KSxlKHQpfXQub24oImVycm9yIix1KSx0Lm9uKCJlbmQiLGMpLGkoKX0pfWZ1bmN0aW9uIHJuKC4uLnQpe1MubG9nKCJbaHR0cHMtcHJveHktYWdlbnRdIiwuLi50KX1jbGFzcyBvbiBleHRlbmRzIHRue3N0YXRpYyBfX2luaXRTdGF0aWMoKXt0aGlzLnByb3RvY29scz1bImh0dHAiLCJodHRwcyJdfWNvbnN0cnVjdG9yKHQsbil7c3VwZXIobiksdGhpcy5vcHRpb25zPXt9LHRoaXMucHJveHk9InN0cmluZyI9PXR5cGVvZiB0P25ldyBVUkwodCk6dCx0aGlzLnByb3h5SGVhZGVycz1uPy5oZWFkZXJzPz97fSxybigiQ3JlYXRpbmcgbmV3IEh0dHBzUHJveHlBZ2VudCBpbnN0YW5jZTogJW8iLHRoaXMucHJveHkuaHJlZik7Y29uc3QgZT0odGhpcy5wcm94eS5ob3N0bmFtZXx8dGhpcy5wcm94eS5ob3N0KS5yZXBsYWNlKC9eXFt8XF0kL2csIiIpLHI9dGhpcy5wcm94eS5wb3J0P3BhcnNlSW50KHRoaXMucHJveHkucG9ydCwxMCk6Imh0dHBzOiI9PT10aGlzLnByb3h5LnByb3RvY29sPzQ0Mzo4MDt0aGlzLmNvbm5lY3RPcHRzPXtBTFBOUHJvdG9jb2xzOlsiaHR0cC8xLjEiXSwuLi5uP2NuKG4sImhlYWRlcnMiKTpudWxsLGhvc3Q6ZSxwb3J0OnJ9fWFzeW5jIGNvbm5lY3QodCxuKXtjb25zdHtwcm94eTplfT10aGlzO2lmKCFuLmhvc3QpdGhyb3cgbmV3IFR5cGVFcnJvcignTm8gImhvc3QiIHByb3ZpZGVkJyk7bGV0IHI7aWYoImh0dHBzOiI9PT1lLnByb3RvY29sKXtybigiQ3JlYXRpbmcgYHRscy5Tb2NrZXRgOiAlbyIsdGhpcy5jb25uZWN0T3B0cyk7Y29uc3QgdD10aGlzLmNvbm5lY3RPcHRzLnNlcnZlcm5hbWV8fHRoaXMuY29ubmVjdE9wdHMuaG9zdDtyPWYuY29ubmVjdCh7Li4udGhpcy5jb25uZWN0T3B0cyxzZXJ2ZXJuYW1lOnQmJmEuaXNJUCh0KT92b2lkIDA6dH0pfWVsc2Ugcm4oIkNyZWF0aW5nIGBuZXQuU29ja2V0YDogJW8iLHRoaXMuY29ubmVjdE9wdHMpLHI9YS5jb25uZWN0KHRoaXMuY29ubmVjdE9wdHMpO2NvbnN0IG89ImZ1bmN0aW9uIj09dHlwZW9mIHRoaXMucHJveHlIZWFkZXJzP3RoaXMucHJveHlIZWFkZXJzKCk6ey4uLnRoaXMucHJveHlIZWFkZXJzfSxpPWEuaXNJUHY2KG4uaG9zdCk/YFske24uaG9zdH1dYDpuLmhvc3Q7bGV0IHM9YENPTk5FQ1QgJHtpfToke24ucG9ydH0gSFRUUC8xLjFcclxuYDtpZihlLnVzZXJuYW1lfHxlLnBhc3N3b3JkKXtjb25zdCB0PWAke2RlY29kZVVSSUNvbXBvbmVudChlLnVzZXJuYW1lKX06JHtkZWNvZGVVUklDb21wb25lbnQoZS5wYXNzd29yZCl9YDtvWyJQcm94eS1BdXRob3JpemF0aW9uIl09YEJhc2ljICR7QnVmZmVyLmZyb20odCkudG9TdHJpbmcoImJhc2U2NCIpfWB9by5Ib3N0PWAke2l9OiR7bi5wb3J0fWAsb1siUHJveHktQ29ubmVjdGlvbiJdfHwob1siUHJveHktQ29ubmVjdGlvbiJdPXRoaXMua2VlcEFsaXZlPyJLZWVwLUFsaXZlIjoiY2xvc2UiKTtmb3IoY29uc3QgdCBvZiBPYmplY3Qua2V5cyhvKSlzKz1gJHt0fTogJHtvW3RdfVxyXG5gO2NvbnN0IGM9ZW4ocik7ci53cml0ZShgJHtzfVxyXG5gKTtjb25zdHtjb25uZWN0OnUsYnVmZmVyZWQ6aH09YXdhaXQgYztpZih0LmVtaXQoInByb3h5Q29ubmVjdCIsdSksdGhpcy5lbWl0KCJwcm94eUNvbm5lY3QiLHUsdCksMjAwPT09dS5zdGF0dXNDb2RlKXtpZih0Lm9uY2UoInNvY2tldCIsc24pLG4uc2VjdXJlRW5kcG9pbnQpe3JuKCJVcGdyYWRpbmcgc29ja2V0IGNvbm5lY3Rpb24gdG8gVExTIik7Y29uc3QgdD1uLnNlcnZlcm5hbWV8fG4uaG9zdDtyZXR1cm4gZi5jb25uZWN0KHsuLi5jbihuLCJob3N0IiwicGF0aCIsInBvcnQiKSxzb2NrZXQ6cixzZXJ2ZXJuYW1lOmEuaXNJUCh0KT92b2lkIDA6dH0pfXJldHVybiByfXIuZGVzdHJveSgpO2NvbnN0IHA9bmV3IGEuU29ja2V0KHt3cml0YWJsZTohMX0pO3JldHVybiBwLnJlYWRhYmxlPSEwLHQub25jZSgic29ja2V0Iix0PT57cm4oIlJlcGxheWluZyBwcm94eSBidWZmZXIgZm9yIGZhaWxlZCByZXF1ZXN0IiksdC5wdXNoKGgpLHQucHVzaChudWxsKX0pLHB9fWZ1bmN0aW9uIHNuKHQpe3QucmVzdW1lKCl9ZnVuY3Rpb24gY24odCwuLi5uKXtjb25zdCBlPXt9O2xldCByO2ZvcihyIGluIHQpbi5pbmNsdWRlcyhyKXx8KGVbcl09dFtyXSk7cmV0dXJuIGV9b24uX19pbml0U3RhdGljKCk7ZnVuY3Rpb24gdW4odCl7cmV0dXJuIHQucmVwbGFjZSgvXltBLVpdOi8sIiIpLnJlcGxhY2UoL1xcL2csIi8iKX1jb25zdCBhbj1uO2xldCBmbixobj0wLHBuPXt9O2Z1bmN0aW9uIGxuKHQpe2FuLmRlYnVnJiZjb25zb2xlLmxvZyhgW0FOUiBXb3JrZXJdICR7dH1gKX12YXIgZG4sbW4sZ247Y29uc3QgeW49ZnVuY3Rpb24odCl7bGV0IG47dHJ5e249bmV3IFVSTCh0LnVybCl9Y2F0Y2gobil7cmV0dXJuIGIoKCk9Pntjb25zb2xlLndhcm4oIltAc2VudHJ5L25vZGVdOiBJbnZhbGlkIGRzbiBvciB0dW5uZWwgb3B0aW9uLCB3aWxsIG5vdCBzZW5kIGFueSBldmVudHMuIFRoZSB0dW5uZWwgb3B0aW9uIG11c3QgYmUgYSBmdWxsIFVSTCB3aGVuIHVzZWQuIil9KSxLdCh0LCgpPT5Qcm9taXNlLnJlc29sdmUoe30pKX1jb25zdCBlPSJodHRwczoiPT09bi5wcm90b2NvbCxyPWZ1bmN0aW9uKHQsbil7Y29uc3R7bm9fcHJveHk6ZX09cHJvY2Vzcy5lbnYscj1lPy5zcGxpdCgiLCIpLnNvbWUobj0+dC5ob3N0LmVuZHNXaXRoKG4pfHx0Lmhvc3RuYW1lLmVuZHNXaXRoKG4pKTtyZXR1cm4gcj92b2lkIDA6bn0obix0LnByb3h5fHwoZT9wcm9jZXNzLmVudi5odHRwc19wcm94eTp2b2lkIDApfHxwcm9jZXNzLmVudi5odHRwX3Byb3h5KSxvPWU/czppLGE9dm9pZCAwIT09dC5rZWVwQWxpdmUmJnQua2VlcEFsaXZlLGY9cj9uZXcgb24ocik6bmV3IG8uQWdlbnQoe2tlZXBBbGl2ZTphLG1heFNvY2tldHM6MzAsdGltZW91dDoyZTN9KSxoPWZ1bmN0aW9uKHQsbixlKXtjb25zdHtob3N0bmFtZTpyLHBhdGhuYW1lOm8scG9ydDppLHByb3RvY29sOnMsc2VhcmNoOmF9PW5ldyBVUkwodC51cmwpO3JldHVybiBmdW5jdGlvbihmKXtyZXR1cm4gbmV3IFByb21pc2UoKGgscCk9PntGdCgoKT0+e2xldCBsPWZ1bmN0aW9uKHQpe3JldHVybiBuZXcgYyh7cmVhZCgpe3RoaXMucHVzaCh0KSx0aGlzLnB1c2gobnVsbCl9fSl9KGYuYm9keSk7Y29uc3QgZD17Li4udC5oZWFkZXJzfTtmLmJvZHkubGVuZ3RoPjMyNzY4JiYoZFsiY29udGVudC1lbmNvZGluZyJdPSJnemlwIixsPWwucGlwZSh1KCkpKTtjb25zdCBtPXIuc3RhcnRzV2l0aCgiWyIpLGc9bi5yZXF1ZXN0KHttZXRob2Q6IlBPU1QiLGFnZW50OmUsaGVhZGVyczpkLGhvc3RuYW1lOm0/ci5zbGljZSgxLC0xKTpyLHBhdGg6YCR7b30ke2F9YCxwb3J0OmkscHJvdG9jb2w6cyxjYTp0LmNhQ2VydHN9LHQ9Pnt0Lm9uKCJkYXRhIiwoKT0+e30pLHQub24oImVuZCIsKCk9Pnt9KSx0LnNldEVuY29kaW5nKCJ1dGY4Iik7Y29uc3Qgbj10LmhlYWRlcnNbInJldHJ5LWFmdGVyIl0/P251bGwsZT10LmhlYWRlcnNbIngtc2VudHJ5LXJhdGUtbGltaXRzIl0/P251bGw7aCh7c3RhdHVzQ29kZTp0LnN0YXR1c0NvZGUsaGVhZGVyczp7InJldHJ5LWFmdGVyIjpuLCJ4LXNlbnRyeS1yYXRlLWxpbWl0cyI6QXJyYXkuaXNBcnJheShlKT9lWzBdfHxudWxsOmV9fSl9KTtnLm9uKCJlcnJvciIscCksbC5waXBlKGcpfSl9KX19KHQsdC5odHRwTW9kdWxlPz9vLGYpO3JldHVybiBLdCh0LGgpfSh7dXJsOihkbj1hbi5kc24sbW49YW4udHVubmVsLGduPWFuLnNka01ldGFkYXRhLnNkayxtbnx8YCR7ZnVuY3Rpb24odCl7cmV0dXJuYCR7ZnVuY3Rpb24odCl7Y29uc3Qgbj10LnByb3RvY29sP2Ake3QucHJvdG9jb2x9OmA6IiIsZT10LnBvcnQ/YDoke3QucG9ydH1gOiIiO3JldHVybmAke259Ly8ke3QuaG9zdH0ke2V9JHt0LnBhdGg/YC8ke3QucGF0aH1gOiIifS9hcGkvYH0odCl9JHt0LnByb2plY3RJZH0vZW52ZWxvcGUvYH0oZG4pfT8ke2Z1bmN0aW9uKHQsbil7Y29uc3QgZT17c2VudHJ5X3ZlcnNpb246IjcifTtyZXR1cm4gdC5wdWJsaWNLZXkmJihlLnNlbnRyeV9rZXk9dC5wdWJsaWNLZXkpLG4mJihlLnNlbnRyeV9jbGllbnQ9YCR7bi5uYW1lfS8ke24udmVyc2lvbn1gKSxuZXcgVVJMU2VhcmNoUGFyYW1zKGUpLnRvU3RyaW5nKCl9KGRuLGduKX1gKX0pO2FzeW5jIGZ1bmN0aW9uIGJuKCl7aWYoZm4pe2xuKCJTZW5kaW5nIGFibm9ybWFsIHNlc3Npb24iKSxWKGZuLHtzdGF0dXM6ImFibm9ybWFsIixhYm5vcm1hbF9tZWNoYW5pc206ImFucl9mb3JlZ3JvdW5kIixyZWxlYXNlOmFuLnJlbGVhc2UsZW52aXJvbm1lbnQ6YW4uZW52aXJvbm1lbnR9KTtjb25zdCB0PWZ1bmN0aW9uKHQsbixlLHIpe2NvbnN0IG89QnQoZSk7cmV0dXJuIFJ0KHtzZW50X2F0OihuZXcgRGF0ZSkudG9JU09TdHJpbmcoKSwuLi5vJiZ7c2RrOm99LC4uLiEhciYmbiYme2RzbjpidChuKX19LFsiYWdncmVnYXRlcyJpbiB0P1t7dHlwZToic2Vzc2lvbnMifSx0XTpbe3R5cGU6InNlc3Npb24ifSx0LnRvSlNPTigpXV0pfShmbixhbi5kc24sYW4uc2RrTWV0YWRhdGEsYW4udHVubmVsKTtsbihKU09OLnN0cmluZ2lmeSh0KSksYXdhaXQgeW4uc2VuZCh0KTt0cnl7ZT8ucG9zdE1lc3NhZ2UoInNlc3Npb24tZW5kZWQiKX1jYXRjaHt9fX1mdW5jdGlvbiB2bih0KXtpZighdClyZXR1cm47Y29uc3Qgbj1mdW5jdGlvbih0KXtpZighdC5sZW5ndGgpcmV0dXJuW107Y29uc3Qgbj1BcnJheS5mcm9tKHQpO3JldHVybi9zZW50cnlXcmFwcGVkLy50ZXN0KEUobikuZnVuY3Rpb258fCIiKSYmbi5wb3AoKSxuLnJldmVyc2UoKSwkLnRlc3QoRShuKS5mdW5jdGlvbnx8IiIpJiYobi5wb3AoKSwkLnRlc3QoRShuKS5mdW5jdGlvbnx8IiIpJiZuLnBvcCgpKSxuLnNsaWNlKDAsNTApLm1hcCh0PT4oey4uLnQsZmlsZW5hbWU6dC5maWxlbmFtZXx8RShuKS5maWxlbmFtZSxmdW5jdGlvbjp0LmZ1bmN0aW9ufHwiPyJ9KSl9KHQpO2lmKGFuLmFwcFJvb3RQYXRoKWZvcihjb25zdCB0IG9mIG4pdC5maWxlbmFtZSYmKHQuZmlsZW5hbWU9T3QodC5maWxlbmFtZSxhbi5hcHBSb290UGF0aCkpO3JldHVybiBufWFzeW5jIGZ1bmN0aW9uIF9uKHQsbil7aWYoaG4+PWFuLm1heEFuckV2ZW50cylyZXR1cm47aG4rPTEsYXdhaXQgYm4oKSxsbigiU2VuZGluZyBldmVudCIpO2NvbnN0IGU9e2V2ZW50X2lkOkYoKSxjb250ZXh0czphbi5jb250ZXh0cyxyZWxlYXNlOmFuLnJlbGVhc2UsZW52aXJvbm1lbnQ6YW4uZW52aXJvbm1lbnQsZGlzdDphbi5kaXN0LHBsYXRmb3JtOiJub2RlIixsZXZlbDoiZXJyb3IiLGV4Y2VwdGlvbjp7dmFsdWVzOlt7dHlwZToiQXBwbGljYXRpb25Ob3RSZXNwb25kaW5nIix2YWx1ZTpgQXBwbGljYXRpb24gTm90IFJlc3BvbmRpbmcgZm9yIGF0IGxlYXN0ICR7YW4uYW5yVGhyZXNob2xkfSBtc2Asc3RhY2t0cmFjZTp7ZnJhbWVzOnZuKHQpfSxtZWNoYW5pc206e3R5cGU6IkFOUiJ9fV19LHRhZ3M6YW4uc3RhdGljVGFnc307biYmZnVuY3Rpb24odCxuKXtpZihHdCh0LG4pLCF0LmNvbnRleHRzPy50cmFjZSl7Y29uc3R7dHJhY2VJZDplLHBhcmVudFNwYW5JZDpyLHByb3BhZ2F0aW9uU3BhbklkOm99PW4ucHJvcGFnYXRpb25Db250ZXh0O3QuY29udGV4dHM9e3RyYWNlOnt0cmFjZV9pZDplLHNwYW5faWQ6b3x8cSgpLHBhcmVudF9zcGFuX2lkOnJ9LC4uLnQuY29udGV4dHN9fX0oZSxuKSxmdW5jdGlvbih0KXtpZigwPT09T2JqZWN0LmtleXMocG4pLmxlbmd0aClyZXR1cm47Y29uc3Qgbj1hbi5hcHBSb290UGF0aD97fTpwbjtpZihhbi5hcHBSb290UGF0aClmb3IoY29uc3RbdCxlXW9mIE9iamVjdC5lbnRyaWVzKHBuKSluW090KHQsYW4uYXBwUm9vdFBhdGgpXT1lO2NvbnN0IGU9bmV3IE1hcDtmb3IoY29uc3QgciBvZiB0LmV4Y2VwdGlvbj8udmFsdWVzfHxbXSlmb3IoY29uc3QgdCBvZiByLnN0YWNrdHJhY2U/LmZyYW1lc3x8W10pe2NvbnN0IHI9dC5hYnNfcGF0aHx8dC5maWxlbmFtZTtyJiZuW3JdJiZlLnNldChyLG5bcl0pfWlmKGUuc2l6ZT4wKXtjb25zdCBuPVtdO2Zvcihjb25zdFt0LHJdb2YgZS5lbnRyaWVzKCkpbi5wdXNoKHt0eXBlOiJzb3VyY2VtYXAiLGNvZGVfZmlsZTp0LGRlYnVnX2lkOnJ9KTt0LmRlYnVnX21ldGE9e2ltYWdlczpufX19KGUpO2NvbnN0IHI9enQoZSxhbi5kc24sYW4uc2RrTWV0YWRhdGEsYW4udHVubmVsKTtsbihKU09OLnN0cmluZ2lmeShyKSksYXdhaXQgeW4uc2VuZChyKSxhd2FpdCB5bi5mbHVzaCgyZTMpLGhuPj1hbi5tYXhBbnJFdmVudHMmJnNldFRpbWVvdXQoKCk9Pntwcm9jZXNzLmV4aXQoMCl9LDVlMyl9bGV0IHduO2lmKGxuKCJTdGFydGVkIiksYW4uY2FwdHVyZVN0YWNrVHJhY2Upe2xuKCJDb25uZWN0aW5nIHRvIGRlYnVnZ2VyIik7Y29uc3Qgbj1uZXcgdDtuLmNvbm5lY3RUb01haW5UaHJlYWQoKSxsbigiQ29ubmVjdGVkIHRvIGRlYnVnZ2VyIik7Y29uc3QgZT1uZXcgTWFwO24ub24oIkRlYnVnZ2VyLnNjcmlwdFBhcnNlZCIsdD0+e2Uuc2V0KHQucGFyYW1zLnNjcmlwdElkLHQucGFyYW1zLnVybCl9KSxuLm9uKCJEZWJ1Z2dlci5wYXVzZWQiLHQ9PntpZigib3RoZXIiPT09dC5wYXJhbXMucmVhc29uKXRyeXtsbigiRGVidWdnZXIgcGF1c2VkIik7Y29uc3QgaT1bLi4udC5wYXJhbXMuY2FsbEZyYW1lc10scz1hbi5hcHBSb290UGF0aD9mdW5jdGlvbih0PShwcm9jZXNzLmFyZ3ZbMV0/cXQocHJvY2Vzcy5hcmd2WzFdKTpwcm9jZXNzLmN3ZCgpKSxuPSJcXCI9PT1vKXtjb25zdCBlPW4/dW4odCk6dDtyZXR1cm4gdD0+e2lmKCF0KXJldHVybjtjb25zdCBvPW4/dW4odCk6dDtsZXR7ZGlyOmksYmFzZTpzLGV4dDpjfT1yLnBhcnNlKG8pOyIuanMiIT09YyYmIi5tanMiIT09YyYmIi5janMiIT09Y3x8KHM9cy5zbGljZSgwLC0xKmMubGVuZ3RoKSk7Y29uc3QgdT1kZWNvZGVVUklDb21wb25lbnQocyk7aXx8KGk9Ii4iKTtjb25zdCBhPWkubGFzdEluZGV4T2YoIi9ub2RlX21vZHVsZXMiKTtpZihhPi0xKXJldHVybmAke2kuc2xpY2UoYSsxNCkucmVwbGFjZSgvXC8vZywiLiIpfToke3V9YDtpZihpLnN0YXJ0c1dpdGgoZSkpe2NvbnN0IHQ9aS5zbGljZShlLmxlbmd0aCsxKS5yZXBsYWNlKC9cLy9nLCIuIik7cmV0dXJuIHQ/YCR7dH06JHt1fWA6dX1yZXR1cm4gdX19KGFuLmFwcFJvb3RQYXRoKTooKT0+e30sYz1pLm1hcCh0PT5mdW5jdGlvbih0LG4sZSl7Y29uc3Qgcj1uP24ucmVwbGFjZSgvXmZpbGU6XC9cLy8sIiIpOnZvaWQgMCxvPXQubG9jYXRpb24uY29sdW1uTnVtYmVyP3QubG9jYXRpb24uY29sdW1uTnVtYmVyKzE6dm9pZCAwLGk9dC5sb2NhdGlvbi5saW5lTnVtYmVyP3QubG9jYXRpb24ubGluZU51bWJlcisxOnZvaWQgMDtyZXR1cm57ZmlsZW5hbWU6cixtb2R1bGU6ZShyKSxmdW5jdGlvbjp0LmZ1bmN0aW9uTmFtZXx8Ij8iLGNvbG5vOm8sbGluZW5vOmksaW5fYXBwOnI/UXQocik6dm9pZCAwfX0odCxlLmdldCh0LmxvY2F0aW9uLnNjcmlwdElkKSxzKSksdT1zZXRUaW1lb3V0KCgpPT57X24oYykudGhlbihudWxsLCgpPT57bG4oIlNlbmRpbmcgQU5SIGV2ZW50IGZhaWxlZC4iKX0pfSw1ZTMpO24ucG9zdCgiUnVudGltZS5ldmFsdWF0ZSIse2V4cHJlc3Npb246Imdsb2JhbC5fX1NFTlRSWV9HRVRfU0NPUEVTX18oKTsiLHNpbGVudDohMCxyZXR1cm5CeVZhbHVlOiEwfSwodCxlKT0+e3QmJmxuKGBFcnJvciBleGVjdXRpbmcgc2NyaXB0OiAnJHt0Lm1lc3NhZ2V9J2ApLGNsZWFyVGltZW91dCh1KTtjb25zdCByPWU/LnJlc3VsdD9lLnJlc3VsdC52YWx1ZTp2b2lkIDA7bi5wb3N0KCJEZWJ1Z2dlci5yZXN1bWUiKSxuLnBvc3QoIkRlYnVnZ2VyLmRpc2FibGUiKSxfbihjLHIpLnRoZW4obnVsbCwoKT0+e2xuKCJTZW5kaW5nIEFOUiBldmVudCBmYWlsZWQuIil9KX0pfWNhdGNoKHQpe3Rocm93IG4ucG9zdCgiRGVidWdnZXIucmVzdW1lIiksbi5wb3N0KCJEZWJ1Z2dlci5kaXNhYmxlIiksdH19KSx3bj0oKT0+e3RyeXtuLnBvc3QoIkRlYnVnZ2VyLmVuYWJsZSIsKCk9PntuLnBvc3QoIkRlYnVnZ2VyLnBhdXNlIil9KX1jYXRjaHt9fX1jb25zdHtwb2xsOlNufT1mdW5jdGlvbih0LG4sZSxyKXtjb25zdCBvPXQoKTtsZXQgaT0hMSxzPSEwO3JldHVybiBzZXRJbnRlcnZhbCgoKT0+e2NvbnN0IHQ9by5nZXRUaW1lTXMoKTshMT09PWkmJnQ+bitlJiYoaT0hMCxzJiZyKCkpLHQ8bitlJiYoaT0hMSl9LDIwKSx7cG9sbDooKT0+e28ucmVzZXQoKX0sZW5hYmxlZDp0PT57cz10fX19KGZ1bmN0aW9uKCl7bGV0IHQ9cHJvY2Vzcy5ocnRpbWUoKTtyZXR1cm57Z2V0VGltZU1zOigpPT57Y29uc3RbbixlXT1wcm9jZXNzLmhydGltZSh0KTtyZXR1cm4gTWF0aC5mbG9vcigxZTMqbitlLzFlNil9LHJlc2V0OigpPT57dD1wcm9jZXNzLmhydGltZSgpfX19LGFuLnBvbGxJbnRlcnZhbCxhbi5hbnJUaHJlc2hvbGQsZnVuY3Rpb24oKXtsbigiV2F0Y2hkb2cgdGltZW91dCIpLHduPyhsbigiUGF1c2luZyBkZWJ1Z2dlciB0byBjYXB0dXJlIHN0YWNrIHRyYWNlIiksd24oKSk6KGxuKCJDYXB0dXJpbmcgZXZlbnQgd2l0aG91dCBhIHN0YWNrIHRyYWNlIiksX24oKS50aGVuKG51bGwsKCk9PntsbigiU2VuZGluZyBBTlIgZXZlbnQgZmFpbGVkIG9uIHdhdGNoZG9nIHRpbWVvdXQuIil9KSl9KTtlPy5vbigibWVzc2FnZSIsdD0+e3Quc2Vzc2lvbiYmKGZuPVkodC5zZXNzaW9uKSksdC5kZWJ1Z0ltYWdlcyYmKHBuPXQuZGVidWdJbWFnZXMpLFNuKCl9KTs='; | ||
| const base64WorkerScript = "LyohIEBzZW50cnkvbm9kZS1jb3JlIDEwLjU0LjAgKGYzMTY4NmIpIHwgaHR0cHM6Ly9naXRodWIuY29tL2dldHNlbnRyeS9zZW50cnktamF2YXNjcmlwdCAqLwppbXBvcnR7U2Vzc2lvbiBhcyB0fWZyb20ibm9kZTppbnNwZWN0b3IiO2ltcG9ydHt3b3JrZXJEYXRhIGFzIG4scGFyZW50UG9ydCBhcyBlfWZyb20ibm9kZTp3b3JrZXJfdGhyZWFkcyI7aW1wb3J0e3Bvc2l4IGFzIHIsc2VwIGFzIG99ZnJvbSJub2RlOnBhdGgiO2ltcG9ydCphcyBpIGZyb20ibm9kZTpodHRwIjtpbXBvcnQqYXMgcyBmcm9tIm5vZGU6aHR0cHMiO2ltcG9ydHtSZWFkYWJsZSBhcyBjfWZyb20ibm9kZTpzdHJlYW0iO2ltcG9ydHtjcmVhdGVHemlwIGFzIHV9ZnJvbSJub2RlOnpsaWIiO2ltcG9ydCphcyBhIGZyb20ibm9kZTpuZXQiO2ltcG9ydCphcyBmIGZyb20ibm9kZTp0bHMiO2NvbnN0IGg9InVuZGVmaW5lZCI9PXR5cGVvZiBfX1NFTlRSWV9ERUJVR19ffHxfX1NFTlRSWV9ERUJVR19fLHA9Z2xvYmFsVGhpcyxsPSIxMC41NC4wIjtmdW5jdGlvbiBkKCl7cmV0dXJuIG0ocCkscH1mdW5jdGlvbiBtKHQpe2NvbnN0IG49dC5fX1NFTlRSWV9fPXQuX19TRU5UUllfX3x8e307cmV0dXJuIG4udmVyc2lvbj1uLnZlcnNpb258fGwsbltsXT1uW2xdfHx7fX1mdW5jdGlvbiBnKHQsbixlPXApe2NvbnN0IHI9ZS5fX1NFTlRSWV9fPWUuX19TRU5UUllfX3x8e30sbz1yW2xdPXJbbF18fHt9O3JldHVybiBvW3RdfHwob1t0XT1uKCkpfWNvbnN0IHk9e307ZnVuY3Rpb24gYih0KXtpZighKCJjb25zb2xlImluIHApKXJldHVybiB0KCk7Y29uc3Qgbj1wLmNvbnNvbGUsZT17fSxyPU9iamVjdC5rZXlzKHkpO3IuZm9yRWFjaCh0PT57Y29uc3Qgcj15W3RdO2VbdF09blt0XSxuW3RdPXJ9KTt0cnl7cmV0dXJuIHQoKX1maW5hbGx5e3IuZm9yRWFjaCh0PT57blt0XT1lW3RdfSl9fWZ1bmN0aW9uIHYoKXtyZXR1cm4gdygpLmVuYWJsZWR9ZnVuY3Rpb24gXyh0LC4uLm4pe2gmJnYoKSYmYigoKT0+e3AuY29uc29sZVt0XShgU2VudHJ5IExvZ2dlciBbJHt0fV06YCwuLi5uKX0pfWZ1bmN0aW9uIHcoKXtyZXR1cm4gaD9nKCJsb2dnZXJTZXR0aW5ncyIsKCk9Pih7ZW5hYmxlZDohMX0pKTp7ZW5hYmxlZDohMX19Y29uc3QgUz17ZW5hYmxlOmZ1bmN0aW9uKCl7dygpLmVuYWJsZWQ9ITB9LGRpc2FibGU6ZnVuY3Rpb24oKXt3KCkuZW5hYmxlZD0hMX0saXNFbmFibGVkOnYsbG9nOmZ1bmN0aW9uKC4uLnQpe18oImxvZyIsLi4udCl9LHdhcm46ZnVuY3Rpb24oLi4udCl7Xygid2FybiIsLi4udCl9LGVycm9yOmZ1bmN0aW9uKC4uLnQpe18oImVycm9yIiwuLi50KX19LCQ9L2NhcHR1cmVNZXNzYWdlfGNhcHR1cmVFeGNlcHRpb24vO2Z1bmN0aW9uIEUodCl7cmV0dXJuIHRbdC5sZW5ndGgtMV18fHt9fWNvbnN0IHg9Ijxhbm9ueW1vdXM+Ijtjb25zdCBOPU9iamVjdC5wcm90b3R5cGUudG9TdHJpbmc7ZnVuY3Rpb24gaih0LG4pe3JldHVybiBOLmNhbGwodCk9PT1gW29iamVjdCAke259XWB9ZnVuY3Rpb24gQyh0KXtyZXR1cm4gaih0LCJTdHJpbmciKX1mdW5jdGlvbiBBKHQpe3JldHVybiBqKHQsIk9iamVjdCIpfWZ1bmN0aW9uIGsodCl7cmV0dXJuIEJvb2xlYW4odD8udGhlbiYmImZ1bmN0aW9uIj09dHlwZW9mIHQudGhlbil9ZnVuY3Rpb24gVCh0LG4pe3RyeXtyZXR1cm4gdCBpbnN0YW5jZW9mIG59Y2F0Y2h7cmV0dXJuITF9fWNvbnN0IEk9cDtmdW5jdGlvbiBPKHQsbil7Y29uc3QgZT10LHI9W107aWYoIWU/LnRhZ05hbWUpcmV0dXJuIiI7aWYoSS5IVE1MRWxlbWVudCYmZSBpbnN0YW5jZW9mIEhUTUxFbGVtZW50JiZlLmRhdGFzZXQpe2lmKGUuZGF0YXNldC5zZW50cnlDb21wb25lbnQpcmV0dXJuIGUuZGF0YXNldC5zZW50cnlDb21wb25lbnQ7aWYoZS5kYXRhc2V0LnNlbnRyeUVsZW1lbnQpcmV0dXJuIGUuZGF0YXNldC5zZW50cnlFbGVtZW50fXIucHVzaChlLnRhZ05hbWUudG9Mb3dlckNhc2UoKSk7Y29uc3Qgbz1uPy5sZW5ndGg/bi5maWx0ZXIodD0+ZS5nZXRBdHRyaWJ1dGUodCkpLm1hcCh0PT5bdCxlLmdldEF0dHJpYnV0ZSh0KV0pOm51bGw7aWYobz8ubGVuZ3RoKW8uZm9yRWFjaCh0PT57ci5wdXNoKGBbJHt0WzBdfT0iJHt0WzFdfSJdYCl9KTtlbHNle2UuaWQmJnIucHVzaChgIyR7ZS5pZH1gKTtjb25zdCB0PWUuY2xhc3NOYW1lO2lmKHQmJkModCkpe2NvbnN0IG49dC5zcGxpdCgvXHMrLyk7Zm9yKGNvbnN0IHQgb2YgbilyLnB1c2goYC4ke3R9YCl9fWZvcihjb25zdCB0IG9mWyJhcmlhLWxhYmVsIiwidHlwZSIsIm5hbWUiLCJ0aXRsZSIsImFsdCJdKXtjb25zdCBuPWUuZ2V0QXR0cmlidXRlKHQpO24mJnIucHVzaChgWyR7dH09IiR7bn0iXWApfXJldHVybiByLmpvaW4oIiIpfWZ1bmN0aW9uIFIodCl7aWYoZnVuY3Rpb24odCl7c3dpdGNoKE4uY2FsbCh0KSl7Y2FzZSJbb2JqZWN0IEVycm9yXSI6Y2FzZSJbb2JqZWN0IEV4Y2VwdGlvbl0iOmNhc2UiW29iamVjdCBET01FeGNlcHRpb25dIjpjYXNlIltvYmplY3QgV2ViQXNzZW1ibHkuRXhjZXB0aW9uXSI6cmV0dXJuITA7ZGVmYXVsdDpyZXR1cm4gVCh0LEVycm9yKX19KHQpKXJldHVybnttZXNzYWdlOnQubWVzc2FnZSxuYW1lOnQubmFtZSxzdGFjazp0LnN0YWNrLC4uLkQodCl9O2lmKG49dCwidW5kZWZpbmVkIiE9dHlwZW9mIEV2ZW50JiZUKG4sRXZlbnQpKXtjb25zdCBuPXt0eXBlOnQudHlwZSx0YXJnZXQ6UCh0LnRhcmdldCksY3VycmVudFRhcmdldDpQKHQuY3VycmVudFRhcmdldCksLi4uRCh0KX07cmV0dXJuInVuZGVmaW5lZCIhPXR5cGVvZiBDdXN0b21FdmVudCYmVCh0LEN1c3RvbUV2ZW50KSYmKG4uZGV0YWlsPXQuZGV0YWlsKSxufXJldHVybiB0O3ZhciBufWZ1bmN0aW9uIFAodCl7dHJ5e3JldHVybiBuPXQsInVuZGVmaW5lZCIhPXR5cGVvZiBFbGVtZW50JiZUKG4sRWxlbWVudCk/ZnVuY3Rpb24odCxuPXt9KXtpZighdClyZXR1cm4iPHVua25vd24+Ijt0cnl7bGV0IGU9dDtjb25zdCByPTUsbz1bXTtsZXQgaT0wLHM9MDtjb25zdCBjPSIgPiAiLHU9Yy5sZW5ndGg7bGV0IGE7Y29uc3QgZj1BcnJheS5pc0FycmF5KG4pP246bi5rZXlBdHRycyxoPSFBcnJheS5pc0FycmF5KG4pJiZuLm1heFN0cmluZ0xlbmd0aHx8ODA7Zm9yKDtlJiZpKys8ciYmKGE9TyhlLGYpLCEoImh0bWwiPT09YXx8aT4xJiZzK28ubGVuZ3RoKnUrYS5sZW5ndGg+PWgpKTspby5wdXNoKGEpLHMrPWEubGVuZ3RoLGU9ZS5wYXJlbnROb2RlO3JldHVybiBvLnJldmVyc2UoKS5qb2luKGMpfWNhdGNoe3JldHVybiI8dW5rbm93bj4ifX0odCk6T2JqZWN0LnByb3RvdHlwZS50b1N0cmluZy5jYWxsKHQpfWNhdGNoe3JldHVybiI8dW5rbm93bj4ifXZhciBufWZ1bmN0aW9uIEQodCl7cmV0dXJuIm9iamVjdCI9PXR5cGVvZiB0JiZudWxsIT09dD9PYmplY3QuZnJvbUVudHJpZXMoT2JqZWN0LmVudHJpZXModCkpOnt9fWxldCBVLEw7ZnVuY3Rpb24gTSh0KXtpZih2b2lkIDAhPT1VKXJldHVybiBVP1UodCk6dCgpO2NvbnN0IG49U3ltYm9sLmZvcigiX19TRU5UUllfU0FGRV9SQU5ET01fSURfV1JBUFBFUl9fIiksZT1wO3JldHVybiBuIGluIGUmJiJmdW5jdGlvbiI9PXR5cGVvZiBlW25dPyhVPWVbbl0sVSh0KSk6KFU9bnVsbCx0KCkpfWZ1bmN0aW9uIEIoKXtyZXR1cm4gTSgoKT0+TWF0aC5yYW5kb20oKSl9ZnVuY3Rpb24geigpe3JldHVybiBNKCgpPT5EYXRlLm5vdygpKX1mdW5jdGlvbiBXKHQsbj0wKXtyZXR1cm4ic3RyaW5nIiE9dHlwZW9mIHR8fDA9PT1ufHx0Lmxlbmd0aDw9bj90OmAke3Quc2xpY2UoMCxuKX0uLi5gfWZ1bmN0aW9uIEYodD1mdW5jdGlvbigpe2NvbnN0IHQ9cDtyZXR1cm4gdC5jcnlwdG98fHQubXNDcnlwdG99KCkpe3RyeXtpZih0Py5yYW5kb21VVUlEKXJldHVybiBNKCgpPT50LnJhbmRvbVVVSUQoKSkucmVwbGFjZSgvLS9nLCIiKX1jYXRjaHt9cmV0dXJuIEx8fChMPSIxMDAwMDAwMDEwMDA0MDAwODAwMDEwMDAwMDAwMDAwMCIpLEwucmVwbGFjZSgvWzAxOF0vZyx0PT4odF4oMTYqQigpJjE1KT4+dC80KS50b1N0cmluZygxNikpfWZ1bmN0aW9uIEcoKXtyZXR1cm4geigpLzFlM31sZXQgSDtmdW5jdGlvbiBKKCl7cmV0dXJuKEg/PyhIPWZ1bmN0aW9uKCl7Y29uc3R7cGVyZm9ybWFuY2U6dH09cDtpZighdD8ubm93fHwhdC50aW1lT3JpZ2luKXJldHVybiBHO2NvbnN0IG49dC50aW1lT3JpZ2luO3JldHVybigpPT4obitNKCgpPT50Lm5vdygpKSkvMWUzfSgpKSkoKX1mdW5jdGlvbiBZKHQpe2NvbnN0IG49SigpLGU9e3NpZDpGKCksaW5pdDohMCx0aW1lc3RhbXA6bixzdGFydGVkOm4sZHVyYXRpb246MCxzdGF0dXM6Im9rIixlcnJvcnM6MCxpZ25vcmVEdXJhdGlvbjohMSx0b0pTT046KCk9PmZ1bmN0aW9uKHQpe3JldHVybntzaWQ6YCR7dC5zaWR9YCxpbml0OnQuaW5pdCxzdGFydGVkOm5ldyBEYXRlKDFlMyp0LnN0YXJ0ZWQpLnRvSVNPU3RyaW5nKCksdGltZXN0YW1wOm5ldyBEYXRlKDFlMyp0LnRpbWVzdGFtcCkudG9JU09TdHJpbmcoKSxzdGF0dXM6dC5zdGF0dXMsZXJyb3JzOnQuZXJyb3JzLGRpZDoibnVtYmVyIj09dHlwZW9mIHQuZGlkfHwic3RyaW5nIj09dHlwZW9mIHQuZGlkP2Ake3QuZGlkfWA6dm9pZCAwLGR1cmF0aW9uOnQuZHVyYXRpb24sYWJub3JtYWxfbWVjaGFuaXNtOnQuYWJub3JtYWxfbWVjaGFuaXNtLGF0dHJzOntyZWxlYXNlOnQucmVsZWFzZSxlbnZpcm9ubWVudDp0LmVudmlyb25tZW50LGlwX2FkZHJlc3M6dC5pcEFkZHJlc3MsdXNlcl9hZ2VudDp0LnVzZXJBZ2VudH19fShlKX07cmV0dXJuIHQmJlYoZSx0KSxlfWZ1bmN0aW9uIFYodCxuPXt9KXtpZihuLnVzZXImJighdC5pcEFkZHJlc3MmJm4udXNlci5pcF9hZGRyZXNzJiYodC5pcEFkZHJlc3M9bi51c2VyLmlwX2FkZHJlc3MpLHQuZGlkfHxuLmRpZHx8KHQuZGlkPW4udXNlci5pZHx8bi51c2VyLmVtYWlsfHxuLnVzZXIudXNlcm5hbWUpKSx0LnRpbWVzdGFtcD1uLnRpbWVzdGFtcHx8SigpLG4uYWJub3JtYWxfbWVjaGFuaXNtJiYodC5hYm5vcm1hbF9tZWNoYW5pc209bi5hYm5vcm1hbF9tZWNoYW5pc20pLG4uaWdub3JlRHVyYXRpb24mJih0Lmlnbm9yZUR1cmF0aW9uPW4uaWdub3JlRHVyYXRpb24pLG4uc2lkJiYodC5zaWQ9MzI9PT1uLnNpZC5sZW5ndGg/bi5zaWQ6RigpKSx2b2lkIDAhPT1uLmluaXQmJih0LmluaXQ9bi5pbml0KSwhdC5kaWQmJm4uZGlkJiYodC5kaWQ9YCR7bi5kaWR9YCksIm51bWJlciI9PXR5cGVvZiBuLnN0YXJ0ZWQmJih0LnN0YXJ0ZWQ9bi5zdGFydGVkKSx0Lmlnbm9yZUR1cmF0aW9uKXQuZHVyYXRpb249dm9pZCAwO2Vsc2UgaWYoIm51bWJlciI9PXR5cGVvZiBuLmR1cmF0aW9uKXQuZHVyYXRpb249bi5kdXJhdGlvbjtlbHNle2NvbnN0IG49dC50aW1lc3RhbXAtdC5zdGFydGVkO3QuZHVyYXRpb249bj49MD9uOjB9bi5yZWxlYXNlJiYodC5yZWxlYXNlPW4ucmVsZWFzZSksbi5lbnZpcm9ubWVudCYmKHQuZW52aXJvbm1lbnQ9bi5lbnZpcm9ubWVudCksIXQuaXBBZGRyZXNzJiZuLmlwQWRkcmVzcyYmKHQuaXBBZGRyZXNzPW4uaXBBZGRyZXNzKSwhdC51c2VyQWdlbnQmJm4udXNlckFnZW50JiYodC51c2VyQWdlbnQ9bi51c2VyQWdlbnQpLCJudW1iZXIiPT10eXBlb2Ygbi5lcnJvcnMmJih0LmVycm9ycz1uLmVycm9ycyksbi5zdGF0dXMmJih0LnN0YXR1cz1uLnN0YXR1cyl9ZnVuY3Rpb24gSyh0LG4sZT0yKXtpZighbnx8Im9iamVjdCIhPXR5cGVvZiBufHxlPD0wKXJldHVybiBuO2lmKHQmJjA9PT1PYmplY3Qua2V5cyhuKS5sZW5ndGgpcmV0dXJuIHQ7Y29uc3Qgcj17Li4udH07Zm9yKGNvbnN0IHQgaW4gbilPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwobix0KSYmKHJbdF09SyhyW3RdLG5bdF0sZS0xKSk7cmV0dXJuIHJ9ZnVuY3Rpb24gWigpe3JldHVybiBGKCl9ZnVuY3Rpb24gcSgpe3JldHVybiBGKCkuc3Vic3RyaW5nKDE2KX1jb25zdCBRPSJfc2VudHJ5U3BhbiI7ZnVuY3Rpb24gWCh0LG4pe24/ZnVuY3Rpb24odCxuLGUpe3RyeXtPYmplY3QuZGVmaW5lUHJvcGVydHkodCxuLHt2YWx1ZTplLHdyaXRhYmxlOiEwLGNvbmZpZ3VyYWJsZTohMH0pfWNhdGNoe2gmJlMubG9nKGBGYWlsZWQgdG8gYWRkIG5vbi1lbnVtZXJhYmxlIHByb3BlcnR5ICIke1N0cmluZyhuKX0iIHRvIG9iamVjdGAsdCl9fSh0LFEsbik6ZGVsZXRlIHRbUV19ZnVuY3Rpb24gdHQodCl7cmV0dXJuIHRbUV19Y2xhc3MgbnR7Y29uc3RydWN0b3IoKXt0aGlzLnQ9ITEsdGhpcy5vPVtdLHRoaXMuaT1bXSx0aGlzLnU9W10sdGhpcy5oPVtdLHRoaXMucD17fSx0aGlzLmw9e30sdGhpcy5tPXt9LHRoaXMudj17fSx0aGlzLl89e30sdGhpcy5TPXt9LHRoaXMuTj17dHJhY2VJZDpaKCksc2FtcGxlUmFuZDpCKCl9fWNsb25lKCl7Y29uc3QgdD1uZXcgbnQ7cmV0dXJuIHQudT1bLi4udGhpcy51XSx0Lmw9ey4uLnRoaXMubH0sdC5tPXsuLi50aGlzLm19LHQudj17Li4udGhpcy52fSx0Ll89ey4uLnRoaXMuX30sdGhpcy5fLmZsYWdzJiYodC5fLmZsYWdzPXt2YWx1ZXM6Wy4uLnRoaXMuXy5mbGFncy52YWx1ZXNdfSksdC5wPXRoaXMucCx0Lmo9dGhpcy5qLHQuQz10aGlzLkMsdC5BPXRoaXMuQSx0Lms9dGhpcy5rLHQuaT1bLi4udGhpcy5pXSx0Lmg9Wy4uLnRoaXMuaF0sdC5TPXsuLi50aGlzLlN9LHQuTj17Li4udGhpcy5OfSx0LlQ9dGhpcy5ULHQuST10aGlzLkksdC5PPXRoaXMuTyxYKHQsdHQodGhpcykpLHR9c2V0Q2xpZW50KHQpe3RoaXMuVD10fXNldExhc3RFdmVudElkKHQpe3RoaXMuST10fWdldENsaWVudCgpe3JldHVybiB0aGlzLlR9bGFzdEV2ZW50SWQoKXtyZXR1cm4gdGhpcy5JfWFkZFNjb3BlTGlzdGVuZXIodCl7dGhpcy5vLnB1c2godCl9YWRkRXZlbnRQcm9jZXNzb3IodCl7cmV0dXJuIHRoaXMuaS5wdXNoKHQpLHRoaXN9c2V0VXNlcih0KXtyZXR1cm4gdGhpcy5wPXR8fHtlbWFpbDp2b2lkIDAsaWQ6dm9pZCAwLGlwX2FkZHJlc3M6dm9pZCAwLHVzZXJuYW1lOnZvaWQgMH0sdGhpcy5DJiZWKHRoaXMuQyx7dXNlcjp0fSksdGhpcy5SKCksdGhpc31nZXRVc2VyKCl7cmV0dXJuIHRoaXMucH1zZXRDb252ZXJzYXRpb25JZCh0KXtyZXR1cm4gdGhpcy5PPXR8fHZvaWQgMCx0aGlzLlIoKSx0aGlzfXNldFRhZ3ModCl7cmV0dXJuIHRoaXMubD17Li4udGhpcy5sLC4uLnR9LHRoaXMuUigpLHRoaXN9c2V0VGFnKHQsbil7cmV0dXJuIHRoaXMuc2V0VGFncyh7W3RdOm59KX1zZXRBdHRyaWJ1dGVzKHQpe3JldHVybiB0aGlzLm09ey4uLnRoaXMubSwuLi50fSx0aGlzLlIoKSx0aGlzfXNldEF0dHJpYnV0ZSh0LG4pe3JldHVybiB0aGlzLnNldEF0dHJpYnV0ZXMoe1t0XTpufSl9cmVtb3ZlQXR0cmlidXRlKHQpe3JldHVybiB0IGluIHRoaXMubSYmKGRlbGV0ZSB0aGlzLm1bdF0sdGhpcy5SKCkpLHRoaXN9c2V0RXh0cmFzKHQpe3JldHVybiB0aGlzLnY9ey4uLnRoaXMudiwuLi50fSx0aGlzLlIoKSx0aGlzfXNldEV4dHJhKHQsbil7cmV0dXJuIHRoaXMudj17Li4udGhpcy52LFt0XTpufSx0aGlzLlIoKSx0aGlzfXNldEZpbmdlcnByaW50KHQpe3JldHVybiB0aGlzLms9dCx0aGlzLlIoKSx0aGlzfXNldExldmVsKHQpe3JldHVybiB0aGlzLmo9dCx0aGlzLlIoKSx0aGlzfXNldFRyYW5zYWN0aW9uTmFtZSh0KXtyZXR1cm4gdGhpcy5BPXQsdGhpcy5SKCksdGhpc31zZXRDb250ZXh0KHQsbil7cmV0dXJuIG51bGw9PT1uP2RlbGV0ZSB0aGlzLl9bdF06dGhpcy5fW3RdPW4sdGhpcy5SKCksdGhpc31zZXRTZXNzaW9uKHQpe3JldHVybiB0P3RoaXMuQz10OmRlbGV0ZSB0aGlzLkMsdGhpcy5SKCksdGhpc31nZXRTZXNzaW9uKCl7cmV0dXJuIHRoaXMuQ311cGRhdGUodCl7aWYoIXQpcmV0dXJuIHRoaXM7Y29uc3Qgbj0iZnVuY3Rpb24iPT10eXBlb2YgdD90KHRoaXMpOnQsZT1uIGluc3RhbmNlb2YgbnQ/bi5nZXRTY29wZURhdGEoKTpBKG4pP3Q6dm9pZCAwLHt0YWdzOnIsYXR0cmlidXRlczpvLGV4dHJhOmksdXNlcjpzLGNvbnRleHRzOmMsbGV2ZWw6dSxmaW5nZXJwcmludDphPVtdLHByb3BhZ2F0aW9uQ29udGV4dDpmLGNvbnZlcnNhdGlvbklkOmh9PWV8fHt9O3JldHVybiB0aGlzLmw9ey4uLnRoaXMubCwuLi5yfSx0aGlzLm09ey4uLnRoaXMubSwuLi5vfSx0aGlzLnY9ey4uLnRoaXMudiwuLi5pfSx0aGlzLl89ey4uLnRoaXMuXywuLi5jfSxzJiZPYmplY3Qua2V5cyhzKS5sZW5ndGgmJih0aGlzLnA9cyksdSYmKHRoaXMuaj11KSxhLmxlbmd0aCYmKHRoaXMuaz1hKSxmJiYodGhpcy5OPWYpLGgmJih0aGlzLk89aCksdGhpc31jbGVhcigpe3JldHVybiB0aGlzLnU9W10sdGhpcy5sPXt9LHRoaXMubT17fSx0aGlzLnY9e30sdGhpcy5wPXt9LHRoaXMuXz17fSx0aGlzLmo9dm9pZCAwLHRoaXMuQT12b2lkIDAsdGhpcy5rPXZvaWQgMCx0aGlzLkM9dm9pZCAwLHRoaXMuTz12b2lkIDAsWCh0aGlzLHZvaWQgMCksdGhpcy5oPVtdLHRoaXMuc2V0UHJvcGFnYXRpb25Db250ZXh0KHt0cmFjZUlkOlooKSxzYW1wbGVSYW5kOkIoKX0pLHRoaXMuUigpLHRoaXN9YWRkQnJlYWRjcnVtYih0LG4pe2NvbnN0IGU9Im51bWJlciI9PXR5cGVvZiBuP246MTAwO2lmKGU8PTApcmV0dXJuIHRoaXM7Y29uc3Qgcj17dGltZXN0YW1wOkcoKSwuLi50LG1lc3NhZ2U6dC5tZXNzYWdlP1codC5tZXNzYWdlLDIwNDgpOnQubWVzc2FnZX07cmV0dXJuIHRoaXMudS5wdXNoKHIpLHRoaXMudS5sZW5ndGg+ZSYmKHRoaXMudT10aGlzLnUuc2xpY2UoLWUpLHRoaXMuVD8ucmVjb3JkRHJvcHBlZEV2ZW50KCJidWZmZXJfb3ZlcmZsb3ciLCJsb2dfaXRlbSIpKSx0aGlzLlIoKSx0aGlzfWdldExhc3RCcmVhZGNydW1iKCl7cmV0dXJuIHRoaXMudVt0aGlzLnUubGVuZ3RoLTFdfWNsZWFyQnJlYWRjcnVtYnMoKXtyZXR1cm4gdGhpcy51PVtdLHRoaXMuUigpLHRoaXN9YWRkQXR0YWNobWVudCh0KXtyZXR1cm4gdGhpcy5oLnB1c2godCksdGhpc31jbGVhckF0dGFjaG1lbnRzKCl7cmV0dXJuIHRoaXMuaD1bXSx0aGlzfWdldFNjb3BlRGF0YSgpe3JldHVybnticmVhZGNydW1iczp0aGlzLnUsYXR0YWNobWVudHM6dGhpcy5oLGNvbnRleHRzOnRoaXMuXyx0YWdzOnRoaXMubCxhdHRyaWJ1dGVzOnRoaXMubSxleHRyYTp0aGlzLnYsdXNlcjp0aGlzLnAsbGV2ZWw6dGhpcy5qLGZpbmdlcnByaW50OnRoaXMua3x8W10sZXZlbnRQcm9jZXNzb3JzOnRoaXMuaSxwcm9wYWdhdGlvbkNvbnRleHQ6dGhpcy5OLHNka1Byb2Nlc3NpbmdNZXRhZGF0YTp0aGlzLlMsdHJhbnNhY3Rpb25OYW1lOnRoaXMuQSxzcGFuOnR0KHRoaXMpLGNvbnZlcnNhdGlvbklkOnRoaXMuT319c2V0U0RLUHJvY2Vzc2luZ01ldGFkYXRhKHQpe3JldHVybiB0aGlzLlM9Syh0aGlzLlMsdCwyKSx0aGlzfXNldFByb3BhZ2F0aW9uQ29udGV4dCh0KXtyZXR1cm4gdGhpcy5OPXQsdGhpc31nZXRQcm9wYWdhdGlvbkNvbnRleHQoKXtyZXR1cm4gdGhpcy5OfWNhcHR1cmVFeGNlcHRpb24odCxuKXtjb25zdCBlPW4/LmV2ZW50X2lkfHxGKCk7aWYoIXRoaXMuVClyZXR1cm4gaCYmUy53YXJuKCJObyBjbGllbnQgY29uZmlndXJlZCBvbiBzY29wZSAtIHdpbGwgbm90IGNhcHR1cmUgZXhjZXB0aW9uISIpLGU7Y29uc3Qgcj1uZXcgRXJyb3IoIlNlbnRyeSBzeW50aGV0aWNFeGNlcHRpb24iKTtyZXR1cm4gdGhpcy5ULmNhcHR1cmVFeGNlcHRpb24odCx7b3JpZ2luYWxFeGNlcHRpb246dCxzeW50aGV0aWNFeGNlcHRpb246ciwuLi5uLGV2ZW50X2lkOmV9LHRoaXMpLGV9Y2FwdHVyZU1lc3NhZ2UodCxuLGUpe2NvbnN0IHI9ZT8uZXZlbnRfaWR8fEYoKTtpZighdGhpcy5UKXJldHVybiBoJiZTLndhcm4oIk5vIGNsaWVudCBjb25maWd1cmVkIG9uIHNjb3BlIC0gd2lsbCBub3QgY2FwdHVyZSBtZXNzYWdlISIpLHI7Y29uc3Qgbz1lPy5zeW50aGV0aWNFeGNlcHRpb24/P25ldyBFcnJvcih0KTtyZXR1cm4gdGhpcy5ULmNhcHR1cmVNZXNzYWdlKHQsbix7b3JpZ2luYWxFeGNlcHRpb246dCxzeW50aGV0aWNFeGNlcHRpb246bywuLi5lLGV2ZW50X2lkOnJ9LHRoaXMpLHJ9Y2FwdHVyZUV2ZW50KHQsbil7Y29uc3QgZT10LmV2ZW50X2lkfHxuPy5ldmVudF9pZHx8RigpO3JldHVybiB0aGlzLlQ/KHRoaXMuVC5jYXB0dXJlRXZlbnQodCx7Li4ubixldmVudF9pZDplfSx0aGlzKSxlKTooaCYmUy53YXJuKCJObyBjbGllbnQgY29uZmlndXJlZCBvbiBzY29wZSAtIHdpbGwgbm90IGNhcHR1cmUgZXZlbnQhIiksZSl9Uigpe3RoaXMudHx8KHRoaXMudD0hMCx0aGlzLm8uZm9yRWFjaCh0PT57dCh0aGlzKX0pLHRoaXMudD0hMSl9fWNvbnN0IGV0PXQ9PnQgaW5zdGFuY2VvZiBQcm9taXNlJiYhdFtydF0scnQ9U3ltYm9sKCJjaGFpbmVkIFByb21pc2VMaWtlIiksb3Q9KHQsbik9PntpZighbilyZXR1cm4gdDtsZXQgZT0hMTtmb3IoY29uc3QgciBpbiB0KXtpZihyIGluIG4pY29udGludWU7ZT0hMDtjb25zdCBvPXRbcl07ImZ1bmN0aW9uIj09dHlwZW9mIG8/T2JqZWN0LmRlZmluZVByb3BlcnR5KG4scix7dmFsdWU6KC4uLm4pPT5vLmFwcGx5KHQsbiksZW51bWVyYWJsZTohMCxjb25maWd1cmFibGU6ITAsd3JpdGFibGU6ITB9KTpuW3JdPW99cmV0dXJuIGUmJk9iamVjdC5hc3NpZ24obix7W3J0XTohMH0pLG59O2NsYXNzIGl0e2NvbnN0cnVjdG9yKHQsbil7bGV0IGUscjtlPXR8fG5ldyBudCxyPW58fG5ldyBudCx0aGlzLlA9W3tzY29wZTplfV0sdGhpcy5EPXJ9d2l0aFNjb3BlKHQpe2NvbnN0IG49dGhpcy5VKCk7bGV0IGU7dHJ5e2U9dChuKX1jYXRjaCh0KXt0aHJvdyB0aGlzLkwoKSx0fXJldHVybiBrKGUpPygodCxuLGUpPT57Y29uc3Qgcj10LnRoZW4odD0+KG4odCksdCksdD0+e3Rocm93IGUodCksdH0pO3JldHVybiBldChyKSYmZXQodCk/cjpvdCh0LHIpfSkoZSwoKT0+dGhpcy5MKCksKCk9PnRoaXMuTCgpKToodGhpcy5MKCksZSl9Z2V0Q2xpZW50KCl7cmV0dXJuIHRoaXMuZ2V0U3RhY2tUb3AoKS5jbGllbnR9Z2V0U2NvcGUoKXtyZXR1cm4gdGhpcy5nZXRTdGFja1RvcCgpLnNjb3BlfWdldElzb2xhdGlvblNjb3BlKCl7cmV0dXJuIHRoaXMuRH1nZXRTdGFja1RvcCgpe3JldHVybiB0aGlzLlBbdGhpcy5QLmxlbmd0aC0xXX1VKCl7Y29uc3QgdD10aGlzLmdldFNjb3BlKCkuY2xvbmUoKTtyZXR1cm4gdGhpcy5QLnB1c2goe2NsaWVudDp0aGlzLmdldENsaWVudCgpLHNjb3BlOnR9KSx0fUwoKXtyZXR1cm4hKHRoaXMuUC5sZW5ndGg8PTEpJiYhIXRoaXMuUC5wb3AoKX19ZnVuY3Rpb24gc3QoKXtjb25zdCB0PW0oZCgpKTtyZXR1cm4gdC5zdGFjaz10LnN0YWNrfHxuZXcgaXQoZygiZGVmYXVsdEN1cnJlbnRTY29wZSIsKCk9Pm5ldyBudCksZygiZGVmYXVsdElzb2xhdGlvblNjb3BlIiwoKT0+bmV3IG50KSl9ZnVuY3Rpb24gY3QodCl7cmV0dXJuIHN0KCkud2l0aFNjb3BlKHQpfWZ1bmN0aW9uIHV0KHQsbil7Y29uc3QgZT1zdCgpO3JldHVybiBlLndpdGhTY29wZSgoKT0+KGUuZ2V0U3RhY2tUb3AoKS5zY29wZT10LG4odCkpKX1mdW5jdGlvbiBhdCh0KXtyZXR1cm4gc3QoKS53aXRoU2NvcGUoKCk9PnQoc3QoKS5nZXRJc29sYXRpb25TY29wZSgpKSl9ZnVuY3Rpb24gZnQodCl7Y29uc3Qgbj1tKHQpO3JldHVybiBuLmFjcz9uLmFjczp7d2l0aElzb2xhdGlvblNjb3BlOmF0LHdpdGhTY29wZTpjdCx3aXRoU2V0U2NvcGU6dXQsd2l0aFNldElzb2xhdGlvblNjb3BlOih0LG4pPT5hdChuKSxnZXRDdXJyZW50U2NvcGU6KCk9PnN0KCkuZ2V0U2NvcGUoKSxnZXRJc29sYXRpb25TY29wZTooKT0+c3QoKS5nZXRJc29sYXRpb25TY29wZSgpfX1mdW5jdGlvbiBodCgpe3JldHVybiBmdChkKCkpLmdldEN1cnJlbnRTY29wZSgpLmdldENsaWVudCgpfWZ1bmN0aW9uIHB0KHQpe2lmKHQpe2lmKCJvYmplY3QiPT10eXBlb2YgdCYmImRlcmVmImluIHQmJiJmdW5jdGlvbiI9PXR5cGVvZiB0LmRlcmVmKXRyeXtyZXR1cm4gdC5kZXJlZigpfWNhdGNoe3JldHVybn1yZXR1cm4gdH19ZnVuY3Rpb24gbHQodCl7Y29uc3Qgbj10O3JldHVybntzY29wZTpuLl9zZW50cnlTY29wZSxpc29sYXRpb25TY29wZTpwdChuLl9zZW50cnlJc29sYXRpb25TY29wZSl9fWNvbnN0IGR0PSJzZW50cnktIjtmdW5jdGlvbiBtdCh0KXtjb25zdCBuPWZ1bmN0aW9uKHQpe2lmKCF0fHwhQyh0KSYmIUFycmF5LmlzQXJyYXkodCkpcmV0dXJuO2lmKEFycmF5LmlzQXJyYXkodCkpcmV0dXJuIHQucmVkdWNlKCh0LG4pPT57Y29uc3QgZT1ndChuKTtyZXR1cm4gT2JqZWN0LmVudHJpZXMoZSkuZm9yRWFjaCgoW24sZV0pPT57dFtuXT1lfSksdH0se30pO3JldHVybiBndCh0KX0odCk7aWYoIW4pcmV0dXJuO2NvbnN0IGU9T2JqZWN0LmVudHJpZXMobikucmVkdWNlKCh0LFtuLGVdKT0+e2lmKG4uc3RhcnRzV2l0aChkdCkpe3Rbbi5zbGljZSg3KV09ZX1yZXR1cm4gdH0se30pO3JldHVybiBPYmplY3Qua2V5cyhlKS5sZW5ndGg+MD9lOnZvaWQgMH1mdW5jdGlvbiBndCh0KXtyZXR1cm4gdC5zcGxpdCgiLCIpLm1hcCh0PT57Y29uc3Qgbj10LmluZGV4T2YoIj0iKTtpZigtMT09PW4pcmV0dXJuW107cmV0dXJuW3Quc2xpY2UoMCxuKSx0LnNsaWNlKG4rMSldLm1hcCh0PT57dHJ5e3JldHVybiBkZWNvZGVVUklDb21wb25lbnQodC50cmltKCkpfWNhdGNoe3JldHVybn19KX0pLnJlZHVjZSgodCxbbixlXSk9PihuJiZlJiYodFtuXT1lKSx0KSx7fSl9Y29uc3QgeXQ9L15vKFxkKylcLi87ZnVuY3Rpb24gYnQodCxuPSExKXtjb25zdHtob3N0OmUscGF0aDpyLHBhc3M6byxwb3J0OmkscHJvamVjdElkOnMscHJvdG9jb2w6YyxwdWJsaWNLZXk6dX09dDtyZXR1cm5gJHtjfTovLyR7dX0ke24mJm8/YDoke299YDoiIn1AJHtlfSR7aT9gOiR7aX1gOiIifS8ke3I/YCR7cn0vYDpyfSR7c31gfWZ1bmN0aW9uIHZ0KHQpe2NvbnN0IG49dC5nZXRPcHRpb25zKCkse2hvc3Q6ZX09dC5nZXREc24oKXx8e307bGV0IHI7cmV0dXJuIG4ub3JnSWQ/cj1TdHJpbmcobi5vcmdJZCk6ZSYmKHI9ZnVuY3Rpb24odCl7Y29uc3Qgbj10Lm1hdGNoKHl0KTtyZXR1cm4gbj8uWzFdfShlKSkscn1mdW5jdGlvbiBfdCh0KXtjb25zdHtzcGFuSWQ6bix0cmFjZUlkOmUsaXNSZW1vdGU6cn09dC5zcGFuQ29udGV4dCgpLG89cj9uOkV0KHQpLnBhcmVudF9zcGFuX2lkLGk9bHQodCkuc2NvcGU7cmV0dXJue3BhcmVudF9zcGFuX2lkOm8sc3Bhbl9pZDpyP2k/LmdldFByb3BhZ2F0aW9uQ29udGV4dCgpLnByb3BhZ2F0aW9uU3BhbklkfHxxKCk6bix0cmFjZV9pZDplfX1mdW5jdGlvbiB3dCh0KXtyZXR1cm4gdCYmdC5sZW5ndGg+MD90Lm1hcCgoe2NvbnRleHQ6e3NwYW5JZDp0LHRyYWNlSWQ6bix0cmFjZUZsYWdzOmUsLi4ucn0sYXR0cmlidXRlczpvfSk9Pih7c3Bhbl9pZDp0LHRyYWNlX2lkOm4sc2FtcGxlZDoxPT09ZSxhdHRyaWJ1dGVzOm8sLi4ucn0pKTp2b2lkIDB9ZnVuY3Rpb24gU3QodCl7cmV0dXJuIm51bWJlciI9PXR5cGVvZiB0PyR0KHQpOkFycmF5LmlzQXJyYXkodCk/dFswXSt0WzFdLzFlOTp0IGluc3RhbmNlb2YgRGF0ZT8kdCh0LmdldFRpbWUoKSk6SigpfWZ1bmN0aW9uICR0KHQpe3JldHVybiB0Pjk5OTk5OTk5OTk/dC8xZTM6dH1mdW5jdGlvbiBFdCh0KXtpZihmdW5jdGlvbih0KXtyZXR1cm4iZnVuY3Rpb24iPT10eXBlb2YgdC5nZXRTcGFuSlNPTn0odCkpcmV0dXJuIHQuZ2V0U3BhbkpTT04oKTtjb25zdHtzcGFuSWQ6bix0cmFjZUlkOmV9PXQuc3BhbkNvbnRleHQoKTtpZihmdW5jdGlvbih0KXtjb25zdCBuPXQ7cmV0dXJuISEobi5hdHRyaWJ1dGVzJiZuLnN0YXJ0VGltZSYmbi5uYW1lJiZuLmVuZFRpbWUmJm4uc3RhdHVzKX0odCkpe2NvbnN0e2F0dHJpYnV0ZXM6cixzdGFydFRpbWU6byxuYW1lOmksZW5kVGltZTpzLHN0YXR1czpjLGxpbmtzOnV9PXQ7cmV0dXJue3NwYW5faWQ6bix0cmFjZV9pZDplLGRhdGE6cixkZXNjcmlwdGlvbjppLHBhcmVudF9zcGFuX2lkOnh0KHQpLHN0YXJ0X3RpbWVzdGFtcDpTdChvKSx0aW1lc3RhbXA6U3Qocyl8fHZvaWQgMCxzdGF0dXM6TnQoYyksb3A6clsic2VudHJ5Lm9wIl0sb3JpZ2luOnJbInNlbnRyeS5vcmlnaW4iXSxsaW5rczp3dCh1KX19cmV0dXJue3NwYW5faWQ6bix0cmFjZV9pZDplLHN0YXJ0X3RpbWVzdGFtcDowLGRhdGE6e319fWZ1bmN0aW9uIHh0KHQpe3JldHVybiJwYXJlbnRTcGFuSWQiaW4gdD90LnBhcmVudFNwYW5JZDoicGFyZW50U3BhbkNvbnRleHQiaW4gdD90LnBhcmVudFNwYW5Db250ZXh0Py5zcGFuSWQ6dm9pZCAwfWZ1bmN0aW9uIE50KHQpe2lmKHQmJjAhPT10LmNvZGUpcmV0dXJuIDE9PT10LmNvZGU/Im9rIjp0Lm1lc3NhZ2V8fCJpbnRlcm5hbF9lcnJvciJ9Y29uc3QganQ9ZnVuY3Rpb24odCl7cmV0dXJuIHQuX3NlbnRyeVJvb3RTcGFufHx0fTtmdW5jdGlvbiBDdCh0KXtjb25zdCBuPWh0KCk7aWYoIW4pcmV0dXJue307Y29uc3QgZT1qdCh0KSxyPUV0KGUpLG89ci5kYXRhLGk9ZS5zcGFuQ29udGV4dCgpLnRyYWNlU3RhdGUscz1pPy5nZXQoInNlbnRyeS5zYW1wbGVfcmF0ZSIpPz9vWyJzZW50cnkuc2FtcGxlX3JhdGUiXT8/b1sic2VudHJ5LnByZXZpb3VzX3RyYWNlX3NhbXBsZV9yYXRlIl07ZnVuY3Rpb24gYyh0KXtyZXR1cm4ibnVtYmVyIiE9dHlwZW9mIHMmJiJzdHJpbmciIT10eXBlb2Ygc3x8KHQuc2FtcGxlX3JhdGU9YCR7c31gKSx0fWNvbnN0IHU9ZS5fZnJvemVuRHNjO2lmKHUpcmV0dXJuIGModSk7Y29uc3QgYT1pPy5nZXQoInNlbnRyeS5kc2MiKSxmPWEmJm10KGEpO2lmKGYpcmV0dXJuIGMoZik7Y29uc3QgaD1mdW5jdGlvbih0LG4pe2NvbnN0IGU9bi5nZXRPcHRpb25zKCkse3B1YmxpY0tleTpyfT1uLmdldERzbigpfHx7fSxvPXtlbnZpcm9ubWVudDplLmVudmlyb25tZW50fHwicHJvZHVjdGlvbiIscmVsZWFzZTplLnJlbGVhc2UscHVibGljX2tleTpyLHRyYWNlX2lkOnQsb3JnX2lkOnZ0KG4pfTtyZXR1cm4gbi5lbWl0KCJjcmVhdGVEc2MiLG8pLG99KHQuc3BhbkNvbnRleHQoKS50cmFjZUlkLG4pLHA9b1sic2VudHJ5LnNvdXJjZSJdPz9vWyJzZW50cnkuc3Bhbi5zb3VyY2UiXSxsPXIuZGVzY3JpcHRpb247cmV0dXJuInVybCIhPT1wJiZsJiYoaC50cmFuc2FjdGlvbj1sKSxmdW5jdGlvbigpe2lmKCJib29sZWFuIj09dHlwZW9mIF9fU0VOVFJZX1RSQUNJTkdfXyYmIV9fU0VOVFJZX1RSQUNJTkdfXylyZXR1cm4hMTtjb25zdCB0PWh0KCk/LmdldE9wdGlvbnMoKTtyZXR1cm4hKCF0fHxudWxsPT10LnRyYWNlc1NhbXBsZVJhdGUmJiF0LnRyYWNlc1NhbXBsZXIpfSgpJiYoaC5zYW1wbGVkPVN0cmluZyhmdW5jdGlvbih0KXtjb25zdHt0cmFjZUZsYWdzOm59PXQuc3BhbkNvbnRleHQoKTtyZXR1cm4gMT09PW59KGUpKSxoLnNhbXBsZV9yYW5kPWk/LmdldCgic2VudHJ5LnNhbXBsZV9yYW5kIik/P2x0KGUpLnNjb3BlPy5nZXRQcm9wYWdhdGlvbkNvbnRleHQoKS5zYW1wbGVSYW5kLnRvU3RyaW5nKCkpLGMoaCksbi5lbWl0KCJjcmVhdGVEc2MiLGgsZSksaH1jb25zdCBBdD1TeW1ib2wuZm9yKCJzZW50cnkuc2tpcE5vcm1hbGl6YXRpb24iKSxrdD1TeW1ib2wuZm9yKCJzZW50cnkub3ZlcnJpZGVOb3JtYWxpemF0aW9uRGVwdGgiKTtmdW5jdGlvbiBUdCh0LG49MTAwLGU9MS8wKXt0cnl7cmV0dXJuIEl0KCIiLHQsbixlKX1jYXRjaCh0KXtyZXR1cm57RVJST1I6YCoqbm9uLXNlcmlhbGl6YWJsZSoqICgke3R9KWB9fX1mdW5jdGlvbiBJdCh0LG4sZT0xLzAscj0xLzAsbz1mdW5jdGlvbigpe2NvbnN0IHQ9bmV3IFdlYWtTZXQ7ZnVuY3Rpb24gbihuKXtyZXR1cm4hIXQuaGFzKG4pfHwodC5hZGQobiksITEpfWZ1bmN0aW9uIGUobil7dC5kZWxldGUobil9cmV0dXJuW24sZV19KCkpe2NvbnN0W2ksc109bztpZihudWxsPT1ufHxbImJvb2xlYW4iLCJzdHJpbmciXS5pbmNsdWRlcyh0eXBlb2Ygbil8fCJudW1iZXIiPT10eXBlb2YgbiYmTnVtYmVyLmlzRmluaXRlKG4pKXJldHVybiBuO2NvbnN0IGM9ZnVuY3Rpb24odCxuKXt0cnl7aWYoImRvbWFpbiI9PT10JiZuJiYib2JqZWN0Ij09dHlwZW9mIG4mJm4uTSlyZXR1cm4iW0RvbWFpbl0iO2lmKCJkb21haW5FbWl0dGVyIj09PXQpcmV0dXJuIltEb21haW5FbWl0dGVyXSI7aWYoInVuZGVmaW5lZCIhPXR5cGVvZiBnbG9iYWwmJm49PT1nbG9iYWwpcmV0dXJuIltHbG9iYWxdIjtpZigidW5kZWZpbmVkIiE9dHlwZW9mIHdpbmRvdyYmbj09PXdpbmRvdylyZXR1cm4iW1dpbmRvd10iO2lmKCJ1bmRlZmluZWQiIT10eXBlb2YgZG9jdW1lbnQmJm49PT1kb2N1bWVudClyZXR1cm4iW0RvY3VtZW50XSI7aWYoIm9iamVjdCI9PXR5cGVvZihlPW4pJiZudWxsIT09ZSYmKGUuX19pc1Z1ZXx8ZS5CfHxlLl9fdl9pc1ZOb2RlKSlyZXR1cm4gZnVuY3Rpb24odCl7cmV0dXJuIl9fdl9pc1ZOb2RlImluIHQmJnQuX192X2lzVk5vZGU/IltWdWVWTm9kZV0iOiJbVnVlVmlld01vZGVsXSJ9KG4pO2lmKGZ1bmN0aW9uKHQpe3JldHVybiBBKHQpJiYibmF0aXZlRXZlbnQiaW4gdCYmInByZXZlbnREZWZhdWx0ImluIHQmJiJzdG9wUHJvcGFnYXRpb24iaW4gdH0obikpcmV0dXJuIltTeW50aGV0aWNFdmVudF0iO2lmKCJudW1iZXIiPT10eXBlb2YgbiYmIU51bWJlci5pc0Zpbml0ZShuKSlyZXR1cm5gWyR7bn1dYDtpZigiZnVuY3Rpb24iPT10eXBlb2YgbilyZXR1cm5gW0Z1bmN0aW9uOiAke2Z1bmN0aW9uKHQpe3RyeXtyZXR1cm4gdCYmImZ1bmN0aW9uIj09dHlwZW9mIHQmJnQubmFtZXx8eH1jYXRjaHtyZXR1cm4geH19KG4pfV1gO2lmKCJzeW1ib2wiPT10eXBlb2YgbilyZXR1cm5gWyR7U3RyaW5nKG4pfV1gO2lmKCJiaWdpbnQiPT10eXBlb2YgbilyZXR1cm5gW0JpZ0ludDogJHtTdHJpbmcobil9XWA7Y29uc3Qgcj1mdW5jdGlvbih0KXtjb25zdCBuPU9iamVjdC5nZXRQcm90b3R5cGVPZih0KTtyZXR1cm4gbj8uY29uc3RydWN0b3I/bi5jb25zdHJ1Y3Rvci5uYW1lOiJudWxsIHByb3RvdHlwZSJ9KG4pO3JldHVybi9eSFRNTChcdyopRWxlbWVudCQvLnRlc3Qocik/YFtIVE1MRWxlbWVudDogJHtyfV1gOmBbb2JqZWN0ICR7cn1dYH1jYXRjaCh0KXtyZXR1cm5gKipub24tc2VyaWFsaXphYmxlKiogKCR7dH0pYH12YXIgZX0odCxuKTtpZighYy5zdGFydHNXaXRoKCJbb2JqZWN0ICIpKXJldHVybiBjO2lmKGZ1bmN0aW9uKHQpe3JldHVybiBCb29sZWFuKHRbQXRdKX0obikpcmV0dXJuIG47Y29uc3QgdT1mdW5jdGlvbih0KXtjb25zdCBuPXRba3RdO3JldHVybiJudW1iZXIiPT10eXBlb2Ygbj9uOnZvaWQgMH0obiksYT12b2lkIDAhPT11P3U6ZTtpZigwPT09YSlyZXR1cm4gYy5yZXBsYWNlKCJvYmplY3QgIiwiIik7aWYoaShuKSlyZXR1cm4iW0NpcmN1bGFyIH5dIjtjb25zdCBmPW47aWYoZiYmImZ1bmN0aW9uIj09dHlwZW9mIGYudG9KU09OKXRyeXtyZXR1cm4gSXQoIiIsZi50b0pTT04oKSxhLTEscixvKX1jYXRjaHt9Y29uc3QgaD1BcnJheS5pc0FycmF5KG4pP1tdOnt9O2xldCBwPTA7Y29uc3QgbD1SKG4pO2Zvcihjb25zdCB0IGluIGwpe2lmKCFPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwobCx0KSljb250aW51ZTtpZihwPj1yKXtoW3RdPSJbTWF4UHJvcGVydGllcyB+XSI7YnJlYWt9Y29uc3Qgbj1sW3RdO2hbdF09SXQodCxuLGEtMSxyLG8pLHArK31yZXR1cm4gcyhuKSxofWZ1bmN0aW9uIE90KHQsbil7Y29uc3QgZT1uLnJlcGxhY2UoL1xcL2csIi8iKS5yZXBsYWNlKC9bfFxce30oKVtcXV4kKyo/Ll0vZywiXFwkJiIpO2xldCByPXQ7dHJ5e3I9ZGVjb2RlVVJJKHQpfWNhdGNoe31yZXR1cm4gci5yZXBsYWNlKC9cXC9nLCIvIikucmVwbGFjZSgvd2VicGFjazpcLz8vZywiIikucmVwbGFjZShuZXcgUmVnRXhwKGAoZmlsZTovLyk/Lyoke2V9LypgLCJpZyIpLCJhcHA6Ly8vIil9ZnVuY3Rpb24gUnQodCxuPVtdKXtyZXR1cm5bdCxuXX1mdW5jdGlvbiBQdCh0LG4pe2NvbnN0IGU9dFsxXTtmb3IoY29uc3QgdCBvZiBlKXtpZihuKHQsdFswXS50eXBlKSlyZXR1cm4hMH1yZXR1cm4hMX1mdW5jdGlvbiBEdCh0KXtjb25zdCBuPW0ocCk7cmV0dXJuIG4uZW5jb2RlUG9seWZpbGw/bi5lbmNvZGVQb2x5ZmlsbCh0KToobmV3IFRleHRFbmNvZGVyKS5lbmNvZGUodCl9ZnVuY3Rpb24gVXQodCl7Y29uc3RbbixlXT10O2xldCByPUpTT04uc3RyaW5naWZ5KG4pO2Z1bmN0aW9uIG8odCl7InN0cmluZyI9PXR5cGVvZiByP3I9InN0cmluZyI9PXR5cGVvZiB0P3IrdDpbRHQociksdF06ci5wdXNoKCJzdHJpbmciPT10eXBlb2YgdD9EdCh0KTp0KX1mb3IoY29uc3QgdCBvZiBlKXtjb25zdFtuLGVdPXQ7aWYobyhgXG4ke0pTT04uc3RyaW5naWZ5KG4pfVxuYCksInN0cmluZyI9PXR5cGVvZiBlfHxlIGluc3RhbmNlb2YgVWludDhBcnJheSlvKGUpO2Vsc2V7bGV0IHQ7dHJ5e3Q9SlNPTi5zdHJpbmdpZnkoZSl9Y2F0Y2h7dD1KU09OLnN0cmluZ2lmeShUdChlKSl9byh0KX19cmV0dXJuInN0cmluZyI9PXR5cGVvZiByP3I6ZnVuY3Rpb24odCl7Y29uc3Qgbj10LnJlZHVjZSgodCxuKT0+dCtuLmxlbmd0aCwwKSxlPW5ldyBVaW50OEFycmF5KG4pO2xldCByPTA7Zm9yKGNvbnN0IG4gb2YgdCllLnNldChuLHIpLHIrPW4ubGVuZ3RoO3JldHVybiBlfShyKX1jb25zdCBMdD17c2Vzc2lvbnM6InNlc3Npb24iLGV2ZW50OiJlcnJvciIsY2xpZW50X3JlcG9ydDoiaW50ZXJuYWwiLHVzZXJfcmVwb3J0OiJkZWZhdWx0Iixwcm9maWxlX2NodW5rOiJwcm9maWxlIixyZXBsYXlfZXZlbnQ6InJlcGxheSIscmVwbGF5X3JlY29yZGluZzoicmVwbGF5IixjaGVja19pbjoibW9uaXRvciIscmF3X3NlY3VyaXR5OiJzZWN1cml0eSIsbG9nOiJsb2dfaXRlbSIsdHJhY2VfbWV0cmljOiJtZXRyaWMifTtmdW5jdGlvbiBNdCh0KXtyZXR1cm4gZnVuY3Rpb24odCl7cmV0dXJuIHQgaW4gTHR9KHQpP0x0W3RdOnR9ZnVuY3Rpb24gQnQodCl7aWYoIXQ/LnNkaylyZXR1cm47Y29uc3R7bmFtZTpuLHZlcnNpb246ZX09dC5zZGs7cmV0dXJue25hbWU6bix2ZXJzaW9uOmV9fWZ1bmN0aW9uIHp0KHQsbixlLHIpe2NvbnN0IG89QnQoZSksaT10LnR5cGUmJiJyZXBsYXlfZXZlbnQiIT09dC50eXBlP3QudHlwZToiZXZlbnQiOyFmdW5jdGlvbih0LG4pe2lmKCFuKXJldHVybiB0O2NvbnN0IGU9dC5zZGt8fHt9O3Quc2RrPXsuLi5lLG5hbWU6ZS5uYW1lfHxuLm5hbWUsdmVyc2lvbjplLnZlcnNpb258fG4udmVyc2lvbixpbnRlZ3JhdGlvbnM6Wy4uLnQuc2RrPy5pbnRlZ3JhdGlvbnN8fFtdLC4uLm4uaW50ZWdyYXRpb25zfHxbXV0scGFja2FnZXM6Wy4uLnQuc2RrPy5wYWNrYWdlc3x8W10sLi4ubi5wYWNrYWdlc3x8W11dLHNldHRpbmdzOnQuc2RrPy5zZXR0aW5nc3x8bi5zZXR0aW5ncz97Li4udC5zZGs/LnNldHRpbmdzLC4uLm4uc2V0dGluZ3N9OnZvaWQgMH19KHQsZT8uc2RrKTtjb25zdCBzPWZ1bmN0aW9uKHQsbixlLHIpe2NvbnN0IG89dC5zZGtQcm9jZXNzaW5nTWV0YWRhdGE/LmR5bmFtaWNTYW1wbGluZ0NvbnRleHQ7cmV0dXJue2V2ZW50X2lkOnQuZXZlbnRfaWQsc2VudF9hdDoobmV3IERhdGUpLnRvSVNPU3RyaW5nKCksLi4ubiYme3NkazpufSwuLi4hIWUmJnImJntkc246YnQocil9LC4uLm8mJnt0cmFjZTpvfX19KHQsbyxyLG4pO2RlbGV0ZSB0LnNka1Byb2Nlc3NpbmdNZXRhZGF0YTtyZXR1cm4gUnQocyxbW3t0eXBlOml9LHRdXSl9Y29uc3QgV3Q9Il9fU0VOVFJZX1NVUFBSRVNTX1RSQUNJTkdfXyI7ZnVuY3Rpb24gRnQodCl7Y29uc3Qgbj1mdChkKCkpO3JldHVybiBuLnN1cHByZXNzVHJhY2luZz9uLnN1cHByZXNzVHJhY2luZyh0KTpmdW5jdGlvbiguLi50KXtjb25zdCBuPWZ0KGQoKSk7aWYoMj09PXQubGVuZ3RoKXtjb25zdFtlLHJdPXQ7cmV0dXJuIGU/bi53aXRoU2V0U2NvcGUoZSxyKTpuLndpdGhTY29wZShyKX1yZXR1cm4gbi53aXRoU2NvcGUodFswXSl9KG49PntuLnNldFNES1Byb2Nlc3NpbmdNZXRhZGF0YSh7W1d0XTohMH0pO2NvbnN0IGU9dCgpO3JldHVybiBuLnNldFNES1Byb2Nlc3NpbmdNZXRhZGF0YSh7W1d0XTp2b2lkIDB9KSxlfSl9ZnVuY3Rpb24gR3QodCxuKXtjb25zdHtmaW5nZXJwcmludDplLHNwYW46cixicmVhZGNydW1iczpvLHNka1Byb2Nlc3NpbmdNZXRhZGF0YTppfT1uOyFmdW5jdGlvbih0LG4pe2NvbnN0e2V4dHJhOmUsdGFnczpyLHVzZXI6byxjb250ZXh0czppLGxldmVsOnMsdHJhbnNhY3Rpb25OYW1lOmN9PW47T2JqZWN0LmtleXMoZSkubGVuZ3RoJiYodC5leHRyYT17Li4uZSwuLi50LmV4dHJhfSk7T2JqZWN0LmtleXMocikubGVuZ3RoJiYodC50YWdzPXsuLi5yLC4uLnQudGFnc30pO09iamVjdC5rZXlzKG8pLmxlbmd0aCYmKHQudXNlcj17Li4ubywuLi50LnVzZXJ9KTtPYmplY3Qua2V5cyhpKS5sZW5ndGgmJih0LmNvbnRleHRzPXsuLi5pLC4uLnQuY29udGV4dHN9KTtzJiYodC5sZXZlbD1zKTtjJiYidHJhbnNhY3Rpb24iIT09dC50eXBlJiYodC50cmFuc2FjdGlvbj1jKX0odCxuKSxyJiZmdW5jdGlvbih0LG4pe3QuY29udGV4dHM9e3RyYWNlOl90KG4pLC4uLnQuY29udGV4dHN9LHQuc2RrUHJvY2Vzc2luZ01ldGFkYXRhPXtkeW5hbWljU2FtcGxpbmdDb250ZXh0OkN0KG4pLC4uLnQuc2RrUHJvY2Vzc2luZ01ldGFkYXRhfTtjb25zdCBlPWp0KG4pLHI9RXQoZSkuZGVzY3JpcHRpb247ciYmIXQudHJhbnNhY3Rpb24mJiJ0cmFuc2FjdGlvbiI9PT10LnR5cGUmJih0LnRyYW5zYWN0aW9uPXIpfSh0LHIpLGZ1bmN0aW9uKHQsbil7dC5maW5nZXJwcmludD10LmZpbmdlcnByaW50P0FycmF5LmlzQXJyYXkodC5maW5nZXJwcmludCk/dC5maW5nZXJwcmludDpbdC5maW5nZXJwcmludF06W10sbiYmKHQuZmluZ2VycHJpbnQ9dC5maW5nZXJwcmludC5jb25jYXQobikpO3QuZmluZ2VycHJpbnQubGVuZ3RofHxkZWxldGUgdC5maW5nZXJwcmludH0odCxlKSxmdW5jdGlvbih0LG4pe2NvbnN0IGU9Wy4uLnQuYnJlYWRjcnVtYnN8fFtdLC4uLm5dO3QuYnJlYWRjcnVtYnM9ZS5sZW5ndGg/ZTp2b2lkIDB9KHQsbyksZnVuY3Rpb24odCxuKXt0LnNka1Byb2Nlc3NpbmdNZXRhZGF0YT17Li4udC5zZGtQcm9jZXNzaW5nTWV0YWRhdGEsLi4ubn19KHQsaSl9Y2xhc3MgSHR7Y29uc3RydWN0b3IodCl7dGhpcy5XPTAsdGhpcy5GPVtdLHRoaXMuRyh0KX10aGVuKHQsbil7cmV0dXJuIG5ldyBIdCgoZSxyKT0+e3RoaXMuRi5wdXNoKFshMSxuPT57aWYodCl0cnl7ZSh0KG4pKX1jYXRjaCh0KXtyKHQpfWVsc2UgZShuKX0sdD0+e2lmKG4pdHJ5e2Uobih0KSl9Y2F0Y2godCl7cih0KX1lbHNlIHIodCl9XSksdGhpcy5IKCl9KX1jYXRjaCh0KXtyZXR1cm4gdGhpcy50aGVuKHQ9PnQsdCl9ZmluYWxseSh0KXtyZXR1cm4gbmV3IEh0KChuLGUpPT57bGV0IHIsbztyZXR1cm4gdGhpcy50aGVuKG49PntvPSExLHI9bix0JiZ0KCl9LG49PntvPSEwLHI9bix0JiZ0KCl9KS50aGVuKCgpPT57bz9lKHIpOm4ocil9KX0pfUgoKXtpZigwPT09dGhpcy5XKXJldHVybjtjb25zdCB0PXRoaXMuRi5zbGljZSgpO3RoaXMuRj1bXSx0LmZvckVhY2godD0+e3RbMF18fCgxPT09dGhpcy5XJiZ0WzFdKHRoaXMuSiksMj09PXRoaXMuVyYmdFsyXSh0aGlzLkopLHRbMF09ITApfSl9Ryh0KXtjb25zdCBuPSh0LG4pPT57MD09PXRoaXMuVyYmKGsobik/bi50aGVuKGUscik6KHRoaXMuVz10LHRoaXMuSj1uLHRoaXMuSCgpKSl9LGU9dD0+e24oMSx0KX0scj10PT57bigyLHQpfTt0cnl7dChlLHIpfWNhdGNoKHQpe3IodCl9fX1jb25zdCBKdD1TeW1ib2wuZm9yKCJTZW50cnlCdWZmZXJGdWxsRXJyb3IiKTtmdW5jdGlvbiBZdCh0PTEwMCl7Y29uc3Qgbj1uZXcgU2V0O2Z1bmN0aW9uIGUodCl7bi5kZWxldGUodCl9cmV0dXJue2dldCAkKCl7cmV0dXJuIEFycmF5LmZyb20obil9LGFkZDpmdW5jdGlvbihyKXtpZighKG4uc2l6ZTx0KSlyZXR1cm4gbz1KdCxuZXcgSHQoKHQsbik9PntuKG8pfSk7dmFyIG87Y29uc3QgaT1yKCk7cmV0dXJuIG4uYWRkKGkpLGkudGhlbigoKT0+ZShpKSwoKT0+ZShpKSksaX0sZHJhaW46ZnVuY3Rpb24odCl7aWYoIW4uc2l6ZSlyZXR1cm4gZT0hMCxuZXcgSHQodD0+e3QoZSl9KTt2YXIgZTtjb25zdCByPVByb21pc2UuYWxsU2V0dGxlZChBcnJheS5mcm9tKG4pKS50aGVuKCgpPT4hMCk7aWYoIXQpcmV0dXJuIHI7Y29uc3Qgbz1bcixuZXcgUHJvbWlzZShuPT57cmV0dXJuIm9iamVjdCI9PXR5cGVvZihlPXNldFRpbWVvdXQoKCk9Pm4oITEpLHQpKSYmImZ1bmN0aW9uIj09dHlwZW9mIGUudW5yZWYmJmUudW5yZWYoKSxlO3ZhciBlfSldO3JldHVybiBQcm9taXNlLnJhY2Uobyl9fX1mdW5jdGlvbiBWdCh0LHtzdGF0dXNDb2RlOm4saGVhZGVyczplfSxyPXooKSl7Y29uc3Qgbz17Li4udH0saT1lPy5bIngtc2VudHJ5LXJhdGUtbGltaXRzIl0scz1lPy5bInJldHJ5LWFmdGVyIl07aWYoaSlmb3IoY29uc3QgdCBvZiBpLnRyaW0oKS5zcGxpdCgiLCIpKXtjb25zdFtuLGUsLCxpXT10LnNwbGl0KCI6Iiw1KSxzPXBhcnNlSW50KG4sMTApLGM9MWUzKihpc05hTihzKT82MDpzKTtpZihlKWZvcihjb25zdCB0IG9mIGUuc3BsaXQoIjsiKSkibWV0cmljX2J1Y2tldCI9PT10JiZpJiYhaS5zcGxpdCgiOyIpLmluY2x1ZGVzKCJjdXN0b20iKXx8KG9bdF09citjKTtlbHNlIG8uYWxsPXIrY31lbHNlIHM/by5hbGw9citmdW5jdGlvbih0LG49eigpKXtjb25zdCBlPXBhcnNlSW50KGAke3R9YCwxMCk7aWYoIWlzTmFOKGUpKXJldHVybiAxZTMqZTtjb25zdCByPURhdGUucGFyc2UoYCR7dH1gKTtyZXR1cm4gaXNOYU4ocik/NmU0OnItbn0ocyxyKTo0Mjk9PT1uJiYoby5hbGw9cis2ZTQpO3JldHVybiBvfWZ1bmN0aW9uIEt0KHQsbixlPVl0KHQuYnVmZmVyU2l6ZXx8NjQpKXtsZXQgcj17fTtyZXR1cm57c2VuZDpmdW5jdGlvbih0KXtjb25zdCBvPVtdO2lmKFB0KHQsKHQsbik9Pntjb25zdCBlPU10KG4pOyhmdW5jdGlvbih0LG4sZT16KCkpe3JldHVybiBmdW5jdGlvbih0LG4pe3JldHVybiB0W25dfHx0LmFsbHx8MH0odCxuKT5lfSkocixlKXx8by5wdXNoKHQpfSksMD09PW8ubGVuZ3RoKXJldHVybiBQcm9taXNlLnJlc29sdmUoe30pO2NvbnN0IGk9UnQodFswXSxvKSxzPXQ9PnshZnVuY3Rpb24odCxuKXtyZXR1cm4gUHQodCwodCxlKT0+bi5pbmNsdWRlcyhlKSl9KGksWyJjbGllbnRfcmVwb3J0Il0pP1B0KGksKHQsbik9Pnt9KTpoJiZTLndhcm4oYERyb3BwaW5nIGNsaWVudCByZXBvcnQuIFdpbGwgbm90IHNlbmQgb3V0Y29tZXMgKHJlYXNvbjogJHt0fSkuYCl9O3JldHVybiBlLmFkZCgoKT0+bih7Ym9keTpVdChpKX0pLnRoZW4odD0+NDEzPT09dC5zdGF0dXNDb2RlPyhoJiZTLmVycm9yKCJTZW50cnkgcmVzcG9uZGVkIHdpdGggc3RhdHVzIGNvZGUgNDEzLiBFbnZlbG9wZSB3YXMgZGlzY2FyZGVkIGR1ZSB0byBleGNlZWRpbmcgc2l6ZSBsaW1pdHMuIikscygic2VuZF9lcnJvciIpLHQpOihoJiZ2b2lkIDAhPT10LnN0YXR1c0NvZGUmJih0LnN0YXR1c0NvZGU8MjAwfHx0LnN0YXR1c0NvZGU+PTMwMCkmJlMud2FybihgU2VudHJ5IHJlc3BvbmRlZCB3aXRoIHN0YXR1cyBjb2RlICR7dC5zdGF0dXNDb2RlfSB0byBzZW50IGV2ZW50LmApLHI9VnQocix0KSx0KSx0PT57dGhyb3cgcygibmV0d29ya19lcnJvciIpLGgmJlMuZXJyb3IoIkVuY291bnRlcmVkIGVycm9yIHJ1bm5pbmcgdHJhbnNwb3J0IHJlcXVlc3Q6Iix0KSx0fSkpLnRoZW4odD0+dCx0PT57aWYodD09PUp0KXJldHVybiBoJiZTLmVycm9yKCJTa2lwcGVkIHNlbmRpbmcgZXZlbnQgYmVjYXVzZSBidWZmZXIgaXMgZnVsbC4iKSxzKCJxdWV1ZV9vdmVyZmxvdyIpLFByb21pc2UucmVzb2x2ZSh7fSk7dGhyb3cgdH0pfSxmbHVzaDp0PT5lLmRyYWluKHQpfX1jb25zdCBadD0vXihcUys6XFx8XC8/KShbXHNcU10qPykoKD86XC57MSwyfXxbXi9cXF0rP3wpKFwuW14uL1xcXSp8KSkoPzpbL1xcXSopJC87ZnVuY3Rpb24gcXQodCl7Y29uc3Qgbj1mdW5jdGlvbih0KXtjb25zdCBuPXQubGVuZ3RoPjEwMjQ/YDx0cnVuY2F0ZWQ+JHt0LnNsaWNlKC0xMDI0KX1gOnQsZT1adC5leGVjKG4pO3JldHVybiBlP2Uuc2xpY2UoMSk6W119KHQpLGU9blswXXx8IiI7bGV0IHI9blsxXTtyZXR1cm4gZXx8cj8ociYmKHI9ci5zbGljZSgwLHIubGVuZ3RoLTEpKSxlK3IpOiIuIn1mdW5jdGlvbiBRdCh0LG49ITEpe3JldHVybiEobnx8dCYmIXQuc3RhcnRzV2l0aCgiLyIpJiYhdC5tYXRjaCgvXltBLVpdOi8pJiYhdC5zdGFydHNXaXRoKCIuIikmJiF0Lm1hdGNoKC9eW2EtekEtWl0oW2EtekEtWjAtOS5cLStdKSo6XC9cLy8pKSYmdm9pZCAwIT09dCYmIXQuaW5jbHVkZXMoIm5vZGVfbW9kdWxlcy8iKX12YXIgWHQ7Y29uc3QgdG49U3ltYm9sKCJBZ2VudEJhc2VJbnRlcm5hbFN0YXRlIik7Y2xhc3Mgbm4gZXh0ZW5kcyhYdD1pLkFnZW50LFh0KXtjb25zdHJ1Y3Rvcih0KXtzdXBlcih0KSx0aGlzW3RuXT17fX1pc1NlY3VyZUVuZHBvaW50KHQpe2lmKHQpe2lmKCJib29sZWFuIj09dHlwZW9mIHQuc2VjdXJlRW5kcG9pbnQpcmV0dXJuIHQuc2VjdXJlRW5kcG9pbnQ7aWYoInN0cmluZyI9PXR5cGVvZiB0LnByb3RvY29sKXJldHVybiJodHRwczoiPT09dC5wcm90b2NvbH1jb25zdHtzdGFjazpufT1uZXcgRXJyb3I7cmV0dXJuInN0cmluZyI9PXR5cGVvZiBuJiZuLnNwbGl0KCJcbiIpLnNvbWUodD0+LTEhPT10LmluZGV4T2YoIihodHRwcy5qczoiKXx8LTEhPT10LmluZGV4T2YoIm5vZGU6aHR0cHM6IikpfWNyZWF0ZVNvY2tldCh0LG4sZSl7Y29uc3Qgcj17Li4ubixzZWN1cmVFbmRwb2ludDp0aGlzLmlzU2VjdXJlRW5kcG9pbnQobil9O1Byb21pc2UucmVzb2x2ZSgpLnRoZW4oKCk9PnRoaXMuY29ubmVjdCh0LHIpKS50aGVuKG89PntpZihvIGluc3RhbmNlb2YgaS5BZ2VudClyZXR1cm4gby5hZGRSZXF1ZXN0KHQscik7dGhpc1t0bl0uY3VycmVudFNvY2tldD1vLHN1cGVyLmNyZWF0ZVNvY2tldCh0LG4sZSl9LGUpfWNyZWF0ZUNvbm5lY3Rpb24oKXtjb25zdCB0PXRoaXNbdG5dLmN1cnJlbnRTb2NrZXQ7aWYodGhpc1t0bl0uY3VycmVudFNvY2tldD12b2lkIDAsIXQpdGhyb3cgbmV3IEVycm9yKCJObyBzb2NrZXQgd2FzIHJldHVybmVkIGluIHRoZSBgY29ubmVjdCgpYCBmdW5jdGlvbiIpO3JldHVybiB0fWdldCBkZWZhdWx0UG9ydCgpe3JldHVybiB0aGlzW3RuXS5kZWZhdWx0UG9ydD8/KCJodHRwczoiPT09dGhpcy5wcm90b2NvbD80NDM6ODApfXNldCBkZWZhdWx0UG9ydCh0KXt0aGlzW3RuXSYmKHRoaXNbdG5dLmRlZmF1bHRQb3J0PXQpfWdldCBwcm90b2NvbCgpe3JldHVybiB0aGlzW3RuXS5wcm90b2NvbD8/KHRoaXMuaXNTZWN1cmVFbmRwb2ludCgpPyJodHRwczoiOiJodHRwOiIpfXNldCBwcm90b2NvbCh0KXt0aGlzW3RuXSYmKHRoaXNbdG5dLnByb3RvY29sPXQpfX1mdW5jdGlvbiBlbiguLi50KXtTLmxvZygiW2h0dHBzLXByb3h5LWFnZW50OnBhcnNlLXByb3h5LXJlc3BvbnNlXSIsLi4udCl9ZnVuY3Rpb24gcm4odCl7cmV0dXJuIG5ldyBQcm9taXNlKChuLGUpPT57bGV0IHI9MDtjb25zdCBvPVtdO2Z1bmN0aW9uIGkoKXtjb25zdCBjPXQucmVhZCgpO2M/ZnVuY3Rpb24oYyl7by5wdXNoKGMpLHIrPWMubGVuZ3RoO2NvbnN0IHU9QnVmZmVyLmNvbmNhdChvLHIpLGE9dS5pbmRleE9mKCJcclxuXHJcbiIpO2lmKC0xPT09YSlyZXR1cm4gZW4oImhhdmUgbm90IHJlY2VpdmVkIGVuZCBvZiBIVFRQIGhlYWRlcnMgeWV0Li4uIiksdm9pZCBpKCk7Y29uc3QgZj11LnN1YmFycmF5KDAsYSkudG9TdHJpbmcoImFzY2lpIikuc3BsaXQoIlxyXG4iKSxoPWYuc2hpZnQoKTtpZighaClyZXR1cm4gdC5kZXN0cm95KCksZShuZXcgRXJyb3IoIk5vIGhlYWRlciByZWNlaXZlZCBmcm9tIHByb3h5IENPTk5FQ1QgcmVzcG9uc2UiKSk7Y29uc3QgcD1oLnNwbGl0KCIgIiksbD0rKHBbMV18fDApLGQ9cC5zbGljZSgyKS5qb2luKCIgIiksbT17fTtmb3IoY29uc3QgbiBvZiBmKXtpZighbiljb250aW51ZTtjb25zdCByPW4uaW5kZXhPZigiOiIpO2lmKC0xPT09cilyZXR1cm4gdC5kZXN0cm95KCksZShuZXcgRXJyb3IoYEludmFsaWQgaGVhZGVyIGZyb20gcHJveHkgQ09OTkVDVCByZXNwb25zZTogIiR7bn0iYCkpO2NvbnN0IG89bi5zbGljZSgwLHIpLnRvTG93ZXJDYXNlKCksaT1uLnNsaWNlKHIrMSkudHJpbVN0YXJ0KCkscz1tW29dOyJzdHJpbmciPT10eXBlb2Ygcz9tW29dPVtzLGldOkFycmF5LmlzQXJyYXkocyk/cy5wdXNoKGkpOm1bb109aX1lbigiZ290IHByb3h5IHNlcnZlciByZXNwb25zZTogJW8gJW8iLGgsbSkscygpLG4oe2Nvbm5lY3Q6e3N0YXR1c0NvZGU6bCxzdGF0dXNUZXh0OmQsaGVhZGVyczptfSxidWZmZXJlZDp1fSl9KGMpOnQub25jZSgicmVhZGFibGUiLGkpfWZ1bmN0aW9uIHMoKXt0LnJlbW92ZUxpc3RlbmVyKCJlbmQiLGMpLHQucmVtb3ZlTGlzdGVuZXIoImVycm9yIix1KSx0LnJlbW92ZUxpc3RlbmVyKCJyZWFkYWJsZSIsaSl9ZnVuY3Rpb24gYygpe3MoKSxlbigib25lbmQiKSxlKG5ldyBFcnJvcigiUHJveHkgY29ubmVjdGlvbiBlbmRlZCBiZWZvcmUgcmVjZWl2aW5nIENPTk5FQ1QgcmVzcG9uc2UiKSl9ZnVuY3Rpb24gdSh0KXtzKCksZW4oIm9uZXJyb3IgJW8iLHQpLGUodCl9dC5vbigiZXJyb3IiLHUpLHQub24oImVuZCIsYyksaSgpfSl9ZnVuY3Rpb24gb24oLi4udCl7Uy5sb2coIltodHRwcy1wcm94eS1hZ2VudF0iLC4uLnQpfWNsYXNzIHNuIGV4dGVuZHMgbm57Y29uc3RydWN0b3IodCxuKXtzdXBlcihuKSx0aGlzLm9wdGlvbnM9e30sdGhpcy5wcm94eT0ic3RyaW5nIj09dHlwZW9mIHQ/bmV3IFVSTCh0KTp0LHRoaXMucHJveHlIZWFkZXJzPW4/LmhlYWRlcnM/P3t9LG9uKCJDcmVhdGluZyBuZXcgSHR0cHNQcm94eUFnZW50IGluc3RhbmNlOiAlbyIsdGhpcy5wcm94eS5ocmVmKTtjb25zdCBlPSh0aGlzLnByb3h5Lmhvc3RuYW1lfHx0aGlzLnByb3h5Lmhvc3QpLnJlcGxhY2UoL15cW3xcXSQvZywiIikscj10aGlzLnByb3h5LnBvcnQ/cGFyc2VJbnQodGhpcy5wcm94eS5wb3J0LDEwKToiaHR0cHM6Ij09PXRoaXMucHJveHkucHJvdG9jb2w/NDQzOjgwO3RoaXMuY29ubmVjdE9wdHM9e0FMUE5Qcm90b2NvbHM6WyJodHRwLzEuMSJdLC4uLm4/dW4obiwiaGVhZGVycyIpOm51bGwsaG9zdDplLHBvcnQ6cn19YXN5bmMgY29ubmVjdCh0LG4pe2NvbnN0e3Byb3h5OmV9PXRoaXM7aWYoIW4uaG9zdCl0aHJvdyBuZXcgVHlwZUVycm9yKCdObyAiaG9zdCIgcHJvdmlkZWQnKTtsZXQgcjtpZigiaHR0cHM6Ij09PWUucHJvdG9jb2wpe29uKCJDcmVhdGluZyBgdGxzLlNvY2tldGA6ICVvIix0aGlzLmNvbm5lY3RPcHRzKTtjb25zdCB0PXRoaXMuY29ubmVjdE9wdHMuc2VydmVybmFtZXx8dGhpcy5jb25uZWN0T3B0cy5ob3N0O3I9Zi5jb25uZWN0KHsuLi50aGlzLmNvbm5lY3RPcHRzLHNlcnZlcm5hbWU6dCYmYS5pc0lQKHQpP3ZvaWQgMDp0fSl9ZWxzZSBvbigiQ3JlYXRpbmcgYG5ldC5Tb2NrZXRgOiAlbyIsdGhpcy5jb25uZWN0T3B0cykscj1hLmNvbm5lY3QodGhpcy5jb25uZWN0T3B0cyk7Y29uc3Qgbz0iZnVuY3Rpb24iPT10eXBlb2YgdGhpcy5wcm94eUhlYWRlcnM/dGhpcy5wcm94eUhlYWRlcnMoKTp7Li4udGhpcy5wcm94eUhlYWRlcnN9LGk9YS5pc0lQdjYobi5ob3N0KT9gWyR7bi5ob3N0fV1gOm4uaG9zdDtsZXQgcz1gQ09OTkVDVCAke2l9OiR7bi5wb3J0fSBIVFRQLzEuMVxyXG5gO2lmKGUudXNlcm5hbWV8fGUucGFzc3dvcmQpe2NvbnN0IHQ9YCR7ZGVjb2RlVVJJQ29tcG9uZW50KGUudXNlcm5hbWUpfToke2RlY29kZVVSSUNvbXBvbmVudChlLnBhc3N3b3JkKX1gO29bIlByb3h5LUF1dGhvcml6YXRpb24iXT1gQmFzaWMgJHtCdWZmZXIuZnJvbSh0KS50b1N0cmluZygiYmFzZTY0Iil9YH1vLkhvc3Q9YCR7aX06JHtuLnBvcnR9YCxvWyJQcm94eS1Db25uZWN0aW9uIl18fChvWyJQcm94eS1Db25uZWN0aW9uIl09dGhpcy5rZWVwQWxpdmU/IktlZXAtQWxpdmUiOiJjbG9zZSIpO2Zvcihjb25zdCB0IG9mIE9iamVjdC5rZXlzKG8pKXMrPWAke3R9OiAke29bdF19XHJcbmA7Y29uc3QgYz1ybihyKTtyLndyaXRlKGAke3N9XHJcbmApO2NvbnN0e2Nvbm5lY3Q6dSxidWZmZXJlZDpofT1hd2FpdCBjO2lmKHQuZW1pdCgicHJveHlDb25uZWN0Iix1KSx0aGlzLmVtaXQoInByb3h5Q29ubmVjdCIsdSx0KSwyMDA9PT11LnN0YXR1c0NvZGUpe2lmKHQub25jZSgic29ja2V0Iixjbiksbi5zZWN1cmVFbmRwb2ludCl7b24oIlVwZ3JhZGluZyBzb2NrZXQgY29ubmVjdGlvbiB0byBUTFMiKTtjb25zdCB0PW4uc2VydmVybmFtZXx8bi5ob3N0O3JldHVybiBmLmNvbm5lY3Qoey4uLnVuKG4sImhvc3QiLCJwYXRoIiwicG9ydCIpLHNvY2tldDpyLHNlcnZlcm5hbWU6YS5pc0lQKHQpP3ZvaWQgMDp0fSl9cmV0dXJuIHJ9ci5kZXN0cm95KCk7Y29uc3QgcD1uZXcgYS5Tb2NrZXQoe3dyaXRhYmxlOiExfSk7cmV0dXJuIHAucmVhZGFibGU9ITAsdC5vbmNlKCJzb2NrZXQiLHQ9PntvbigiUmVwbGF5aW5nIHByb3h5IGJ1ZmZlciBmb3IgZmFpbGVkIHJlcXVlc3QiKSx0LnB1c2goaCksdC5wdXNoKG51bGwpfSkscH19ZnVuY3Rpb24gY24odCl7dC5yZXN1bWUoKX1mdW5jdGlvbiB1bih0LC4uLm4pe2NvbnN0IGU9e307bGV0IHI7Zm9yKHIgaW4gdCluLmluY2x1ZGVzKHIpfHwoZVtyXT10W3JdKTtyZXR1cm4gZX1zbi5wcm90b2NvbHM9WyJodHRwIiwiaHR0cHMiXTtmdW5jdGlvbiBhbih0KXtyZXR1cm4gdC5yZXBsYWNlKC9eW0EtWl06LywiIikucmVwbGFjZSgvXFwvZywiLyIpfWNvbnN0IGZuPW47bGV0IGhuLHBuPTAsbG49e307ZnVuY3Rpb24gZG4odCl7Zm4uZGVidWcmJmNvbnNvbGUubG9nKGBbQU5SIFdvcmtlcl0gJHt0fWApfXZhciBtbixnbix5bjtjb25zdCBibj1mdW5jdGlvbih0KXtsZXQgbjt0cnl7bj1uZXcgVVJMKHQudXJsKX1jYXRjaChuKXtyZXR1cm4gYigoKT0+e2NvbnNvbGUud2FybigiW0BzZW50cnkvbm9kZV06IEludmFsaWQgZHNuIG9yIHR1bm5lbCBvcHRpb24sIHdpbGwgbm90IHNlbmQgYW55IGV2ZW50cy4gVGhlIHR1bm5lbCBvcHRpb24gbXVzdCBiZSBhIGZ1bGwgVVJMIHdoZW4gdXNlZC4iKX0pLEt0KHQsKCk9PlByb21pc2UucmVzb2x2ZSh7fSkpfWNvbnN0IGU9Imh0dHBzOiI9PT1uLnByb3RvY29sLHI9ZnVuY3Rpb24odCxuKXtjb25zdHtub19wcm94eTplfT1wcm9jZXNzLmVudixyPWU/LnNwbGl0KCIsIikuc29tZShuPT50Lmhvc3QuZW5kc1dpdGgobil8fHQuaG9zdG5hbWUuZW5kc1dpdGgobikpO3JldHVybiByP3ZvaWQgMDpufShuLHQucHJveHl8fChlP3Byb2Nlc3MuZW52Lmh0dHBzX3Byb3h5OnZvaWQgMCl8fHByb2Nlc3MuZW52Lmh0dHBfcHJveHkpLG89ZT9zOmksYT12b2lkIDAhPT10LmtlZXBBbGl2ZSYmdC5rZWVwQWxpdmUsZj1yP25ldyBzbihyKTpuZXcgby5BZ2VudCh7a2VlcEFsaXZlOmEsbWF4U29ja2V0czozMCx0aW1lb3V0OjJlM30pLGg9ZnVuY3Rpb24odCxuLGUpe2NvbnN0e2hvc3RuYW1lOnIscGF0aG5hbWU6byxwb3J0OmkscHJvdG9jb2w6cyxzZWFyY2g6YX09bmV3IFVSTCh0LnVybCk7cmV0dXJuIGZ1bmN0aW9uKGYpe3JldHVybiBuZXcgUHJvbWlzZSgoaCxwKT0+e0Z0KCgpPT57bGV0IGw9ZnVuY3Rpb24odCl7cmV0dXJuIG5ldyBjKHtyZWFkKCl7dGhpcy5wdXNoKHQpLHRoaXMucHVzaChudWxsKX19KX0oZi5ib2R5KTtjb25zdCBkPXsuLi50LmhlYWRlcnN9O2YuYm9keS5sZW5ndGg+MzI3NjgmJihkWyJjb250ZW50LWVuY29kaW5nIl09Imd6aXAiLGw9bC5waXBlKHUoKSkpO2NvbnN0IG09ci5zdGFydHNXaXRoKCJbIiksZz1uLnJlcXVlc3Qoe21ldGhvZDoiUE9TVCIsYWdlbnQ6ZSxoZWFkZXJzOmQsaG9zdG5hbWU6bT9yLnNsaWNlKDEsLTEpOnIscGF0aDpgJHtvfSR7YX1gLHBvcnQ6aSxwcm90b2NvbDpzLGNhOnQuY2FDZXJ0c30sdD0+e3Qub24oImRhdGEiLCgpPT57fSksdC5vbigiZW5kIiwoKT0+e30pLHQuc2V0RW5jb2RpbmcoInV0ZjgiKTtjb25zdCBuPXQuaGVhZGVyc1sicmV0cnktYWZ0ZXIiXT8/bnVsbCxlPXQuaGVhZGVyc1sieC1zZW50cnktcmF0ZS1saW1pdHMiXT8/bnVsbDtoKHtzdGF0dXNDb2RlOnQuc3RhdHVzQ29kZSxoZWFkZXJzOnsicmV0cnktYWZ0ZXIiOm4sIngtc2VudHJ5LXJhdGUtbGltaXRzIjpBcnJheS5pc0FycmF5KGUpP2VbMF18fG51bGw6ZX19KX0pO2cub24oImVycm9yIixwKSxsLnBpcGUoZyl9KX0pfX0odCx0Lmh0dHBNb2R1bGU/P28sZik7cmV0dXJuIEt0KHQsaCl9KHt1cmw6KG1uPWZuLmRzbixnbj1mbi50dW5uZWwseW49Zm4uc2RrTWV0YWRhdGEuc2RrLGdufHxgJHtmdW5jdGlvbih0KXtyZXR1cm5gJHtmdW5jdGlvbih0KXtjb25zdCBuPXQucHJvdG9jb2w/YCR7dC5wcm90b2NvbH06YDoiIixlPXQucG9ydD9gOiR7dC5wb3J0fWA6IiI7cmV0dXJuYCR7bn0vLyR7dC5ob3N0fSR7ZX0ke3QucGF0aD9gLyR7dC5wYXRofWA6IiJ9L2FwaS9gfSh0KX0ke3QucHJvamVjdElkfS9lbnZlbG9wZS9gfShtbil9PyR7ZnVuY3Rpb24odCxuKXtjb25zdCBlPXtzZW50cnlfdmVyc2lvbjoiNyJ9O3JldHVybiB0LnB1YmxpY0tleSYmKGUuc2VudHJ5X2tleT10LnB1YmxpY0tleSksbiYmKGUuc2VudHJ5X2NsaWVudD1gJHtuLm5hbWV9LyR7bi52ZXJzaW9ufWApLG5ldyBVUkxTZWFyY2hQYXJhbXMoZSkudG9TdHJpbmcoKX0obW4seW4pfWApfSk7YXN5bmMgZnVuY3Rpb24gdm4oKXtpZihobil7ZG4oIlNlbmRpbmcgYWJub3JtYWwgc2Vzc2lvbiIpLFYoaG4se3N0YXR1czoiYWJub3JtYWwiLGFibm9ybWFsX21lY2hhbmlzbToiYW5yX2ZvcmVncm91bmQiLHJlbGVhc2U6Zm4ucmVsZWFzZSxlbnZpcm9ubWVudDpmbi5lbnZpcm9ubWVudH0pO2NvbnN0IHQ9ZnVuY3Rpb24odCxuLGUscil7Y29uc3Qgbz1CdChlKTtyZXR1cm4gUnQoe3NlbnRfYXQ6KG5ldyBEYXRlKS50b0lTT1N0cmluZygpLC4uLm8mJntzZGs6b30sLi4uISFyJiZuJiZ7ZHNuOmJ0KG4pfX0sWyJhZ2dyZWdhdGVzImluIHQ/W3t0eXBlOiJzZXNzaW9ucyJ9LHRdOlt7dHlwZToic2Vzc2lvbiJ9LHQudG9KU09OKCldXSl9KGhuLGZuLmRzbixmbi5zZGtNZXRhZGF0YSxmbi50dW5uZWwpO2RuKEpTT04uc3RyaW5naWZ5KHQpKSxhd2FpdCBibi5zZW5kKHQpO3RyeXtlPy5wb3N0TWVzc2FnZSgic2Vzc2lvbi1lbmRlZCIpfWNhdGNoe319fWZ1bmN0aW9uIF9uKHQpe2lmKCF0KXJldHVybjtjb25zdCBuPWZ1bmN0aW9uKHQpe2lmKCF0Lmxlbmd0aClyZXR1cm5bXTtjb25zdCBuPUFycmF5LmZyb20odCk7cmV0dXJuL3NlbnRyeVdyYXBwZWQvLnRlc3QoRShuKS5mdW5jdGlvbnx8IiIpJiZuLnBvcCgpLG4ucmV2ZXJzZSgpLCQudGVzdChFKG4pLmZ1bmN0aW9ufHwiIikmJihuLnBvcCgpLCQudGVzdChFKG4pLmZ1bmN0aW9ufHwiIikmJm4ucG9wKCkpLG4uc2xpY2UoMCw1MCkubWFwKHQ9Pih7Li4udCxmaWxlbmFtZTp0LmZpbGVuYW1lfHxFKG4pLmZpbGVuYW1lLGZ1bmN0aW9uOnQuZnVuY3Rpb258fCI/In0pKX0odCk7aWYoZm4uYXBwUm9vdFBhdGgpZm9yKGNvbnN0IHQgb2Ygbil0LmZpbGVuYW1lJiYodC5maWxlbmFtZT1PdCh0LmZpbGVuYW1lLGZuLmFwcFJvb3RQYXRoKSk7cmV0dXJuIG59YXN5bmMgZnVuY3Rpb24gd24odCxuKXtpZihwbj49Zm4ubWF4QW5yRXZlbnRzKXJldHVybjtwbis9MSxhd2FpdCB2bigpLGRuKCJTZW5kaW5nIGV2ZW50Iik7Y29uc3QgZT17ZXZlbnRfaWQ6RigpLGNvbnRleHRzOmZuLmNvbnRleHRzLHJlbGVhc2U6Zm4ucmVsZWFzZSxlbnZpcm9ubWVudDpmbi5lbnZpcm9ubWVudCxkaXN0OmZuLmRpc3QscGxhdGZvcm06Im5vZGUiLGxldmVsOiJlcnJvciIsZXhjZXB0aW9uOnt2YWx1ZXM6W3t0eXBlOiJBcHBsaWNhdGlvbk5vdFJlc3BvbmRpbmciLHZhbHVlOmBBcHBsaWNhdGlvbiBOb3QgUmVzcG9uZGluZyBmb3IgYXQgbGVhc3QgJHtmbi5hbnJUaHJlc2hvbGR9IG1zYCxzdGFja3RyYWNlOntmcmFtZXM6X24odCl9LG1lY2hhbmlzbTp7dHlwZToiQU5SIn19XX0sdGFnczpmbi5zdGF0aWNUYWdzfTtuJiZmdW5jdGlvbih0LG4pe2lmKEd0KHQsbiksIXQuY29udGV4dHM/LnRyYWNlKXtjb25zdHt0cmFjZUlkOmUscGFyZW50U3BhbklkOnIscHJvcGFnYXRpb25TcGFuSWQ6b309bi5wcm9wYWdhdGlvbkNvbnRleHQ7dC5jb250ZXh0cz17dHJhY2U6e3RyYWNlX2lkOmUsc3Bhbl9pZDpvfHxxKCkscGFyZW50X3NwYW5faWQ6cn0sLi4udC5jb250ZXh0c319fShlLG4pLGZ1bmN0aW9uKHQpe2lmKDA9PT1PYmplY3Qua2V5cyhsbikubGVuZ3RoKXJldHVybjtjb25zdCBuPWZuLmFwcFJvb3RQYXRoP3t9OmxuO2lmKGZuLmFwcFJvb3RQYXRoKWZvcihjb25zdFt0LGVdb2YgT2JqZWN0LmVudHJpZXMobG4pKW5bT3QodCxmbi5hcHBSb290UGF0aCldPWU7Y29uc3QgZT1uZXcgTWFwO2Zvcihjb25zdCByIG9mIHQuZXhjZXB0aW9uPy52YWx1ZXN8fFtdKWZvcihjb25zdCB0IG9mIHIuc3RhY2t0cmFjZT8uZnJhbWVzfHxbXSl7Y29uc3Qgcj10LmFic19wYXRofHx0LmZpbGVuYW1lO3ImJm5bcl0mJmUuc2V0KHIsbltyXSl9aWYoZS5zaXplPjApe2NvbnN0IG49W107Zm9yKGNvbnN0W3Qscl1vZiBlLmVudHJpZXMoKSluLnB1c2goe3R5cGU6InNvdXJjZW1hcCIsY29kZV9maWxlOnQsZGVidWdfaWQ6cn0pO3QuZGVidWdfbWV0YT17aW1hZ2VzOm59fX0oZSk7Y29uc3Qgcj16dChlLGZuLmRzbixmbi5zZGtNZXRhZGF0YSxmbi50dW5uZWwpO2RuKEpTT04uc3RyaW5naWZ5KHIpKSxhd2FpdCBibi5zZW5kKHIpLGF3YWl0IGJuLmZsdXNoKDJlMykscG4+PWZuLm1heEFuckV2ZW50cyYmc2V0VGltZW91dCgoKT0+e3Byb2Nlc3MuZXhpdCgwKX0sNWUzKX1sZXQgU247aWYoZG4oIlN0YXJ0ZWQiKSxmbi5jYXB0dXJlU3RhY2tUcmFjZSl7ZG4oIkNvbm5lY3RpbmcgdG8gZGVidWdnZXIiKTtjb25zdCBuPW5ldyB0O24uY29ubmVjdFRvTWFpblRocmVhZCgpLGRuKCJDb25uZWN0ZWQgdG8gZGVidWdnZXIiKTtjb25zdCBlPW5ldyBNYXA7bi5vbigiRGVidWdnZXIuc2NyaXB0UGFyc2VkIix0PT57ZS5zZXQodC5wYXJhbXMuc2NyaXB0SWQsdC5wYXJhbXMudXJsKX0pLG4ub24oIkRlYnVnZ2VyLnBhdXNlZCIsdD0+e2lmKCJvdGhlciI9PT10LnBhcmFtcy5yZWFzb24pdHJ5e2RuKCJEZWJ1Z2dlciBwYXVzZWQiKTtjb25zdCBpPVsuLi50LnBhcmFtcy5jYWxsRnJhbWVzXSxzPWZuLmFwcFJvb3RQYXRoP2Z1bmN0aW9uKHQ9KHByb2Nlc3MuYXJndlsxXT9xdChwcm9jZXNzLmFyZ3ZbMV0pOnByb2Nlc3MuY3dkKCkpLG49IlxcIj09PW8pe2NvbnN0IGU9bj9hbih0KTp0O3JldHVybiB0PT57aWYoIXQpcmV0dXJuO2NvbnN0IG89bj9hbih0KTp0O2xldHtkaXI6aSxiYXNlOnMsZXh0OmN9PXIucGFyc2Uobyk7Ii5qcyIhPT1jJiYiLm1qcyIhPT1jJiYiLmNqcyIhPT1jfHwocz1zLnNsaWNlKDAsLTEqYy5sZW5ndGgpKTtjb25zdCB1PWRlY29kZVVSSUNvbXBvbmVudChzKTtpfHwoaT0iLiIpO2NvbnN0IGE9aS5sYXN0SW5kZXhPZigiL25vZGVfbW9kdWxlcyIpO2lmKGE+LTEpcmV0dXJuYCR7aS5zbGljZShhKzE0KS5yZXBsYWNlKC9cLy9nLCIuIil9OiR7dX1gO2lmKGkuc3RhcnRzV2l0aChlKSl7Y29uc3QgdD1pLnNsaWNlKGUubGVuZ3RoKzEpLnJlcGxhY2UoL1wvL2csIi4iKTtyZXR1cm4gdD9gJHt0fToke3V9YDp1fXJldHVybiB1fX0oZm4uYXBwUm9vdFBhdGgpOigpPT57fSxjPWkubWFwKHQ9PmZ1bmN0aW9uKHQsbixlKXtjb25zdCByPW4/bi5yZXBsYWNlKC9eZmlsZTpcL1wvLywiIik6dm9pZCAwLG89dC5sb2NhdGlvbi5jb2x1bW5OdW1iZXI/dC5sb2NhdGlvbi5jb2x1bW5OdW1iZXIrMTp2b2lkIDAsaT10LmxvY2F0aW9uLmxpbmVOdW1iZXI/dC5sb2NhdGlvbi5saW5lTnVtYmVyKzE6dm9pZCAwO3JldHVybntmaWxlbmFtZTpyLG1vZHVsZTplKHIpLGZ1bmN0aW9uOnQuZnVuY3Rpb25OYW1lfHwiPyIsY29sbm86byxsaW5lbm86aSxpbl9hcHA6cj9RdChyKTp2b2lkIDB9fSh0LGUuZ2V0KHQubG9jYXRpb24uc2NyaXB0SWQpLHMpKSx1PXNldFRpbWVvdXQoKCk9Pnt3bihjKS50aGVuKG51bGwsKCk9PntkbigiU2VuZGluZyBBTlIgZXZlbnQgZmFpbGVkLiIpfSl9LDVlMyk7bi5wb3N0KCJSdW50aW1lLmV2YWx1YXRlIix7ZXhwcmVzc2lvbjoiZ2xvYmFsLl9fU0VOVFJZX0dFVF9TQ09QRVNfXygpOyIsc2lsZW50OiEwLHJldHVybkJ5VmFsdWU6ITB9LCh0LGUpPT57dCYmZG4oYEVycm9yIGV4ZWN1dGluZyBzY3JpcHQ6ICcke3QubWVzc2FnZX0nYCksY2xlYXJUaW1lb3V0KHUpO2NvbnN0IHI9ZT8ucmVzdWx0P2UucmVzdWx0LnZhbHVlOnZvaWQgMDtuLnBvc3QoIkRlYnVnZ2VyLnJlc3VtZSIpLG4ucG9zdCgiRGVidWdnZXIuZGlzYWJsZSIpLHduKGMscikudGhlbihudWxsLCgpPT57ZG4oIlNlbmRpbmcgQU5SIGV2ZW50IGZhaWxlZC4iKX0pfSl9Y2F0Y2godCl7dGhyb3cgbi5wb3N0KCJEZWJ1Z2dlci5yZXN1bWUiKSxuLnBvc3QoIkRlYnVnZ2VyLmRpc2FibGUiKSx0fX0pLFNuPSgpPT57dHJ5e24ucG9zdCgiRGVidWdnZXIuZW5hYmxlIiwoKT0+e24ucG9zdCgiRGVidWdnZXIucGF1c2UiKX0pfWNhdGNoe319fWNvbnN0e3BvbGw6JG59PWZ1bmN0aW9uKHQsbixlLHIpe2NvbnN0IG89dCgpO2xldCBpPSExLHM9ITA7cmV0dXJuIHNldEludGVydmFsKCgpPT57Y29uc3QgdD1vLmdldFRpbWVNcygpOyExPT09aSYmdD5uK2UmJihpPSEwLHMmJnIoKSksdDxuK2UmJihpPSExKX0sMjApLHtwb2xsOigpPT57by5yZXNldCgpfSxlbmFibGVkOnQ9PntzPXR9fX0oZnVuY3Rpb24oKXtsZXQgdD1wcm9jZXNzLmhydGltZSgpO3JldHVybntnZXRUaW1lTXM6KCk9Pntjb25zdFtuLGVdPXByb2Nlc3MuaHJ0aW1lKHQpO3JldHVybiBNYXRoLmZsb29yKDFlMypuK2UvMWU2KX0scmVzZXQ6KCk9Pnt0PXByb2Nlc3MuaHJ0aW1lKCl9fX0sZm4ucG9sbEludGVydmFsLGZuLmFuclRocmVzaG9sZCxmdW5jdGlvbigpe2RuKCJXYXRjaGRvZyB0aW1lb3V0IiksU24/KGRuKCJQYXVzaW5nIGRlYnVnZ2VyIHRvIGNhcHR1cmUgc3RhY2sgdHJhY2UiKSxTbigpKTooZG4oIkNhcHR1cmluZyBldmVudCB3aXRob3V0IGEgc3RhY2sgdHJhY2UiKSx3bigpLnRoZW4obnVsbCwoKT0+e2RuKCJTZW5kaW5nIEFOUiBldmVudCBmYWlsZWQgb24gd2F0Y2hkb2cgdGltZW91dC4iKX0pKX0pO2U/Lm9uKCJtZXNzYWdlIix0PT57dC5zZXNzaW9uJiYoaG49WSh0LnNlc3Npb24pKSx0LmRlYnVnSW1hZ2VzJiYobG49dC5kZWJ1Z0ltYWdlcyksJG4oKX0pOw=="; | ||
| const DEFAULT_INTERVAL = 50; | ||
| const DEFAULT_HANG_THRESHOLD = 5000; | ||
| const DEFAULT_HANG_THRESHOLD = 5e3; | ||
| function log(message, ...args) { | ||
| core.debug.log(`[ANR] ${message}`, ...args); | ||
| } | ||
| function globalWithScopeFetchFn() { | ||
| return core.GLOBAL_OBJ; | ||
| } | ||
| /** Fetches merged scope data */ | ||
| function getScopeData() { | ||
| const scope = core.getCombinedScopeData(core.getIsolationScope(), core.getCurrentScope()); | ||
| // We remove attachments because they likely won't serialize well as json | ||
| scope.attachments = []; | ||
| // We can't serialize event processor functions | ||
| scope.eventProcessors = []; | ||
| return scope; | ||
| } | ||
| /** | ||
| * Gets contexts by calling all event processors. This shouldn't be called until all integrations are setup | ||
| */ | ||
| async function getContexts(client) { | ||
| let event = { message: 'ANR' }; | ||
| let event = { message: "ANR" }; | ||
| const eventHint = {}; | ||
| for (const processor of client.getEventProcessors()) { | ||
@@ -49,22 +33,13 @@ if (event === null) break; | ||
| } | ||
| return event?.contexts || {}; | ||
| } | ||
| const INTEGRATION_NAME = 'Anr'; | ||
| // eslint-disable-next-line deprecation/deprecation | ||
| const INTEGRATION_NAME = "Anr"; | ||
| const _anrIntegration = ((options = {}) => { | ||
| if (nodeVersion.NODE_VERSION.major < 16 || (nodeVersion.NODE_VERSION.major === 16 && nodeVersion.NODE_VERSION.minor < 17)) { | ||
| throw new Error('ANR detection requires Node 16.17.0 or later'); | ||
| if (nodeVersion.NODE_VERSION.major < 16 || nodeVersion.NODE_VERSION.major === 16 && nodeVersion.NODE_VERSION.minor < 17) { | ||
| throw new Error("ANR detection requires Node 16.17.0 or later"); | ||
| } | ||
| let worker; | ||
| let client; | ||
| // Hookup the scope fetch function to the global object so that it can be called from the worker thread via the | ||
| // debugger when it pauses | ||
| const gbl = globalWithScopeFetchFn(); | ||
| gbl.__SENTRY_GET_SCOPES__ = getScopeData; | ||
| return { | ||
@@ -76,3 +51,2 @@ name: INTEGRATION_NAME, | ||
| } | ||
| if (client) { | ||
@@ -84,6 +58,5 @@ worker = _startWorker(client, options); | ||
| if (worker) { | ||
| // eslint-disable-next-line @typescript-eslint/no-floating-promises | ||
| worker.then(stop => { | ||
| worker.then((stop) => { | ||
| stop(); | ||
| worker = undefined; | ||
| worker = void 0; | ||
| }); | ||
@@ -94,86 +67,25 @@ } | ||
| client = initClient; | ||
| if (options.captureStackTrace && (await debug.isDebuggerEnabled())) { | ||
| core.debug.warn('ANR captureStackTrace has been disabled because the debugger was already enabled'); | ||
| if (options.captureStackTrace && await debug.isDebuggerEnabled()) { | ||
| core.debug.warn("ANR captureStackTrace has been disabled because the debugger was already enabled"); | ||
| options.captureStackTrace = false; | ||
| } | ||
| // setImmediate is used to ensure that all other integrations have had their setup called first. | ||
| // This allows us to call into all integrations to fetch the full context | ||
| setImmediate(() => this.startWorker()); | ||
| }, | ||
| } ; | ||
| }) ; | ||
| // eslint-disable-next-line deprecation/deprecation | ||
| /** | ||
| * Application Not Responding (ANR) integration for Node.js applications. | ||
| * | ||
| * @deprecated The ANR integration has been deprecated. Use `eventLoopBlockIntegration` from `@sentry/node-native` instead. | ||
| * | ||
| * Detects when the Node.js main thread event loop is blocked for more than the configured | ||
| * threshold (5 seconds by default) and reports these as Sentry events. | ||
| * | ||
| * ANR detection uses a worker thread to monitor the event loop in the main app thread. | ||
| * The main app thread sends a heartbeat message to the ANR worker thread every 50ms by default. | ||
| * If the ANR worker does not receive a heartbeat message for the configured threshold duration, | ||
| * it triggers an ANR event. | ||
| * | ||
| * - Node.js 16.17.0 or higher | ||
| * - Only supported in the Node.js runtime (not browsers) | ||
| * - Not supported for Node.js clusters | ||
| * | ||
| * Overhead should be minimal: | ||
| * - Main thread: Only polling the ANR worker over IPC every 50ms | ||
| * - Worker thread: Consumes around 10-20 MB of RAM | ||
| * - When ANR detected: Brief pause in debugger to capture stack trace (negligible compared to the blocking) | ||
| * | ||
| * @example | ||
| * ```javascript | ||
| * Sentry.init({ | ||
| * dsn: "https://examplePublicKey@o0.ingest.sentry.io/0", | ||
| * integrations: [ | ||
| * Sentry.anrIntegration({ | ||
| * anrThreshold: 5000, | ||
| * captureStackTrace: true, | ||
| * pollInterval: 50, | ||
| * }), | ||
| * ], | ||
| * }); | ||
| * ``` | ||
| */ | ||
| const anrIntegration = core.defineIntegration(_anrIntegration) ; | ||
| /** | ||
| * Starts the ANR worker thread | ||
| * | ||
| * @returns A function to stop the worker | ||
| */ | ||
| async function _startWorker( | ||
| client, | ||
| // eslint-disable-next-line deprecation/deprecation | ||
| integrationOptions, | ||
| ) { | ||
| } | ||
| }; | ||
| }); | ||
| const anrIntegration = core.defineIntegration(_anrIntegration); | ||
| async function _startWorker(client, integrationOptions) { | ||
| const dsn = client.getDsn(); | ||
| if (!dsn) { | ||
| return () => { | ||
| // | ||
| }; | ||
| } | ||
| const contexts = await getContexts(client); | ||
| // These will not be accurate if sent later from the worker thread | ||
| delete contexts.app?.app_memory; | ||
| delete contexts.device?.free_memory; | ||
| const initOptions = client.getOptions(); | ||
| const sdkMetadata = client.getSdkMetadata() || {}; | ||
| if (sdkMetadata.sdk) { | ||
| sdkMetadata.sdk.integrations = initOptions.integrations.map(i => i.name); | ||
| sdkMetadata.sdk.integrations = initOptions.integrations.map((i) => i.name); | ||
| } | ||
| const options = { | ||
@@ -183,3 +95,3 @@ debug: core.debug.isEnabled(), | ||
| tunnel: initOptions.tunnel, | ||
| environment: initOptions.environment || 'production', | ||
| environment: initOptions.environment || "production", | ||
| release: initOptions.release, | ||
@@ -194,5 +106,4 @@ dist: initOptions.dist, | ||
| staticTags: integrationOptions.staticTags || {}, | ||
| contexts, | ||
| contexts | ||
| }; | ||
| if (options.captureStackTrace) { | ||
@@ -204,3 +115,2 @@ const inspector = await import('node:inspector'); | ||
| } | ||
| const worker = new node_worker_threads.Worker(new URL(`data:application/javascript;base64,${base64WorkerScript}`), { | ||
@@ -210,47 +120,32 @@ workerData: options, | ||
| execArgv: [], | ||
| env: { ...process.env, NODE_OPTIONS: undefined }, | ||
| env: { ...process.env, NODE_OPTIONS: void 0 } | ||
| }); | ||
| process.on('exit', () => { | ||
| // eslint-disable-next-line @typescript-eslint/no-floating-promises | ||
| process.on("exit", () => { | ||
| worker.terminate(); | ||
| }); | ||
| const timer = setInterval(() => { | ||
| try { | ||
| const currentSession = core.getIsolationScope().getSession(); | ||
| // We need to copy the session object and remove the toJSON method so it can be sent to the worker | ||
| // serialized without making it a SerializedSession | ||
| const session = currentSession ? { ...currentSession, toJSON: undefined } : undefined; | ||
| // message the worker to tell it the main event loop is still running | ||
| const session = currentSession ? { ...currentSession, toJSON: void 0 } : void 0; | ||
| worker.postMessage({ session, debugImages: core.getFilenameToDebugIdMap(initOptions.stackParser) }); | ||
| } catch { | ||
| // | ||
| } | ||
| }, options.pollInterval); | ||
| // Timer should not block exit | ||
| timer.unref(); | ||
| worker.on('message', (msg) => { | ||
| if (msg === 'session-ended') { | ||
| log('ANR event sent from ANR worker. Clearing session in this thread.'); | ||
| core.getIsolationScope().setSession(undefined); | ||
| worker.on("message", (msg) => { | ||
| if (msg === "session-ended") { | ||
| log("ANR event sent from ANR worker. Clearing session in this thread."); | ||
| core.getIsolationScope().setSession(void 0); | ||
| } | ||
| }); | ||
| worker.once('error', (err) => { | ||
| worker.once("error", (err) => { | ||
| clearInterval(timer); | ||
| log('ANR worker error', err); | ||
| log("ANR worker error", err); | ||
| }); | ||
| worker.once('exit', (code) => { | ||
| worker.once("exit", (code) => { | ||
| clearInterval(timer); | ||
| log('ANR worker exit', code); | ||
| log("ANR worker exit", code); | ||
| }); | ||
| // Ensure this thread can't block app exit | ||
| worker.unref(); | ||
| return () => { | ||
| // eslint-disable-next-line @typescript-eslint/no-floating-promises | ||
| worker.terminate(); | ||
@@ -260,27 +155,8 @@ clearInterval(timer); | ||
| } | ||
| /** | ||
| * @see {@link disableBlockDetectionForCallback} | ||
| * | ||
| * @deprecated The ANR integration has been deprecated. Use `eventLoopBlockIntegration` from `@sentry/node-native` instead. | ||
| */ | ||
| /** | ||
| * Temporarily disables ANR detection for the duration of a callback function. | ||
| * | ||
| * This utility function allows you to disable ANR detection during operations that | ||
| * are expected to block the event loop, such as intensive computational tasks or | ||
| * synchronous I/O operations. | ||
| * | ||
| * @deprecated The ANR integration has been deprecated. Use `eventLoopBlockIntegration` from `@sentry/node-native` instead. | ||
| */ | ||
| function disableAnrDetectionForCallback(callback) { | ||
| const integration = core.getClient()?.getIntegrationByName(INTEGRATION_NAME) ; | ||
| const integration = core.getClient()?.getIntegrationByName(INTEGRATION_NAME); | ||
| if (!integration) { | ||
| return callback(); | ||
| } | ||
| integration.stopWorker(); | ||
| const result = callback(); | ||
@@ -290,3 +166,2 @@ if (isPromise(result)) { | ||
| } | ||
| integration.startWorker(); | ||
@@ -293,0 +168,0 @@ return result; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.js","sources":["../../../../src/integrations/anr/index.ts"],"sourcesContent":["import { types } from 'node:util';\nimport { Worker } from 'node:worker_threads';\nimport type { Contexts, Event, EventHint, Integration, IntegrationFn, ScopeData } from '@sentry/core';\nimport {\n debug,\n defineIntegration,\n getClient,\n getCombinedScopeData,\n getCurrentScope,\n getFilenameToDebugIdMap,\n getIsolationScope,\n GLOBAL_OBJ,\n} from '@sentry/core';\nimport { NODE_VERSION } from '../../nodeVersion';\nimport type { NodeClient } from '../../sdk/client';\nimport { isDebuggerEnabled } from '../../utils/debug';\nimport type { AnrIntegrationOptions, WorkerStartData } from './common';\n\nconst { isPromise } = types;\n\n// This string is a placeholder that gets overwritten with the worker code.\nexport const base64WorkerScript = '###AnrWorkerScript###';\n\nconst DEFAULT_INTERVAL = 50;\nconst DEFAULT_HANG_THRESHOLD = 5000;\n\nfunction log(message: string, ...args: unknown[]): void {\n debug.log(`[ANR] ${message}`, ...args);\n}\n\nfunction globalWithScopeFetchFn(): typeof GLOBAL_OBJ & { __SENTRY_GET_SCOPES__?: () => ScopeData } {\n return GLOBAL_OBJ;\n}\n\n/** Fetches merged scope data */\nfunction getScopeData(): ScopeData {\n const scope = getCombinedScopeData(getIsolationScope(), getCurrentScope());\n\n // We remove attachments because they likely won't serialize well as json\n scope.attachments = [];\n // We can't serialize event processor functions\n scope.eventProcessors = [];\n\n return scope;\n}\n\n/**\n * Gets contexts by calling all event processors. This shouldn't be called until all integrations are setup\n */\nasync function getContexts(client: NodeClient): Promise<Contexts> {\n let event: Event | null = { message: 'ANR' };\n const eventHint: EventHint = {};\n\n for (const processor of client.getEventProcessors()) {\n if (event === null) break;\n event = await processor(event, eventHint);\n }\n\n return event?.contexts || {};\n}\n\nconst INTEGRATION_NAME = 'Anr';\n\ntype AnrInternal = { startWorker: () => void; stopWorker: () => void };\n\n// eslint-disable-next-line deprecation/deprecation\nconst _anrIntegration = ((options: Partial<AnrIntegrationOptions> = {}) => {\n if (NODE_VERSION.major < 16 || (NODE_VERSION.major === 16 && NODE_VERSION.minor < 17)) {\n throw new Error('ANR detection requires Node 16.17.0 or later');\n }\n\n let worker: Promise<() => void> | undefined;\n let client: NodeClient | undefined;\n\n // Hookup the scope fetch function to the global object so that it can be called from the worker thread via the\n // debugger when it pauses\n const gbl = globalWithScopeFetchFn();\n gbl.__SENTRY_GET_SCOPES__ = getScopeData;\n\n return {\n name: INTEGRATION_NAME,\n startWorker: () => {\n if (worker) {\n return;\n }\n\n if (client) {\n worker = _startWorker(client, options);\n }\n },\n stopWorker: () => {\n if (worker) {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n worker.then(stop => {\n stop();\n worker = undefined;\n });\n }\n },\n async setup(initClient: NodeClient) {\n client = initClient;\n\n if (options.captureStackTrace && (await isDebuggerEnabled())) {\n debug.warn('ANR captureStackTrace has been disabled because the debugger was already enabled');\n options.captureStackTrace = false;\n }\n\n // setImmediate is used to ensure that all other integrations have had their setup called first.\n // This allows us to call into all integrations to fetch the full context\n setImmediate(() => this.startWorker());\n },\n } as Integration & AnrInternal;\n}) satisfies IntegrationFn;\n\n// eslint-disable-next-line deprecation/deprecation\ntype AnrReturn = (options?: Partial<AnrIntegrationOptions>) => Integration & AnrInternal;\n\n/**\n * Application Not Responding (ANR) integration for Node.js applications.\n *\n * @deprecated The ANR integration has been deprecated. Use `eventLoopBlockIntegration` from `@sentry/node-native` instead.\n *\n * Detects when the Node.js main thread event loop is blocked for more than the configured\n * threshold (5 seconds by default) and reports these as Sentry events.\n *\n * ANR detection uses a worker thread to monitor the event loop in the main app thread.\n * The main app thread sends a heartbeat message to the ANR worker thread every 50ms by default.\n * If the ANR worker does not receive a heartbeat message for the configured threshold duration,\n * it triggers an ANR event.\n *\n * - Node.js 16.17.0 or higher\n * - Only supported in the Node.js runtime (not browsers)\n * - Not supported for Node.js clusters\n *\n * Overhead should be minimal:\n * - Main thread: Only polling the ANR worker over IPC every 50ms\n * - Worker thread: Consumes around 10-20 MB of RAM\n * - When ANR detected: Brief pause in debugger to capture stack trace (negligible compared to the blocking)\n *\n * @example\n * ```javascript\n * Sentry.init({\n * dsn: \"https://examplePublicKey@o0.ingest.sentry.io/0\",\n * integrations: [\n * Sentry.anrIntegration({\n * anrThreshold: 5000,\n * captureStackTrace: true,\n * pollInterval: 50,\n * }),\n * ],\n * });\n * ```\n */\nexport const anrIntegration = defineIntegration(_anrIntegration) as AnrReturn;\n\n/**\n * Starts the ANR worker thread\n *\n * @returns A function to stop the worker\n */\nasync function _startWorker(\n client: NodeClient,\n // eslint-disable-next-line deprecation/deprecation\n integrationOptions: Partial<AnrIntegrationOptions>,\n): Promise<() => void> {\n const dsn = client.getDsn();\n\n if (!dsn) {\n return () => {\n //\n };\n }\n\n const contexts = await getContexts(client);\n\n // These will not be accurate if sent later from the worker thread\n delete contexts.app?.app_memory;\n delete contexts.device?.free_memory;\n\n const initOptions = client.getOptions();\n\n const sdkMetadata = client.getSdkMetadata() || {};\n if (sdkMetadata.sdk) {\n sdkMetadata.sdk.integrations = initOptions.integrations.map(i => i.name);\n }\n\n const options: WorkerStartData = {\n debug: debug.isEnabled(),\n dsn,\n tunnel: initOptions.tunnel,\n environment: initOptions.environment || 'production',\n release: initOptions.release,\n dist: initOptions.dist,\n sdkMetadata,\n appRootPath: integrationOptions.appRootPath,\n pollInterval: integrationOptions.pollInterval || DEFAULT_INTERVAL,\n anrThreshold: integrationOptions.anrThreshold || DEFAULT_HANG_THRESHOLD,\n captureStackTrace: !!integrationOptions.captureStackTrace,\n maxAnrEvents: integrationOptions.maxAnrEvents || 1,\n staticTags: integrationOptions.staticTags || {},\n contexts,\n };\n\n if (options.captureStackTrace) {\n const inspector = await import('node:inspector');\n if (!inspector.url()) {\n inspector.open(0);\n }\n }\n\n const worker = new Worker(new URL(`data:application/javascript;base64,${base64WorkerScript}`), {\n workerData: options,\n // We don't want any Node args to be passed to the worker\n execArgv: [],\n env: { ...process.env, NODE_OPTIONS: undefined },\n });\n\n process.on('exit', () => {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n worker.terminate();\n });\n\n const timer = setInterval(() => {\n try {\n const currentSession = getIsolationScope().getSession();\n // We need to copy the session object and remove the toJSON method so it can be sent to the worker\n // serialized without making it a SerializedSession\n const session = currentSession ? { ...currentSession, toJSON: undefined } : undefined;\n // message the worker to tell it the main event loop is still running\n worker.postMessage({ session, debugImages: getFilenameToDebugIdMap(initOptions.stackParser) });\n } catch {\n //\n }\n }, options.pollInterval);\n // Timer should not block exit\n timer.unref();\n\n worker.on('message', (msg: string) => {\n if (msg === 'session-ended') {\n log('ANR event sent from ANR worker. Clearing session in this thread.');\n getIsolationScope().setSession(undefined);\n }\n });\n\n worker.once('error', (err: Error) => {\n clearInterval(timer);\n log('ANR worker error', err);\n });\n\n worker.once('exit', (code: number) => {\n clearInterval(timer);\n log('ANR worker exit', code);\n });\n\n // Ensure this thread can't block app exit\n worker.unref();\n\n return () => {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n worker.terminate();\n clearInterval(timer);\n };\n}\n\n/**\n * @see {@link disableBlockDetectionForCallback}\n *\n * @deprecated The ANR integration has been deprecated. Use `eventLoopBlockIntegration` from `@sentry/node-native` instead.\n */\nexport function disableAnrDetectionForCallback<T>(callback: () => T): T;\n/**\n * @see {@link disableBlockDetectionForCallback}\n *\n * @deprecated The ANR integration has been deprecated. Use `eventLoopBlockIntegration` from `@sentry/node-native` instead.\n */\nexport function disableAnrDetectionForCallback<T>(callback: () => Promise<T>): Promise<T>;\n/**\n * Temporarily disables ANR detection for the duration of a callback function.\n *\n * This utility function allows you to disable ANR detection during operations that\n * are expected to block the event loop, such as intensive computational tasks or\n * synchronous I/O operations.\n *\n * @deprecated The ANR integration has been deprecated. Use `eventLoopBlockIntegration` from `@sentry/node-native` instead.\n */\nexport function disableAnrDetectionForCallback<T>(callback: () => T | Promise<T>): T | Promise<T> {\n const integration = getClient()?.getIntegrationByName(INTEGRATION_NAME) as AnrInternal | undefined;\n\n if (!integration) {\n return callback();\n }\n\n integration.stopWorker();\n\n const result = callback();\n if (isPromise(result)) {\n return result.finally(() => integration.startWorker());\n }\n\n integration.startWorker();\n return result;\n}\n"],"names":["types","debug","GLOBAL_OBJ","getCombinedScopeData","getIsolationScope","getCurrentScope","NODE_VERSION","isDebuggerEnabled","defineIntegration","Worker","getFilenameToDebugIdMap","getClient"],"mappings":"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"} | ||
| {"version":3,"file":"index.js","sources":["../../../../src/integrations/anr/index.ts"],"sourcesContent":["import { types } from 'node:util';\nimport { Worker } from 'node:worker_threads';\nimport type { Contexts, Event, EventHint, Integration, IntegrationFn, ScopeData } from '@sentry/core';\nimport {\n debug,\n defineIntegration,\n getClient,\n getCombinedScopeData,\n getCurrentScope,\n getFilenameToDebugIdMap,\n getIsolationScope,\n GLOBAL_OBJ,\n} from '@sentry/core';\nimport { NODE_VERSION } from '../../nodeVersion';\nimport type { NodeClient } from '../../sdk/client';\nimport { isDebuggerEnabled } from '../../utils/debug';\nimport type { AnrIntegrationOptions, WorkerStartData } from './common';\n\nconst { isPromise } = types;\n\n// This string is a placeholder that gets overwritten with the worker code.\nexport const base64WorkerScript = '###AnrWorkerScript###';\n\nconst DEFAULT_INTERVAL = 50;\nconst DEFAULT_HANG_THRESHOLD = 5000;\n\nfunction log(message: string, ...args: unknown[]): void {\n debug.log(`[ANR] ${message}`, ...args);\n}\n\nfunction globalWithScopeFetchFn(): typeof GLOBAL_OBJ & { __SENTRY_GET_SCOPES__?: () => ScopeData } {\n return GLOBAL_OBJ;\n}\n\n/** Fetches merged scope data */\nfunction getScopeData(): ScopeData {\n const scope = getCombinedScopeData(getIsolationScope(), getCurrentScope());\n\n // We remove attachments because they likely won't serialize well as json\n scope.attachments = [];\n // We can't serialize event processor functions\n scope.eventProcessors = [];\n\n return scope;\n}\n\n/**\n * Gets contexts by calling all event processors. This shouldn't be called until all integrations are setup\n */\nasync function getContexts(client: NodeClient): Promise<Contexts> {\n let event: Event | null = { message: 'ANR' };\n const eventHint: EventHint = {};\n\n for (const processor of client.getEventProcessors()) {\n if (event === null) break;\n event = await processor(event, eventHint);\n }\n\n return event?.contexts || {};\n}\n\nconst INTEGRATION_NAME = 'Anr';\n\ntype AnrInternal = { startWorker: () => void; stopWorker: () => void };\n\n// eslint-disable-next-line deprecation/deprecation\nconst _anrIntegration = ((options: Partial<AnrIntegrationOptions> = {}) => {\n if (NODE_VERSION.major < 16 || (NODE_VERSION.major === 16 && NODE_VERSION.minor < 17)) {\n throw new Error('ANR detection requires Node 16.17.0 or later');\n }\n\n let worker: Promise<() => void> | undefined;\n let client: NodeClient | undefined;\n\n // Hookup the scope fetch function to the global object so that it can be called from the worker thread via the\n // debugger when it pauses\n const gbl = globalWithScopeFetchFn();\n gbl.__SENTRY_GET_SCOPES__ = getScopeData;\n\n return {\n name: INTEGRATION_NAME,\n startWorker: () => {\n if (worker) {\n return;\n }\n\n if (client) {\n worker = _startWorker(client, options);\n }\n },\n stopWorker: () => {\n if (worker) {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n worker.then(stop => {\n stop();\n worker = undefined;\n });\n }\n },\n async setup(initClient: NodeClient) {\n client = initClient;\n\n if (options.captureStackTrace && (await isDebuggerEnabled())) {\n debug.warn('ANR captureStackTrace has been disabled because the debugger was already enabled');\n options.captureStackTrace = false;\n }\n\n // setImmediate is used to ensure that all other integrations have had their setup called first.\n // This allows us to call into all integrations to fetch the full context\n setImmediate(() => this.startWorker());\n },\n } as Integration & AnrInternal;\n}) satisfies IntegrationFn;\n\n// eslint-disable-next-line deprecation/deprecation\ntype AnrReturn = (options?: Partial<AnrIntegrationOptions>) => Integration & AnrInternal;\n\n/**\n * Application Not Responding (ANR) integration for Node.js applications.\n *\n * @deprecated The ANR integration has been deprecated. Use `eventLoopBlockIntegration` from `@sentry/node-native` instead.\n *\n * Detects when the Node.js main thread event loop is blocked for more than the configured\n * threshold (5 seconds by default) and reports these as Sentry events.\n *\n * ANR detection uses a worker thread to monitor the event loop in the main app thread.\n * The main app thread sends a heartbeat message to the ANR worker thread every 50ms by default.\n * If the ANR worker does not receive a heartbeat message for the configured threshold duration,\n * it triggers an ANR event.\n *\n * - Node.js 16.17.0 or higher\n * - Only supported in the Node.js runtime (not browsers)\n * - Not supported for Node.js clusters\n *\n * Overhead should be minimal:\n * - Main thread: Only polling the ANR worker over IPC every 50ms\n * - Worker thread: Consumes around 10-20 MB of RAM\n * - When ANR detected: Brief pause in debugger to capture stack trace (negligible compared to the blocking)\n *\n * @example\n * ```javascript\n * Sentry.init({\n * dsn: \"https://examplePublicKey@o0.ingest.sentry.io/0\",\n * integrations: [\n * Sentry.anrIntegration({\n * anrThreshold: 5000,\n * captureStackTrace: true,\n * pollInterval: 50,\n * }),\n * ],\n * });\n * ```\n */\nexport const anrIntegration = defineIntegration(_anrIntegration) as AnrReturn;\n\n/**\n * Starts the ANR worker thread\n *\n * @returns A function to stop the worker\n */\nasync function _startWorker(\n client: NodeClient,\n // eslint-disable-next-line deprecation/deprecation\n integrationOptions: Partial<AnrIntegrationOptions>,\n): Promise<() => void> {\n const dsn = client.getDsn();\n\n if (!dsn) {\n return () => {\n //\n };\n }\n\n const contexts = await getContexts(client);\n\n // These will not be accurate if sent later from the worker thread\n delete contexts.app?.app_memory;\n delete contexts.device?.free_memory;\n\n const initOptions = client.getOptions();\n\n const sdkMetadata = client.getSdkMetadata() || {};\n if (sdkMetadata.sdk) {\n sdkMetadata.sdk.integrations = initOptions.integrations.map(i => i.name);\n }\n\n const options: WorkerStartData = {\n debug: debug.isEnabled(),\n dsn,\n tunnel: initOptions.tunnel,\n environment: initOptions.environment || 'production',\n release: initOptions.release,\n dist: initOptions.dist,\n sdkMetadata,\n appRootPath: integrationOptions.appRootPath,\n pollInterval: integrationOptions.pollInterval || DEFAULT_INTERVAL,\n anrThreshold: integrationOptions.anrThreshold || DEFAULT_HANG_THRESHOLD,\n captureStackTrace: !!integrationOptions.captureStackTrace,\n maxAnrEvents: integrationOptions.maxAnrEvents || 1,\n staticTags: integrationOptions.staticTags || {},\n contexts,\n };\n\n if (options.captureStackTrace) {\n const inspector = await import('node:inspector');\n if (!inspector.url()) {\n inspector.open(0);\n }\n }\n\n const worker = new Worker(new URL(`data:application/javascript;base64,${base64WorkerScript}`), {\n workerData: options,\n // We don't want any Node args to be passed to the worker\n execArgv: [],\n env: { ...process.env, NODE_OPTIONS: undefined },\n });\n\n process.on('exit', () => {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n worker.terminate();\n });\n\n const timer = setInterval(() => {\n try {\n const currentSession = getIsolationScope().getSession();\n // We need to copy the session object and remove the toJSON method so it can be sent to the worker\n // serialized without making it a SerializedSession\n const session = currentSession ? { ...currentSession, toJSON: undefined } : undefined;\n // message the worker to tell it the main event loop is still running\n worker.postMessage({ session, debugImages: getFilenameToDebugIdMap(initOptions.stackParser) });\n } catch {\n //\n }\n }, options.pollInterval);\n // Timer should not block exit\n timer.unref();\n\n worker.on('message', (msg: string) => {\n if (msg === 'session-ended') {\n log('ANR event sent from ANR worker. Clearing session in this thread.');\n getIsolationScope().setSession(undefined);\n }\n });\n\n worker.once('error', (err: Error) => {\n clearInterval(timer);\n log('ANR worker error', err);\n });\n\n worker.once('exit', (code: number) => {\n clearInterval(timer);\n log('ANR worker exit', code);\n });\n\n // Ensure this thread can't block app exit\n worker.unref();\n\n return () => {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n worker.terminate();\n clearInterval(timer);\n };\n}\n\n/**\n * @see {@link disableBlockDetectionForCallback}\n *\n * @deprecated The ANR integration has been deprecated. Use `eventLoopBlockIntegration` from `@sentry/node-native` instead.\n */\nexport function disableAnrDetectionForCallback<T>(callback: () => T): T;\n/**\n * @see {@link disableBlockDetectionForCallback}\n *\n * @deprecated The ANR integration has been deprecated. Use `eventLoopBlockIntegration` from `@sentry/node-native` instead.\n */\nexport function disableAnrDetectionForCallback<T>(callback: () => Promise<T>): Promise<T>;\n/**\n * Temporarily disables ANR detection for the duration of a callback function.\n *\n * This utility function allows you to disable ANR detection during operations that\n * are expected to block the event loop, such as intensive computational tasks or\n * synchronous I/O operations.\n *\n * @deprecated The ANR integration has been deprecated. Use `eventLoopBlockIntegration` from `@sentry/node-native` instead.\n */\nexport function disableAnrDetectionForCallback<T>(callback: () => T | Promise<T>): T | Promise<T> {\n const integration = getClient()?.getIntegrationByName(INTEGRATION_NAME) as AnrInternal | undefined;\n\n if (!integration) {\n return callback();\n }\n\n integration.stopWorker();\n\n const result = callback();\n if (isPromise(result)) {\n return result.finally(() => integration.startWorker());\n }\n\n integration.startWorker();\n return result;\n}\n"],"names":["types","debug","GLOBAL_OBJ","getCombinedScopeData","getIsolationScope","getCurrentScope","NODE_VERSION","isDebuggerEnabled","defineIntegration","Worker","getFilenameToDebugIdMap","getClient"],"mappings":"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"} |
@@ -6,7 +6,3 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const INTEGRATION_NAME = 'ChildProcess'; | ||
| /** | ||
| * Capture breadcrumbs and events for child processes and worker threads. | ||
| */ | ||
| const INTEGRATION_NAME = "ChildProcess"; | ||
| const childProcessIntegration = core.defineIntegration((options = {}) => { | ||
@@ -16,84 +12,69 @@ return { | ||
| setup() { | ||
| diagnosticsChannel.channel('child_process').subscribe((event) => { | ||
| if (event && typeof event === 'object' && 'process' in event) { | ||
| captureChildProcessEvents(event.process , options); | ||
| diagnosticsChannel.channel("child_process").subscribe((event) => { | ||
| if (event && typeof event === "object" && "process" in event) { | ||
| captureChildProcessEvents(event.process, options); | ||
| } | ||
| }); | ||
| diagnosticsChannel.channel('worker_threads').subscribe((event) => { | ||
| if (event && typeof event === 'object' && 'worker' in event) { | ||
| captureWorkerThreadEvents(event.worker , options); | ||
| diagnosticsChannel.channel("worker_threads").subscribe((event) => { | ||
| if (event && typeof event === "object" && "worker" in event) { | ||
| captureWorkerThreadEvents(event.worker, options); | ||
| } | ||
| }); | ||
| }, | ||
| } | ||
| }; | ||
| }); | ||
| function captureChildProcessEvents(child, options) { | ||
| let hasExited = false; | ||
| let data; | ||
| child | ||
| .on('spawn', () => { | ||
| // This is Sentry getting macOS OS context | ||
| if (child.spawnfile === '/usr/bin/sw_vers') { | ||
| hasExited = true; | ||
| return; | ||
| } | ||
| data = { spawnfile: child.spawnfile }; | ||
| if (options.includeChildProcessArgs) { | ||
| data.spawnargs = child.spawnargs; | ||
| } | ||
| }) | ||
| .on('exit', code => { | ||
| if (!hasExited) { | ||
| hasExited = true; | ||
| // Only log for non-zero exit codes | ||
| if (code !== null && code !== 0) { | ||
| core.addBreadcrumb({ | ||
| category: 'child_process', | ||
| message: `Child process exited with code '${code}'`, | ||
| level: code === 0 ? 'info' : 'warning', | ||
| data, | ||
| }); | ||
| } | ||
| } | ||
| }) | ||
| .on('error', error => { | ||
| if (!hasExited) { | ||
| hasExited = true; | ||
| child.on("spawn", () => { | ||
| if (child.spawnfile === "/usr/bin/sw_vers") { | ||
| hasExited = true; | ||
| return; | ||
| } | ||
| data = { spawnfile: child.spawnfile }; | ||
| if (options.includeChildProcessArgs) { | ||
| data.spawnargs = child.spawnargs; | ||
| } | ||
| }).on("exit", (code) => { | ||
| if (!hasExited) { | ||
| hasExited = true; | ||
| if (code !== null && code !== 0) { | ||
| core.addBreadcrumb({ | ||
| category: 'child_process', | ||
| message: `Child process errored with '${error.message}'`, | ||
| level: 'error', | ||
| data, | ||
| category: "child_process", | ||
| message: `Child process exited with code '${code}'`, | ||
| level: code === 0 ? "info" : "warning", | ||
| data | ||
| }); | ||
| } | ||
| }); | ||
| } | ||
| }).on("error", (error) => { | ||
| if (!hasExited) { | ||
| hasExited = true; | ||
| core.addBreadcrumb({ | ||
| category: "child_process", | ||
| message: `Child process errored with '${error.message}'`, | ||
| level: "error", | ||
| data | ||
| }); | ||
| } | ||
| }); | ||
| } | ||
| function captureWorkerThreadEvents(worker, options) { | ||
| let threadId; | ||
| worker | ||
| .on('online', () => { | ||
| threadId = worker.threadId; | ||
| }) | ||
| .on('error', error => { | ||
| if (options.captureWorkerErrors !== false) { | ||
| core.captureException(error, { | ||
| mechanism: { type: 'auto.child_process.worker_thread', handled: false, data: { threadId: String(threadId) } }, | ||
| }); | ||
| } else { | ||
| core.addBreadcrumb({ | ||
| category: 'worker_thread', | ||
| message: `Worker thread errored with '${error.message}'`, | ||
| level: 'error', | ||
| data: { threadId }, | ||
| }); | ||
| } | ||
| }); | ||
| worker.on("online", () => { | ||
| threadId = worker.threadId; | ||
| }).on("error", (error) => { | ||
| if (options.captureWorkerErrors !== false) { | ||
| core.captureException(error, { | ||
| mechanism: { type: "auto.child_process.worker_thread", handled: false, data: { threadId: String(threadId) } } | ||
| }); | ||
| } else { | ||
| core.addBreadcrumb({ | ||
| category: "worker_thread", | ||
| message: `Worker thread errored with '${error.message}'`, | ||
| level: "error", | ||
| data: { threadId } | ||
| }); | ||
| } | ||
| }); | ||
| } | ||
@@ -100,0 +81,0 @@ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"childProcess.js","sources":["../../../src/integrations/childProcess.ts"],"sourcesContent":["import type { ChildProcess } from 'node:child_process';\nimport * as diagnosticsChannel from 'node:diagnostics_channel';\nimport type { Worker } from 'node:worker_threads';\nimport { addBreadcrumb, captureException, defineIntegration } from '@sentry/core';\n\ninterface Options {\n /**\n * Whether to include child process arguments in breadcrumbs data.\n *\n * @default false\n */\n includeChildProcessArgs?: boolean;\n\n /**\n * Whether to capture errors from worker threads.\n *\n * @default true\n */\n captureWorkerErrors?: boolean;\n}\n\nconst INTEGRATION_NAME = 'ChildProcess';\n\n/**\n * Capture breadcrumbs and events for child processes and worker threads.\n */\nexport const childProcessIntegration = defineIntegration((options: Options = {}) => {\n return {\n name: INTEGRATION_NAME,\n setup() {\n diagnosticsChannel.channel('child_process').subscribe((event: unknown) => {\n if (event && typeof event === 'object' && 'process' in event) {\n captureChildProcessEvents(event.process as ChildProcess, options);\n }\n });\n\n diagnosticsChannel.channel('worker_threads').subscribe((event: unknown) => {\n if (event && typeof event === 'object' && 'worker' in event) {\n captureWorkerThreadEvents(event.worker as Worker, options);\n }\n });\n },\n };\n});\n\nfunction captureChildProcessEvents(child: ChildProcess, options: Options): void {\n let hasExited = false;\n let data: Record<string, unknown> | undefined;\n\n child\n .on('spawn', () => {\n // This is Sentry getting macOS OS context\n if (child.spawnfile === '/usr/bin/sw_vers') {\n hasExited = true;\n return;\n }\n\n data = { spawnfile: child.spawnfile };\n if (options.includeChildProcessArgs) {\n data.spawnargs = child.spawnargs;\n }\n })\n .on('exit', code => {\n if (!hasExited) {\n hasExited = true;\n\n // Only log for non-zero exit codes\n if (code !== null && code !== 0) {\n addBreadcrumb({\n category: 'child_process',\n message: `Child process exited with code '${code}'`,\n level: code === 0 ? 'info' : 'warning',\n data,\n });\n }\n }\n })\n .on('error', error => {\n if (!hasExited) {\n hasExited = true;\n\n addBreadcrumb({\n category: 'child_process',\n message: `Child process errored with '${error.message}'`,\n level: 'error',\n data,\n });\n }\n });\n}\n\nfunction captureWorkerThreadEvents(worker: Worker, options: Options): void {\n let threadId: number | undefined;\n\n worker\n .on('online', () => {\n threadId = worker.threadId;\n })\n .on('error', error => {\n if (options.captureWorkerErrors !== false) {\n captureException(error, {\n mechanism: { type: 'auto.child_process.worker_thread', handled: false, data: { threadId: String(threadId) } },\n });\n } else {\n addBreadcrumb({\n category: 'worker_thread',\n message: `Worker thread errored with '${error.message}'`,\n level: 'error',\n data: { threadId },\n });\n }\n });\n}\n"],"names":["defineIntegration","addBreadcrumb","captureException"],"mappings":"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"} | ||
| {"version":3,"file":"childProcess.js","sources":["../../../src/integrations/childProcess.ts"],"sourcesContent":["import type { ChildProcess } from 'node:child_process';\nimport * as diagnosticsChannel from 'node:diagnostics_channel';\nimport type { Worker } from 'node:worker_threads';\nimport { addBreadcrumb, captureException, defineIntegration } from '@sentry/core';\n\ninterface Options {\n /**\n * Whether to include child process arguments in breadcrumbs data.\n *\n * @default false\n */\n includeChildProcessArgs?: boolean;\n\n /**\n * Whether to capture errors from worker threads.\n *\n * @default true\n */\n captureWorkerErrors?: boolean;\n}\n\nconst INTEGRATION_NAME = 'ChildProcess';\n\n/**\n * Capture breadcrumbs and events for child processes and worker threads.\n */\nexport const childProcessIntegration = defineIntegration((options: Options = {}) => {\n return {\n name: INTEGRATION_NAME,\n setup() {\n diagnosticsChannel.channel('child_process').subscribe((event: unknown) => {\n if (event && typeof event === 'object' && 'process' in event) {\n captureChildProcessEvents(event.process as ChildProcess, options);\n }\n });\n\n diagnosticsChannel.channel('worker_threads').subscribe((event: unknown) => {\n if (event && typeof event === 'object' && 'worker' in event) {\n captureWorkerThreadEvents(event.worker as Worker, options);\n }\n });\n },\n };\n});\n\nfunction captureChildProcessEvents(child: ChildProcess, options: Options): void {\n let hasExited = false;\n let data: Record<string, unknown> | undefined;\n\n child\n .on('spawn', () => {\n // This is Sentry getting macOS OS context\n if (child.spawnfile === '/usr/bin/sw_vers') {\n hasExited = true;\n return;\n }\n\n data = { spawnfile: child.spawnfile };\n if (options.includeChildProcessArgs) {\n data.spawnargs = child.spawnargs;\n }\n })\n .on('exit', code => {\n if (!hasExited) {\n hasExited = true;\n\n // Only log for non-zero exit codes\n if (code !== null && code !== 0) {\n addBreadcrumb({\n category: 'child_process',\n message: `Child process exited with code '${code}'`,\n level: code === 0 ? 'info' : 'warning',\n data,\n });\n }\n }\n })\n .on('error', error => {\n if (!hasExited) {\n hasExited = true;\n\n addBreadcrumb({\n category: 'child_process',\n message: `Child process errored with '${error.message}'`,\n level: 'error',\n data,\n });\n }\n });\n}\n\nfunction captureWorkerThreadEvents(worker: Worker, options: Options): void {\n let threadId: number | undefined;\n\n worker\n .on('online', () => {\n threadId = worker.threadId;\n })\n .on('error', error => {\n if (options.captureWorkerErrors !== false) {\n captureException(error, {\n mechanism: { type: 'auto.child_process.worker_thread', handled: false, data: { threadId: String(threadId) } },\n });\n } else {\n addBreadcrumb({\n category: 'worker_thread',\n message: `Worker thread errored with '${error.message}'`,\n level: 'error',\n data: { threadId },\n });\n }\n });\n}\n"],"names":["defineIntegration","addBreadcrumb","captureException"],"mappings":"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"} |
@@ -5,37 +5,21 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| /** | ||
| * Node-specific console integration that captures breadcrumbs and handles | ||
| * the AWS Lambda runtime replacing console methods after our patch. | ||
| * | ||
| * In Lambda, console methods are patched via `Object.defineProperty` so that | ||
| * external replacements (by the Lambda runtime) are absorbed as the delegate | ||
| * while our wrapper stays in place. Outside Lambda, this delegates entirely | ||
| * to the core `consoleIntegration` which uses the simpler `fill`-based patch. | ||
| */ | ||
| const consoleIntegration = core.defineIntegration((options = {}) => { | ||
| return { | ||
| name: 'Console', | ||
| name: "Console", | ||
| setup(client) { | ||
| if (process.env.LAMBDA_TASK_ROOT) { | ||
| core.maybeInstrument('console', instrumentConsoleLambda); | ||
| core.maybeInstrument("console", instrumentConsoleLambda); | ||
| } | ||
| // Delegate breadcrumb handling to the core console integration. | ||
| const core$1 = core.consoleIntegration({ | ||
| ...options, | ||
| filter: [ | ||
| ...(options.filter || []), | ||
| ...options.filter || [], | ||
| // Deprecation on Node 26 for module.require(), which is used by IITM | ||
| '[DEP0205] DeprecationWarning', | ||
| ], | ||
| "[DEP0205] DeprecationWarning" | ||
| ] | ||
| }); | ||
| core$1.setup?.(client); | ||
| }, | ||
| } | ||
| }; | ||
| }); | ||
| /** | ||
| * NOTE: This currently ignores the filter option. | ||
| * We can revisit this later. | ||
| */ | ||
| function instrumentConsoleLambda() { | ||
@@ -46,3 +30,2 @@ const consoleObj = core.GLOBAL_OBJ?.console; | ||
| } | ||
| core.CONSOLE_LEVELS.forEach((level) => { | ||
@@ -54,16 +37,10 @@ if (level in consoleObj) { | ||
| } | ||
| function patchWithDefineProperty(consoleObj, level) { | ||
| const nativeMethod = consoleObj[level] ; | ||
| const nativeMethod = consoleObj[level]; | ||
| core.originalConsoleMethods[level] = nativeMethod; | ||
| let delegate = nativeMethod; | ||
| let savedDelegate; | ||
| let isExecuting = false; | ||
| const wrapper = function (...args) { | ||
| const wrapper = function(...args) { | ||
| if (isExecuting) { | ||
| // Re-entrant call: a third party captured `wrapper` via the getter and calls it from inside their replacement. We must | ||
| // use `nativeMethod` (not `delegate`) to break the cycle, and we intentionally skip `triggerHandlers` to avoid duplicate | ||
| // breadcrumbs. The outer invocation already triggered the handlers for this console call. | ||
| nativeMethod.apply(consoleObj, args); | ||
@@ -74,3 +51,3 @@ return; | ||
| try { | ||
| core.triggerHandlers('console', { args, level } ); | ||
| core.triggerHandlers("console", { args, level }); | ||
| delegate.apply(consoleObj, args); | ||
@@ -81,12 +58,7 @@ } finally { | ||
| }; | ||
| core.markFunctionWrapped(wrapper , nativeMethod ); | ||
| // consoleSandbox reads originalConsoleMethods[level] to temporarily bypass instrumentation. We replace it with a distinct reference (.bind creates a | ||
| // new function identity) so the setter can tell apart "consoleSandbox bypass" from "external code restoring a native method captured before Sentry init." | ||
| core.markFunctionWrapped(wrapper, nativeMethod); | ||
| const sandboxBypass = nativeMethod.bind(consoleObj); | ||
| core.originalConsoleMethods[level] = sandboxBypass; | ||
| try { | ||
| let current = wrapper; | ||
| Object.defineProperty(consoleObj, level, { | ||
@@ -100,13 +72,11 @@ configurable: true, | ||
| if (newValue === wrapper) { | ||
| // consoleSandbox restoring the wrapper: recover the saved delegate. | ||
| if (savedDelegate !== undefined) { | ||
| if (savedDelegate !== void 0) { | ||
| delegate = savedDelegate; | ||
| savedDelegate = undefined; | ||
| savedDelegate = void 0; | ||
| } | ||
| current = wrapper; | ||
| } else if (newValue === sandboxBypass) { | ||
| // consoleSandbox entering bypass: save delegate, let getter return sandboxBypass directly so calls skip the wrapper entirely. | ||
| savedDelegate = delegate; | ||
| current = sandboxBypass; | ||
| } else if (typeof newValue === 'function' && !(newValue ).__sentry_original__) { | ||
| } else if (typeof newValue === "function" && !newValue.__sentry_original__) { | ||
| delegate = newValue; | ||
@@ -117,11 +87,9 @@ current = wrapper; | ||
| } | ||
| }, | ||
| } | ||
| }); | ||
| } catch { | ||
| // Fall back to fill-based patching if defineProperty fails | ||
| core.fill(consoleObj, level, function (originalConsoleMethod) { | ||
| core.fill(consoleObj, level, function(originalConsoleMethod) { | ||
| core.originalConsoleMethods[level] = originalConsoleMethod; | ||
| return function ( ...args) { | ||
| core.triggerHandlers('console', { args, level } ); | ||
| return function(...args) { | ||
| core.triggerHandlers("console", { args, level }); | ||
| core.originalConsoleMethods[level]?.apply(this, args); | ||
@@ -128,0 +96,0 @@ }; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"console.js","sources":["../../../src/integrations/console.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type { ConsoleLevel, HandlerDataConsole, WrappedFunction } from '@sentry/core';\nimport {\n CONSOLE_LEVELS,\n GLOBAL_OBJ,\n consoleIntegration as coreConsoleIntegration,\n defineIntegration,\n fill,\n markFunctionWrapped,\n maybeInstrument,\n originalConsoleMethods,\n triggerHandlers,\n} from '@sentry/core';\n\ninterface ConsoleIntegrationOptions {\n levels: ConsoleLevel[];\n /**\n * Filter out console messages that match the given strings or regular expressions.\n * These will neither be passed to the handler, and they will also not be logged to the user, unless they have debug enabled.\n */\n filter?: (string | RegExp)[];\n}\n\n/**\n * Node-specific console integration that captures breadcrumbs and handles\n * the AWS Lambda runtime replacing console methods after our patch.\n *\n * In Lambda, console methods are patched via `Object.defineProperty` so that\n * external replacements (by the Lambda runtime) are absorbed as the delegate\n * while our wrapper stays in place. Outside Lambda, this delegates entirely\n * to the core `consoleIntegration` which uses the simpler `fill`-based patch.\n */\nexport const consoleIntegration = defineIntegration((options: Partial<ConsoleIntegrationOptions> = {}) => {\n return {\n name: 'Console',\n setup(client) {\n if (process.env.LAMBDA_TASK_ROOT) {\n maybeInstrument('console', instrumentConsoleLambda);\n }\n\n // Delegate breadcrumb handling to the core console integration.\n const core = coreConsoleIntegration({\n ...options,\n filter: [\n ...(options.filter || []),\n // Deprecation on Node 26 for module.require(), which is used by IITM\n '[DEP0205] DeprecationWarning',\n ],\n });\n core.setup?.(client);\n },\n };\n});\n\n/**\n * NOTE: This currently ignores the filter option.\n * We can revisit this later.\n */\nfunction instrumentConsoleLambda(): void {\n const consoleObj = GLOBAL_OBJ?.console;\n if (!consoleObj) {\n return;\n }\n\n CONSOLE_LEVELS.forEach((level: ConsoleLevel) => {\n if (level in consoleObj) {\n patchWithDefineProperty(consoleObj, level);\n }\n });\n}\n\nfunction patchWithDefineProperty(consoleObj: Console, level: ConsoleLevel): void {\n const nativeMethod = consoleObj[level] as (...args: unknown[]) => void;\n originalConsoleMethods[level] = nativeMethod;\n\n let delegate: Function = nativeMethod;\n let savedDelegate: Function | undefined;\n let isExecuting = false;\n\n const wrapper = function (...args: any[]): void {\n if (isExecuting) {\n // Re-entrant call: a third party captured `wrapper` via the getter and calls it from inside their replacement. We must\n // use `nativeMethod` (not `delegate`) to break the cycle, and we intentionally skip `triggerHandlers` to avoid duplicate\n // breadcrumbs. The outer invocation already triggered the handlers for this console call.\n nativeMethod.apply(consoleObj, args);\n return;\n }\n isExecuting = true;\n try {\n triggerHandlers('console', { args, level } as HandlerDataConsole);\n delegate.apply(consoleObj, args);\n } finally {\n isExecuting = false;\n }\n };\n markFunctionWrapped(wrapper as unknown as WrappedFunction, nativeMethod as unknown as WrappedFunction);\n\n // consoleSandbox reads originalConsoleMethods[level] to temporarily bypass instrumentation. We replace it with a distinct reference (.bind creates a\n // new function identity) so the setter can tell apart \"consoleSandbox bypass\" from \"external code restoring a native method captured before Sentry init.\"\n const sandboxBypass = nativeMethod.bind(consoleObj);\n originalConsoleMethods[level] = sandboxBypass;\n\n try {\n let current: any = wrapper;\n\n Object.defineProperty(consoleObj, level, {\n configurable: true,\n enumerable: true,\n get() {\n return current;\n },\n set(newValue) {\n if (newValue === wrapper) {\n // consoleSandbox restoring the wrapper: recover the saved delegate.\n if (savedDelegate !== undefined) {\n delegate = savedDelegate;\n savedDelegate = undefined;\n }\n current = wrapper;\n } else if (newValue === sandboxBypass) {\n // consoleSandbox entering bypass: save delegate, let getter return sandboxBypass directly so calls skip the wrapper entirely.\n savedDelegate = delegate;\n current = sandboxBypass;\n } else if (typeof newValue === 'function' && !(newValue as WrappedFunction).__sentry_original__) {\n delegate = newValue;\n current = wrapper;\n } else {\n current = newValue;\n }\n },\n });\n } catch {\n // Fall back to fill-based patching if defineProperty fails\n fill(consoleObj, level, function (originalConsoleMethod: () => any): Function {\n originalConsoleMethods[level] = originalConsoleMethod;\n\n return function (this: Console, ...args: any[]): void {\n triggerHandlers('console', { args, level } as HandlerDataConsole);\n originalConsoleMethods[level]?.apply(this, args);\n };\n });\n }\n}\n"],"names":["defineIntegration","maybeInstrument","core","coreConsoleIntegration","GLOBAL_OBJ","CONSOLE_LEVELS","originalConsoleMethods","triggerHandlers","markFunctionWrapped","fill"],"mappings":"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"} | ||
| {"version":3,"file":"console.js","sources":["../../../src/integrations/console.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type { ConsoleLevel, HandlerDataConsole, WrappedFunction } from '@sentry/core';\nimport {\n CONSOLE_LEVELS,\n GLOBAL_OBJ,\n consoleIntegration as coreConsoleIntegration,\n defineIntegration,\n fill,\n markFunctionWrapped,\n maybeInstrument,\n originalConsoleMethods,\n triggerHandlers,\n} from '@sentry/core';\n\ninterface ConsoleIntegrationOptions {\n levels: ConsoleLevel[];\n /**\n * Filter out console messages that match the given strings or regular expressions.\n * These will neither be passed to the handler, and they will also not be logged to the user, unless they have debug enabled.\n */\n filter?: (string | RegExp)[];\n}\n\n/**\n * Node-specific console integration that captures breadcrumbs and handles\n * the AWS Lambda runtime replacing console methods after our patch.\n *\n * In Lambda, console methods are patched via `Object.defineProperty` so that\n * external replacements (by the Lambda runtime) are absorbed as the delegate\n * while our wrapper stays in place. Outside Lambda, this delegates entirely\n * to the core `consoleIntegration` which uses the simpler `fill`-based patch.\n */\nexport const consoleIntegration = defineIntegration((options: Partial<ConsoleIntegrationOptions> = {}) => {\n return {\n name: 'Console',\n setup(client) {\n if (process.env.LAMBDA_TASK_ROOT) {\n maybeInstrument('console', instrumentConsoleLambda);\n }\n\n // Delegate breadcrumb handling to the core console integration.\n const core = coreConsoleIntegration({\n ...options,\n filter: [\n ...(options.filter || []),\n // Deprecation on Node 26 for module.require(), which is used by IITM\n '[DEP0205] DeprecationWarning',\n ],\n });\n core.setup?.(client);\n },\n };\n});\n\n/**\n * NOTE: This currently ignores the filter option.\n * We can revisit this later.\n */\nfunction instrumentConsoleLambda(): void {\n const consoleObj = GLOBAL_OBJ?.console;\n if (!consoleObj) {\n return;\n }\n\n CONSOLE_LEVELS.forEach((level: ConsoleLevel) => {\n if (level in consoleObj) {\n patchWithDefineProperty(consoleObj, level);\n }\n });\n}\n\nfunction patchWithDefineProperty(consoleObj: Console, level: ConsoleLevel): void {\n const nativeMethod = consoleObj[level] as (...args: unknown[]) => void;\n originalConsoleMethods[level] = nativeMethod;\n\n let delegate: Function = nativeMethod;\n let savedDelegate: Function | undefined;\n let isExecuting = false;\n\n const wrapper = function (...args: any[]): void {\n if (isExecuting) {\n // Re-entrant call: a third party captured `wrapper` via the getter and calls it from inside their replacement. We must\n // use `nativeMethod` (not `delegate`) to break the cycle, and we intentionally skip `triggerHandlers` to avoid duplicate\n // breadcrumbs. The outer invocation already triggered the handlers for this console call.\n nativeMethod.apply(consoleObj, args);\n return;\n }\n isExecuting = true;\n try {\n triggerHandlers('console', { args, level } as HandlerDataConsole);\n delegate.apply(consoleObj, args);\n } finally {\n isExecuting = false;\n }\n };\n markFunctionWrapped(wrapper as unknown as WrappedFunction, nativeMethod as unknown as WrappedFunction);\n\n // consoleSandbox reads originalConsoleMethods[level] to temporarily bypass instrumentation. We replace it with a distinct reference (.bind creates a\n // new function identity) so the setter can tell apart \"consoleSandbox bypass\" from \"external code restoring a native method captured before Sentry init.\"\n const sandboxBypass = nativeMethod.bind(consoleObj);\n originalConsoleMethods[level] = sandboxBypass;\n\n try {\n let current: any = wrapper;\n\n Object.defineProperty(consoleObj, level, {\n configurable: true,\n enumerable: true,\n get() {\n return current;\n },\n set(newValue) {\n if (newValue === wrapper) {\n // consoleSandbox restoring the wrapper: recover the saved delegate.\n if (savedDelegate !== undefined) {\n delegate = savedDelegate;\n savedDelegate = undefined;\n }\n current = wrapper;\n } else if (newValue === sandboxBypass) {\n // consoleSandbox entering bypass: save delegate, let getter return sandboxBypass directly so calls skip the wrapper entirely.\n savedDelegate = delegate;\n current = sandboxBypass;\n } else if (typeof newValue === 'function' && !(newValue as WrappedFunction).__sentry_original__) {\n delegate = newValue;\n current = wrapper;\n } else {\n current = newValue;\n }\n },\n });\n } catch {\n // Fall back to fill-based patching if defineProperty fails\n fill(consoleObj, level, function (originalConsoleMethod: () => any): Function {\n originalConsoleMethods[level] = originalConsoleMethod;\n\n return function (this: Console, ...args: any[]): void {\n triggerHandlers('console', { args, level } as HandlerDataConsole);\n originalConsoleMethods[level]?.apply(this, args);\n };\n });\n }\n}\n"],"names":["defineIntegration","maybeInstrument","core","coreConsoleIntegration","GLOBAL_OBJ","CONSOLE_LEVELS","originalConsoleMethods","triggerHandlers","markFunctionWrapped","fill"],"mappings":"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"} |
@@ -10,13 +10,5 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| /* eslint-disable max-lines */ | ||
| const readFileAsync = util.promisify(node_fs.readFile); | ||
| const readDirAsync = util.promisify(node_fs.readdir); | ||
| // Process enhanced with methods from Node 18, 20, 22 as @types/node | ||
| // is on `14.18.0` to match minimum version requirements of the SDK | ||
| const INTEGRATION_NAME = 'Context'; | ||
| const INTEGRATION_NAME = "Context"; | ||
| const _nodeContextIntegration = ((options = {}) => { | ||
@@ -29,16 +21,12 @@ const _options = { | ||
| cloudResource: true, | ||
| ...options, | ||
| ...options | ||
| }; | ||
| // Compute contexts eagerly (shared between tx and span paths) | ||
| const appContext = _options.app ? getAppContext() : undefined; | ||
| const deviceContext = _options.device ? getDeviceContext(_options.device) : undefined; | ||
| const cultureContext = _options.culture ? getCultureContext() : undefined; | ||
| const cloudResourceContext = _options.cloudResource ? getCloudResourceContext() : undefined; | ||
| const osContextPromise = _options.os ? getOsContext() : undefined; | ||
| // Map static context data to span attributes | ||
| const appContext = _options.app ? getAppContext() : void 0; | ||
| const deviceContext = _options.device ? getDeviceContext(_options.device) : void 0; | ||
| const cultureContext = _options.culture ? getCultureContext() : void 0; | ||
| const cloudResourceContext = _options.cloudResource ? getCloudResourceContext() : void 0; | ||
| const osContextPromise = _options.os ? getOsContext() : void 0; | ||
| const cachedSpanAttributes = { | ||
| 'process.runtime.engine.name': 'v8', | ||
| 'process.runtime.engine.version': process.versions.v8, | ||
| "process.runtime.engine.name": "v8", | ||
| "process.runtime.engine.version": process.versions.v8, | ||
| ...contextsToSpanAttributes({ | ||
@@ -48,15 +36,9 @@ app: appContext, | ||
| culture: cultureContext, | ||
| cloud_resource: cloudResourceContext, | ||
| }), | ||
| cloud_resource: cloudResourceContext | ||
| }) | ||
| }; | ||
| if (osContextPromise) { | ||
| osContextPromise | ||
| .then(osCtx => Object.assign(cachedSpanAttributes, contextsToSpanAttributes({ os: osCtx }))) | ||
| .catch(() => { | ||
| // Ignore - os attributes will be undefined | ||
| }); | ||
| osContextPromise.then((osCtx) => Object.assign(cachedSpanAttributes, contextsToSpanAttributes({ os: osCtx }))).catch(() => { | ||
| }); | ||
| } | ||
| // Build contexts for event processing (reuses same data, awaits async OS context) | ||
| const contextsPromise = (async () => { | ||
@@ -81,7 +63,4 @@ const contexts = {}; | ||
| })(); | ||
| async function addContext(event) { | ||
| const updatedContext = _updateContext(await contextsPromise); | ||
| // TODO(v11): conditional with `sendDefaultPii` here? | ||
| event.contexts = { | ||
@@ -93,8 +72,6 @@ ...event.contexts, | ||
| culture: { ...updatedContext.culture, ...event.contexts?.culture }, | ||
| cloud_resource: { ...updatedContext.cloud_resource, ...event.contexts?.cloud_resource }, | ||
| cloud_resource: { ...updatedContext.cloud_resource, ...event.contexts?.cloud_resource } | ||
| }; | ||
| return event; | ||
| } | ||
| return { | ||
@@ -108,23 +85,12 @@ name: INTEGRATION_NAME, | ||
| core.safeSetSpanJSONAttributes(span, getDynamicSpanAttributes(appContext, deviceContext)); | ||
| }, | ||
| } | ||
| }; | ||
| }) ; | ||
| /** | ||
| * Capture context about the environment and the device that the client is running on, to events. | ||
| */ | ||
| }); | ||
| const nodeContextIntegration = core.defineIntegration(_nodeContextIntegration); | ||
| /** | ||
| * Updates the context with dynamic values that can change | ||
| */ | ||
| function _updateContext(contexts) { | ||
| // Only update properties if they exist | ||
| if (contexts.app?.app_memory) { | ||
| contexts.app.app_memory = process.memoryUsage().rss; | ||
| } | ||
| if (contexts.app?.free_memory && typeof (process ).availableMemory === 'function') { | ||
| const freeMemory = (process ).availableMemory?.(); | ||
| if (contexts.app?.free_memory && typeof process.availableMemory === "function") { | ||
| const freeMemory = process.availableMemory?.(); | ||
| if (freeMemory != null) { | ||
@@ -134,67 +100,57 @@ contexts.app.free_memory = freeMemory; | ||
| } | ||
| if (contexts.device?.free_memory) { | ||
| contexts.device.free_memory = os.freemem(); | ||
| } | ||
| return contexts; | ||
| } | ||
| function contextsToSpanAttributes(contexts) { | ||
| const attrs = {}; | ||
| const { app, device, os: osCtx, culture, cloud_resource } = contexts; | ||
| if (app) { | ||
| if (app.app_start_time) { | ||
| attrs['app.start_time'] = app.app_start_time; | ||
| attrs["app.start_time"] = app.app_start_time; | ||
| } | ||
| } | ||
| if (device) { | ||
| if (device.arch) { | ||
| attrs['device.archs'] = [device.arch]; | ||
| attrs["device.archs"] = [device.arch]; | ||
| } | ||
| if (device.boot_time) { | ||
| attrs['device.boot_time'] = device.boot_time; | ||
| attrs["device.boot_time"] = device.boot_time; | ||
| } | ||
| if (device.memory_size != null) { | ||
| attrs['device.memory_size'] = device.memory_size; | ||
| attrs["device.memory_size"] = device.memory_size; | ||
| } | ||
| if (device.processor_count != null) { | ||
| attrs['device.processor_count'] = device.processor_count; | ||
| attrs["device.processor_count"] = device.processor_count; | ||
| } | ||
| if (device.cpu_description) { | ||
| attrs['device.cpu_description'] = device.cpu_description; | ||
| attrs["device.cpu_description"] = device.cpu_description; | ||
| } | ||
| if (device.processor_frequency != null) { | ||
| attrs['device.processor_frequency'] = device.processor_frequency; | ||
| attrs["device.processor_frequency"] = device.processor_frequency; | ||
| } | ||
| } | ||
| if (osCtx) { | ||
| if (osCtx.name) { | ||
| attrs['os.name'] = osCtx.name; | ||
| attrs["os.name"] = osCtx.name; | ||
| } | ||
| if (osCtx.version) { | ||
| attrs['os.version'] = osCtx.version; | ||
| attrs["os.version"] = osCtx.version; | ||
| } | ||
| if (osCtx.kernel_version) { | ||
| attrs['os.kernel_version'] = osCtx.kernel_version; | ||
| attrs["os.kernel_version"] = osCtx.kernel_version; | ||
| } | ||
| if (osCtx.build) { | ||
| attrs['os.build'] = osCtx.build; | ||
| attrs["os.build"] = osCtx.build; | ||
| } | ||
| } | ||
| if (culture) { | ||
| if (culture.locale) { | ||
| attrs['culture.locale'] = culture.locale; | ||
| attrs["culture.locale"] = culture.locale; | ||
| } | ||
| if (culture.timezone) { | ||
| attrs['culture.timezone'] = culture.timezone; | ||
| attrs["culture.timezone"] = culture.timezone; | ||
| } | ||
| } | ||
| // CloudResourceContext already uses dot-notation keys matching span attribute conventions | ||
| if (cloud_resource) { | ||
@@ -207,50 +163,26 @@ for (const [key, value] of Object.entries(cloud_resource)) { | ||
| } | ||
| return attrs; | ||
| } | ||
| function getDynamicSpanAttributes( | ||
| appContext, | ||
| deviceContext, | ||
| ) { | ||
| function getDynamicSpanAttributes(appContext, deviceContext) { | ||
| const attrs = {}; | ||
| if (appContext) { | ||
| attrs['app.memory'] = process.memoryUsage().rss; | ||
| if (typeof (process ).availableMemory === 'function') { | ||
| const freeMemory = (process ).availableMemory?.(); | ||
| attrs["app.memory"] = process.memoryUsage().rss; | ||
| if (typeof process.availableMemory === "function") { | ||
| const freeMemory = process.availableMemory?.(); | ||
| if (freeMemory != null) { | ||
| attrs['app.free_memory'] = freeMemory; | ||
| attrs["app.free_memory"] = freeMemory; | ||
| } | ||
| } | ||
| } | ||
| // Only include if memory tracking was initially enabled (indicated by free_memory being set) | ||
| if (deviceContext?.free_memory != null) { | ||
| attrs['device.free_memory'] = os.freemem(); | ||
| attrs["device.free_memory"] = os.freemem(); | ||
| } | ||
| return attrs; | ||
| } | ||
| /** | ||
| * Returns the operating system context. | ||
| * | ||
| * Based on the current platform, this uses a different strategy to provide the | ||
| * most accurate OS information. Since this might involve spawning subprocesses | ||
| * or accessing the file system, this should only be executed lazily and cached. | ||
| * | ||
| * - On macOS (Darwin), this will execute the `sw_vers` utility. The context | ||
| * has a `name`, `version`, `build` and `kernel_version` set. | ||
| * - On Linux, this will try to load a distribution release from `/etc` and set | ||
| * the `name`, `version` and `kernel_version` fields. | ||
| * - On all other platforms, only a `name` and `version` will be returned. Note | ||
| * that `version` might actually be the kernel version. | ||
| */ | ||
| async function getOsContext() { | ||
| const platformId = os.platform(); | ||
| switch (platformId) { | ||
| case 'darwin': | ||
| case "darwin": | ||
| return getDarwinInfo(); | ||
| case 'linux': | ||
| case "linux": | ||
| return getLinuxInfo(); | ||
@@ -260,47 +192,30 @@ default: | ||
| name: PLATFORM_NAMES[platformId] || platformId, | ||
| version: os.release(), | ||
| version: os.release() | ||
| }; | ||
| } | ||
| } | ||
| function getCultureContext() { | ||
| try { | ||
| if (typeof process.versions.icu !== 'string') { | ||
| // Node was built without ICU support | ||
| if (typeof process.versions.icu !== "string") { | ||
| return; | ||
| } | ||
| // Check that node was built with full Intl support. Its possible it was built without support for non-English | ||
| // locales which will make resolvedOptions inaccurate | ||
| // | ||
| // https://nodejs.org/api/intl.html#detecting-internationalization-support | ||
| const january = new Date(9e8); | ||
| const spanish = new Intl.DateTimeFormat('es', { month: 'long' }); | ||
| if (spanish.format(january) === 'enero') { | ||
| const january = /* @__PURE__ */ new Date(9e8); | ||
| const spanish = new Intl.DateTimeFormat("es", { month: "long" }); | ||
| if (spanish.format(january) === "enero") { | ||
| const options = Intl.DateTimeFormat().resolvedOptions(); | ||
| return { | ||
| locale: options.locale, | ||
| timezone: options.timeZone, | ||
| timezone: options.timeZone | ||
| }; | ||
| } | ||
| } catch { | ||
| // | ||
| } | ||
| return; | ||
| } | ||
| /** | ||
| * Get app context information from process | ||
| */ | ||
| function getAppContext() { | ||
| const app_memory = process.memoryUsage().rss; | ||
| // oxlint-disable-next-line sdk/no-unsafe-random-apis | ||
| const app_start_time = new Date(Date.now() - process.uptime() * 1000).toISOString(); | ||
| // https://nodejs.org/api/process.html#processavailablememory | ||
| const app_start_time = new Date(Date.now() - process.uptime() * 1e3).toISOString(); | ||
| const appContext = { app_start_time, app_memory }; | ||
| if (typeof (process ).availableMemory === 'function') { | ||
| const freeMemory = (process ).availableMemory?.(); | ||
| if (typeof process.availableMemory === "function") { | ||
| const freeMemory = process.availableMemory?.(); | ||
| if (freeMemory != null) { | ||
@@ -310,13 +225,6 @@ appContext.free_memory = freeMemory; | ||
| } | ||
| return appContext; | ||
| } | ||
| /** | ||
| * Gets device information from os | ||
| */ | ||
| function getDeviceContext(deviceOpt) { | ||
| const device = {}; | ||
| // Sometimes os.uptime() throws due to lacking permissions: https://github.com/getsentry/sentry-javascript/issues/8202 | ||
| let uptime; | ||
@@ -326,15 +234,7 @@ try { | ||
| } catch { | ||
| // noop | ||
| } | ||
| // os.uptime or its return value seem to be undefined in certain environments (e.g. Azure functions). | ||
| // Hence, we only set boot time, if we get a valid uptime value. | ||
| // @see https://github.com/getsentry/sentry-javascript/issues/5856 | ||
| if (typeof uptime === 'number') { | ||
| // oxlint-disable-next-line sdk/no-unsafe-random-apis | ||
| device.boot_time = new Date(Date.now() - uptime * 1000).toISOString(); | ||
| if (typeof uptime === "number") { | ||
| device.boot_time = new Date(Date.now() - uptime * 1e3).toISOString(); | ||
| } | ||
| device.arch = os.arch(); | ||
| if (deviceOpt === true || deviceOpt.memory) { | ||
@@ -344,5 +244,4 @@ device.memory_size = os.totalmem(); | ||
| } | ||
| if (deviceOpt === true || deviceOpt.cpu) { | ||
| const cpuInfo = os.cpus() ; | ||
| const cpuInfo = os.cpus(); | ||
| const firstCpu = cpuInfo?.[0]; | ||
@@ -355,79 +254,50 @@ if (firstCpu) { | ||
| } | ||
| return device; | ||
| } | ||
| /** Mapping of Node's platform names to actual OS names. */ | ||
| const PLATFORM_NAMES = { | ||
| aix: 'IBM AIX', | ||
| freebsd: 'FreeBSD', | ||
| openbsd: 'OpenBSD', | ||
| sunos: 'SunOS', | ||
| win32: 'Windows', | ||
| ohos: 'OpenHarmony', | ||
| android: 'Android', | ||
| aix: "IBM AIX", | ||
| freebsd: "FreeBSD", | ||
| openbsd: "OpenBSD", | ||
| sunos: "SunOS", | ||
| win32: "Windows", | ||
| ohos: "OpenHarmony", | ||
| android: "Android" | ||
| }; | ||
| /** Linux version file to check for a distribution. */ | ||
| /** Mapping of linux release files located in /etc to distributions. */ | ||
| const LINUX_DISTROS = [ | ||
| { name: 'fedora-release', distros: ['Fedora'] }, | ||
| { name: 'redhat-release', distros: ['Red Hat Linux', 'Centos'] }, | ||
| { name: 'redhat_version', distros: ['Red Hat Linux'] }, | ||
| { name: 'SuSE-release', distros: ['SUSE Linux'] }, | ||
| { name: 'lsb-release', distros: ['Ubuntu Linux', 'Arch Linux'] }, | ||
| { name: 'debian_version', distros: ['Debian'] }, | ||
| { name: 'debian_release', distros: ['Debian'] }, | ||
| { name: 'arch-release', distros: ['Arch Linux'] }, | ||
| { name: 'gentoo-release', distros: ['Gentoo Linux'] }, | ||
| { name: 'novell-release', distros: ['SUSE Linux'] }, | ||
| { name: 'alpine-release', distros: ['Alpine Linux'] }, | ||
| { name: "fedora-release", distros: ["Fedora"] }, | ||
| { name: "redhat-release", distros: ["Red Hat Linux", "Centos"] }, | ||
| { name: "redhat_version", distros: ["Red Hat Linux"] }, | ||
| { name: "SuSE-release", distros: ["SUSE Linux"] }, | ||
| { name: "lsb-release", distros: ["Ubuntu Linux", "Arch Linux"] }, | ||
| { name: "debian_version", distros: ["Debian"] }, | ||
| { name: "debian_release", distros: ["Debian"] }, | ||
| { name: "arch-release", distros: ["Arch Linux"] }, | ||
| { name: "gentoo-release", distros: ["Gentoo Linux"] }, | ||
| { name: "novell-release", distros: ["SUSE Linux"] }, | ||
| { name: "alpine-release", distros: ["Alpine Linux"] } | ||
| ]; | ||
| /** Functions to extract the OS version from Linux release files. */ | ||
| const LINUX_VERSIONS | ||
| = { | ||
| alpine: content => content, | ||
| arch: content => matchFirst(/distrib_release=(.*)/, content), | ||
| centos: content => matchFirst(/release ([^ ]+)/, content), | ||
| debian: content => content, | ||
| fedora: content => matchFirst(/release (..)/, content), | ||
| mint: content => matchFirst(/distrib_release=(.*)/, content), | ||
| red: content => matchFirst(/release ([^ ]+)/, content), | ||
| suse: content => matchFirst(/VERSION = (.*)\n/, content), | ||
| ubuntu: content => matchFirst(/distrib_release=(.*)/, content), | ||
| const LINUX_VERSIONS = { | ||
| alpine: (content) => content, | ||
| arch: (content) => matchFirst(/distrib_release=(.*)/, content), | ||
| centos: (content) => matchFirst(/release ([^ ]+)/, content), | ||
| debian: (content) => content, | ||
| fedora: (content) => matchFirst(/release (..)/, content), | ||
| mint: (content) => matchFirst(/distrib_release=(.*)/, content), | ||
| red: (content) => matchFirst(/release ([^ ]+)/, content), | ||
| suse: (content) => matchFirst(/VERSION = (.*)\n/, content), | ||
| ubuntu: (content) => matchFirst(/distrib_release=(.*)/, content) | ||
| }; | ||
| /** | ||
| * Executes a regular expression with one capture group. | ||
| * | ||
| * @param regex A regular expression to execute. | ||
| * @param text Content to execute the RegEx on. | ||
| * @returns The captured string if matched; otherwise undefined. | ||
| */ | ||
| function matchFirst(regex, text) { | ||
| const match = regex.exec(text); | ||
| return match ? match[1] : undefined; | ||
| return match ? match[1] : void 0; | ||
| } | ||
| /** Loads the macOS operating system context. */ | ||
| async function getDarwinInfo() { | ||
| // Default values that will be used in case no operating system information | ||
| // can be loaded. The default version is computed via heuristics from the | ||
| // kernel version, but the build ID is missing. | ||
| const darwinInfo = { | ||
| kernel_version: os.release(), | ||
| name: 'Mac OS X', | ||
| version: `10.${Number(os.release().split('.')[0]) - 4}`, | ||
| name: "Mac OS X", | ||
| version: `10.${Number(os.release().split(".")[0]) - 4}` | ||
| }; | ||
| try { | ||
| // We try to load the actual macOS version by executing the `sw_vers` tool. | ||
| // This tool should be available on every standard macOS installation. In | ||
| // case this fails, we stick with the values computed above. | ||
| const output = await new Promise((resolve, reject) => { | ||
| node_child_process.execFile('/usr/bin/sw_vers', (error, stdout) => { | ||
| node_child_process.execFile("/usr/bin/sw_vers", (error, stdout) => { | ||
| if (error) { | ||
@@ -440,3 +310,2 @@ reject(error); | ||
| }); | ||
| darwinInfo.name = matchFirst(/^ProductName:\s+(.*)$/m, output); | ||
@@ -446,129 +315,82 @@ darwinInfo.version = matchFirst(/^ProductVersion:\s+(.*)$/m, output); | ||
| } catch { | ||
| // ignore | ||
| } | ||
| return darwinInfo; | ||
| } | ||
| /** Returns a distribution identifier to look up version callbacks. */ | ||
| function getLinuxDistroId(name) { | ||
| return (name.split(' ') )[0].toLowerCase(); | ||
| return name.split(" ")[0].toLowerCase(); | ||
| } | ||
| /** Loads the Linux operating system context. */ | ||
| async function getLinuxInfo() { | ||
| // By default, we cannot assume anything about the distribution or Linux | ||
| // version. `os.release()` returns the kernel version and we assume a generic | ||
| // "Linux" name, which will be replaced down below. | ||
| const linuxInfo = { | ||
| kernel_version: os.release(), | ||
| name: 'Linux', | ||
| name: "Linux" | ||
| }; | ||
| try { | ||
| // We start guessing the distribution by listing files in the /etc | ||
| // directory. This is were most Linux distributions (except Knoppix) store | ||
| // release files with certain distribution-dependent meta data. We search | ||
| // for exactly one known file defined in `LINUX_DISTROS` and exit if none | ||
| // are found. In case there are more than one file, we just stick with the | ||
| // first one. | ||
| const etcFiles = await readDirAsync('/etc'); | ||
| const distroFile = LINUX_DISTROS.find(file => etcFiles.includes(file.name)); | ||
| const etcFiles = await readDirAsync("/etc"); | ||
| const distroFile = LINUX_DISTROS.find((file) => etcFiles.includes(file.name)); | ||
| if (!distroFile) { | ||
| return linuxInfo; | ||
| } | ||
| // Once that file is known, load its contents. To make searching in those | ||
| // files easier, we lowercase the file contents. Since these files are | ||
| // usually quite small, this should not allocate too much memory and we only | ||
| // hold on to it for a very short amount of time. | ||
| const distroPath = node_path.join('/etc', distroFile.name); | ||
| const contents = (await readFileAsync(distroPath, { encoding: 'utf-8' })).toLowerCase(); | ||
| // Some Linux distributions store their release information in the same file | ||
| // (e.g. RHEL and Centos). In those cases, we scan the file for an | ||
| // identifier, that basically consists of the first word of the linux | ||
| // distribution name (e.g. "red" for Red Hat). In case there is no match, we | ||
| // just assume the first distribution in our list. | ||
| const distroPath = node_path.join("/etc", distroFile.name); | ||
| const contents = (await readFileAsync(distroPath, { encoding: "utf-8" })).toLowerCase(); | ||
| const { distros } = distroFile; | ||
| linuxInfo.name = distros.find(d => contents.indexOf(getLinuxDistroId(d)) >= 0) || distros[0]; | ||
| // Based on the found distribution, we can now compute the actual version | ||
| // number. This is different for every distribution, so several strategies | ||
| // are computed in `LINUX_VERSIONS`. | ||
| linuxInfo.name = distros.find((d) => contents.indexOf(getLinuxDistroId(d)) >= 0) || distros[0]; | ||
| const id = getLinuxDistroId(linuxInfo.name); | ||
| linuxInfo.version = LINUX_VERSIONS[id]?.(contents); | ||
| } catch { | ||
| // ignore | ||
| } | ||
| return linuxInfo; | ||
| } | ||
| /** | ||
| * Grabs some information about hosting provider based on best effort. | ||
| */ | ||
| function getCloudResourceContext() { | ||
| if (process.env.VERCEL) { | ||
| // https://vercel.com/docs/concepts/projects/environment-variables/system-environment-variables#system-environment-variables | ||
| return { | ||
| 'cloud.provider': 'vercel', | ||
| 'cloud.region': process.env.VERCEL_REGION, | ||
| "cloud.provider": "vercel", | ||
| "cloud.region": process.env.VERCEL_REGION | ||
| }; | ||
| } else if (process.env.AWS_REGION) { | ||
| // https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html | ||
| return { | ||
| 'cloud.provider': 'aws', | ||
| 'cloud.region': process.env.AWS_REGION, | ||
| 'cloud.platform': process.env.AWS_EXECUTION_ENV, | ||
| "cloud.provider": "aws", | ||
| "cloud.region": process.env.AWS_REGION, | ||
| "cloud.platform": process.env.AWS_EXECUTION_ENV | ||
| }; | ||
| } else if (process.env.GCP_PROJECT) { | ||
| // https://cloud.google.com/composer/docs/how-to/managing/environment-variables#reserved_variables | ||
| return { | ||
| 'cloud.provider': 'gcp', | ||
| "cloud.provider": "gcp" | ||
| }; | ||
| } else if (process.env.ALIYUN_REGION_ID) { | ||
| // TODO: find where I found these environment variables - at least gc.github.com returns something | ||
| return { | ||
| 'cloud.provider': 'alibaba_cloud', | ||
| 'cloud.region': process.env.ALIYUN_REGION_ID, | ||
| "cloud.provider": "alibaba_cloud", | ||
| "cloud.region": process.env.ALIYUN_REGION_ID | ||
| }; | ||
| } else if (process.env.WEBSITE_SITE_NAME && process.env.REGION_NAME) { | ||
| // https://learn.microsoft.com/en-us/azure/app-service/reference-app-settings?tabs=kudu%2Cdotnet#app-environment | ||
| return { | ||
| 'cloud.provider': 'azure', | ||
| 'cloud.region': process.env.REGION_NAME, | ||
| "cloud.provider": "azure", | ||
| "cloud.region": process.env.REGION_NAME | ||
| }; | ||
| } else if (process.env.IBM_CLOUD_REGION) { | ||
| // TODO: find where I found these environment variables - at least gc.github.com returns something | ||
| return { | ||
| 'cloud.provider': 'ibm_cloud', | ||
| 'cloud.region': process.env.IBM_CLOUD_REGION, | ||
| "cloud.provider": "ibm_cloud", | ||
| "cloud.region": process.env.IBM_CLOUD_REGION | ||
| }; | ||
| } else if (process.env.TENCENTCLOUD_REGION) { | ||
| // https://www.tencentcloud.com/document/product/583/32748 | ||
| return { | ||
| 'cloud.provider': 'tencent_cloud', | ||
| 'cloud.region': process.env.TENCENTCLOUD_REGION, | ||
| 'cloud.account.id': process.env.TENCENTCLOUD_APPID, | ||
| 'cloud.availability_zone': process.env.TENCENTCLOUD_ZONE, | ||
| "cloud.provider": "tencent_cloud", | ||
| "cloud.region": process.env.TENCENTCLOUD_REGION, | ||
| "cloud.account.id": process.env.TENCENTCLOUD_APPID, | ||
| "cloud.availability_zone": process.env.TENCENTCLOUD_ZONE | ||
| }; | ||
| } else if (process.env.NETLIFY) { | ||
| // https://docs.netlify.com/configure-builds/environment-variables/#read-only-variables | ||
| return { | ||
| 'cloud.provider': 'netlify', | ||
| "cloud.provider": "netlify" | ||
| }; | ||
| } else if (process.env.FLY_REGION) { | ||
| // https://fly.io/docs/reference/runtime-environment/ | ||
| return { | ||
| 'cloud.provider': 'fly.io', | ||
| 'cloud.region': process.env.FLY_REGION, | ||
| "cloud.provider": "fly.io", | ||
| "cloud.region": process.env.FLY_REGION | ||
| }; | ||
| } else if (process.env.DYNO) { | ||
| // https://devcenter.heroku.com/articles/dynos#local-environment-variables | ||
| return { | ||
| 'cloud.provider': 'heroku', | ||
| "cloud.provider": "heroku" | ||
| }; | ||
| } else { | ||
| return undefined; | ||
| return void 0; | ||
| } | ||
@@ -575,0 +397,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"context.js","sources":["../../../src/integrations/context.ts"],"sourcesContent":["/* eslint-disable max-lines */\n\nimport { execFile } from 'node:child_process';\nimport { readdir, readFile } from 'node:fs';\nimport * as os from 'node:os';\nimport { join } from 'node:path';\nimport { promisify } from 'node:util';\nimport type {\n AppContext,\n CloudResourceContext,\n Contexts,\n CultureContext,\n DeviceContext,\n Event,\n IntegrationFn,\n OsContext,\n} from '@sentry/core';\nimport { defineIntegration, safeSetSpanJSONAttributes } from '@sentry/core';\n\nexport const readFileAsync = promisify(readFile);\nexport const readDirAsync = promisify(readdir);\n\n// Process enhanced with methods from Node 18, 20, 22 as @types/node\n// is on `14.18.0` to match minimum version requirements of the SDK\ninterface ProcessWithCurrentValues extends NodeJS.Process {\n availableMemory?(): number;\n}\n\nconst INTEGRATION_NAME = 'Context';\n\ninterface DeviceContextOptions {\n cpu?: boolean;\n memory?: boolean;\n}\n\ninterface ContextOptions {\n app?: boolean;\n os?: boolean;\n device?: DeviceContextOptions | boolean;\n culture?: boolean;\n cloudResource?: boolean;\n}\n\nconst _nodeContextIntegration = ((options: ContextOptions = {}) => {\n const _options = {\n app: true,\n os: true,\n device: true,\n culture: true,\n cloudResource: true,\n ...options,\n };\n\n // Compute contexts eagerly (shared between tx and span paths)\n const appContext = _options.app ? getAppContext() : undefined;\n const deviceContext = _options.device ? getDeviceContext(_options.device) : undefined;\n const cultureContext = _options.culture ? getCultureContext() : undefined;\n const cloudResourceContext = _options.cloudResource ? getCloudResourceContext() : undefined;\n const osContextPromise = _options.os ? getOsContext() : undefined;\n\n // Map static context data to span attributes\n const cachedSpanAttributes: Record<string, unknown> = {\n 'process.runtime.engine.name': 'v8',\n 'process.runtime.engine.version': process.versions.v8,\n ...contextsToSpanAttributes({\n app: appContext,\n device: deviceContext,\n culture: cultureContext,\n cloud_resource: cloudResourceContext,\n }),\n };\n\n if (osContextPromise) {\n osContextPromise\n .then(osCtx => Object.assign(cachedSpanAttributes, contextsToSpanAttributes({ os: osCtx })))\n .catch(() => {\n // Ignore - os attributes will be undefined\n });\n }\n\n // Build contexts for event processing (reuses same data, awaits async OS context)\n const contextsPromise: Promise<Contexts> = (async () => {\n const contexts: Contexts = {};\n if (osContextPromise) {\n contexts.os = await osContextPromise;\n }\n if (appContext) {\n contexts.app = appContext;\n }\n if (deviceContext) {\n contexts.device = deviceContext;\n }\n if (cultureContext) {\n contexts.culture = cultureContext;\n }\n if (cloudResourceContext) {\n contexts.cloud_resource = cloudResourceContext;\n }\n return contexts;\n })();\n\n async function addContext(event: Event): Promise<Event> {\n const updatedContext = _updateContext(await contextsPromise);\n\n // TODO(v11): conditional with `sendDefaultPii` here?\n event.contexts = {\n ...event.contexts,\n app: { ...updatedContext.app, ...event.contexts?.app },\n os: { ...updatedContext.os, ...event.contexts?.os },\n device: { ...updatedContext.device, ...event.contexts?.device },\n culture: { ...updatedContext.culture, ...event.contexts?.culture },\n cloud_resource: { ...updatedContext.cloud_resource, ...event.contexts?.cloud_resource },\n };\n\n return event;\n }\n\n return {\n name: INTEGRATION_NAME,\n processEvent(event) {\n return addContext(event);\n },\n processSegmentSpan(span) {\n safeSetSpanJSONAttributes(span, cachedSpanAttributes);\n safeSetSpanJSONAttributes(span, getDynamicSpanAttributes(appContext, deviceContext));\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Capture context about the environment and the device that the client is running on, to events.\n */\nexport const nodeContextIntegration = defineIntegration(_nodeContextIntegration);\n\n/**\n * Updates the context with dynamic values that can change\n */\nfunction _updateContext(contexts: Contexts): Contexts {\n // Only update properties if they exist\n\n if (contexts.app?.app_memory) {\n contexts.app.app_memory = process.memoryUsage().rss;\n }\n\n if (contexts.app?.free_memory && typeof (process as ProcessWithCurrentValues).availableMemory === 'function') {\n const freeMemory = (process as ProcessWithCurrentValues).availableMemory?.();\n if (freeMemory != null) {\n contexts.app.free_memory = freeMemory;\n }\n }\n\n if (contexts.device?.free_memory) {\n contexts.device.free_memory = os.freemem();\n }\n\n return contexts;\n}\n\nexport function contextsToSpanAttributes(contexts: Contexts): Record<string, unknown> {\n const attrs: Record<string, unknown> = {};\n\n const { app, device, os: osCtx, culture, cloud_resource } = contexts;\n\n if (app) {\n if (app.app_start_time) {\n attrs['app.start_time'] = app.app_start_time;\n }\n }\n\n if (device) {\n if (device.arch) {\n attrs['device.archs'] = [device.arch];\n }\n if (device.boot_time) {\n attrs['device.boot_time'] = device.boot_time;\n }\n if (device.memory_size != null) {\n attrs['device.memory_size'] = device.memory_size;\n }\n if (device.processor_count != null) {\n attrs['device.processor_count'] = device.processor_count;\n }\n if (device.cpu_description) {\n attrs['device.cpu_description'] = device.cpu_description;\n }\n if (device.processor_frequency != null) {\n attrs['device.processor_frequency'] = device.processor_frequency;\n }\n }\n\n if (osCtx) {\n if (osCtx.name) {\n attrs['os.name'] = osCtx.name;\n }\n if (osCtx.version) {\n attrs['os.version'] = osCtx.version;\n }\n if (osCtx.kernel_version) {\n attrs['os.kernel_version'] = osCtx.kernel_version;\n }\n if (osCtx.build) {\n attrs['os.build'] = osCtx.build;\n }\n }\n\n if (culture) {\n if (culture.locale) {\n attrs['culture.locale'] = culture.locale;\n }\n if (culture.timezone) {\n attrs['culture.timezone'] = culture.timezone;\n }\n }\n\n // CloudResourceContext already uses dot-notation keys matching span attribute conventions\n if (cloud_resource) {\n for (const [key, value] of Object.entries(cloud_resource)) {\n if (value != null) {\n attrs[key] = value;\n }\n }\n }\n\n return attrs;\n}\n\nexport function getDynamicSpanAttributes(\n appContext: AppContext | undefined,\n deviceContext: DeviceContext | undefined,\n): Record<string, unknown> {\n const attrs: Record<string, unknown> = {};\n\n if (appContext) {\n attrs['app.memory'] = process.memoryUsage().rss;\n if (typeof (process as ProcessWithCurrentValues).availableMemory === 'function') {\n const freeMemory = (process as ProcessWithCurrentValues).availableMemory?.();\n if (freeMemory != null) {\n attrs['app.free_memory'] = freeMemory;\n }\n }\n }\n\n // Only include if memory tracking was initially enabled (indicated by free_memory being set)\n if (deviceContext?.free_memory != null) {\n attrs['device.free_memory'] = os.freemem();\n }\n\n return attrs;\n}\n\n/**\n * Returns the operating system context.\n *\n * Based on the current platform, this uses a different strategy to provide the\n * most accurate OS information. Since this might involve spawning subprocesses\n * or accessing the file system, this should only be executed lazily and cached.\n *\n * - On macOS (Darwin), this will execute the `sw_vers` utility. The context\n * has a `name`, `version`, `build` and `kernel_version` set.\n * - On Linux, this will try to load a distribution release from `/etc` and set\n * the `name`, `version` and `kernel_version` fields.\n * - On all other platforms, only a `name` and `version` will be returned. Note\n * that `version` might actually be the kernel version.\n */\nasync function getOsContext(): Promise<OsContext> {\n const platformId = os.platform();\n switch (platformId) {\n case 'darwin':\n return getDarwinInfo();\n case 'linux':\n return getLinuxInfo();\n default:\n return {\n name: PLATFORM_NAMES[platformId] || platformId,\n version: os.release(),\n };\n }\n}\n\nfunction getCultureContext(): CultureContext | undefined {\n try {\n if (typeof process.versions.icu !== 'string') {\n // Node was built without ICU support\n return;\n }\n\n // Check that node was built with full Intl support. Its possible it was built without support for non-English\n // locales which will make resolvedOptions inaccurate\n //\n // https://nodejs.org/api/intl.html#detecting-internationalization-support\n const january = new Date(9e8);\n const spanish = new Intl.DateTimeFormat('es', { month: 'long' });\n if (spanish.format(january) === 'enero') {\n const options = Intl.DateTimeFormat().resolvedOptions();\n\n return {\n locale: options.locale,\n timezone: options.timeZone,\n };\n }\n } catch {\n //\n }\n\n return;\n}\n\n/**\n * Get app context information from process\n */\nexport function getAppContext(): AppContext {\n const app_memory = process.memoryUsage().rss;\n // oxlint-disable-next-line sdk/no-unsafe-random-apis\n const app_start_time = new Date(Date.now() - process.uptime() * 1000).toISOString();\n // https://nodejs.org/api/process.html#processavailablememory\n const appContext: AppContext = { app_start_time, app_memory };\n\n if (typeof (process as ProcessWithCurrentValues).availableMemory === 'function') {\n const freeMemory = (process as ProcessWithCurrentValues).availableMemory?.();\n if (freeMemory != null) {\n appContext.free_memory = freeMemory;\n }\n }\n\n return appContext;\n}\n\n/**\n * Gets device information from os\n */\nexport function getDeviceContext(deviceOpt: DeviceContextOptions | true): DeviceContext {\n const device: DeviceContext = {};\n\n // Sometimes os.uptime() throws due to lacking permissions: https://github.com/getsentry/sentry-javascript/issues/8202\n let uptime;\n try {\n uptime = os.uptime();\n } catch {\n // noop\n }\n\n // os.uptime or its return value seem to be undefined in certain environments (e.g. Azure functions).\n // Hence, we only set boot time, if we get a valid uptime value.\n // @see https://github.com/getsentry/sentry-javascript/issues/5856\n if (typeof uptime === 'number') {\n // oxlint-disable-next-line sdk/no-unsafe-random-apis\n device.boot_time = new Date(Date.now() - uptime * 1000).toISOString();\n }\n\n device.arch = os.arch();\n\n if (deviceOpt === true || deviceOpt.memory) {\n device.memory_size = os.totalmem();\n device.free_memory = os.freemem();\n }\n\n if (deviceOpt === true || deviceOpt.cpu) {\n const cpuInfo = os.cpus() as os.CpuInfo[] | undefined;\n const firstCpu = cpuInfo?.[0];\n if (firstCpu) {\n device.processor_count = cpuInfo.length;\n device.cpu_description = firstCpu.model;\n device.processor_frequency = firstCpu.speed;\n }\n }\n\n return device;\n}\n\n/** Mapping of Node's platform names to actual OS names. */\nconst PLATFORM_NAMES: { [platform: string]: string } = {\n aix: 'IBM AIX',\n freebsd: 'FreeBSD',\n openbsd: 'OpenBSD',\n sunos: 'SunOS',\n win32: 'Windows',\n ohos: 'OpenHarmony',\n android: 'Android',\n};\n\n/** Linux version file to check for a distribution. */\ninterface DistroFile {\n /** The file name, located in `/etc`. */\n name: string;\n /** Potential distributions to check. */\n distros: [string, ...string[]];\n}\n\n/** Mapping of linux release files located in /etc to distributions. */\nconst LINUX_DISTROS: DistroFile[] = [\n { name: 'fedora-release', distros: ['Fedora'] },\n { name: 'redhat-release', distros: ['Red Hat Linux', 'Centos'] },\n { name: 'redhat_version', distros: ['Red Hat Linux'] },\n { name: 'SuSE-release', distros: ['SUSE Linux'] },\n { name: 'lsb-release', distros: ['Ubuntu Linux', 'Arch Linux'] },\n { name: 'debian_version', distros: ['Debian'] },\n { name: 'debian_release', distros: ['Debian'] },\n { name: 'arch-release', distros: ['Arch Linux'] },\n { name: 'gentoo-release', distros: ['Gentoo Linux'] },\n { name: 'novell-release', distros: ['SUSE Linux'] },\n { name: 'alpine-release', distros: ['Alpine Linux'] },\n];\n\n/** Functions to extract the OS version from Linux release files. */\nconst LINUX_VERSIONS: {\n [identifier: string]: (content: string) => string | undefined;\n} = {\n alpine: content => content,\n arch: content => matchFirst(/distrib_release=(.*)/, content),\n centos: content => matchFirst(/release ([^ ]+)/, content),\n debian: content => content,\n fedora: content => matchFirst(/release (..)/, content),\n mint: content => matchFirst(/distrib_release=(.*)/, content),\n red: content => matchFirst(/release ([^ ]+)/, content),\n suse: content => matchFirst(/VERSION = (.*)\\n/, content),\n ubuntu: content => matchFirst(/distrib_release=(.*)/, content),\n};\n\n/**\n * Executes a regular expression with one capture group.\n *\n * @param regex A regular expression to execute.\n * @param text Content to execute the RegEx on.\n * @returns The captured string if matched; otherwise undefined.\n */\nfunction matchFirst(regex: RegExp, text: string): string | undefined {\n const match = regex.exec(text);\n return match ? match[1] : undefined;\n}\n\n/** Loads the macOS operating system context. */\nasync function getDarwinInfo(): Promise<OsContext> {\n // Default values that will be used in case no operating system information\n // can be loaded. The default version is computed via heuristics from the\n // kernel version, but the build ID is missing.\n const darwinInfo: OsContext = {\n kernel_version: os.release(),\n name: 'Mac OS X',\n version: `10.${Number(os.release().split('.')[0]) - 4}`,\n };\n\n try {\n // We try to load the actual macOS version by executing the `sw_vers` tool.\n // This tool should be available on every standard macOS installation. In\n // case this fails, we stick with the values computed above.\n\n const output = await new Promise<string>((resolve, reject) => {\n execFile('/usr/bin/sw_vers', (error: Error | null, stdout: string) => {\n if (error) {\n reject(error);\n return;\n }\n resolve(stdout);\n });\n });\n\n darwinInfo.name = matchFirst(/^ProductName:\\s+(.*)$/m, output);\n darwinInfo.version = matchFirst(/^ProductVersion:\\s+(.*)$/m, output);\n darwinInfo.build = matchFirst(/^BuildVersion:\\s+(.*)$/m, output);\n } catch {\n // ignore\n }\n\n return darwinInfo;\n}\n\n/** Returns a distribution identifier to look up version callbacks. */\nfunction getLinuxDistroId(name: string): string {\n return (name.split(' ') as [string])[0].toLowerCase();\n}\n\n/** Loads the Linux operating system context. */\nasync function getLinuxInfo(): Promise<OsContext> {\n // By default, we cannot assume anything about the distribution or Linux\n // version. `os.release()` returns the kernel version and we assume a generic\n // \"Linux\" name, which will be replaced down below.\n const linuxInfo: OsContext = {\n kernel_version: os.release(),\n name: 'Linux',\n };\n\n try {\n // We start guessing the distribution by listing files in the /etc\n // directory. This is were most Linux distributions (except Knoppix) store\n // release files with certain distribution-dependent meta data. We search\n // for exactly one known file defined in `LINUX_DISTROS` and exit if none\n // are found. In case there are more than one file, we just stick with the\n // first one.\n const etcFiles = await readDirAsync('/etc');\n const distroFile = LINUX_DISTROS.find(file => etcFiles.includes(file.name));\n if (!distroFile) {\n return linuxInfo;\n }\n\n // Once that file is known, load its contents. To make searching in those\n // files easier, we lowercase the file contents. Since these files are\n // usually quite small, this should not allocate too much memory and we only\n // hold on to it for a very short amount of time.\n const distroPath = join('/etc', distroFile.name);\n const contents = (await readFileAsync(distroPath, { encoding: 'utf-8' })).toLowerCase();\n\n // Some Linux distributions store their release information in the same file\n // (e.g. RHEL and Centos). In those cases, we scan the file for an\n // identifier, that basically consists of the first word of the linux\n // distribution name (e.g. \"red\" for Red Hat). In case there is no match, we\n // just assume the first distribution in our list.\n const { distros } = distroFile;\n linuxInfo.name = distros.find(d => contents.indexOf(getLinuxDistroId(d)) >= 0) || distros[0];\n\n // Based on the found distribution, we can now compute the actual version\n // number. This is different for every distribution, so several strategies\n // are computed in `LINUX_VERSIONS`.\n const id = getLinuxDistroId(linuxInfo.name);\n linuxInfo.version = LINUX_VERSIONS[id]?.(contents);\n } catch {\n // ignore\n }\n\n return linuxInfo;\n}\n\n/**\n * Grabs some information about hosting provider based on best effort.\n */\nfunction getCloudResourceContext(): CloudResourceContext | undefined {\n if (process.env.VERCEL) {\n // https://vercel.com/docs/concepts/projects/environment-variables/system-environment-variables#system-environment-variables\n return {\n 'cloud.provider': 'vercel',\n 'cloud.region': process.env.VERCEL_REGION,\n };\n } else if (process.env.AWS_REGION) {\n // https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html\n return {\n 'cloud.provider': 'aws',\n 'cloud.region': process.env.AWS_REGION,\n 'cloud.platform': process.env.AWS_EXECUTION_ENV,\n };\n } else if (process.env.GCP_PROJECT) {\n // https://cloud.google.com/composer/docs/how-to/managing/environment-variables#reserved_variables\n return {\n 'cloud.provider': 'gcp',\n };\n } else if (process.env.ALIYUN_REGION_ID) {\n // TODO: find where I found these environment variables - at least gc.github.com returns something\n return {\n 'cloud.provider': 'alibaba_cloud',\n 'cloud.region': process.env.ALIYUN_REGION_ID,\n };\n } else if (process.env.WEBSITE_SITE_NAME && process.env.REGION_NAME) {\n // https://learn.microsoft.com/en-us/azure/app-service/reference-app-settings?tabs=kudu%2Cdotnet#app-environment\n return {\n 'cloud.provider': 'azure',\n 'cloud.region': process.env.REGION_NAME,\n };\n } else if (process.env.IBM_CLOUD_REGION) {\n // TODO: find where I found these environment variables - at least gc.github.com returns something\n return {\n 'cloud.provider': 'ibm_cloud',\n 'cloud.region': process.env.IBM_CLOUD_REGION,\n };\n } else if (process.env.TENCENTCLOUD_REGION) {\n // https://www.tencentcloud.com/document/product/583/32748\n return {\n 'cloud.provider': 'tencent_cloud',\n 'cloud.region': process.env.TENCENTCLOUD_REGION,\n 'cloud.account.id': process.env.TENCENTCLOUD_APPID,\n 'cloud.availability_zone': process.env.TENCENTCLOUD_ZONE,\n };\n } else if (process.env.NETLIFY) {\n // https://docs.netlify.com/configure-builds/environment-variables/#read-only-variables\n return {\n 'cloud.provider': 'netlify',\n };\n } else if (process.env.FLY_REGION) {\n // https://fly.io/docs/reference/runtime-environment/\n return {\n 'cloud.provider': 'fly.io',\n 'cloud.region': process.env.FLY_REGION,\n };\n } else if (process.env.DYNO) {\n // https://devcenter.heroku.com/articles/dynos#local-environment-variables\n return {\n 'cloud.provider': 'heroku',\n };\n } else {\n return undefined;\n }\n}\n"],"names":["promisify","readFile","readdir","safeSetSpanJSONAttributes","defineIntegration","execFile","join"],"mappings":";;;;;;;;;AAAA;;;MAmBa,aAAA,GAAgBA,cAAS,CAACC,gBAAQ;MAClC,YAAA,GAAeD,cAAS,CAACE,eAAO;;AAE7C;AACA;;AAKA,MAAM,gBAAA,GAAmB,SAAS;;AAelC,MAAM,uBAAA,IAA2B,CAAC,OAAO,GAAmB,EAAE,KAAK;AACnE,EAAE,MAAM,WAAW;AACnB,IAAI,GAAG,EAAE,IAAI;AACb,IAAI,EAAE,EAAE,IAAI;AACZ,IAAI,MAAM,EAAE,IAAI;AAChB,IAAI,OAAO,EAAE,IAAI;AACjB,IAAI,aAAa,EAAE,IAAI;AACvB,IAAI,GAAG,OAAO;AACd,GAAG;;AAEH;AACA,EAAE,MAAM,UAAA,GAAa,QAAQ,CAAC,GAAA,GAAM,aAAa,EAAC,GAAI,SAAS;AAC/D,EAAE,MAAM,aAAA,GAAgB,QAAQ,CAAC,MAAA,GAAS,gBAAgB,CAAC,QAAQ,CAAC,MAAM,CAAA,GAAI,SAAS;AACvF,EAAE,MAAM,cAAA,GAAiB,QAAQ,CAAC,OAAA,GAAU,iBAAiB,EAAC,GAAI,SAAS;AAC3E,EAAE,MAAM,oBAAA,GAAuB,QAAQ,CAAC,aAAA,GAAgB,uBAAuB,EAAC,GAAI,SAAS;AAC7F,EAAE,MAAM,gBAAA,GAAmB,QAAQ,CAAC,EAAA,GAAK,YAAY,EAAC,GAAI,SAAS;;AAEnE;AACA,EAAE,MAAM,oBAAoB,GAA4B;AACxD,IAAI,6BAA6B,EAAE,IAAI;AACvC,IAAI,gCAAgC,EAAE,OAAO,CAAC,QAAQ,CAAC,EAAE;AACzD,IAAI,GAAG,wBAAwB,CAAC;AAChC,MAAM,GAAG,EAAE,UAAU;AACrB,MAAM,MAAM,EAAE,aAAa;AAC3B,MAAM,OAAO,EAAE,cAAc;AAC7B,MAAM,cAAc,EAAE,oBAAoB;AAC1C,KAAK,CAAC;AACN,GAAG;;AAEH,EAAE,IAAI,gBAAgB,EAAE;AACxB,IAAI;AACJ,OAAO,IAAI,CAAC,SAAS,MAAM,CAAC,MAAM,CAAC,oBAAoB,EAAE,wBAAwB,CAAC,EAAE,EAAE,EAAE,KAAA,EAAO,CAAC,CAAC;AACjG,OAAO,KAAK,CAAC,MAAM;AACnB;AACA,MAAM,CAAC,CAAC;AACR,EAAE;;AAEF;AACA,EAAE,MAAM,eAAe,GAAsB,CAAC,YAAY;AAC1D,IAAI,MAAM,QAAQ,GAAa,EAAE;AACjC,IAAI,IAAI,gBAAgB,EAAE;AAC1B,MAAM,QAAQ,CAAC,EAAA,GAAK,MAAM,gBAAgB;AAC1C,IAAI;AACJ,IAAI,IAAI,UAAU,EAAE;AACpB,MAAM,QAAQ,CAAC,GAAA,GAAM,UAAU;AAC/B,IAAI;AACJ,IAAI,IAAI,aAAa,EAAE;AACvB,MAAM,QAAQ,CAAC,MAAA,GAAS,aAAa;AACrC,IAAI;AACJ,IAAI,IAAI,cAAc,EAAE;AACxB,MAAM,QAAQ,CAAC,OAAA,GAAU,cAAc;AACvC,IAAI;AACJ,IAAI,IAAI,oBAAoB,EAAE;AAC9B,MAAM,QAAQ,CAAC,cAAA,GAAiB,oBAAoB;AACpD,IAAI;AACJ,IAAI,OAAO,QAAQ;AACnB,EAAE,CAAC,GAAG;;AAEN,EAAE,eAAe,UAAU,CAAC,KAAK,EAAyB;AAC1D,IAAI,MAAM,cAAA,GAAiB,cAAc,CAAC,MAAM,eAAe,CAAC;;AAEhE;AACA,IAAI,KAAK,CAAC,QAAA,GAAW;AACrB,MAAM,GAAG,KAAK,CAAC,QAAQ;AACvB,MAAM,GAAG,EAAE,EAAE,GAAG,cAAc,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,QAAQ,EAAE,KAAK;AAC5D,MAAM,EAAE,EAAE,EAAE,GAAG,cAAc,CAAC,EAAE,EAAE,GAAG,KAAK,CAAC,QAAQ,EAAE,IAAI;AACzD,MAAM,MAAM,EAAE,EAAE,GAAG,cAAc,CAAC,MAAM,EAAE,GAAG,KAAK,CAAC,QAAQ,EAAE,QAAQ;AACrE,MAAM,OAAO,EAAE,EAAE,GAAG,cAAc,CAAC,OAAO,EAAE,GAAG,KAAK,CAAC,QAAQ,EAAE,SAAS;AACxE,MAAM,cAAc,EAAE,EAAE,GAAG,cAAc,CAAC,cAAc,EAAE,GAAG,KAAK,CAAC,QAAQ,EAAE,gBAAgB;AAC7F,KAAK;;AAEL,IAAI,OAAO,KAAK;AAChB,EAAE;;AAEF,EAAE,OAAO;AACT,IAAI,IAAI,EAAE,gBAAgB;AAC1B,IAAI,YAAY,CAAC,KAAK,EAAE;AACxB,MAAM,OAAO,UAAU,CAAC,KAAK,CAAC;AAC9B,IAAI,CAAC;AACL,IAAI,kBAAkB,CAAC,IAAI,EAAE;AAC7B,MAAMC,8BAAyB,CAAC,IAAI,EAAE,oBAAoB,CAAC;AAC3D,MAAMA,8BAAyB,CAAC,IAAI,EAAE,wBAAwB,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;AAC1F,IAAI,CAAC;AACL,GAAG;AACH,CAAC,CAAA;;AAED;AACA;AACA;MACa,sBAAA,GAAyBC,sBAAiB,CAAC,uBAAuB;;AAE/E;AACA;AACA;AACA,SAAS,cAAc,CAAC,QAAQ,EAAsB;AACtD;;AAEA,EAAE,IAAI,QAAQ,CAAC,GAAG,EAAE,UAAU,EAAE;AAChC,IAAI,QAAQ,CAAC,GAAG,CAAC,UAAA,GAAa,OAAO,CAAC,WAAW,EAAE,CAAC,GAAG;AACvD,EAAE;;AAEF,EAAE,IAAI,QAAQ,CAAC,GAAG,EAAE,WAAA,IAAe,OAAO,CAAC,UAAqC,eAAA,KAAoB,UAAU,EAAE;AAChH,IAAI,MAAM,UAAA,GAAa,CAAC,OAAA,GAAqC,eAAe,IAAI;AAChF,IAAI,IAAI,UAAA,IAAc,IAAI,EAAE;AAC5B,MAAM,QAAQ,CAAC,GAAG,CAAC,WAAA,GAAc,UAAU;AAC3C,IAAI;AACJ,EAAE;;AAEF,EAAE,IAAI,QAAQ,CAAC,MAAM,EAAE,WAAW,EAAE;AACpC,IAAI,QAAQ,CAAC,MAAM,CAAC,WAAA,GAAc,EAAE,CAAC,OAAO,EAAE;AAC9C,EAAE;;AAEF,EAAE,OAAO,QAAQ;AACjB;;AAEO,SAAS,wBAAwB,CAAC,QAAQ,EAAqC;AACtF,EAAE,MAAM,KAAK,GAA4B,EAAE;;AAE3C,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,cAAA,EAAe,GAAI,QAAQ;;AAEtE,EAAE,IAAI,GAAG,EAAE;AACX,IAAI,IAAI,GAAG,CAAC,cAAc,EAAE;AAC5B,MAAM,KAAK,CAAC,gBAAgB,IAAI,GAAG,CAAC,cAAc;AAClD,IAAI;AACJ,EAAE;;AAEF,EAAE,IAAI,MAAM,EAAE;AACd,IAAI,IAAI,MAAM,CAAC,IAAI,EAAE;AACrB,MAAM,KAAK,CAAC,cAAc,CAAA,GAAI,CAAC,MAAM,CAAC,IAAI,CAAC;AAC3C,IAAI;AACJ,IAAI,IAAI,MAAM,CAAC,SAAS,EAAE;AAC1B,MAAM,KAAK,CAAC,kBAAkB,IAAI,MAAM,CAAC,SAAS;AAClD,IAAI;AACJ,IAAI,IAAI,MAAM,CAAC,WAAA,IAAe,IAAI,EAAE;AACpC,MAAM,KAAK,CAAC,oBAAoB,IAAI,MAAM,CAAC,WAAW;AACtD,IAAI;AACJ,IAAI,IAAI,MAAM,CAAC,eAAA,IAAmB,IAAI,EAAE;AACxC,MAAM,KAAK,CAAC,wBAAwB,IAAI,MAAM,CAAC,eAAe;AAC9D,IAAI;AACJ,IAAI,IAAI,MAAM,CAAC,eAAe,EAAE;AAChC,MAAM,KAAK,CAAC,wBAAwB,IAAI,MAAM,CAAC,eAAe;AAC9D,IAAI;AACJ,IAAI,IAAI,MAAM,CAAC,mBAAA,IAAuB,IAAI,EAAE;AAC5C,MAAM,KAAK,CAAC,4BAA4B,IAAI,MAAM,CAAC,mBAAmB;AACtE,IAAI;AACJ,EAAE;;AAEF,EAAE,IAAI,KAAK,EAAE;AACb,IAAI,IAAI,KAAK,CAAC,IAAI,EAAE;AACpB,MAAM,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,IAAI;AACnC,IAAI;AACJ,IAAI,IAAI,KAAK,CAAC,OAAO,EAAE;AACvB,MAAM,KAAK,CAAC,YAAY,IAAI,KAAK,CAAC,OAAO;AACzC,IAAI;AACJ,IAAI,IAAI,KAAK,CAAC,cAAc,EAAE;AAC9B,MAAM,KAAK,CAAC,mBAAmB,IAAI,KAAK,CAAC,cAAc;AACvD,IAAI;AACJ,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE;AACrB,MAAM,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,KAAK;AACrC,IAAI;AACJ,EAAE;;AAEF,EAAE,IAAI,OAAO,EAAE;AACf,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE;AACxB,MAAM,KAAK,CAAC,gBAAgB,IAAI,OAAO,CAAC,MAAM;AAC9C,IAAI;AACJ,IAAI,IAAI,OAAO,CAAC,QAAQ,EAAE;AAC1B,MAAM,KAAK,CAAC,kBAAkB,IAAI,OAAO,CAAC,QAAQ;AAClD,IAAI;AACJ,EAAE;;AAEF;AACA,EAAE,IAAI,cAAc,EAAE;AACtB,IAAI,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAA,IAAK,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE;AAC/D,MAAM,IAAI,KAAA,IAAS,IAAI,EAAE;AACzB,QAAQ,KAAK,CAAC,GAAG,CAAA,GAAI,KAAK;AAC1B,MAAM;AACN,IAAI;AACJ,EAAE;;AAEF,EAAE,OAAO,KAAK;AACd;;AAEO,SAAS,wBAAwB;AACxC,EAAE,UAAU;AACZ,EAAE,aAAa;AACf,EAA2B;AAC3B,EAAE,MAAM,KAAK,GAA4B,EAAE;;AAE3C,EAAE,IAAI,UAAU,EAAE;AAClB,IAAI,KAAK,CAAC,YAAY,CAAA,GAAI,OAAO,CAAC,WAAW,EAAE,CAAC,GAAG;AACnD,IAAI,IAAI,OAAO,CAAC,OAAA,GAAqC,eAAA,KAAoB,UAAU,EAAE;AACrF,MAAM,MAAM,UAAA,GAAa,CAAC,OAAA,GAAqC,eAAe,IAAI;AAClF,MAAM,IAAI,UAAA,IAAc,IAAI,EAAE;AAC9B,QAAQ,KAAK,CAAC,iBAAiB,CAAA,GAAI,UAAU;AAC7C,MAAM;AACN,IAAI;AACJ,EAAE;;AAEF;AACA,EAAE,IAAI,aAAa,EAAE,WAAA,IAAe,IAAI,EAAE;AAC1C,IAAI,KAAK,CAAC,oBAAoB,CAAA,GAAI,EAAE,CAAC,OAAO,EAAE;AAC9C,EAAE;;AAEF,EAAE,OAAO,KAAK;AACd;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,eAAe,YAAY,GAAuB;AAClD,EAAE,MAAM,UAAA,GAAa,EAAE,CAAC,QAAQ,EAAE;AAClC,EAAE,QAAQ,UAAU;AACpB,IAAI,KAAK,QAAQ;AACjB,MAAM,OAAO,aAAa,EAAE;AAC5B,IAAI,KAAK,OAAO;AAChB,MAAM,OAAO,YAAY,EAAE;AAC3B,IAAI;AACJ,MAAM,OAAO;AACb,QAAQ,IAAI,EAAE,cAAc,CAAC,UAAU,CAAA,IAAK,UAAU;AACtD,QAAQ,OAAO,EAAE,EAAE,CAAC,OAAO,EAAE;AAC7B,OAAO;AACP;AACA;;AAEA,SAAS,iBAAiB,GAA+B;AACzD,EAAE,IAAI;AACN,IAAI,IAAI,OAAO,OAAO,CAAC,QAAQ,CAAC,GAAA,KAAQ,QAAQ,EAAE;AAClD;AACA,MAAM;AACN,IAAI;;AAEJ;AACA;AACA;AACA;AACA,IAAI,MAAM,OAAA,GAAU,IAAI,IAAI,CAAC,GAAG,CAAC;AACjC,IAAI,MAAM,OAAA,GAAU,IAAI,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,MAAA,EAAQ,CAAC;AACpE,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,OAAO,CAAA,KAAM,OAAO,EAAE;AAC7C,MAAM,MAAM,OAAA,GAAU,IAAI,CAAC,cAAc,EAAE,CAAC,eAAe,EAAE;;AAE7D,MAAM,OAAO;AACb,QAAQ,MAAM,EAAE,OAAO,CAAC,MAAM;AAC9B,QAAQ,QAAQ,EAAE,OAAO,CAAC,QAAQ;AAClC,OAAO;AACP,IAAI;AACJ,EAAE,EAAE,MAAM;AACV;AACA,EAAE;;AAEF,EAAE;AACF;;AAEA;AACA;AACA;AACO,SAAS,aAAa,GAAe;AAC5C,EAAE,MAAM,aAAa,OAAO,CAAC,WAAW,EAAE,CAAC,GAAG;AAC9C;AACA,EAAE,MAAM,iBAAiB,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAC,GAAI,OAAO,CAAC,MAAM,EAAC,GAAI,IAAI,CAAC,CAAC,WAAW,EAAE;AACrF;AACA,EAAE,MAAM,UAAU,GAAe,EAAE,cAAc,EAAE,YAAY;;AAE/D,EAAE,IAAI,OAAO,CAAC,OAAA,GAAqC,eAAA,KAAoB,UAAU,EAAE;AACnF,IAAI,MAAM,UAAA,GAAa,CAAC,OAAA,GAAqC,eAAe,IAAI;AAChF,IAAI,IAAI,UAAA,IAAc,IAAI,EAAE;AAC5B,MAAM,UAAU,CAAC,WAAA,GAAc,UAAU;AACzC,IAAI;AACJ,EAAE;;AAEF,EAAE,OAAO,UAAU;AACnB;;AAEA;AACA;AACA;AACO,SAAS,gBAAgB,CAAC,SAAS,EAA8C;AACxF,EAAE,MAAM,MAAM,GAAkB,EAAE;;AAElC;AACA,EAAE,IAAI,MAAM;AACZ,EAAE,IAAI;AACN,IAAI,SAAS,EAAE,CAAC,MAAM,EAAE;AACxB,EAAE,EAAE,MAAM;AACV;AACA,EAAE;;AAEF;AACA;AACA;AACA,EAAE,IAAI,OAAO,MAAA,KAAW,QAAQ,EAAE;AAClC;AACA,IAAI,MAAM,CAAC,SAAA,GAAY,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAC,GAAI,SAAS,IAAI,CAAC,CAAC,WAAW,EAAE;AACzE,EAAE;;AAEF,EAAE,MAAM,CAAC,IAAA,GAAO,EAAE,CAAC,IAAI,EAAE;;AAEzB,EAAE,IAAI,SAAA,KAAc,QAAQ,SAAS,CAAC,MAAM,EAAE;AAC9C,IAAI,MAAM,CAAC,WAAA,GAAc,EAAE,CAAC,QAAQ,EAAE;AACtC,IAAI,MAAM,CAAC,WAAA,GAAc,EAAE,CAAC,OAAO,EAAE;AACrC,EAAE;;AAEF,EAAE,IAAI,SAAA,KAAc,QAAQ,SAAS,CAAC,GAAG,EAAE;AAC3C,IAAI,MAAM,OAAA,GAAU,EAAE,CAAC,IAAI,EAAC;AAC5B,IAAI,MAAM,QAAA,GAAW,OAAO,GAAG,CAAC,CAAC;AACjC,IAAI,IAAI,QAAQ,EAAE;AAClB,MAAM,MAAM,CAAC,eAAA,GAAkB,OAAO,CAAC,MAAM;AAC7C,MAAM,MAAM,CAAC,eAAA,GAAkB,QAAQ,CAAC,KAAK;AAC7C,MAAM,MAAM,CAAC,mBAAA,GAAsB,QAAQ,CAAC,KAAK;AACjD,IAAI;AACJ,EAAE;;AAEF,EAAE,OAAO,MAAM;AACf;;AAEA;AACA,MAAM,cAAc,GAAmC;AACvD,EAAE,GAAG,EAAE,SAAS;AAChB,EAAE,OAAO,EAAE,SAAS;AACpB,EAAE,OAAO,EAAE,SAAS;AACpB,EAAE,KAAK,EAAE,OAAO;AAChB,EAAE,KAAK,EAAE,SAAS;AAClB,EAAE,IAAI,EAAE,aAAa;AACrB,EAAE,OAAO,EAAE,SAAS;AACpB,CAAC;;AAED;;AAQA;AACA,MAAM,aAAa,GAAiB;AACpC,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,CAAC,QAAQ,CAAA,EAAG;AACjD,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,CAAC,eAAe,EAAE,QAAQ,CAAA,EAAG;AAClE,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,CAAC,eAAe,CAAA,EAAG;AACxD,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC,YAAY,CAAA,EAAG;AACnD,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC,cAAc,EAAE,YAAY,CAAA,EAAG;AAClE,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,CAAC,QAAQ,CAAA,EAAG;AACjD,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,CAAC,QAAQ,CAAA,EAAG;AACjD,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC,YAAY,CAAA,EAAG;AACnD,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,CAAC,cAAc,CAAA,EAAG;AACvD,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,CAAC,YAAY,CAAA,EAAG;AACrD,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,CAAC,cAAc,CAAA,EAAG;AACvD,CAAC;;AAED;AACA,MAAM;;AAEN,GAAI;AACJ,EAAE,MAAM,EAAE,OAAA,IAAW,OAAO;AAC5B,EAAE,IAAI,EAAE,OAAA,IAAW,UAAU,CAAC,sBAAsB,EAAE,OAAO,CAAC;AAC9D,EAAE,MAAM,EAAE,OAAA,IAAW,UAAU,CAAC,iBAAiB,EAAE,OAAO,CAAC;AAC3D,EAAE,MAAM,EAAE,OAAA,IAAW,OAAO;AAC5B,EAAE,MAAM,EAAE,OAAA,IAAW,UAAU,CAAC,cAAc,EAAE,OAAO,CAAC;AACxD,EAAE,IAAI,EAAE,OAAA,IAAW,UAAU,CAAC,sBAAsB,EAAE,OAAO,CAAC;AAC9D,EAAE,GAAG,EAAE,OAAA,IAAW,UAAU,CAAC,iBAAiB,EAAE,OAAO,CAAC;AACxD,EAAE,IAAI,EAAE,OAAA,IAAW,UAAU,CAAC,kBAAkB,EAAE,OAAO,CAAC;AAC1D,EAAE,MAAM,EAAE,OAAA,IAAW,UAAU,CAAC,sBAAsB,EAAE,OAAO,CAAC;AAChE,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,UAAU,CAAC,KAAK,EAAU,IAAI,EAA8B;AACrE,EAAE,MAAM,QAAQ,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;AAChC,EAAE,OAAO,QAAQ,KAAK,CAAC,CAAC,CAAA,GAAI,SAAS;AACrC;;AAEA;AACA,eAAe,aAAa,GAAuB;AACnD;AACA;AACA;AACA,EAAE,MAAM,UAAU,GAAc;AAChC,IAAI,cAAc,EAAE,EAAE,CAAC,OAAO,EAAE;AAChC,IAAI,IAAI,EAAE,UAAU;AACpB,IAAI,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA,GAAI,CAAC,CAAC,CAAA;AACA,GAAA;;AAEA,EAAA,IAAA;AACA;AACA;AACA;;AAEA,IAAA,MAAA,MAAA,GAAA,MAAA,IAAA,OAAA,CAAA,CAAA,OAAA,EAAA,MAAA,KAAA;AACA,MAAAC,2BAAA,CAAA,kBAAA,EAAA,CAAA,KAAA,EAAA,MAAA,KAAA;AACA,QAAA,IAAA,KAAA,EAAA;AACA,UAAA,MAAA,CAAA,KAAA,CAAA;AACA,UAAA;AACA,QAAA;AACA,QAAA,OAAA,CAAA,MAAA,CAAA;AACA,MAAA,CAAA,CAAA;AACA,IAAA,CAAA,CAAA;;AAEA,IAAA,UAAA,CAAA,IAAA,GAAA,UAAA,CAAA,wBAAA,EAAA,MAAA,CAAA;AACA,IAAA,UAAA,CAAA,OAAA,GAAA,UAAA,CAAA,2BAAA,EAAA,MAAA,CAAA;AACA,IAAA,UAAA,CAAA,KAAA,GAAA,UAAA,CAAA,yBAAA,EAAA,MAAA,CAAA;AACA,EAAA,CAAA,CAAA,MAAA;AACA;AACA,EAAA;;AAEA,EAAA,OAAA,UAAA;AACA;;AAEA;AACA,SAAA,gBAAA,CAAA,IAAA,EAAA;AACA,EAAA,OAAA,CAAA,IAAA,CAAA,KAAA,CAAA,GAAA,CAAA,GAAA,CAAA,CAAA,CAAA,WAAA,EAAA;AACA;;AAEA;AACA,eAAA,YAAA,GAAA;AACA;AACA;AACA;AACA,EAAA,MAAA,SAAA,GAAA;AACA,IAAA,cAAA,EAAA,EAAA,CAAA,OAAA,EAAA;AACA,IAAA,IAAA,EAAA,OAAA;AACA,GAAA;;AAEA,EAAA,IAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAA,MAAA,QAAA,GAAA,MAAA,YAAA,CAAA,MAAA,CAAA;AACA,IAAA,MAAA,UAAA,GAAA,aAAA,CAAA,IAAA,CAAA,IAAA,IAAA,QAAA,CAAA,QAAA,CAAA,IAAA,CAAA,IAAA,CAAA,CAAA;AACA,IAAA,IAAA,CAAA,UAAA,EAAA;AACA,MAAA,OAAA,SAAA;AACA,IAAA;;AAEA;AACA;AACA;AACA;AACA,IAAA,MAAA,UAAA,GAAAC,cAAA,CAAA,MAAA,EAAA,UAAA,CAAA,IAAA,CAAA;AACA,IAAA,MAAA,QAAA,GAAA,CAAA,MAAA,aAAA,CAAA,UAAA,EAAA,EAAA,QAAA,EAAA,OAAA,EAAA,CAAA,EAAA,WAAA,EAAA;;AAEA;AACA;AACA;AACA;AACA;AACA,IAAA,MAAA,EAAA,OAAA,EAAA,GAAA,UAAA;AACA,IAAA,SAAA,CAAA,IAAA,GAAA,OAAA,CAAA,IAAA,CAAA,CAAA,IAAA,QAAA,CAAA,OAAA,CAAA,gBAAA,CAAA,CAAA,CAAA,CAAA,IAAA,CAAA,CAAA,IAAA,OAAA,CAAA,CAAA,CAAA;;AAEA;AACA;AACA;AACA,IAAA,MAAA,EAAA,GAAA,gBAAA,CAAA,SAAA,CAAA,IAAA,CAAA;AACA,IAAA,SAAA,CAAA,OAAA,GAAA,cAAA,CAAA,EAAA,CAAA,GAAA,QAAA,CAAA;AACA,EAAA,CAAA,CAAA,MAAA;AACA;AACA,EAAA;;AAEA,EAAA,OAAA,SAAA;AACA;;AAEA;AACA;AACA;AACA,SAAA,uBAAA,GAAA;AACA,EAAA,IAAA,OAAA,CAAA,GAAA,CAAA,MAAA,EAAA;AACA;AACA,IAAA,OAAA;AACA,MAAA,gBAAA,EAAA,QAAA;AACA,MAAA,cAAA,EAAA,OAAA,CAAA,GAAA,CAAA,aAAA;AACA,KAAA;AACA,EAAA,CAAA,MAAA,IAAA,OAAA,CAAA,GAAA,CAAA,UAAA,EAAA;AACA;AACA,IAAA,OAAA;AACA,MAAA,gBAAA,EAAA,KAAA;AACA,MAAA,cAAA,EAAA,OAAA,CAAA,GAAA,CAAA,UAAA;AACA,MAAA,gBAAA,EAAA,OAAA,CAAA,GAAA,CAAA,iBAAA;AACA,KAAA;AACA,EAAA,CAAA,MAAA,IAAA,OAAA,CAAA,GAAA,CAAA,WAAA,EAAA;AACA;AACA,IAAA,OAAA;AACA,MAAA,gBAAA,EAAA,KAAA;AACA,KAAA;AACA,EAAA,CAAA,MAAA,IAAA,OAAA,CAAA,GAAA,CAAA,gBAAA,EAAA;AACA;AACA,IAAA,OAAA;AACA,MAAA,gBAAA,EAAA,eAAA;AACA,MAAA,cAAA,EAAA,OAAA,CAAA,GAAA,CAAA,gBAAA;AACA,KAAA;AACA,EAAA,CAAA,MAAA,IAAA,OAAA,CAAA,GAAA,CAAA,iBAAA,IAAA,OAAA,CAAA,GAAA,CAAA,WAAA,EAAA;AACA;AACA,IAAA,OAAA;AACA,MAAA,gBAAA,EAAA,OAAA;AACA,MAAA,cAAA,EAAA,OAAA,CAAA,GAAA,CAAA,WAAA;AACA,KAAA;AACA,EAAA,CAAA,MAAA,IAAA,OAAA,CAAA,GAAA,CAAA,gBAAA,EAAA;AACA;AACA,IAAA,OAAA;AACA,MAAA,gBAAA,EAAA,WAAA;AACA,MAAA,cAAA,EAAA,OAAA,CAAA,GAAA,CAAA,gBAAA;AACA,KAAA;AACA,EAAA,CAAA,MAAA,IAAA,OAAA,CAAA,GAAA,CAAA,mBAAA,EAAA;AACA;AACA,IAAA,OAAA;AACA,MAAA,gBAAA,EAAA,eAAA;AACA,MAAA,cAAA,EAAA,OAAA,CAAA,GAAA,CAAA,mBAAA;AACA,MAAA,kBAAA,EAAA,OAAA,CAAA,GAAA,CAAA,kBAAA;AACA,MAAA,yBAAA,EAAA,OAAA,CAAA,GAAA,CAAA,iBAAA;AACA,KAAA;AACA,EAAA,CAAA,MAAA,IAAA,OAAA,CAAA,GAAA,CAAA,OAAA,EAAA;AACA;AACA,IAAA,OAAA;AACA,MAAA,gBAAA,EAAA,SAAA;AACA,KAAA;AACA,EAAA,CAAA,MAAA,IAAA,OAAA,CAAA,GAAA,CAAA,UAAA,EAAA;AACA;AACA,IAAA,OAAA;AACA,MAAA,gBAAA,EAAA,QAAA;AACA,MAAA,cAAA,EAAA,OAAA,CAAA,GAAA,CAAA,UAAA;AACA,KAAA;AACA,EAAA,CAAA,MAAA,IAAA,OAAA,CAAA,GAAA,CAAA,IAAA,EAAA;AACA;AACA,IAAA,OAAA;AACA,MAAA,gBAAA,EAAA,QAAA;AACA,KAAA;AACA,EAAA,CAAA,MAAA;AACA,IAAA,OAAA,SAAA;AACA,EAAA;AACA;;;;;;;;;;"} | ||
| {"version":3,"file":"context.js","sources":["../../../src/integrations/context.ts"],"sourcesContent":["/* eslint-disable max-lines */\n\nimport { execFile } from 'node:child_process';\nimport { readdir, readFile } from 'node:fs';\nimport * as os from 'node:os';\nimport { join } from 'node:path';\nimport { promisify } from 'node:util';\nimport type {\n AppContext,\n CloudResourceContext,\n Contexts,\n CultureContext,\n DeviceContext,\n Event,\n IntegrationFn,\n OsContext,\n} from '@sentry/core';\nimport { defineIntegration, safeSetSpanJSONAttributes } from '@sentry/core';\n\nexport const readFileAsync = promisify(readFile);\nexport const readDirAsync = promisify(readdir);\n\n// Process enhanced with methods from Node 18, 20, 22 as @types/node\n// is on `14.18.0` to match minimum version requirements of the SDK\ninterface ProcessWithCurrentValues extends NodeJS.Process {\n availableMemory?(): number;\n}\n\nconst INTEGRATION_NAME = 'Context';\n\ninterface DeviceContextOptions {\n cpu?: boolean;\n memory?: boolean;\n}\n\ninterface ContextOptions {\n app?: boolean;\n os?: boolean;\n device?: DeviceContextOptions | boolean;\n culture?: boolean;\n cloudResource?: boolean;\n}\n\nconst _nodeContextIntegration = ((options: ContextOptions = {}) => {\n const _options = {\n app: true,\n os: true,\n device: true,\n culture: true,\n cloudResource: true,\n ...options,\n };\n\n // Compute contexts eagerly (shared between tx and span paths)\n const appContext = _options.app ? getAppContext() : undefined;\n const deviceContext = _options.device ? getDeviceContext(_options.device) : undefined;\n const cultureContext = _options.culture ? getCultureContext() : undefined;\n const cloudResourceContext = _options.cloudResource ? getCloudResourceContext() : undefined;\n const osContextPromise = _options.os ? getOsContext() : undefined;\n\n // Map static context data to span attributes\n const cachedSpanAttributes: Record<string, unknown> = {\n 'process.runtime.engine.name': 'v8',\n 'process.runtime.engine.version': process.versions.v8,\n ...contextsToSpanAttributes({\n app: appContext,\n device: deviceContext,\n culture: cultureContext,\n cloud_resource: cloudResourceContext,\n }),\n };\n\n if (osContextPromise) {\n osContextPromise\n .then(osCtx => Object.assign(cachedSpanAttributes, contextsToSpanAttributes({ os: osCtx })))\n .catch(() => {\n // Ignore - os attributes will be undefined\n });\n }\n\n // Build contexts for event processing (reuses same data, awaits async OS context)\n const contextsPromise: Promise<Contexts> = (async () => {\n const contexts: Contexts = {};\n if (osContextPromise) {\n contexts.os = await osContextPromise;\n }\n if (appContext) {\n contexts.app = appContext;\n }\n if (deviceContext) {\n contexts.device = deviceContext;\n }\n if (cultureContext) {\n contexts.culture = cultureContext;\n }\n if (cloudResourceContext) {\n contexts.cloud_resource = cloudResourceContext;\n }\n return contexts;\n })();\n\n async function addContext(event: Event): Promise<Event> {\n const updatedContext = _updateContext(await contextsPromise);\n\n // TODO(v11): conditional with `sendDefaultPii` here?\n event.contexts = {\n ...event.contexts,\n app: { ...updatedContext.app, ...event.contexts?.app },\n os: { ...updatedContext.os, ...event.contexts?.os },\n device: { ...updatedContext.device, ...event.contexts?.device },\n culture: { ...updatedContext.culture, ...event.contexts?.culture },\n cloud_resource: { ...updatedContext.cloud_resource, ...event.contexts?.cloud_resource },\n };\n\n return event;\n }\n\n return {\n name: INTEGRATION_NAME,\n processEvent(event) {\n return addContext(event);\n },\n processSegmentSpan(span) {\n safeSetSpanJSONAttributes(span, cachedSpanAttributes);\n safeSetSpanJSONAttributes(span, getDynamicSpanAttributes(appContext, deviceContext));\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Capture context about the environment and the device that the client is running on, to events.\n */\nexport const nodeContextIntegration = defineIntegration(_nodeContextIntegration);\n\n/**\n * Updates the context with dynamic values that can change\n */\nfunction _updateContext(contexts: Contexts): Contexts {\n // Only update properties if they exist\n\n if (contexts.app?.app_memory) {\n contexts.app.app_memory = process.memoryUsage().rss;\n }\n\n if (contexts.app?.free_memory && typeof (process as ProcessWithCurrentValues).availableMemory === 'function') {\n const freeMemory = (process as ProcessWithCurrentValues).availableMemory?.();\n if (freeMemory != null) {\n contexts.app.free_memory = freeMemory;\n }\n }\n\n if (contexts.device?.free_memory) {\n contexts.device.free_memory = os.freemem();\n }\n\n return contexts;\n}\n\nexport function contextsToSpanAttributes(contexts: Contexts): Record<string, unknown> {\n const attrs: Record<string, unknown> = {};\n\n const { app, device, os: osCtx, culture, cloud_resource } = contexts;\n\n if (app) {\n if (app.app_start_time) {\n attrs['app.start_time'] = app.app_start_time;\n }\n }\n\n if (device) {\n if (device.arch) {\n attrs['device.archs'] = [device.arch];\n }\n if (device.boot_time) {\n attrs['device.boot_time'] = device.boot_time;\n }\n if (device.memory_size != null) {\n attrs['device.memory_size'] = device.memory_size;\n }\n if (device.processor_count != null) {\n attrs['device.processor_count'] = device.processor_count;\n }\n if (device.cpu_description) {\n attrs['device.cpu_description'] = device.cpu_description;\n }\n if (device.processor_frequency != null) {\n attrs['device.processor_frequency'] = device.processor_frequency;\n }\n }\n\n if (osCtx) {\n if (osCtx.name) {\n attrs['os.name'] = osCtx.name;\n }\n if (osCtx.version) {\n attrs['os.version'] = osCtx.version;\n }\n if (osCtx.kernel_version) {\n attrs['os.kernel_version'] = osCtx.kernel_version;\n }\n if (osCtx.build) {\n attrs['os.build'] = osCtx.build;\n }\n }\n\n if (culture) {\n if (culture.locale) {\n attrs['culture.locale'] = culture.locale;\n }\n if (culture.timezone) {\n attrs['culture.timezone'] = culture.timezone;\n }\n }\n\n // CloudResourceContext already uses dot-notation keys matching span attribute conventions\n if (cloud_resource) {\n for (const [key, value] of Object.entries(cloud_resource)) {\n if (value != null) {\n attrs[key] = value;\n }\n }\n }\n\n return attrs;\n}\n\nexport function getDynamicSpanAttributes(\n appContext: AppContext | undefined,\n deviceContext: DeviceContext | undefined,\n): Record<string, unknown> {\n const attrs: Record<string, unknown> = {};\n\n if (appContext) {\n attrs['app.memory'] = process.memoryUsage().rss;\n if (typeof (process as ProcessWithCurrentValues).availableMemory === 'function') {\n const freeMemory = (process as ProcessWithCurrentValues).availableMemory?.();\n if (freeMemory != null) {\n attrs['app.free_memory'] = freeMemory;\n }\n }\n }\n\n // Only include if memory tracking was initially enabled (indicated by free_memory being set)\n if (deviceContext?.free_memory != null) {\n attrs['device.free_memory'] = os.freemem();\n }\n\n return attrs;\n}\n\n/**\n * Returns the operating system context.\n *\n * Based on the current platform, this uses a different strategy to provide the\n * most accurate OS information. Since this might involve spawning subprocesses\n * or accessing the file system, this should only be executed lazily and cached.\n *\n * - On macOS (Darwin), this will execute the `sw_vers` utility. The context\n * has a `name`, `version`, `build` and `kernel_version` set.\n * - On Linux, this will try to load a distribution release from `/etc` and set\n * the `name`, `version` and `kernel_version` fields.\n * - On all other platforms, only a `name` and `version` will be returned. Note\n * that `version` might actually be the kernel version.\n */\nasync function getOsContext(): Promise<OsContext> {\n const platformId = os.platform();\n switch (platformId) {\n case 'darwin':\n return getDarwinInfo();\n case 'linux':\n return getLinuxInfo();\n default:\n return {\n name: PLATFORM_NAMES[platformId] || platformId,\n version: os.release(),\n };\n }\n}\n\nfunction getCultureContext(): CultureContext | undefined {\n try {\n if (typeof process.versions.icu !== 'string') {\n // Node was built without ICU support\n return;\n }\n\n // Check that node was built with full Intl support. Its possible it was built without support for non-English\n // locales which will make resolvedOptions inaccurate\n //\n // https://nodejs.org/api/intl.html#detecting-internationalization-support\n const january = new Date(9e8);\n const spanish = new Intl.DateTimeFormat('es', { month: 'long' });\n if (spanish.format(january) === 'enero') {\n const options = Intl.DateTimeFormat().resolvedOptions();\n\n return {\n locale: options.locale,\n timezone: options.timeZone,\n };\n }\n } catch {\n //\n }\n\n return;\n}\n\n/**\n * Get app context information from process\n */\nexport function getAppContext(): AppContext {\n const app_memory = process.memoryUsage().rss;\n // oxlint-disable-next-line sdk/no-unsafe-random-apis\n const app_start_time = new Date(Date.now() - process.uptime() * 1000).toISOString();\n // https://nodejs.org/api/process.html#processavailablememory\n const appContext: AppContext = { app_start_time, app_memory };\n\n if (typeof (process as ProcessWithCurrentValues).availableMemory === 'function') {\n const freeMemory = (process as ProcessWithCurrentValues).availableMemory?.();\n if (freeMemory != null) {\n appContext.free_memory = freeMemory;\n }\n }\n\n return appContext;\n}\n\n/**\n * Gets device information from os\n */\nexport function getDeviceContext(deviceOpt: DeviceContextOptions | true): DeviceContext {\n const device: DeviceContext = {};\n\n // Sometimes os.uptime() throws due to lacking permissions: https://github.com/getsentry/sentry-javascript/issues/8202\n let uptime;\n try {\n uptime = os.uptime();\n } catch {\n // noop\n }\n\n // os.uptime or its return value seem to be undefined in certain environments (e.g. Azure functions).\n // Hence, we only set boot time, if we get a valid uptime value.\n // @see https://github.com/getsentry/sentry-javascript/issues/5856\n if (typeof uptime === 'number') {\n // oxlint-disable-next-line sdk/no-unsafe-random-apis\n device.boot_time = new Date(Date.now() - uptime * 1000).toISOString();\n }\n\n device.arch = os.arch();\n\n if (deviceOpt === true || deviceOpt.memory) {\n device.memory_size = os.totalmem();\n device.free_memory = os.freemem();\n }\n\n if (deviceOpt === true || deviceOpt.cpu) {\n const cpuInfo = os.cpus() as os.CpuInfo[] | undefined;\n const firstCpu = cpuInfo?.[0];\n if (firstCpu) {\n device.processor_count = cpuInfo.length;\n device.cpu_description = firstCpu.model;\n device.processor_frequency = firstCpu.speed;\n }\n }\n\n return device;\n}\n\n/** Mapping of Node's platform names to actual OS names. */\nconst PLATFORM_NAMES: { [platform: string]: string } = {\n aix: 'IBM AIX',\n freebsd: 'FreeBSD',\n openbsd: 'OpenBSD',\n sunos: 'SunOS',\n win32: 'Windows',\n ohos: 'OpenHarmony',\n android: 'Android',\n};\n\n/** Linux version file to check for a distribution. */\ninterface DistroFile {\n /** The file name, located in `/etc`. */\n name: string;\n /** Potential distributions to check. */\n distros: [string, ...string[]];\n}\n\n/** Mapping of linux release files located in /etc to distributions. */\nconst LINUX_DISTROS: DistroFile[] = [\n { name: 'fedora-release', distros: ['Fedora'] },\n { name: 'redhat-release', distros: ['Red Hat Linux', 'Centos'] },\n { name: 'redhat_version', distros: ['Red Hat Linux'] },\n { name: 'SuSE-release', distros: ['SUSE Linux'] },\n { name: 'lsb-release', distros: ['Ubuntu Linux', 'Arch Linux'] },\n { name: 'debian_version', distros: ['Debian'] },\n { name: 'debian_release', distros: ['Debian'] },\n { name: 'arch-release', distros: ['Arch Linux'] },\n { name: 'gentoo-release', distros: ['Gentoo Linux'] },\n { name: 'novell-release', distros: ['SUSE Linux'] },\n { name: 'alpine-release', distros: ['Alpine Linux'] },\n];\n\n/** Functions to extract the OS version from Linux release files. */\nconst LINUX_VERSIONS: {\n [identifier: string]: (content: string) => string | undefined;\n} = {\n alpine: content => content,\n arch: content => matchFirst(/distrib_release=(.*)/, content),\n centos: content => matchFirst(/release ([^ ]+)/, content),\n debian: content => content,\n fedora: content => matchFirst(/release (..)/, content),\n mint: content => matchFirst(/distrib_release=(.*)/, content),\n red: content => matchFirst(/release ([^ ]+)/, content),\n suse: content => matchFirst(/VERSION = (.*)\\n/, content),\n ubuntu: content => matchFirst(/distrib_release=(.*)/, content),\n};\n\n/**\n * Executes a regular expression with one capture group.\n *\n * @param regex A regular expression to execute.\n * @param text Content to execute the RegEx on.\n * @returns The captured string if matched; otherwise undefined.\n */\nfunction matchFirst(regex: RegExp, text: string): string | undefined {\n const match = regex.exec(text);\n return match ? match[1] : undefined;\n}\n\n/** Loads the macOS operating system context. */\nasync function getDarwinInfo(): Promise<OsContext> {\n // Default values that will be used in case no operating system information\n // can be loaded. The default version is computed via heuristics from the\n // kernel version, but the build ID is missing.\n const darwinInfo: OsContext = {\n kernel_version: os.release(),\n name: 'Mac OS X',\n version: `10.${Number(os.release().split('.')[0]) - 4}`,\n };\n\n try {\n // We try to load the actual macOS version by executing the `sw_vers` tool.\n // This tool should be available on every standard macOS installation. In\n // case this fails, we stick with the values computed above.\n\n const output = await new Promise<string>((resolve, reject) => {\n execFile('/usr/bin/sw_vers', (error: Error | null, stdout: string) => {\n if (error) {\n reject(error);\n return;\n }\n resolve(stdout);\n });\n });\n\n darwinInfo.name = matchFirst(/^ProductName:\\s+(.*)$/m, output);\n darwinInfo.version = matchFirst(/^ProductVersion:\\s+(.*)$/m, output);\n darwinInfo.build = matchFirst(/^BuildVersion:\\s+(.*)$/m, output);\n } catch {\n // ignore\n }\n\n return darwinInfo;\n}\n\n/** Returns a distribution identifier to look up version callbacks. */\nfunction getLinuxDistroId(name: string): string {\n return (name.split(' ') as [string])[0].toLowerCase();\n}\n\n/** Loads the Linux operating system context. */\nasync function getLinuxInfo(): Promise<OsContext> {\n // By default, we cannot assume anything about the distribution or Linux\n // version. `os.release()` returns the kernel version and we assume a generic\n // \"Linux\" name, which will be replaced down below.\n const linuxInfo: OsContext = {\n kernel_version: os.release(),\n name: 'Linux',\n };\n\n try {\n // We start guessing the distribution by listing files in the /etc\n // directory. This is were most Linux distributions (except Knoppix) store\n // release files with certain distribution-dependent meta data. We search\n // for exactly one known file defined in `LINUX_DISTROS` and exit if none\n // are found. In case there are more than one file, we just stick with the\n // first one.\n const etcFiles = await readDirAsync('/etc');\n const distroFile = LINUX_DISTROS.find(file => etcFiles.includes(file.name));\n if (!distroFile) {\n return linuxInfo;\n }\n\n // Once that file is known, load its contents. To make searching in those\n // files easier, we lowercase the file contents. Since these files are\n // usually quite small, this should not allocate too much memory and we only\n // hold on to it for a very short amount of time.\n const distroPath = join('/etc', distroFile.name);\n const contents = (await readFileAsync(distroPath, { encoding: 'utf-8' })).toLowerCase();\n\n // Some Linux distributions store their release information in the same file\n // (e.g. RHEL and Centos). In those cases, we scan the file for an\n // identifier, that basically consists of the first word of the linux\n // distribution name (e.g. \"red\" for Red Hat). In case there is no match, we\n // just assume the first distribution in our list.\n const { distros } = distroFile;\n linuxInfo.name = distros.find(d => contents.indexOf(getLinuxDistroId(d)) >= 0) || distros[0];\n\n // Based on the found distribution, we can now compute the actual version\n // number. This is different for every distribution, so several strategies\n // are computed in `LINUX_VERSIONS`.\n const id = getLinuxDistroId(linuxInfo.name);\n linuxInfo.version = LINUX_VERSIONS[id]?.(contents);\n } catch {\n // ignore\n }\n\n return linuxInfo;\n}\n\n/**\n * Grabs some information about hosting provider based on best effort.\n */\nfunction getCloudResourceContext(): CloudResourceContext | undefined {\n if (process.env.VERCEL) {\n // https://vercel.com/docs/concepts/projects/environment-variables/system-environment-variables#system-environment-variables\n return {\n 'cloud.provider': 'vercel',\n 'cloud.region': process.env.VERCEL_REGION,\n };\n } else if (process.env.AWS_REGION) {\n // https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html\n return {\n 'cloud.provider': 'aws',\n 'cloud.region': process.env.AWS_REGION,\n 'cloud.platform': process.env.AWS_EXECUTION_ENV,\n };\n } else if (process.env.GCP_PROJECT) {\n // https://cloud.google.com/composer/docs/how-to/managing/environment-variables#reserved_variables\n return {\n 'cloud.provider': 'gcp',\n };\n } else if (process.env.ALIYUN_REGION_ID) {\n // TODO: find where I found these environment variables - at least gc.github.com returns something\n return {\n 'cloud.provider': 'alibaba_cloud',\n 'cloud.region': process.env.ALIYUN_REGION_ID,\n };\n } else if (process.env.WEBSITE_SITE_NAME && process.env.REGION_NAME) {\n // https://learn.microsoft.com/en-us/azure/app-service/reference-app-settings?tabs=kudu%2Cdotnet#app-environment\n return {\n 'cloud.provider': 'azure',\n 'cloud.region': process.env.REGION_NAME,\n };\n } else if (process.env.IBM_CLOUD_REGION) {\n // TODO: find where I found these environment variables - at least gc.github.com returns something\n return {\n 'cloud.provider': 'ibm_cloud',\n 'cloud.region': process.env.IBM_CLOUD_REGION,\n };\n } else if (process.env.TENCENTCLOUD_REGION) {\n // https://www.tencentcloud.com/document/product/583/32748\n return {\n 'cloud.provider': 'tencent_cloud',\n 'cloud.region': process.env.TENCENTCLOUD_REGION,\n 'cloud.account.id': process.env.TENCENTCLOUD_APPID,\n 'cloud.availability_zone': process.env.TENCENTCLOUD_ZONE,\n };\n } else if (process.env.NETLIFY) {\n // https://docs.netlify.com/configure-builds/environment-variables/#read-only-variables\n return {\n 'cloud.provider': 'netlify',\n };\n } else if (process.env.FLY_REGION) {\n // https://fly.io/docs/reference/runtime-environment/\n return {\n 'cloud.provider': 'fly.io',\n 'cloud.region': process.env.FLY_REGION,\n };\n } else if (process.env.DYNO) {\n // https://devcenter.heroku.com/articles/dynos#local-environment-variables\n return {\n 'cloud.provider': 'heroku',\n };\n } else {\n return undefined;\n }\n}\n"],"names":["promisify","readFile","readdir","safeSetSpanJSONAttributes","defineIntegration","execFile","join"],"mappings":"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"} |
@@ -11,68 +11,36 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const DEFAULT_LINES_OF_CONTEXT = 7; | ||
| const INTEGRATION_NAME = 'ContextLines'; | ||
| // Determines the upper bound of lineno/colno that we will attempt to read. Large colno values are likely to be | ||
| // minified code while large lineno values are likely to be bundled code. | ||
| // Exported for testing purposes. | ||
| const MAX_CONTEXTLINES_COLNO = 1000; | ||
| const MAX_CONTEXTLINES_LINENO = 10000; | ||
| /** | ||
| * Get or init map value | ||
| */ | ||
| const INTEGRATION_NAME = "ContextLines"; | ||
| const MAX_CONTEXTLINES_COLNO = 1e3; | ||
| const MAX_CONTEXTLINES_LINENO = 1e4; | ||
| function emplace(map, key, contents) { | ||
| const value = map.get(key); | ||
| if (value === undefined) { | ||
| if (value === void 0) { | ||
| map.set(key, contents); | ||
| return contents; | ||
| } | ||
| return value; | ||
| } | ||
| /** | ||
| * Determines if context lines should be skipped for a file. | ||
| * - .min.(mjs|cjs|js) files are and not useful since they dont point to the original source | ||
| * - node: prefixed modules are part of the runtime and cannot be resolved to a file | ||
| * - data: skip json, wasm and inline js https://nodejs.org/api/esm.html#data-imports | ||
| */ | ||
| function shouldSkipContextLinesForFile(path) { | ||
| // Test the most common prefix and extension first. These are the ones we | ||
| // are most likely to see in user applications and are the ones we can break out of first. | ||
| if (path.startsWith('node:')) return true; | ||
| if (path.endsWith('.min.js')) return true; | ||
| if (path.endsWith('.min.cjs')) return true; | ||
| if (path.endsWith('.min.mjs')) return true; | ||
| if (path.startsWith('data:')) return true; | ||
| if (path.startsWith("node:")) return true; | ||
| if (path.endsWith(".min.js")) return true; | ||
| if (path.endsWith(".min.cjs")) return true; | ||
| if (path.endsWith(".min.mjs")) return true; | ||
| if (path.startsWith("data:")) return true; | ||
| return false; | ||
| } | ||
| /** | ||
| * Determines if we should skip contextlines based off the max lineno and colno values. | ||
| */ | ||
| function shouldSkipContextLinesForFrame(frame) { | ||
| if (frame.lineno !== undefined && frame.lineno > MAX_CONTEXTLINES_LINENO) return true; | ||
| if (frame.colno !== undefined && frame.colno > MAX_CONTEXTLINES_COLNO) return true; | ||
| if (frame.lineno !== void 0 && frame.lineno > MAX_CONTEXTLINES_LINENO) return true; | ||
| if (frame.colno !== void 0 && frame.colno > MAX_CONTEXTLINES_COLNO) return true; | ||
| return false; | ||
| } | ||
| /** | ||
| * Checks if we have all the contents that we need in the cache. | ||
| */ | ||
| function rangeExistsInContentCache(file, range) { | ||
| const contents = LRU_FILE_CONTENTS_CACHE.get(file); | ||
| if (contents === undefined) return false; | ||
| if (contents === void 0) return false; | ||
| for (let i = range[0]; i <= range[1]; i++) { | ||
| if (contents[i] === undefined) { | ||
| if (contents[i] === void 0) { | ||
| return false; | ||
| } | ||
| } | ||
| return true; | ||
| } | ||
| /** | ||
| * Creates contiguous ranges of lines to read from a file. In the case where context lines overlap, | ||
| * the ranges are merged to create a single range. | ||
| */ | ||
| function makeLineReaderRanges(lines, linecontext) { | ||
@@ -82,13 +50,9 @@ if (!lines.length) { | ||
| } | ||
| let i = 0; | ||
| const line = lines[0]; | ||
| if (typeof line !== 'number') { | ||
| if (typeof line !== "number") { | ||
| return []; | ||
| } | ||
| let current = makeContextRange(line, linecontext); | ||
| const out = []; | ||
| // eslint-disable-next-line no-constant-condition | ||
| while (true) { | ||
@@ -99,6 +63,4 @@ if (i === lines.length - 1) { | ||
| } | ||
| // If the next line falls into the current range, extend the current range to lineno + linecontext. | ||
| const next = lines[i + 1]; | ||
| if (typeof next !== 'number') { | ||
| if (typeof next !== "number") { | ||
| break; | ||
@@ -112,25 +74,12 @@ } | ||
| } | ||
| i++; | ||
| } | ||
| return out; | ||
| } | ||
| /** | ||
| * Extracts lines from a file and stores them in a cache. | ||
| */ | ||
| function getContextLinesFromFile(path, ranges, output) { | ||
| return new Promise((resolve, _reject) => { | ||
| // It is important *not* to have any async code between createInterface and the 'line' event listener | ||
| // as it will cause the 'line' event to | ||
| // be emitted before the listener is attached. | ||
| const stream = node_fs.createReadStream(path); | ||
| const lineReaded = node_readline.createInterface({ | ||
| input: stream, | ||
| input: stream | ||
| }); | ||
| // We need to explicitly destroy the stream to prevent memory leaks, | ||
| // removing the listeners on the readline interface is not enough. | ||
| // See: https://github.com/nodejs/node/issues/9002 and https://github.com/getsentry/sentry-javascript/issues/14892 | ||
| function destroyStreamAndResolve() { | ||
@@ -140,9 +89,6 @@ stream.destroy(); | ||
| } | ||
| // Init at zero and increment at the start of the loop because lines are 1 indexed. | ||
| let lineNumber = 0; | ||
| let currentRangeIndex = 0; | ||
| const range = ranges[currentRangeIndex]; | ||
| if (range === undefined) { | ||
| // We should never reach this point, but if we do, we should resolve the promise to prevent it from hanging. | ||
| if (range === void 0) { | ||
| destroyStreamAndResolve(); | ||
@@ -153,7 +99,3 @@ return; | ||
| let rangeEnd = range[1]; | ||
| // We use this inside Promise.all, so we need to resolve the promise even if there is an error | ||
| // to prevent Promise.all from short circuiting the rest. | ||
| function onStreamError(e) { | ||
| // Mark file path as failed to read and prevent multiple read attempts. | ||
| LRU_FILE_CONTENTS_FS_READ_FAILED.set(path, 1); | ||
@@ -165,19 +107,11 @@ debugBuild.DEBUG_BUILD && core.debug.error(`Failed to read file: ${path}. Error: ${e}`); | ||
| } | ||
| // We need to handle the error event to prevent the process from crashing in < Node 16 | ||
| // https://github.com/nodejs/node/pull/31603 | ||
| stream.on('error', onStreamError); | ||
| lineReaded.on('error', onStreamError); | ||
| lineReaded.on('close', destroyStreamAndResolve); | ||
| lineReaded.on('line', line => { | ||
| stream.on("error", onStreamError); | ||
| lineReaded.on("error", onStreamError); | ||
| lineReaded.on("close", destroyStreamAndResolve); | ||
| lineReaded.on("line", (line) => { | ||
| lineNumber++; | ||
| if (lineNumber < rangeStart) return; | ||
| // !Warning: This mutates the cache by storing the snipped line into the cache. | ||
| output[lineNumber] = core.snipLine(line, 0); | ||
| if (lineNumber >= rangeEnd) { | ||
| if (currentRangeIndex === ranges.length - 1) { | ||
| // We need to close the file stream and remove listeners, else the reader will continue to run our listener; | ||
| lineReaded.close(); | ||
@@ -188,5 +122,4 @@ lineReaded.removeAllListeners(); | ||
| currentRangeIndex++; | ||
| const range = ranges[currentRangeIndex]; | ||
| if (range === undefined) { | ||
| // This should never happen as it means we have a bug in the context. | ||
| const range2 = ranges[currentRangeIndex]; | ||
| if (range2 === void 0) { | ||
| lineReaded.close(); | ||
@@ -196,4 +129,4 @@ lineReaded.removeAllListeners(); | ||
| } | ||
| rangeStart = range[0]; | ||
| rangeEnd = range[1]; | ||
| rangeStart = range2[0]; | ||
| rangeEnd = range2[1]; | ||
| } | ||
@@ -203,15 +136,4 @@ }); | ||
| } | ||
| /** | ||
| * Adds surrounding (context) lines of the line that an exception occurred on to the event. | ||
| * This is done by reading the file line by line and extracting the lines. The extracted lines are stored in | ||
| * a cache to prevent multiple reads of the same file. Failures to read a file are similarly cached to prevent multiple | ||
| * failing reads from happening. | ||
| */ | ||
| /* eslint-disable complexity */ | ||
| async function addSourceContext(event, contextLines) { | ||
| // keep a lookup map of which files we've already enqueued to read, | ||
| // so we don't enqueue the same file multiple times which would cause multiple i/o reads | ||
| const filesToLines = {}; | ||
| if (contextLines > 0 && event.exception?.values) { | ||
@@ -222,22 +144,10 @@ for (const exception of event.exception.values) { | ||
| } | ||
| // Maps preserve insertion order, so we iterate in reverse, starting at the | ||
| // outermost frame and closer to where the exception has occurred (poor mans priority) | ||
| for (let i = exception.stacktrace.frames.length - 1; i >= 0; i--) { | ||
| const frame = exception.stacktrace.frames[i]; | ||
| const filename = frame?.filename; | ||
| if ( | ||
| !frame || | ||
| typeof filename !== 'string' || | ||
| typeof frame.lineno !== 'number' || | ||
| shouldSkipContextLinesForFile(filename) || | ||
| shouldSkipContextLinesForFrame(frame) | ||
| ) { | ||
| if (!frame || typeof filename !== "string" || typeof frame.lineno !== "number" || shouldSkipContextLinesForFile(filename) || shouldSkipContextLinesForFrame(frame)) { | ||
| continue; | ||
| } | ||
| const filesToLinesOutput = filesToLines[filename]; | ||
| if (!filesToLinesOutput) filesToLines[filename] = []; | ||
| // @ts-expect-error this is defined above | ||
| filesToLines[filename].push(frame.lineno); | ||
@@ -247,3 +157,2 @@ } | ||
| } | ||
| const files = Object.keys(filesToLines); | ||
@@ -253,10 +162,7 @@ if (files.length == 0) { | ||
| } | ||
| const readlinePromises = []; | ||
| for (const file of files) { | ||
| // If we failed to read this before, dont try reading it again. | ||
| if (LRU_FILE_CONTENTS_FS_READ_FAILED.get(file)) { | ||
| continue; | ||
| } | ||
| const filesToLineRanges = filesToLines[file]; | ||
@@ -266,22 +172,13 @@ if (!filesToLineRanges) { | ||
| } | ||
| // Sort ranges so that they are sorted by line increasing order and match how the file is read. | ||
| filesToLineRanges.sort((a, b) => a - b); | ||
| // Check if the contents are already in the cache and if we can avoid reading the file again. | ||
| const ranges = makeLineReaderRanges(filesToLineRanges, contextLines); | ||
| if (ranges.every(r => rangeExistsInContentCache(file, r))) { | ||
| if (ranges.every((r) => rangeExistsInContentCache(file, r))) { | ||
| continue; | ||
| } | ||
| const cache = emplace(LRU_FILE_CONTENTS_CACHE, file, {}); | ||
| readlinePromises.push(getContextLinesFromFile(file, ranges, cache)); | ||
| } | ||
| // The promise rejections are caught in order to prevent them from short circuiting Promise.all | ||
| await Promise.all(readlinePromises).catch(() => { | ||
| debugBuild.DEBUG_BUILD && core.debug.log('Failed to read one or more source files and resolve context lines'); | ||
| debugBuild.DEBUG_BUILD && core.debug.log("Failed to read one or more source files and resolve context lines"); | ||
| }); | ||
| // Perform the same loop as above, but this time we can assume all files are in the cache | ||
| // and attempt to add source context to frames. | ||
| if (contextLines > 0 && event.exception?.values) { | ||
@@ -294,21 +191,11 @@ for (const exception of event.exception.values) { | ||
| } | ||
| return event; | ||
| } | ||
| /* eslint-enable complexity */ | ||
| /** Adds context lines to frames */ | ||
| function addSourceContextToFrames( | ||
| frames, | ||
| contextLines, | ||
| cache, | ||
| ) { | ||
| function addSourceContextToFrames(frames, contextLines, cache) { | ||
| for (const frame of frames) { | ||
| // Only add context if we have a filename and it hasn't already been added | ||
| if (frame.filename && frame.context_line === undefined && typeof frame.lineno === 'number') { | ||
| if (frame.filename && frame.context_line === void 0 && typeof frame.lineno === "number") { | ||
| const contents = cache.get(frame.filename); | ||
| if (contents === undefined) { | ||
| if (contents === void 0) { | ||
| continue; | ||
| } | ||
| addContextToFrame(frame.lineno, frame, contextLines, contents); | ||
@@ -318,7 +205,2 @@ } | ||
| } | ||
| /** | ||
| * Clears the context lines from a frame, used to reset a frame to its original state | ||
| * if we fail to resolve all context lines for it. | ||
| */ | ||
| function clearLineContext(frame) { | ||
@@ -329,25 +211,11 @@ delete frame.pre_context; | ||
| } | ||
| /** | ||
| * Resolves context lines before and after the given line number and appends them to the frame; | ||
| */ | ||
| function addContextToFrame( | ||
| lineno, | ||
| frame, | ||
| contextLines, | ||
| contents, | ||
| ) { | ||
| // When there is no line number in the frame, attaching context is nonsensical and will even break grouping. | ||
| // We already check for lineno before calling this, but since StackFrame lineno ism optional, we check it again. | ||
| if (frame.lineno === undefined || contents === undefined) { | ||
| debugBuild.DEBUG_BUILD && core.debug.error('Cannot resolve context for frame with no lineno or file contents'); | ||
| function addContextToFrame(lineno, frame, contextLines, contents) { | ||
| if (frame.lineno === void 0 || contents === void 0) { | ||
| debugBuild.DEBUG_BUILD && core.debug.error("Cannot resolve context for frame with no lineno or file contents"); | ||
| return; | ||
| } | ||
| frame.pre_context = []; | ||
| for (let i = makeRangeStart(lineno, contextLines); i < lineno; i++) { | ||
| // We always expect the start context as line numbers cannot be negative. If we dont find a line, then | ||
| // something went wrong somewhere. Clear the context and return without adding any linecontext. | ||
| const line = contents[i]; | ||
| if (line === undefined) { | ||
| if (line === void 0) { | ||
| clearLineContext(frame); | ||
@@ -357,9 +225,5 @@ debugBuild.DEBUG_BUILD && core.debug.error(`Could not find line ${i} in file ${frame.filename}`); | ||
| } | ||
| frame.pre_context.push(line); | ||
| } | ||
| // We should always have the context line. If we dont, something went wrong, so we clear the context and return | ||
| // without adding any linecontext. | ||
| if (contents[lineno] === undefined) { | ||
| if (contents[lineno] === void 0) { | ||
| clearLineContext(frame); | ||
@@ -369,12 +233,8 @@ debugBuild.DEBUG_BUILD && core.debug.error(`Could not find line ${lineno} in file ${frame.filename}`); | ||
| } | ||
| frame.context_line = contents[lineno]; | ||
| const end = makeRangeEnd(lineno, contextLines); | ||
| frame.post_context = []; | ||
| for (let i = lineno + 1; i <= end; i++) { | ||
| // Since we dont track when the file ends, we cant clear the context if we dont find a line as it could | ||
| // just be that we reached the end of the file. | ||
| const line = contents[i]; | ||
| if (line === undefined) { | ||
| if (line === void 0) { | ||
| break; | ||
@@ -385,23 +245,13 @@ } | ||
| } | ||
| // Helper functions for generating line context ranges. They take a line number and the number of lines of context to | ||
| // include before and after the line and generate an inclusive range of indices. | ||
| // Compute inclusive end context range | ||
| function makeRangeStart(line, linecontext) { | ||
| return Math.max(1, line - linecontext); | ||
| } | ||
| // Compute inclusive start context range | ||
| function makeRangeEnd(line, linecontext) { | ||
| return line + linecontext; | ||
| } | ||
| // Determine start and end indices for context range (inclusive); | ||
| function makeContextRange(line, linecontext) { | ||
| return [makeRangeStart(line, linecontext), makeRangeEnd(line, linecontext)]; | ||
| } | ||
| /** Exported only for tests, as a type-safe variant. */ | ||
| const _contextLinesIntegration = ((options = {}) => { | ||
| const contextLines = options.frameContextLines !== undefined ? options.frameContextLines : DEFAULT_LINES_OF_CONTEXT; | ||
| const contextLines = options.frameContextLines !== void 0 ? options.frameContextLines : DEFAULT_LINES_OF_CONTEXT; | ||
| return { | ||
@@ -411,9 +261,5 @@ name: INTEGRATION_NAME, | ||
| return addSourceContext(event, contextLines); | ||
| }, | ||
| } | ||
| }; | ||
| }) ; | ||
| /** | ||
| * Capture the lines before and after the frame's context. | ||
| */ | ||
| }); | ||
| const contextLinesIntegration = core.defineIntegration(_contextLinesIntegration); | ||
@@ -420,0 +266,0 @@ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"contextlines.js","sources":["../../../src/integrations/contextlines.ts"],"sourcesContent":["import { createReadStream } from 'node:fs';\nimport { createInterface } from 'node:readline';\nimport type { Event, IntegrationFn, StackFrame } from '@sentry/core';\nimport { debug, defineIntegration, LRUMap, snipLine } from '@sentry/core';\nimport { DEBUG_BUILD } from '../debug-build';\n\nconst LRU_FILE_CONTENTS_CACHE = new LRUMap<string, Record<number, string>>(10);\nconst LRU_FILE_CONTENTS_FS_READ_FAILED = new LRUMap<string, 1>(20);\nconst DEFAULT_LINES_OF_CONTEXT = 7;\nconst INTEGRATION_NAME = 'ContextLines';\n// Determines the upper bound of lineno/colno that we will attempt to read. Large colno values are likely to be\n// minified code while large lineno values are likely to be bundled code.\n// Exported for testing purposes.\nexport const MAX_CONTEXTLINES_COLNO: number = 1000;\nexport const MAX_CONTEXTLINES_LINENO: number = 10000;\n\ninterface ContextLinesOptions {\n /**\n * Sets the number of context lines for each frame when loading a file.\n * Defaults to 7.\n *\n * Set to 0 to disable loading and inclusion of source files.\n **/\n frameContextLines?: number;\n}\n\n/**\n * Exported for testing purposes.\n */\nexport function resetFileContentCache(): void {\n LRU_FILE_CONTENTS_CACHE.clear();\n}\n\n/**\n * Get or init map value\n */\nfunction emplace<T extends LRUMap<K, V>, K extends string, V>(map: T, key: K, contents: V): V {\n const value = map.get(key);\n\n if (value === undefined) {\n map.set(key, contents);\n return contents;\n }\n\n return value;\n}\n\n/**\n * Determines if context lines should be skipped for a file.\n * - .min.(mjs|cjs|js) files are and not useful since they dont point to the original source\n * - node: prefixed modules are part of the runtime and cannot be resolved to a file\n * - data: skip json, wasm and inline js https://nodejs.org/api/esm.html#data-imports\n */\nfunction shouldSkipContextLinesForFile(path: string): boolean {\n // Test the most common prefix and extension first. These are the ones we\n // are most likely to see in user applications and are the ones we can break out of first.\n if (path.startsWith('node:')) return true;\n if (path.endsWith('.min.js')) return true;\n if (path.endsWith('.min.cjs')) return true;\n if (path.endsWith('.min.mjs')) return true;\n if (path.startsWith('data:')) return true;\n return false;\n}\n\n/**\n * Determines if we should skip contextlines based off the max lineno and colno values.\n */\nfunction shouldSkipContextLinesForFrame(frame: StackFrame): boolean {\n if (frame.lineno !== undefined && frame.lineno > MAX_CONTEXTLINES_LINENO) return true;\n if (frame.colno !== undefined && frame.colno > MAX_CONTEXTLINES_COLNO) return true;\n return false;\n}\n/**\n * Checks if we have all the contents that we need in the cache.\n */\nfunction rangeExistsInContentCache(file: string, range: ReadlineRange): boolean {\n const contents = LRU_FILE_CONTENTS_CACHE.get(file);\n if (contents === undefined) return false;\n\n for (let i = range[0]; i <= range[1]; i++) {\n if (contents[i] === undefined) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Creates contiguous ranges of lines to read from a file. In the case where context lines overlap,\n * the ranges are merged to create a single range.\n */\nfunction makeLineReaderRanges(lines: number[], linecontext: number): ReadlineRange[] {\n if (!lines.length) {\n return [];\n }\n\n let i = 0;\n const line = lines[0];\n\n if (typeof line !== 'number') {\n return [];\n }\n\n let current = makeContextRange(line, linecontext);\n const out: ReadlineRange[] = [];\n // eslint-disable-next-line no-constant-condition\n while (true) {\n if (i === lines.length - 1) {\n out.push(current);\n break;\n }\n\n // If the next line falls into the current range, extend the current range to lineno + linecontext.\n const next = lines[i + 1];\n if (typeof next !== 'number') {\n break;\n }\n if (next <= current[1]) {\n current[1] = next + linecontext;\n } else {\n out.push(current);\n current = makeContextRange(next, linecontext);\n }\n\n i++;\n }\n\n return out;\n}\n\n/**\n * Extracts lines from a file and stores them in a cache.\n */\nfunction getContextLinesFromFile(path: string, ranges: ReadlineRange[], output: Record<number, string>): Promise<void> {\n return new Promise((resolve, _reject) => {\n // It is important *not* to have any async code between createInterface and the 'line' event listener\n // as it will cause the 'line' event to\n // be emitted before the listener is attached.\n const stream = createReadStream(path);\n const lineReaded = createInterface({\n input: stream,\n });\n\n // We need to explicitly destroy the stream to prevent memory leaks,\n // removing the listeners on the readline interface is not enough.\n // See: https://github.com/nodejs/node/issues/9002 and https://github.com/getsentry/sentry-javascript/issues/14892\n function destroyStreamAndResolve(): void {\n stream.destroy();\n resolve();\n }\n\n // Init at zero and increment at the start of the loop because lines are 1 indexed.\n let lineNumber = 0;\n let currentRangeIndex = 0;\n const range = ranges[currentRangeIndex];\n if (range === undefined) {\n // We should never reach this point, but if we do, we should resolve the promise to prevent it from hanging.\n destroyStreamAndResolve();\n return;\n }\n let rangeStart = range[0];\n let rangeEnd = range[1];\n\n // We use this inside Promise.all, so we need to resolve the promise even if there is an error\n // to prevent Promise.all from short circuiting the rest.\n function onStreamError(e: Error): void {\n // Mark file path as failed to read and prevent multiple read attempts.\n LRU_FILE_CONTENTS_FS_READ_FAILED.set(path, 1);\n DEBUG_BUILD && debug.error(`Failed to read file: ${path}. Error: ${e}`);\n lineReaded.close();\n lineReaded.removeAllListeners();\n destroyStreamAndResolve();\n }\n\n // We need to handle the error event to prevent the process from crashing in < Node 16\n // https://github.com/nodejs/node/pull/31603\n stream.on('error', onStreamError);\n lineReaded.on('error', onStreamError);\n lineReaded.on('close', destroyStreamAndResolve);\n\n lineReaded.on('line', line => {\n lineNumber++;\n if (lineNumber < rangeStart) return;\n\n // !Warning: This mutates the cache by storing the snipped line into the cache.\n output[lineNumber] = snipLine(line, 0);\n\n if (lineNumber >= rangeEnd) {\n if (currentRangeIndex === ranges.length - 1) {\n // We need to close the file stream and remove listeners, else the reader will continue to run our listener;\n lineReaded.close();\n lineReaded.removeAllListeners();\n return;\n }\n currentRangeIndex++;\n const range = ranges[currentRangeIndex];\n if (range === undefined) {\n // This should never happen as it means we have a bug in the context.\n lineReaded.close();\n lineReaded.removeAllListeners();\n return;\n }\n rangeStart = range[0];\n rangeEnd = range[1];\n }\n });\n });\n}\n\n/**\n * Adds surrounding (context) lines of the line that an exception occurred on to the event.\n * This is done by reading the file line by line and extracting the lines. The extracted lines are stored in\n * a cache to prevent multiple reads of the same file. Failures to read a file are similarly cached to prevent multiple\n * failing reads from happening.\n */\n/* eslint-disable complexity */\nasync function addSourceContext(event: Event, contextLines: number): Promise<Event> {\n // keep a lookup map of which files we've already enqueued to read,\n // so we don't enqueue the same file multiple times which would cause multiple i/o reads\n const filesToLines: Record<string, number[]> = {};\n\n if (contextLines > 0 && event.exception?.values) {\n for (const exception of event.exception.values) {\n if (!exception.stacktrace?.frames?.length) {\n continue;\n }\n\n // Maps preserve insertion order, so we iterate in reverse, starting at the\n // outermost frame and closer to where the exception has occurred (poor mans priority)\n for (let i = exception.stacktrace.frames.length - 1; i >= 0; i--) {\n const frame: StackFrame | undefined = exception.stacktrace.frames[i];\n const filename = frame?.filename;\n\n if (\n !frame ||\n typeof filename !== 'string' ||\n typeof frame.lineno !== 'number' ||\n shouldSkipContextLinesForFile(filename) ||\n shouldSkipContextLinesForFrame(frame)\n ) {\n continue;\n }\n\n const filesToLinesOutput = filesToLines[filename];\n if (!filesToLinesOutput) filesToLines[filename] = [];\n // @ts-expect-error this is defined above\n filesToLines[filename].push(frame.lineno);\n }\n }\n }\n\n const files = Object.keys(filesToLines);\n if (files.length == 0) {\n return event;\n }\n\n const readlinePromises: Promise<void>[] = [];\n for (const file of files) {\n // If we failed to read this before, dont try reading it again.\n if (LRU_FILE_CONTENTS_FS_READ_FAILED.get(file)) {\n continue;\n }\n\n const filesToLineRanges = filesToLines[file];\n if (!filesToLineRanges) {\n continue;\n }\n\n // Sort ranges so that they are sorted by line increasing order and match how the file is read.\n filesToLineRanges.sort((a, b) => a - b);\n // Check if the contents are already in the cache and if we can avoid reading the file again.\n const ranges = makeLineReaderRanges(filesToLineRanges, contextLines);\n if (ranges.every(r => rangeExistsInContentCache(file, r))) {\n continue;\n }\n\n const cache = emplace(LRU_FILE_CONTENTS_CACHE, file, {});\n readlinePromises.push(getContextLinesFromFile(file, ranges, cache));\n }\n\n // The promise rejections are caught in order to prevent them from short circuiting Promise.all\n await Promise.all(readlinePromises).catch(() => {\n DEBUG_BUILD && debug.log('Failed to read one or more source files and resolve context lines');\n });\n\n // Perform the same loop as above, but this time we can assume all files are in the cache\n // and attempt to add source context to frames.\n if (contextLines > 0 && event.exception?.values) {\n for (const exception of event.exception.values) {\n if (exception.stacktrace?.frames && exception.stacktrace.frames.length > 0) {\n addSourceContextToFrames(exception.stacktrace.frames, contextLines, LRU_FILE_CONTENTS_CACHE);\n }\n }\n }\n\n return event;\n}\n/* eslint-enable complexity */\n\n/** Adds context lines to frames */\nfunction addSourceContextToFrames(\n frames: StackFrame[],\n contextLines: number,\n cache: LRUMap<string, Record<number, string>>,\n): void {\n for (const frame of frames) {\n // Only add context if we have a filename and it hasn't already been added\n if (frame.filename && frame.context_line === undefined && typeof frame.lineno === 'number') {\n const contents = cache.get(frame.filename);\n if (contents === undefined) {\n continue;\n }\n\n addContextToFrame(frame.lineno, frame, contextLines, contents);\n }\n }\n}\n\n/**\n * Clears the context lines from a frame, used to reset a frame to its original state\n * if we fail to resolve all context lines for it.\n */\nfunction clearLineContext(frame: StackFrame): void {\n delete frame.pre_context;\n delete frame.context_line;\n delete frame.post_context;\n}\n\n/**\n * Resolves context lines before and after the given line number and appends them to the frame;\n */\nexport function addContextToFrame(\n lineno: number,\n frame: StackFrame,\n contextLines: number,\n contents: Record<number, string> | undefined,\n): void {\n // When there is no line number in the frame, attaching context is nonsensical and will even break grouping.\n // We already check for lineno before calling this, but since StackFrame lineno ism optional, we check it again.\n if (frame.lineno === undefined || contents === undefined) {\n DEBUG_BUILD && debug.error('Cannot resolve context for frame with no lineno or file contents');\n return;\n }\n\n frame.pre_context = [];\n for (let i = makeRangeStart(lineno, contextLines); i < lineno; i++) {\n // We always expect the start context as line numbers cannot be negative. If we dont find a line, then\n // something went wrong somewhere. Clear the context and return without adding any linecontext.\n const line = contents[i];\n if (line === undefined) {\n clearLineContext(frame);\n DEBUG_BUILD && debug.error(`Could not find line ${i} in file ${frame.filename}`);\n return;\n }\n\n frame.pre_context.push(line);\n }\n\n // We should always have the context line. If we dont, something went wrong, so we clear the context and return\n // without adding any linecontext.\n if (contents[lineno] === undefined) {\n clearLineContext(frame);\n DEBUG_BUILD && debug.error(`Could not find line ${lineno} in file ${frame.filename}`);\n return;\n }\n\n frame.context_line = contents[lineno];\n\n const end = makeRangeEnd(lineno, contextLines);\n frame.post_context = [];\n for (let i = lineno + 1; i <= end; i++) {\n // Since we dont track when the file ends, we cant clear the context if we dont find a line as it could\n // just be that we reached the end of the file.\n const line = contents[i];\n if (line === undefined) {\n break;\n }\n frame.post_context.push(line);\n }\n}\n\n// Helper functions for generating line context ranges. They take a line number and the number of lines of context to\n// include before and after the line and generate an inclusive range of indices.\ntype ReadlineRange = [start: number, end: number];\n// Compute inclusive end context range\nfunction makeRangeStart(line: number, linecontext: number): number {\n return Math.max(1, line - linecontext);\n}\n// Compute inclusive start context range\nfunction makeRangeEnd(line: number, linecontext: number): number {\n return line + linecontext;\n}\n// Determine start and end indices for context range (inclusive);\nfunction makeContextRange(line: number, linecontext: number): [start: number, end: number] {\n return [makeRangeStart(line, linecontext), makeRangeEnd(line, linecontext)];\n}\n\n/** Exported only for tests, as a type-safe variant. */\nexport const _contextLinesIntegration = ((options: ContextLinesOptions = {}) => {\n const contextLines = options.frameContextLines !== undefined ? options.frameContextLines : DEFAULT_LINES_OF_CONTEXT;\n\n return {\n name: INTEGRATION_NAME,\n processEvent(event) {\n return addSourceContext(event, contextLines);\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Capture the lines before and after the frame's context.\n */\nexport const contextLinesIntegration = defineIntegration(_contextLinesIntegration);\n"],"names":["LRUMap","createReadStream","createInterface","DEBUG_BUILD","debug","snipLine","defineIntegration"],"mappings":"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"} | ||
| {"version":3,"file":"contextlines.js","sources":["../../../src/integrations/contextlines.ts"],"sourcesContent":["import { createReadStream } from 'node:fs';\nimport { createInterface } from 'node:readline';\nimport type { Event, IntegrationFn, StackFrame } from '@sentry/core';\nimport { debug, defineIntegration, LRUMap, snipLine } from '@sentry/core';\nimport { DEBUG_BUILD } from '../debug-build';\n\nconst LRU_FILE_CONTENTS_CACHE = new LRUMap<string, Record<number, string>>(10);\nconst LRU_FILE_CONTENTS_FS_READ_FAILED = new LRUMap<string, 1>(20);\nconst DEFAULT_LINES_OF_CONTEXT = 7;\nconst INTEGRATION_NAME = 'ContextLines';\n// Determines the upper bound of lineno/colno that we will attempt to read. Large colno values are likely to be\n// minified code while large lineno values are likely to be bundled code.\n// Exported for testing purposes.\nexport const MAX_CONTEXTLINES_COLNO: number = 1000;\nexport const MAX_CONTEXTLINES_LINENO: number = 10000;\n\ninterface ContextLinesOptions {\n /**\n * Sets the number of context lines for each frame when loading a file.\n * Defaults to 7.\n *\n * Set to 0 to disable loading and inclusion of source files.\n **/\n frameContextLines?: number;\n}\n\n/**\n * Exported for testing purposes.\n */\nexport function resetFileContentCache(): void {\n LRU_FILE_CONTENTS_CACHE.clear();\n}\n\n/**\n * Get or init map value\n */\nfunction emplace<T extends LRUMap<K, V>, K extends string, V>(map: T, key: K, contents: V): V {\n const value = map.get(key);\n\n if (value === undefined) {\n map.set(key, contents);\n return contents;\n }\n\n return value;\n}\n\n/**\n * Determines if context lines should be skipped for a file.\n * - .min.(mjs|cjs|js) files are and not useful since they dont point to the original source\n * - node: prefixed modules are part of the runtime and cannot be resolved to a file\n * - data: skip json, wasm and inline js https://nodejs.org/api/esm.html#data-imports\n */\nfunction shouldSkipContextLinesForFile(path: string): boolean {\n // Test the most common prefix and extension first. These are the ones we\n // are most likely to see in user applications and are the ones we can break out of first.\n if (path.startsWith('node:')) return true;\n if (path.endsWith('.min.js')) return true;\n if (path.endsWith('.min.cjs')) return true;\n if (path.endsWith('.min.mjs')) return true;\n if (path.startsWith('data:')) return true;\n return false;\n}\n\n/**\n * Determines if we should skip contextlines based off the max lineno and colno values.\n */\nfunction shouldSkipContextLinesForFrame(frame: StackFrame): boolean {\n if (frame.lineno !== undefined && frame.lineno > MAX_CONTEXTLINES_LINENO) return true;\n if (frame.colno !== undefined && frame.colno > MAX_CONTEXTLINES_COLNO) return true;\n return false;\n}\n/**\n * Checks if we have all the contents that we need in the cache.\n */\nfunction rangeExistsInContentCache(file: string, range: ReadlineRange): boolean {\n const contents = LRU_FILE_CONTENTS_CACHE.get(file);\n if (contents === undefined) return false;\n\n for (let i = range[0]; i <= range[1]; i++) {\n if (contents[i] === undefined) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Creates contiguous ranges of lines to read from a file. In the case where context lines overlap,\n * the ranges are merged to create a single range.\n */\nfunction makeLineReaderRanges(lines: number[], linecontext: number): ReadlineRange[] {\n if (!lines.length) {\n return [];\n }\n\n let i = 0;\n const line = lines[0];\n\n if (typeof line !== 'number') {\n return [];\n }\n\n let current = makeContextRange(line, linecontext);\n const out: ReadlineRange[] = [];\n // eslint-disable-next-line no-constant-condition\n while (true) {\n if (i === lines.length - 1) {\n out.push(current);\n break;\n }\n\n // If the next line falls into the current range, extend the current range to lineno + linecontext.\n const next = lines[i + 1];\n if (typeof next !== 'number') {\n break;\n }\n if (next <= current[1]) {\n current[1] = next + linecontext;\n } else {\n out.push(current);\n current = makeContextRange(next, linecontext);\n }\n\n i++;\n }\n\n return out;\n}\n\n/**\n * Extracts lines from a file and stores them in a cache.\n */\nfunction getContextLinesFromFile(path: string, ranges: ReadlineRange[], output: Record<number, string>): Promise<void> {\n return new Promise((resolve, _reject) => {\n // It is important *not* to have any async code between createInterface and the 'line' event listener\n // as it will cause the 'line' event to\n // be emitted before the listener is attached.\n const stream = createReadStream(path);\n const lineReaded = createInterface({\n input: stream,\n });\n\n // We need to explicitly destroy the stream to prevent memory leaks,\n // removing the listeners on the readline interface is not enough.\n // See: https://github.com/nodejs/node/issues/9002 and https://github.com/getsentry/sentry-javascript/issues/14892\n function destroyStreamAndResolve(): void {\n stream.destroy();\n resolve();\n }\n\n // Init at zero and increment at the start of the loop because lines are 1 indexed.\n let lineNumber = 0;\n let currentRangeIndex = 0;\n const range = ranges[currentRangeIndex];\n if (range === undefined) {\n // We should never reach this point, but if we do, we should resolve the promise to prevent it from hanging.\n destroyStreamAndResolve();\n return;\n }\n let rangeStart = range[0];\n let rangeEnd = range[1];\n\n // We use this inside Promise.all, so we need to resolve the promise even if there is an error\n // to prevent Promise.all from short circuiting the rest.\n function onStreamError(e: Error): void {\n // Mark file path as failed to read and prevent multiple read attempts.\n LRU_FILE_CONTENTS_FS_READ_FAILED.set(path, 1);\n DEBUG_BUILD && debug.error(`Failed to read file: ${path}. Error: ${e}`);\n lineReaded.close();\n lineReaded.removeAllListeners();\n destroyStreamAndResolve();\n }\n\n // We need to handle the error event to prevent the process from crashing in < Node 16\n // https://github.com/nodejs/node/pull/31603\n stream.on('error', onStreamError);\n lineReaded.on('error', onStreamError);\n lineReaded.on('close', destroyStreamAndResolve);\n\n lineReaded.on('line', line => {\n lineNumber++;\n if (lineNumber < rangeStart) return;\n\n // !Warning: This mutates the cache by storing the snipped line into the cache.\n output[lineNumber] = snipLine(line, 0);\n\n if (lineNumber >= rangeEnd) {\n if (currentRangeIndex === ranges.length - 1) {\n // We need to close the file stream and remove listeners, else the reader will continue to run our listener;\n lineReaded.close();\n lineReaded.removeAllListeners();\n return;\n }\n currentRangeIndex++;\n const range = ranges[currentRangeIndex];\n if (range === undefined) {\n // This should never happen as it means we have a bug in the context.\n lineReaded.close();\n lineReaded.removeAllListeners();\n return;\n }\n rangeStart = range[0];\n rangeEnd = range[1];\n }\n });\n });\n}\n\n/**\n * Adds surrounding (context) lines of the line that an exception occurred on to the event.\n * This is done by reading the file line by line and extracting the lines. The extracted lines are stored in\n * a cache to prevent multiple reads of the same file. Failures to read a file are similarly cached to prevent multiple\n * failing reads from happening.\n */\n/* eslint-disable complexity */\nasync function addSourceContext(event: Event, contextLines: number): Promise<Event> {\n // keep a lookup map of which files we've already enqueued to read,\n // so we don't enqueue the same file multiple times which would cause multiple i/o reads\n const filesToLines: Record<string, number[]> = {};\n\n if (contextLines > 0 && event.exception?.values) {\n for (const exception of event.exception.values) {\n if (!exception.stacktrace?.frames?.length) {\n continue;\n }\n\n // Maps preserve insertion order, so we iterate in reverse, starting at the\n // outermost frame and closer to where the exception has occurred (poor mans priority)\n for (let i = exception.stacktrace.frames.length - 1; i >= 0; i--) {\n const frame: StackFrame | undefined = exception.stacktrace.frames[i];\n const filename = frame?.filename;\n\n if (\n !frame ||\n typeof filename !== 'string' ||\n typeof frame.lineno !== 'number' ||\n shouldSkipContextLinesForFile(filename) ||\n shouldSkipContextLinesForFrame(frame)\n ) {\n continue;\n }\n\n const filesToLinesOutput = filesToLines[filename];\n if (!filesToLinesOutput) filesToLines[filename] = [];\n // @ts-expect-error this is defined above\n filesToLines[filename].push(frame.lineno);\n }\n }\n }\n\n const files = Object.keys(filesToLines);\n if (files.length == 0) {\n return event;\n }\n\n const readlinePromises: Promise<void>[] = [];\n for (const file of files) {\n // If we failed to read this before, dont try reading it again.\n if (LRU_FILE_CONTENTS_FS_READ_FAILED.get(file)) {\n continue;\n }\n\n const filesToLineRanges = filesToLines[file];\n if (!filesToLineRanges) {\n continue;\n }\n\n // Sort ranges so that they are sorted by line increasing order and match how the file is read.\n filesToLineRanges.sort((a, b) => a - b);\n // Check if the contents are already in the cache and if we can avoid reading the file again.\n const ranges = makeLineReaderRanges(filesToLineRanges, contextLines);\n if (ranges.every(r => rangeExistsInContentCache(file, r))) {\n continue;\n }\n\n const cache = emplace(LRU_FILE_CONTENTS_CACHE, file, {});\n readlinePromises.push(getContextLinesFromFile(file, ranges, cache));\n }\n\n // The promise rejections are caught in order to prevent them from short circuiting Promise.all\n await Promise.all(readlinePromises).catch(() => {\n DEBUG_BUILD && debug.log('Failed to read one or more source files and resolve context lines');\n });\n\n // Perform the same loop as above, but this time we can assume all files are in the cache\n // and attempt to add source context to frames.\n if (contextLines > 0 && event.exception?.values) {\n for (const exception of event.exception.values) {\n if (exception.stacktrace?.frames && exception.stacktrace.frames.length > 0) {\n addSourceContextToFrames(exception.stacktrace.frames, contextLines, LRU_FILE_CONTENTS_CACHE);\n }\n }\n }\n\n return event;\n}\n/* eslint-enable complexity */\n\n/** Adds context lines to frames */\nfunction addSourceContextToFrames(\n frames: StackFrame[],\n contextLines: number,\n cache: LRUMap<string, Record<number, string>>,\n): void {\n for (const frame of frames) {\n // Only add context if we have a filename and it hasn't already been added\n if (frame.filename && frame.context_line === undefined && typeof frame.lineno === 'number') {\n const contents = cache.get(frame.filename);\n if (contents === undefined) {\n continue;\n }\n\n addContextToFrame(frame.lineno, frame, contextLines, contents);\n }\n }\n}\n\n/**\n * Clears the context lines from a frame, used to reset a frame to its original state\n * if we fail to resolve all context lines for it.\n */\nfunction clearLineContext(frame: StackFrame): void {\n delete frame.pre_context;\n delete frame.context_line;\n delete frame.post_context;\n}\n\n/**\n * Resolves context lines before and after the given line number and appends them to the frame;\n */\nexport function addContextToFrame(\n lineno: number,\n frame: StackFrame,\n contextLines: number,\n contents: Record<number, string> | undefined,\n): void {\n // When there is no line number in the frame, attaching context is nonsensical and will even break grouping.\n // We already check for lineno before calling this, but since StackFrame lineno ism optional, we check it again.\n if (frame.lineno === undefined || contents === undefined) {\n DEBUG_BUILD && debug.error('Cannot resolve context for frame with no lineno or file contents');\n return;\n }\n\n frame.pre_context = [];\n for (let i = makeRangeStart(lineno, contextLines); i < lineno; i++) {\n // We always expect the start context as line numbers cannot be negative. If we dont find a line, then\n // something went wrong somewhere. Clear the context and return without adding any linecontext.\n const line = contents[i];\n if (line === undefined) {\n clearLineContext(frame);\n DEBUG_BUILD && debug.error(`Could not find line ${i} in file ${frame.filename}`);\n return;\n }\n\n frame.pre_context.push(line);\n }\n\n // We should always have the context line. If we dont, something went wrong, so we clear the context and return\n // without adding any linecontext.\n if (contents[lineno] === undefined) {\n clearLineContext(frame);\n DEBUG_BUILD && debug.error(`Could not find line ${lineno} in file ${frame.filename}`);\n return;\n }\n\n frame.context_line = contents[lineno];\n\n const end = makeRangeEnd(lineno, contextLines);\n frame.post_context = [];\n for (let i = lineno + 1; i <= end; i++) {\n // Since we dont track when the file ends, we cant clear the context if we dont find a line as it could\n // just be that we reached the end of the file.\n const line = contents[i];\n if (line === undefined) {\n break;\n }\n frame.post_context.push(line);\n }\n}\n\n// Helper functions for generating line context ranges. They take a line number and the number of lines of context to\n// include before and after the line and generate an inclusive range of indices.\ntype ReadlineRange = [start: number, end: number];\n// Compute inclusive end context range\nfunction makeRangeStart(line: number, linecontext: number): number {\n return Math.max(1, line - linecontext);\n}\n// Compute inclusive start context range\nfunction makeRangeEnd(line: number, linecontext: number): number {\n return line + linecontext;\n}\n// Determine start and end indices for context range (inclusive);\nfunction makeContextRange(line: number, linecontext: number): [start: number, end: number] {\n return [makeRangeStart(line, linecontext), makeRangeEnd(line, linecontext)];\n}\n\n/** Exported only for tests, as a type-safe variant. */\nexport const _contextLinesIntegration = ((options: ContextLinesOptions = {}) => {\n const contextLines = options.frameContextLines !== undefined ? options.frameContextLines : DEFAULT_LINES_OF_CONTEXT;\n\n return {\n name: INTEGRATION_NAME,\n processEvent(event) {\n return addSourceContext(event, contextLines);\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Capture the lines before and after the frame's context.\n */\nexport const contextLinesIntegration = defineIntegration(_contextLinesIntegration);\n"],"names":["LRUMap","createReadStream","createInterface","DEBUG_BUILD","debug","snipLine","range","defineIntegration"],"mappings":"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"} |
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const INSTRUMENTATION_NAME = '@sentry/instrumentation-http'; | ||
| const INSTRUMENTATION_NAME = "@sentry/instrumentation-http"; | ||
| exports.INSTRUMENTATION_NAME = INSTRUMENTATION_NAME; | ||
| //# sourceMappingURL=constants.js.map |
@@ -7,253 +7,72 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const debugBuild = require('../../debug-build.js'); | ||
| const captureRequestBody = require('../../utils/captureRequestBody.js'); | ||
| const HTTP_SERVER_INSTRUMENTED_KEY = api.createContextKey('sentry_http_server_instrumented'); | ||
| const INTEGRATION_NAME = 'Http.Server'; | ||
| const clientToRequestSessionAggregatesMap = new Map | ||
| (); | ||
| // We keep track of emit functions we wrapped, to avoid double wrapping | ||
| // We do this instead of putting a non-enumerable property on the function, because | ||
| // sometimes the property seems to be migrated to forks of the emit function, which we do not want to happen | ||
| // This was the case in the nestjs-distributed-tracing E2E test | ||
| const wrappedEmitFns = new WeakSet(); | ||
| /** | ||
| * Add a callback to the request object that will be called when the request is started. | ||
| * The callback will receive the next function to continue processing the request. | ||
| */ | ||
| const HTTP_SERVER_INSTRUMENTED_KEY = api.createContextKey("sentry_http_server_instrumented"); | ||
| const INTEGRATION_NAME = "Http.Server"; | ||
| function addStartSpanCallback(request, callback) { | ||
| core.addNonEnumerableProperty(request, '_startSpanCallback', new WeakRef(callback)); | ||
| core.addNonEnumerableProperty(request, "_startSpanCallback", new WeakRef(callback)); | ||
| } | ||
| const _httpServerIntegration = ((options = {}) => { | ||
| const _options = { | ||
| sessions: options.sessions ?? true, | ||
| sessionFlushingDelayMS: options.sessionFlushingDelayMS ?? 60000, | ||
| maxRequestBodySize: options.maxRequestBodySize ?? 'medium', | ||
| sessionFlushingDelayMS: options.sessionFlushingDelayMS ?? 6e4, | ||
| maxRequestBodySize: options.maxRequestBodySize ?? "medium", | ||
| // Server spans are created by `httpServerSpansIntegration` via the | ||
| // `httpServerRequest` client event + `_startSpanCallback`, not by the | ||
| // core subscription helper. Explicitly opt out so the helper does not | ||
| // double-create spans when the client has tracing enabled. | ||
| spans: false, | ||
| // Cast: core uses HttpIncomingMessage; node consumers pass | ||
| // RequestOptions-typed callbacks. | ||
| // The two are structurally compatible for the fields the callback reads | ||
| // (url, method, headers). | ||
| ignoreRequestBody: options.ignoreRequestBody, | ||
| /** | ||
| * Hook called by core's `instrumentServer` to wrap the upstream | ||
| * `emit('request')` call. | ||
| * | ||
| * We use it to extract OTel context from request headers and re-enter | ||
| * the OTel context before the framework sees the request, so subsequent | ||
| * spans (eg from `httpServerSpansIntegration`) attach to the right trace. | ||
| */ | ||
| wrapServerEmitRequest(request, response, normalizedRequest, next) { | ||
| const client = core.getClient(); | ||
| if (!client) return next(); | ||
| if (api.context.active().getValue(HTTP_SERVER_INSTRUMENTED_KEY)) { | ||
| return next(); | ||
| } | ||
| const ctx = api.propagation.extract(api.context.active(), normalizedRequest.headers).setValue(HTTP_SERVER_INSTRUMENTED_KEY, true); | ||
| api.context.with(ctx, () => { | ||
| client.emit("httpServerRequest", request, response, normalizedRequest); | ||
| const callback = request._startSpanCallback?.deref(); | ||
| if (callback) { | ||
| callback(() => { | ||
| next(); | ||
| return true; | ||
| }); | ||
| } else { | ||
| next(); | ||
| } | ||
| }); | ||
| } | ||
| }; | ||
| return { | ||
| name: INTEGRATION_NAME, | ||
| setupOnce() { | ||
| const onHttpServerRequestStart = ((_data) => { | ||
| const data = _data ; | ||
| instrumentServer(data.server, _options); | ||
| }) ; | ||
| diagnosticsChannel.subscribe('http.server.request.start', onHttpServerRequestStart); | ||
| const { [core.HTTP_ON_SERVER_REQUEST]: onHttpServerRequestStart } = core.getHttpServerSubscriptions(_options); | ||
| diagnosticsChannel.subscribe(core.HTTP_ON_SERVER_REQUEST, onHttpServerRequestStart); | ||
| }, | ||
| afterAllSetup(client) { | ||
| if (debugBuild.DEBUG_BUILD && client.getIntegrationByName('Http')) { | ||
| if (debugBuild.DEBUG_BUILD && client.getIntegrationByName("Http")) { | ||
| core.debug.warn( | ||
| 'It seems that you have manually added `httpServerIntegration` while `httpIntegration` is also present. Make sure to remove `httpServerIntegration` when adding `httpIntegration`.', | ||
| "It seems that you have manually added `httpServerIntegration` while `httpIntegration` is also present. Make sure to remove `httpServerIntegration` when adding `httpIntegration`." | ||
| ); | ||
| } | ||
| }, | ||
| } | ||
| }; | ||
| }) ; | ||
| }); | ||
| const httpServerIntegration = _httpServerIntegration; | ||
| /** | ||
| * This integration handles request isolation, trace continuation and other core Sentry functionality around incoming http requests | ||
| * handled via the node `http` module. | ||
| * | ||
| * This version uses OpenTelemetry for context propagation and span management. | ||
| * | ||
| * @see {@link ../../light/integrations/httpServerIntegration.ts} for the lightweight version without OpenTelemetry | ||
| */ | ||
| const httpServerIntegration = _httpServerIntegration | ||
| ; | ||
| /** | ||
| * Instrument a server to capture incoming requests. | ||
| * | ||
| */ | ||
| function instrumentServer( | ||
| server, | ||
| { | ||
| ignoreRequestBody, | ||
| maxRequestBodySize, | ||
| sessions, | ||
| sessionFlushingDelayMS, | ||
| } | ||
| , | ||
| ) { | ||
| // eslint-disable-next-line @typescript-eslint/unbound-method | ||
| const originalEmit = server.emit; | ||
| if (wrappedEmitFns.has(originalEmit)) { | ||
| return; | ||
| } | ||
| const newEmit = new Proxy(originalEmit, { | ||
| apply(target, thisArg, args) { | ||
| // Only traces request events | ||
| if (args[0] !== 'request') { | ||
| return target.apply(thisArg, args); | ||
| } | ||
| const client = core.getClient(); | ||
| // Make sure we do not double execute our wrapper code, for edge cases... | ||
| // Without this check, if we double-wrap emit, for whatever reason, you'd get two http.server spans (one the children of the other) | ||
| if (api.context.active().getValue(HTTP_SERVER_INSTRUMENTED_KEY) || !client) { | ||
| return target.apply(thisArg, args); | ||
| } | ||
| debugBuild.DEBUG_BUILD && core.debug.log(INTEGRATION_NAME, 'Handling incoming request'); | ||
| const isolationScope = core.getIsolationScope().clone(); | ||
| const request = args[1] ; | ||
| const response = args[2] ; | ||
| const normalizedRequest = core.httpRequestToRequestData(request); | ||
| // request.ip is non-standard but some frameworks set this | ||
| const ipAddress = (request ).ip || request.socket?.remoteAddress; | ||
| const url = request.url || '/'; | ||
| if (maxRequestBodySize !== 'none' && !ignoreRequestBody?.(url, request)) { | ||
| captureRequestBody.patchRequestToCaptureBody(request, isolationScope, maxRequestBodySize, INTEGRATION_NAME); | ||
| } | ||
| // Update the isolation scope, isolate this request | ||
| isolationScope.setSDKProcessingMetadata({ normalizedRequest, ipAddress }); | ||
| // attempt to update the scope's `transactionName` based on the request URL | ||
| // Ideally, framework instrumentations coming after the HttpInstrumentation | ||
| // update the transactionName once we get a parameterized route. | ||
| const httpMethod = (request.method || 'GET').toUpperCase(); | ||
| const httpTargetWithoutQueryFragment = core.stripUrlQueryAndFragment(url); | ||
| const bestEffortTransactionName = `${httpMethod} ${httpTargetWithoutQueryFragment}`; | ||
| isolationScope.setTransactionName(bestEffortTransactionName); | ||
| if (sessions && client) { | ||
| recordRequestSession(client, { | ||
| requestIsolationScope: isolationScope, | ||
| response, | ||
| sessionFlushingDelayMS: sessionFlushingDelayMS ?? 60000, | ||
| }); | ||
| } | ||
| return core.withIsolationScope(isolationScope, () => { | ||
| const newPropagationContext = { | ||
| traceId: core.generateTraceId(), | ||
| sampleRand: core._INTERNAL_safeMathRandom(), | ||
| propagationSpanId: core.generateSpanId(), | ||
| }; | ||
| // - Set a fresh propagation context so each request gets a unique traceId. | ||
| // When there are incoming trace headers, propagation.extract() below sets a remote | ||
| // span on the OTel context which takes precedence in getTraceContextForScope(). | ||
| // - We can write directly to the current scope here because it is forked implicitly via | ||
| // `context.with` in `withIsolationScope` (See `SentryContextManager`). | ||
| // - explicitly making a deep copy to avoid mutation of original PC on the other scope | ||
| core.getCurrentScope().setPropagationContext({ ...newPropagationContext }); | ||
| isolationScope.setPropagationContext({ ...newPropagationContext }); | ||
| const ctx = api.propagation | ||
| .extract(api.context.active(), normalizedRequest.headers) | ||
| .setValue(HTTP_SERVER_INSTRUMENTED_KEY, true); | ||
| return api.context.with(ctx, () => { | ||
| // This is used (optionally) by the httpServerSpansIntegration to attach _startSpanCallback to the request object | ||
| client.emit('httpServerRequest', request, response, normalizedRequest); | ||
| const callback = (request )._startSpanCallback?.deref(); | ||
| if (callback) { | ||
| return callback(() => target.apply(thisArg, args)); | ||
| } | ||
| return target.apply(thisArg, args); | ||
| }); | ||
| }); | ||
| }, | ||
| }); | ||
| wrappedEmitFns.add(newEmit); | ||
| server.emit = newEmit; | ||
| } | ||
| /** | ||
| * Starts a session and tracks it in the context of a given isolation scope. | ||
| * When the passed response is finished, the session is put into a task and is | ||
| * aggregated with other sessions that may happen in a certain time window | ||
| * (sessionFlushingDelayMs). | ||
| * | ||
| * The sessions are always aggregated by the client that is on the current scope | ||
| * at the time of ending the response (if there is one). | ||
| */ | ||
| // Exported for unit tests | ||
| function recordRequestSession( | ||
| client, | ||
| { | ||
| requestIsolationScope, | ||
| response, | ||
| sessionFlushingDelayMS, | ||
| } | ||
| , | ||
| ) { | ||
| requestIsolationScope.setSDKProcessingMetadata({ | ||
| requestSession: { status: 'ok' }, | ||
| }); | ||
| response.once('close', () => { | ||
| const requestSession = requestIsolationScope.getScopeData().sdkProcessingMetadata.requestSession; | ||
| if (client && requestSession) { | ||
| debugBuild.DEBUG_BUILD && core.debug.log(`Recorded request session with status: ${requestSession.status}`); | ||
| const roundedDate = new Date(); | ||
| roundedDate.setSeconds(0, 0); | ||
| const dateBucketKey = roundedDate.toISOString(); | ||
| const existingClientAggregate = clientToRequestSessionAggregatesMap.get(client); | ||
| const bucket = existingClientAggregate?.[dateBucketKey] || { exited: 0, crashed: 0, errored: 0 }; | ||
| bucket[({ ok: 'exited', crashed: 'crashed', errored: 'errored' } )[requestSession.status]]++; | ||
| if (existingClientAggregate) { | ||
| existingClientAggregate[dateBucketKey] = bucket; | ||
| } else { | ||
| debugBuild.DEBUG_BUILD && core.debug.log('Opened new request session aggregate.'); | ||
| const newClientAggregate = { [dateBucketKey]: bucket }; | ||
| clientToRequestSessionAggregatesMap.set(client, newClientAggregate); | ||
| const flushPendingClientAggregates = () => { | ||
| clearTimeout(timeout); | ||
| unregisterClientFlushHook(); | ||
| clientToRequestSessionAggregatesMap.delete(client); | ||
| const aggregatePayload = Object.entries(newClientAggregate).map( | ||
| ([timestamp, value]) => ({ | ||
| started: timestamp, | ||
| exited: value.exited, | ||
| errored: value.errored, | ||
| crashed: value.crashed, | ||
| }), | ||
| ); | ||
| client.sendSession({ aggregates: aggregatePayload }); | ||
| }; | ||
| const unregisterClientFlushHook = client.on('flush', () => { | ||
| debugBuild.DEBUG_BUILD && core.debug.log('Sending request session aggregate due to client flush'); | ||
| flushPendingClientAggregates(); | ||
| }); | ||
| const timeout = setTimeout(() => { | ||
| debugBuild.DEBUG_BUILD && core.debug.log('Sending request session aggregate due to flushing schedule'); | ||
| flushPendingClientAggregates(); | ||
| }, sessionFlushingDelayMS).unref(); | ||
| } | ||
| } | ||
| }); | ||
| } | ||
| exports.recordRequestSession = core.recordRequestSession; | ||
| exports.addStartSpanCallback = addStartSpanCallback; | ||
| exports.httpServerIntegration = httpServerIntegration; | ||
| exports.recordRequestSession = recordRequestSession; | ||
| //# sourceMappingURL=httpServerIntegration.js.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"httpServerIntegration.js","sources":["../../../../src/integrations/http/httpServerIntegration.ts"],"sourcesContent":["import type { ChannelListener } from 'node:diagnostics_channel';\nimport { subscribe } from 'node:diagnostics_channel';\nimport type { EventEmitter } from 'node:events';\nimport type { IncomingMessage, RequestOptions, Server, ServerResponse } from 'node:http';\nimport type { Socket } from 'node:net';\nimport { context, createContextKey, propagation } from '@opentelemetry/api';\nimport type { AggregationCounts, Client, HttpIncomingMessage, Integration, IntegrationFn, Scope } from '@sentry/core';\nimport {\n _INTERNAL_safeMathRandom,\n addNonEnumerableProperty,\n debug,\n generateSpanId,\n generateTraceId,\n getClient,\n getCurrentScope,\n getIsolationScope,\n httpRequestToRequestData,\n stripUrlQueryAndFragment,\n withIsolationScope,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport type { NodeClient } from '../../sdk/client';\nimport { patchRequestToCaptureBody } from '../../utils/captureRequestBody';\n\ntype ServerEmit = typeof Server.prototype.emit;\n\n// Inlining this type to not depend on newer TS types\ninterface WeakRefImpl<T> {\n deref(): T | undefined;\n}\n\ntype StartSpanCallback = (next: () => boolean) => boolean;\ntype RequestWithOptionalStartSpanCallback = HttpIncomingMessage & {\n _startSpanCallback?: WeakRefImpl<StartSpanCallback>;\n};\n\nconst HTTP_SERVER_INSTRUMENTED_KEY = createContextKey('sentry_http_server_instrumented');\nconst INTEGRATION_NAME = 'Http.Server';\n\nconst clientToRequestSessionAggregatesMap = new Map<\n Client,\n { [timestampRoundedToSeconds: string]: { exited: number; crashed: number; errored: number } }\n>();\n\n// We keep track of emit functions we wrapped, to avoid double wrapping\n// We do this instead of putting a non-enumerable property on the function, because\n// sometimes the property seems to be migrated to forks of the emit function, which we do not want to happen\n// This was the case in the nestjs-distributed-tracing E2E test\nconst wrappedEmitFns = new WeakSet<ServerEmit>();\n\nexport interface HttpServerIntegrationOptions {\n /**\n * Whether the integration should create [Sessions](https://docs.sentry.io/product/releases/health/#sessions) for incoming requests to track the health and crash-free rate of your releases in Sentry.\n * Read more about Release Health: https://docs.sentry.io/product/releases/health/\n *\n * Defaults to `true`.\n */\n sessions?: boolean;\n\n /**\n * Number of milliseconds until sessions tracked with `trackIncomingRequestsAsSessions` will be flushed as a session aggregate.\n *\n * Defaults to `60000` (60s).\n */\n sessionFlushingDelayMS?: number;\n\n /**\n * Do not capture the request body for incoming HTTP requests to URLs where the given callback returns `true`.\n * This can be useful for long running requests where the body is not needed and we want to avoid capturing it.\n *\n * @param url Contains the entire URL, including query string (if any), protocol, host, etc. of the incoming request.\n * @param request Contains the {@type RequestOptions} object used to make the incoming request.\n */\n ignoreRequestBody?: (url: string, request: RequestOptions) => boolean;\n\n /**\n * Controls the maximum size of incoming HTTP request bodies attached to events.\n *\n * Available options:\n * - 'none': No request bodies will be attached\n * - 'small': Request bodies up to 1,000 bytes will be attached\n * - 'medium': Request bodies up to 10,000 bytes will be attached (default)\n * - 'always': Request bodies will always be attached\n *\n * Note that even with 'always' setting, bodies exceeding 1MB will never be attached\n * for performance and security reasons.\n *\n * @default 'medium'\n */\n maxRequestBodySize?: 'none' | 'small' | 'medium' | 'always';\n}\n\n/**\n * Add a callback to the request object that will be called when the request is started.\n * The callback will receive the next function to continue processing the request.\n */\nexport function addStartSpanCallback(request: RequestWithOptionalStartSpanCallback, callback: StartSpanCallback): void {\n addNonEnumerableProperty(request, '_startSpanCallback', new WeakRef(callback));\n}\n\nconst _httpServerIntegration = ((options: HttpServerIntegrationOptions = {}) => {\n const _options = {\n sessions: options.sessions ?? true,\n sessionFlushingDelayMS: options.sessionFlushingDelayMS ?? 60_000,\n maxRequestBodySize: options.maxRequestBodySize ?? 'medium',\n ignoreRequestBody: options.ignoreRequestBody,\n };\n\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n const onHttpServerRequestStart = ((_data: unknown) => {\n const data = _data as { server: Server };\n\n instrumentServer(data.server, _options);\n }) satisfies ChannelListener;\n\n subscribe('http.server.request.start', onHttpServerRequestStart);\n },\n afterAllSetup(client) {\n if (DEBUG_BUILD && client.getIntegrationByName('Http')) {\n debug.warn(\n 'It seems that you have manually added `httpServerIntegration` while `httpIntegration` is also present. Make sure to remove `httpServerIntegration` when adding `httpIntegration`.',\n );\n }\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * This integration handles request isolation, trace continuation and other core Sentry functionality around incoming http requests\n * handled via the node `http` module.\n *\n * This version uses OpenTelemetry for context propagation and span management.\n *\n * @see {@link ../../light/integrations/httpServerIntegration.ts} for the lightweight version without OpenTelemetry\n */\nexport const httpServerIntegration = _httpServerIntegration as (\n options?: HttpServerIntegrationOptions,\n) => Integration & {\n name: 'HttpServer';\n setupOnce: () => void;\n};\n\n/**\n * Instrument a server to capture incoming requests.\n *\n */\nfunction instrumentServer(\n server: Server,\n {\n ignoreRequestBody,\n maxRequestBodySize,\n sessions,\n sessionFlushingDelayMS,\n }: {\n ignoreRequestBody?: (url: string, request: IncomingMessage) => boolean;\n maxRequestBodySize: 'small' | 'medium' | 'always' | 'none';\n sessions: boolean;\n sessionFlushingDelayMS: number;\n },\n): void {\n // eslint-disable-next-line @typescript-eslint/unbound-method\n const originalEmit: ServerEmit = server.emit;\n\n if (wrappedEmitFns.has(originalEmit)) {\n return;\n }\n\n const newEmit = new Proxy(originalEmit, {\n apply(target, thisArg, args: [event: string, ...args: unknown[]]) {\n // Only traces request events\n if (args[0] !== 'request') {\n return target.apply(thisArg, args);\n }\n\n const client = getClient<NodeClient>();\n\n // Make sure we do not double execute our wrapper code, for edge cases...\n // Without this check, if we double-wrap emit, for whatever reason, you'd get two http.server spans (one the children of the other)\n if (context.active().getValue(HTTP_SERVER_INSTRUMENTED_KEY) || !client) {\n return target.apply(thisArg, args);\n }\n\n DEBUG_BUILD && debug.log(INTEGRATION_NAME, 'Handling incoming request');\n\n const isolationScope = getIsolationScope().clone();\n const request = args[1] as IncomingMessage;\n const response = args[2] as ServerResponse & { socket: Socket };\n\n const normalizedRequest = httpRequestToRequestData(request);\n\n // request.ip is non-standard but some frameworks set this\n const ipAddress = (request as { ip?: string }).ip || request.socket?.remoteAddress;\n\n const url = request.url || '/';\n if (maxRequestBodySize !== 'none' && !ignoreRequestBody?.(url, request)) {\n patchRequestToCaptureBody(request, isolationScope, maxRequestBodySize, INTEGRATION_NAME);\n }\n\n // Update the isolation scope, isolate this request\n isolationScope.setSDKProcessingMetadata({ normalizedRequest, ipAddress });\n\n // attempt to update the scope's `transactionName` based on the request URL\n // Ideally, framework instrumentations coming after the HttpInstrumentation\n // update the transactionName once we get a parameterized route.\n const httpMethod = (request.method || 'GET').toUpperCase();\n const httpTargetWithoutQueryFragment = stripUrlQueryAndFragment(url);\n\n const bestEffortTransactionName = `${httpMethod} ${httpTargetWithoutQueryFragment}`;\n\n isolationScope.setTransactionName(bestEffortTransactionName);\n\n if (sessions && client) {\n recordRequestSession(client, {\n requestIsolationScope: isolationScope,\n response,\n sessionFlushingDelayMS: sessionFlushingDelayMS ?? 60_000,\n });\n }\n\n return withIsolationScope(isolationScope, () => {\n const newPropagationContext = {\n traceId: generateTraceId(),\n sampleRand: _INTERNAL_safeMathRandom(),\n propagationSpanId: generateSpanId(),\n };\n // - Set a fresh propagation context so each request gets a unique traceId.\n // When there are incoming trace headers, propagation.extract() below sets a remote\n // span on the OTel context which takes precedence in getTraceContextForScope().\n // - We can write directly to the current scope here because it is forked implicitly via\n // `context.with` in `withIsolationScope` (See `SentryContextManager`).\n // - explicitly making a deep copy to avoid mutation of original PC on the other scope\n getCurrentScope().setPropagationContext({ ...newPropagationContext });\n isolationScope.setPropagationContext({ ...newPropagationContext });\n\n const ctx = propagation\n .extract(context.active(), normalizedRequest.headers)\n .setValue(HTTP_SERVER_INSTRUMENTED_KEY, true);\n\n return context.with(ctx, () => {\n // This is used (optionally) by the httpServerSpansIntegration to attach _startSpanCallback to the request object\n client.emit('httpServerRequest', request, response, normalizedRequest);\n\n const callback = (request as RequestWithOptionalStartSpanCallback)._startSpanCallback?.deref();\n if (callback) {\n return callback(() => target.apply(thisArg, args));\n }\n return target.apply(thisArg, args);\n });\n });\n },\n });\n\n wrappedEmitFns.add(newEmit);\n server.emit = newEmit;\n}\n\n/**\n * Starts a session and tracks it in the context of a given isolation scope.\n * When the passed response is finished, the session is put into a task and is\n * aggregated with other sessions that may happen in a certain time window\n * (sessionFlushingDelayMs).\n *\n * The sessions are always aggregated by the client that is on the current scope\n * at the time of ending the response (if there is one).\n */\n// Exported for unit tests\nexport function recordRequestSession(\n client: Client,\n {\n requestIsolationScope,\n response,\n sessionFlushingDelayMS,\n }: {\n requestIsolationScope: Scope;\n response: EventEmitter;\n sessionFlushingDelayMS?: number;\n },\n): void {\n requestIsolationScope.setSDKProcessingMetadata({\n requestSession: { status: 'ok' },\n });\n response.once('close', () => {\n const requestSession = requestIsolationScope.getScopeData().sdkProcessingMetadata.requestSession;\n\n if (client && requestSession) {\n DEBUG_BUILD && debug.log(`Recorded request session with status: ${requestSession.status}`);\n\n const roundedDate = new Date();\n roundedDate.setSeconds(0, 0);\n const dateBucketKey = roundedDate.toISOString();\n\n const existingClientAggregate = clientToRequestSessionAggregatesMap.get(client);\n const bucket = existingClientAggregate?.[dateBucketKey] || { exited: 0, crashed: 0, errored: 0 };\n bucket[({ ok: 'exited', crashed: 'crashed', errored: 'errored' } as const)[requestSession.status]]++;\n\n if (existingClientAggregate) {\n existingClientAggregate[dateBucketKey] = bucket;\n } else {\n DEBUG_BUILD && debug.log('Opened new request session aggregate.');\n const newClientAggregate = { [dateBucketKey]: bucket };\n clientToRequestSessionAggregatesMap.set(client, newClientAggregate);\n\n const flushPendingClientAggregates = (): void => {\n clearTimeout(timeout);\n unregisterClientFlushHook();\n clientToRequestSessionAggregatesMap.delete(client);\n\n const aggregatePayload: AggregationCounts[] = Object.entries(newClientAggregate).map(\n ([timestamp, value]) => ({\n started: timestamp,\n exited: value.exited,\n errored: value.errored,\n crashed: value.crashed,\n }),\n );\n client.sendSession({ aggregates: aggregatePayload });\n };\n\n const unregisterClientFlushHook = client.on('flush', () => {\n DEBUG_BUILD && debug.log('Sending request session aggregate due to client flush');\n flushPendingClientAggregates();\n });\n const timeout = setTimeout(() => {\n DEBUG_BUILD && debug.log('Sending request session aggregate due to flushing schedule');\n flushPendingClientAggregates();\n }, sessionFlushingDelayMS).unref();\n }\n }\n });\n}\n"],"names":["createContextKey","addNonEnumerableProperty","subscribe","DEBUG_BUILD","debug","getClient","context","getIsolationScope","httpRequestToRequestData","patchRequestToCaptureBody","stripUrlQueryAndFragment","withIsolationScope","generateTraceId","_INTERNAL_safeMathRandom","generateSpanId","getCurrentScope","propagation"],"mappings":"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"} | ||
| {"version":3,"file":"httpServerIntegration.js","sources":["../../../../src/integrations/http/httpServerIntegration.ts"],"sourcesContent":["import { subscribe } from 'node:diagnostics_channel';\nimport type { RequestOptions } from 'node:http';\nimport { context, createContextKey, propagation } from '@opentelemetry/api';\nimport type { HttpIncomingMessage, HttpServerResponse, Integration, IntegrationFn } from '@sentry/core';\nimport {\n addNonEnumerableProperty,\n debug,\n getClient,\n getHttpServerSubscriptions,\n HTTP_ON_SERVER_REQUEST,\n recordRequestSession,\n} from '@sentry/core';\nimport type { RequestEventData } from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\n\n// Re-export so existing test imports continue to work; the implementation now lives in core.\nexport { recordRequestSession };\n\nconst HTTP_SERVER_INSTRUMENTED_KEY = createContextKey('sentry_http_server_instrumented');\nconst INTEGRATION_NAME = 'Http.Server';\n\n// Inlining this type to not depend on newer TS types\ninterface WeakRefImpl<T> {\n deref(): T | undefined;\n}\n\ntype StartSpanCallback = (next: () => boolean) => boolean;\ntype RequestWithOptionalStartSpanCallback = HttpIncomingMessage & {\n _startSpanCallback?: WeakRefImpl<StartSpanCallback>;\n};\n\nexport interface HttpServerIntegrationOptions {\n /**\n * Whether the integration should create [Sessions](https://docs.sentry.io/product/releases/health/#sessions) for incoming requests to track the health and crash-free rate of your releases in Sentry.\n * Read more about Release Health: https://docs.sentry.io/product/releases/health/\n *\n * Defaults to `true`.\n */\n sessions?: boolean;\n\n /**\n * Number of milliseconds until sessions tracked with `trackIncomingRequestsAsSessions` will be flushed as a session aggregate.\n *\n * Defaults to `60000` (60s).\n */\n sessionFlushingDelayMS?: number;\n\n /**\n * Do not capture the request body for incoming HTTP requests to URLs where the given callback returns `true`.\n * This can be useful for long running requests where the body is not needed and we want to avoid capturing it.\n *\n * @param url Contains the entire URL, including query string (if any), protocol, host, etc. of the incoming request.\n * @param request Contains the {@type RequestOptions} object used to make the incoming request.\n */\n ignoreRequestBody?: (url: string, request: RequestOptions) => boolean;\n\n /**\n * Controls the maximum size of incoming HTTP request bodies attached to events.\n *\n * Available options:\n * - 'none': No request bodies will be attached\n * - 'small': Request bodies up to 1,000 bytes will be attached\n * - 'medium': Request bodies up to 10,000 bytes will be attached (default)\n * - 'always': Request bodies will always be attached\n *\n * Note that even with 'always' setting, bodies exceeding 1MB will never be attached\n * for performance and security reasons.\n *\n * @default 'medium'\n */\n maxRequestBodySize?: 'none' | 'small' | 'medium' | 'always';\n}\n\n/**\n * Add a callback to the request object that will be called when the request is started.\n * The callback will receive the next function to continue processing the request.\n */\nexport function addStartSpanCallback(request: RequestWithOptionalStartSpanCallback, callback: StartSpanCallback): void {\n addNonEnumerableProperty(request, '_startSpanCallback', new WeakRef(callback));\n}\n\nconst _httpServerIntegration = ((options: HttpServerIntegrationOptions = {}) => {\n const _options = {\n sessions: options.sessions ?? true,\n sessionFlushingDelayMS: options.sessionFlushingDelayMS ?? 60_000,\n maxRequestBodySize: options.maxRequestBodySize ?? 'medium',\n // Server spans are created by `httpServerSpansIntegration` via the\n // `httpServerRequest` client event + `_startSpanCallback`, not by the\n // core subscription helper. Explicitly opt out so the helper does not\n // double-create spans when the client has tracing enabled.\n spans: false as const,\n // Cast: core uses HttpIncomingMessage; node consumers pass\n // RequestOptions-typed callbacks.\n // The two are structurally compatible for the fields the callback reads\n // (url, method, headers).\n ignoreRequestBody: options.ignoreRequestBody as\n | ((url: string, request: HttpIncomingMessage) => boolean)\n | undefined,\n\n /**\n * Hook called by core's `instrumentServer` to wrap the upstream\n * `emit('request')` call.\n *\n * We use it to extract OTel context from request headers and re-enter\n * the OTel context before the framework sees the request, so subsequent\n * spans (eg from `httpServerSpansIntegration`) attach to the right trace.\n */\n wrapServerEmitRequest(\n request: HttpIncomingMessage,\n response: HttpServerResponse,\n normalizedRequest: RequestEventData,\n next: () => void,\n ): void {\n const client = getClient();\n if (!client) return next();\n\n // Guard against double-wrapping: if our wrapper somehow runs inside an\n // already-instrumented context, just continue without re-extracting\n // and re-emitting.\n if (context.active().getValue(HTTP_SERVER_INSTRUMENTED_KEY)) {\n return next();\n }\n\n const ctx = propagation\n .extract(context.active(), normalizedRequest.headers)\n .setValue(HTTP_SERVER_INSTRUMENTED_KEY, true);\n\n context.with(ctx, () => {\n // httpServerSpansIntegration listens for this and may attach\n // `_startSpanCallback` to the request to wrap span creation around\n // the emit.\n client.emit('httpServerRequest', request, response, normalizedRequest);\n\n const callback = (request as RequestWithOptionalStartSpanCallback)._startSpanCallback?.deref();\n if (callback) {\n callback(() => {\n next();\n return true;\n });\n } else {\n next();\n }\n });\n },\n };\n\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n const { [HTTP_ON_SERVER_REQUEST]: onHttpServerRequestStart } = getHttpServerSubscriptions(_options);\n subscribe(HTTP_ON_SERVER_REQUEST, onHttpServerRequestStart);\n },\n afterAllSetup(client) {\n if (DEBUG_BUILD && client.getIntegrationByName('Http')) {\n debug.warn(\n 'It seems that you have manually added `httpServerIntegration` while `httpIntegration` is also present. Make sure to remove `httpServerIntegration` when adding `httpIntegration`.',\n );\n }\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * This integration handles request isolation, trace continuation and other core Sentry functionality around incoming http requests\n * handled via the node `http` module.\n *\n * This version uses OpenTelemetry for context propagation and span management.\n *\n * @see {@link ../../light/integrations/httpServerIntegration.ts} for the lightweight version without OpenTelemetry\n */\nexport const httpServerIntegration = _httpServerIntegration as (\n options?: HttpServerIntegrationOptions,\n) => Integration & {\n name: 'HttpServer';\n setupOnce: () => void;\n};\n"],"names":["createContextKey","addNonEnumerableProperty","getClient","context","propagation","HTTP_ON_SERVER_REQUEST","getHttpServerSubscriptions","subscribe","DEBUG_BUILD","debug"],"mappings":";;;;;;;AAkBA,MAAM,4BAAA,GAA+BA,qBAAiB,iCAAiC,CAAA;AACvF,MAAM,gBAAA,GAAmB,aAAA;AA0DlB,SAAS,oBAAA,CAAqB,SAA+C,QAAA,EAAmC;AACrH,EAAAC,6BAAA,CAAyB,OAAA,EAAS,oBAAA,EAAsB,IAAI,OAAA,CAAQ,QAAQ,CAAC,CAAA;AAC/E;AAEA,MAAM,sBAAA,IAA0B,CAAC,OAAA,GAAwC,EAAC,KAAM;AAC9E,EAAA,MAAM,QAAA,GAAW;AAAA,IACf,QAAA,EAAU,QAAQ,QAAA,IAAY,IAAA;AAAA,IAC9B,sBAAA,EAAwB,QAAQ,sBAAA,IAA0B,GAAA;AAAA,IAC1D,kBAAA,EAAoB,QAAQ,kBAAA,IAAsB,QAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAKlD,KAAA,EAAO,KAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAKP,mBAAmB,OAAA,CAAQ,iBAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAY3B,qBAAA,CACE,OAAA,EACA,QAAA,EACA,iBAAA,EACA,IAAA,EACM;AACN,MAAA,MAAM,SAASC,cAAA,EAAU;AACzB,MAAA,IAAI,CAAC,MAAA,EAAQ,OAAO,IAAA,EAAK;AAKzB,MAAA,IAAIC,WAAA,CAAQ,MAAA,EAAO,CAAE,QAAA,CAAS,4BAA4B,CAAA,EAAG;AAC3D,QAAA,OAAO,IAAA,EAAK;AAAA,MACd;AAEA,MAAA,MAAM,GAAA,GAAMC,eAAA,CACT,OAAA,CAAQD,WAAA,CAAQ,MAAA,EAAO,EAAG,iBAAA,CAAkB,OAAO,CAAA,CACnD,QAAA,CAAS,4BAAA,EAA8B,IAAI,CAAA;AAE9C,MAAAA,WAAA,CAAQ,IAAA,CAAK,KAAK,MAAM;AAItB,QAAA,MAAA,CAAO,IAAA,CAAK,mBAAA,EAAqB,OAAA,EAAS,QAAA,EAAU,iBAAiB,CAAA;AAErE,QAAA,MAAM,QAAA,GAAY,OAAA,CAAiD,kBAAA,EAAoB,KAAA,EAAM;AAC7F,QAAA,IAAI,QAAA,EAAU;AACZ,UAAA,QAAA,CAAS,MAAM;AACb,YAAA,IAAA,EAAK;AACL,YAAA,OAAO,IAAA;AAAA,UACT,CAAC,CAAA;AAAA,QACH,CAAA,MAAO;AACL,UAAA,IAAA,EAAK;AAAA,QACP;AAAA,MACF,CAAC,CAAA;AAAA,IACH;AAAA,GACF;AAEA,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,SAAA,GAAY;AACV,MAAA,MAAM,EAAE,CAACE,2BAAsB,GAAG,wBAAA,EAAyB,GAAIC,gCAA2B,QAAQ,CAAA;AAClG,MAAAC,4BAAA,CAAUF,6BAAwB,wBAAwB,CAAA;AAAA,IAC5D,CAAA;AAAA,IACA,cAAc,MAAA,EAAQ;AACpB,MAAA,IAAIG,sBAAA,IAAe,MAAA,CAAO,oBAAA,CAAqB,MAAM,CAAA,EAAG;AACtD,QAAAC,UAAA,CAAM,IAAA;AAAA,UACJ;AAAA,SACF;AAAA,MACF;AAAA,IACF;AAAA,GACF;AACF,CAAA,CAAA;AAUO,MAAM,qBAAA,GAAwB;;;;;;"} |
@@ -11,6 +11,3 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const INTEGRATION_NAME = 'Http.ServerSpans'; | ||
| // Tree-shakable guard to remove all code related to tracing | ||
| const INTEGRATION_NAME = "Http.ServerSpans"; | ||
| const _httpServerSpansIntegration = ((options = {}) => { | ||
@@ -23,51 +20,36 @@ const ignoreStaticAssets = options.ignoreStaticAssets ?? true; | ||
| [301, 303], | ||
| [305, 399], | ||
| [305, 399] | ||
| ]; | ||
| const { onSpanCreated } = options; | ||
| // eslint-disable-next-line deprecation/deprecation | ||
| const { requestHook, responseHook, applyCustomAttributesOnSpan } = options.instrumentation ?? {}; | ||
| return { | ||
| name: INTEGRATION_NAME, | ||
| setup(client) { | ||
| // If no tracing, we can just skip everything here | ||
| if (typeof __SENTRY_TRACING__ !== 'undefined' && !__SENTRY_TRACING__) { | ||
| if (typeof __SENTRY_TRACING__ !== "undefined" && !__SENTRY_TRACING__) { | ||
| return; | ||
| } | ||
| client.on('httpServerRequest', (_request, _response, normalizedRequest) => { | ||
| // Type-casting this here because we do not want to put the node types into core | ||
| const request = _request ; | ||
| const response = _response ; | ||
| client.on("httpServerRequest", (_request, _response, normalizedRequest) => { | ||
| const request = _request; | ||
| const response = _response; | ||
| const startSpan = (next) => { | ||
| if ( | ||
| shouldIgnoreSpansForIncomingRequest(request, { | ||
| ignoreStaticAssets, | ||
| ignoreIncomingRequests, | ||
| }) | ||
| ) { | ||
| debugBuild.DEBUG_BUILD && core.debug.log(INTEGRATION_NAME, 'Skipping span creation for incoming request', request.url); | ||
| if (shouldIgnoreSpansForIncomingRequest(request, { | ||
| ignoreStaticAssets, | ||
| ignoreIncomingRequests | ||
| })) { | ||
| debugBuild.DEBUG_BUILD && core.debug.log(INTEGRATION_NAME, "Skipping span creation for incoming request", request.url); | ||
| return next(); | ||
| } | ||
| const fullUrl = normalizedRequest.url || request.url || '/'; | ||
| const fullUrl = normalizedRequest.url || request.url || "/"; | ||
| const urlObj = core.parseStringToURLObject(fullUrl); | ||
| const headers = request.headers; | ||
| const userAgent = headers['user-agent']; | ||
| const ips = headers['x-forwarded-for']; | ||
| const userAgent = headers["user-agent"]; | ||
| const ips = headers["x-forwarded-for"]; | ||
| const httpVersion = request.httpVersion; | ||
| const host = headers.host ; | ||
| const hostname = host?.replace(/^(.*)(:[0-9]{1,5})/, '$1') || 'localhost'; | ||
| const host = headers.host; | ||
| const hostname = host?.replace(/^(.*)(:[0-9]{1,5})/, "$1") || "localhost"; | ||
| const tracer = client.tracer; | ||
| const scheme = fullUrl.startsWith('https') ? 'https' : 'http'; | ||
| const method = normalizedRequest.method || request.method?.toUpperCase() || 'GET'; | ||
| const scheme = fullUrl.startsWith("https") ? "https" : "http"; | ||
| const method = normalizedRequest.method || request.method?.toUpperCase() || "GET"; | ||
| const httpTargetWithoutQueryFragment = urlObj ? urlObj.pathname : core.stripUrlQueryAndFragment(fullUrl); | ||
| const bestEffortTransactionName = `${method} ${httpTargetWithoutQueryFragment}`; | ||
| // We use the plain tracer.startSpan here so we can pass the span kind | ||
| const span = tracer.startSpan(bestEffortTransactionName, { | ||
@@ -77,25 +59,23 @@ kind: api.SpanKind.SERVER, | ||
| // Sentry specific attributes | ||
| [core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'http.server', | ||
| [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.otel.http', | ||
| 'sentry.http.prefetch': isKnownPrefetchRequest(request) || undefined, | ||
| [core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: "http.server", | ||
| [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.http.otel.http", | ||
| "sentry.http.prefetch": isKnownPrefetchRequest(request) || void 0, | ||
| // Old Semantic Conventions attributes - added for compatibility with what `@opentelemetry/instrumentation-http` output before | ||
| 'http.url': fullUrl, | ||
| 'http.method': normalizedRequest.method, | ||
| 'http.target': urlObj ? `${urlObj.pathname}${urlObj.search}` : httpTargetWithoutQueryFragment, | ||
| 'http.host': host, | ||
| 'net.host.name': hostname, | ||
| 'http.client_ip': typeof ips === 'string' ? ips.split(',')[0] : undefined, | ||
| 'http.user_agent': userAgent, | ||
| 'http.scheme': scheme, | ||
| 'http.flavor': httpVersion, | ||
| 'net.transport': httpVersion?.toUpperCase() === 'QUIC' ? 'ip_udp' : 'ip_tcp', | ||
| "http.url": fullUrl, | ||
| "http.method": normalizedRequest.method, | ||
| "http.target": urlObj ? `${urlObj.pathname}${urlObj.search}` : httpTargetWithoutQueryFragment, | ||
| "http.host": host, | ||
| "net.host.name": hostname, | ||
| "http.client_ip": typeof ips === "string" ? ips.split(",")[0] : void 0, | ||
| "http.user_agent": userAgent, | ||
| "http.scheme": scheme, | ||
| "http.flavor": httpVersion, | ||
| "net.transport": httpVersion?.toUpperCase() === "QUIC" ? "ip_udp" : "ip_tcp", | ||
| ...getRequestContentLengthAttribute(request), | ||
| ...core.httpHeadersToSpanAttributes( | ||
| normalizedRequest.headers || {}, | ||
| client.getOptions().sendDefaultPii ?? false, | ||
| ), | ||
| }, | ||
| client.getOptions().sendDefaultPii ?? false | ||
| ) | ||
| } | ||
| }); | ||
| // TODO v11: Remove the following three hooks, only onSpanCreated should remain | ||
| requestHook?.(span, request); | ||
@@ -105,14 +85,9 @@ responseHook?.(span, response); | ||
| onSpanCreated?.(span, request, response); | ||
| const rpcMetadata = { | ||
| type: core$1.RPCType.HTTP, | ||
| span, | ||
| span | ||
| }; | ||
| return api.context.with(core$1.setRPCMetadata(api.trace.setSpan(api.context.active(), span), rpcMetadata), () => { | ||
| api.context.bind(api.context.active(), request); | ||
| api.context.bind(api.context.active(), response); | ||
| // Ensure we only end the span once | ||
| // E.g. error can be emitted before close is emitted | ||
| let isEnded = false; | ||
@@ -123,5 +98,3 @@ function endSpan(status) { | ||
| } | ||
| isEnded = true; | ||
| const newAttributes = getIncomingRequestAttributesOnResponse(request, response); | ||
@@ -131,11 +104,8 @@ span.setAttributes(newAttributes); | ||
| span.end(); | ||
| // Update the transaction name if the route has changed | ||
| const route = newAttributes['http.route']; | ||
| const route = newAttributes["http.route"]; | ||
| if (route) { | ||
| core.getIsolationScope().setTransactionName(`${request.method?.toUpperCase() || 'GET'} ${route}`); | ||
| core.getIsolationScope().setTransactionName(`${request.method?.toUpperCase() || "GET"} ${route}`); | ||
| } | ||
| } | ||
| response.on('close', () => { | ||
| response.on("close", () => { | ||
| endSpan(core.getSpanStatusFromHttpCode(response.statusCode)); | ||
@@ -145,10 +115,7 @@ }); | ||
| const httpStatus = core.getSpanStatusFromHttpCode(response.statusCode); | ||
| // Ensure we def. have an error status here | ||
| endSpan(httpStatus.code === core.SPAN_STATUS_ERROR ? httpStatus : { code: core.SPAN_STATUS_ERROR }); | ||
| }); | ||
| return next(); | ||
| }); | ||
| }; | ||
| httpServerIntegration.addStartSpanCallback(request, startSpan); | ||
@@ -158,9 +125,8 @@ }); | ||
| processEvent(event) { | ||
| // Drop transaction if it has a status code that should be ignored | ||
| if (event.type === 'transaction') { | ||
| const statusCode = event.contexts?.trace?.data?.['http.response.status_code']; | ||
| if (typeof statusCode === 'number') { | ||
| if (event.type === "transaction") { | ||
| const statusCode = event.contexts?.trace?.data?.["http.response.status_code"]; | ||
| if (typeof statusCode === "number") { | ||
| const shouldDrop = shouldFilterStatusCode(statusCode, ignoreStatusCodes); | ||
| if (shouldDrop) { | ||
| debugBuild.DEBUG_BUILD && core.debug.log('Dropping transaction due to status code', statusCode); | ||
| debugBuild.DEBUG_BUILD && core.debug.log("Dropping transaction due to status code", statusCode); | ||
| return null; | ||
@@ -170,3 +136,2 @@ } | ||
| } | ||
| return event; | ||
@@ -178,86 +143,49 @@ }, | ||
| } | ||
| if (client.getIntegrationByName('Http')) { | ||
| if (client.getIntegrationByName("Http")) { | ||
| core.debug.warn( | ||
| 'It seems that you have manually added `httpServerSpansIntegration` while `httpIntegration` is also present. Make sure to remove `httpIntegration` when adding `httpServerSpansIntegration`.', | ||
| "It seems that you have manually added `httpServerSpansIntegration` while `httpIntegration` is also present. Make sure to remove `httpIntegration` when adding `httpServerSpansIntegration`." | ||
| ); | ||
| } | ||
| if (!client.getIntegrationByName('Http.Server')) { | ||
| if (!client.getIntegrationByName("Http.Server")) { | ||
| core.debug.error( | ||
| 'It seems that you have manually added `httpServerSpansIntegration` without adding `httpServerIntegration`. This is a requiement for spans to be created - please add the `httpServerIntegration` integration.', | ||
| "It seems that you have manually added `httpServerSpansIntegration` without adding `httpServerIntegration`. This is a requiement for spans to be created - please add the `httpServerIntegration` integration." | ||
| ); | ||
| } | ||
| }, | ||
| } | ||
| }; | ||
| }) ; | ||
| /** | ||
| * This integration emits spans for incoming requests handled via the node `http` module. | ||
| * It requires the `httpServerIntegration` to be present. | ||
| */ | ||
| const httpServerSpansIntegration = _httpServerSpansIntegration | ||
| ; | ||
| }); | ||
| const httpServerSpansIntegration = _httpServerSpansIntegration; | ||
| function isKnownPrefetchRequest(req) { | ||
| // Currently only handles Next.js prefetch requests but may check other frameworks in the future. | ||
| return req.headers['next-router-prefetch'] === '1'; | ||
| return req.headers["next-router-prefetch"] === "1"; | ||
| } | ||
| /** | ||
| * Check if a request is for a common static asset that should be ignored by default. | ||
| * | ||
| * Only exported for tests. | ||
| */ | ||
| function isStaticAssetRequest(urlPath) { | ||
| const path = core.stripUrlQueryAndFragment(urlPath); | ||
| // Common static file extensions | ||
| if (path.match(/\.(ico|png|jpg|jpeg|gif|svg|css|js|woff|woff2|ttf|eot|webp|avif)$/)) { | ||
| return true; | ||
| } | ||
| // Common metadata files | ||
| if (path.match(/^\/(robots\.txt|sitemap\.xml|manifest\.json|browserconfig\.xml)$/)) { | ||
| return true; | ||
| } | ||
| return false; | ||
| } | ||
| function shouldIgnoreSpansForIncomingRequest( | ||
| request, | ||
| { | ||
| ignoreStaticAssets, | ||
| ignoreIncomingRequests, | ||
| } | ||
| , | ||
| ) { | ||
| function shouldIgnoreSpansForIncomingRequest(request, { | ||
| ignoreStaticAssets, | ||
| ignoreIncomingRequests | ||
| }) { | ||
| if (core$1.isTracingSuppressed(api.context.active())) { | ||
| return true; | ||
| } | ||
| // request.url is the only property that holds any information about the url | ||
| // it only consists of the URL path and query string (if any) | ||
| const urlPath = request.url; | ||
| const method = request.method?.toUpperCase(); | ||
| // We do not capture OPTIONS/HEAD requests as spans | ||
| if (method === 'OPTIONS' || method === 'HEAD' || !urlPath) { | ||
| if (method === "OPTIONS" || method === "HEAD" || !urlPath) { | ||
| return true; | ||
| } | ||
| // Default static asset filtering | ||
| if (ignoreStaticAssets && method === 'GET' && isStaticAssetRequest(urlPath)) { | ||
| if (ignoreStaticAssets && method === "GET" && isStaticAssetRequest(urlPath)) { | ||
| return true; | ||
| } | ||
| if (ignoreIncomingRequests?.(urlPath, request)) { | ||
| return true; | ||
| } | ||
| return false; | ||
| } | ||
| function getRequestContentLengthAttribute(request) { | ||
@@ -268,39 +196,26 @@ const length = getContentLength(request.headers); | ||
| } | ||
| if (isCompressed(request.headers)) { | ||
| return { | ||
| ['http.request_content_length']: length, | ||
| ["http.request_content_length"]: length | ||
| }; | ||
| } else { | ||
| return { | ||
| ['http.request_content_length_uncompressed']: length, | ||
| ["http.request_content_length_uncompressed"]: length | ||
| }; | ||
| } | ||
| } | ||
| function getContentLength(headers) { | ||
| const contentLengthHeader = headers['content-length']; | ||
| if (contentLengthHeader === undefined) return null; | ||
| const contentLengthHeader = headers["content-length"]; | ||
| if (contentLengthHeader === void 0) return null; | ||
| const contentLength = parseInt(contentLengthHeader, 10); | ||
| if (isNaN(contentLength)) return null; | ||
| return contentLength; | ||
| } | ||
| function isCompressed(headers) { | ||
| const encoding = headers['content-encoding']; | ||
| return !!encoding && encoding !== 'identity'; | ||
| const encoding = headers["content-encoding"]; | ||
| return !!encoding && encoding !== "identity"; | ||
| } | ||
| function getIncomingRequestAttributesOnResponse( | ||
| request, | ||
| response, | ||
| ) { | ||
| // take socket from the request, | ||
| // since it may be detached from the response object in keep-alive mode | ||
| function getIncomingRequestAttributesOnResponse(request, response) { | ||
| const { socket } = request; | ||
| const { statusCode, statusMessage } = response; | ||
| const newAttributes = { | ||
@@ -310,37 +225,25 @@ [semanticConventions.ATTR_HTTP_RESPONSE_STATUS_CODE]: statusCode, | ||
| [semanticConventions.SEMATTRS_HTTP_STATUS_CODE]: statusCode, | ||
| 'http.status_text': statusMessage?.toUpperCase(), | ||
| "http.status_text": statusMessage?.toUpperCase() | ||
| }; | ||
| const rpcMetadata = core$1.getRPCMetadata(api.context.active()); | ||
| if (socket) { | ||
| const { localAddress, localPort, remoteAddress, remotePort } = socket; | ||
| // eslint-disable-next-line deprecation/deprecation | ||
| newAttributes[semanticConventions.SEMATTRS_NET_HOST_IP] = localAddress; | ||
| // eslint-disable-next-line deprecation/deprecation | ||
| newAttributes[semanticConventions.SEMATTRS_NET_HOST_PORT] = localPort; | ||
| // eslint-disable-next-line deprecation/deprecation | ||
| newAttributes[semanticConventions.SEMATTRS_NET_PEER_IP] = remoteAddress; | ||
| newAttributes['net.peer.port'] = remotePort; | ||
| newAttributes["net.peer.port"] = remotePort; | ||
| } | ||
| // eslint-disable-next-line deprecation/deprecation | ||
| newAttributes[semanticConventions.SEMATTRS_HTTP_STATUS_CODE] = statusCode; | ||
| newAttributes['http.status_text'] = (statusMessage || '').toUpperCase(); | ||
| if (rpcMetadata?.type === core$1.RPCType.HTTP && rpcMetadata.route !== undefined) { | ||
| newAttributes["http.status_text"] = (statusMessage || "").toUpperCase(); | ||
| if (rpcMetadata?.type === core$1.RPCType.HTTP && rpcMetadata.route !== void 0) { | ||
| const routeName = rpcMetadata.route; | ||
| newAttributes[semanticConventions.ATTR_HTTP_ROUTE] = routeName; | ||
| } | ||
| return newAttributes; | ||
| } | ||
| /** | ||
| * If the given status code should be filtered for the given list of status codes/ranges. | ||
| */ | ||
| function shouldFilterStatusCode(statusCode, dropForStatusCodes) { | ||
| return dropForStatusCodes.some(code => { | ||
| if (typeof code === 'number') { | ||
| return dropForStatusCodes.some((code) => { | ||
| if (typeof code === "number") { | ||
| return code === statusCode; | ||
| } | ||
| const [min, max] = code; | ||
@@ -347,0 +250,0 @@ return statusCode >= min && statusCode <= max; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"httpServerSpansIntegration.js","sources":["../../../../src/integrations/http/httpServerSpansIntegration.ts"],"sourcesContent":["import { errorMonitor } from 'node:events';\nimport type { IncomingHttpHeaders } from 'node:http';\nimport { context, SpanKind, trace } from '@opentelemetry/api';\nimport type { RPCMetadata } from '@opentelemetry/core';\nimport { getRPCMetadata, isTracingSuppressed, RPCType, setRPCMetadata } from '@opentelemetry/core';\nimport {\n ATTR_HTTP_RESPONSE_STATUS_CODE,\n ATTR_HTTP_ROUTE,\n SEMATTRS_HTTP_STATUS_CODE,\n SEMATTRS_NET_HOST_IP,\n SEMATTRS_NET_HOST_PORT,\n SEMATTRS_NET_PEER_IP,\n} from '@opentelemetry/semantic-conventions';\nimport type {\n Event,\n HttpClientRequest,\n HttpIncomingMessage,\n HttpServerResponse,\n Integration,\n IntegrationFn,\n Span,\n SpanAttributes,\n SpanStatus,\n} from '@sentry/core';\nimport {\n debug,\n getIsolationScope,\n getSpanStatusFromHttpCode,\n httpHeadersToSpanAttributes,\n parseStringToURLObject,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_STATUS_ERROR,\n stripUrlQueryAndFragment,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport type { NodeClient } from '../../sdk/client';\nimport { addStartSpanCallback } from './httpServerIntegration';\n\nconst INTEGRATION_NAME = 'Http.ServerSpans';\n\n// Tree-shakable guard to remove all code related to tracing\ndeclare const __SENTRY_TRACING__: boolean;\n\nexport interface HttpServerSpansIntegrationOptions {\n /**\n * Do not capture spans for incoming HTTP requests to URLs where the given callback returns `true`.\n * Spans will be non recording if tracing is disabled.\n *\n * The `urlPath` param consists of the URL path and query string (if any) of the incoming request.\n * For example: `'/users/details?id=123'`\n *\n * The `request` param contains the original {@type IncomingMessage} object of the incoming request.\n * You can use it to filter on additional properties like method, headers, etc.\n */\n ignoreIncomingRequests?: (urlPath: string, request: HttpIncomingMessage) => boolean;\n\n /**\n * Whether to automatically ignore common static asset requests like favicon.ico, robots.txt, etc.\n * This helps reduce noise in your transactions.\n *\n * @default `true`\n */\n ignoreStaticAssets?: boolean;\n\n /**\n * Do not capture spans for incoming HTTP requests with the given status codes.\n * By default, spans with some 3xx and 4xx status codes are ignored (see @default).\n * Expects an array of status codes or a range of status codes, e.g. [[300,399], 404] would ignore 3xx and 404 status codes.\n *\n * @default `[[401, 404], [301, 303], [305, 399]]`\n */\n ignoreStatusCodes?: (number | [number, number])[];\n\n /**\n * @deprecated This is deprecated in favor of `incomingRequestSpanHook`.\n */\n instrumentation?: {\n requestHook?: (span: Span, req: HttpClientRequest | HttpIncomingMessage) => void;\n responseHook?: (span: Span, response: HttpIncomingMessage | HttpServerResponse) => void;\n applyCustomAttributesOnSpan?: (\n span: Span,\n request: HttpClientRequest | HttpIncomingMessage,\n response: HttpIncomingMessage | HttpServerResponse,\n ) => void;\n };\n\n /**\n * A hook that can be used to mutate the span for incoming requests.\n * This is triggered after the span is created, but before it is recorded.\n */\n onSpanCreated?: (span: Span, request: HttpIncomingMessage, response: HttpServerResponse) => void;\n}\n\nconst _httpServerSpansIntegration = ((options: HttpServerSpansIntegrationOptions = {}) => {\n const ignoreStaticAssets = options.ignoreStaticAssets ?? true;\n const ignoreIncomingRequests = options.ignoreIncomingRequests;\n const ignoreStatusCodes = options.ignoreStatusCodes ?? [\n [401, 404],\n // 300 and 304 are possibly valid status codes we do not want to filter\n [301, 303],\n [305, 399],\n ];\n\n const { onSpanCreated } = options;\n // eslint-disable-next-line deprecation/deprecation\n const { requestHook, responseHook, applyCustomAttributesOnSpan } = options.instrumentation ?? {};\n\n return {\n name: INTEGRATION_NAME,\n setup(client: NodeClient) {\n // If no tracing, we can just skip everything here\n if (typeof __SENTRY_TRACING__ !== 'undefined' && !__SENTRY_TRACING__) {\n return;\n }\n\n client.on('httpServerRequest', (_request, _response, normalizedRequest) => {\n // Type-casting this here because we do not want to put the node types into core\n const request = _request as HttpIncomingMessage;\n const response = _response as HttpServerResponse;\n\n const startSpan = (next: () => boolean): boolean => {\n if (\n shouldIgnoreSpansForIncomingRequest(request, {\n ignoreStaticAssets,\n ignoreIncomingRequests,\n })\n ) {\n DEBUG_BUILD && debug.log(INTEGRATION_NAME, 'Skipping span creation for incoming request', request.url);\n return next();\n }\n\n const fullUrl = normalizedRequest.url || request.url || '/';\n const urlObj = parseStringToURLObject(fullUrl);\n\n const headers = request.headers;\n const userAgent = headers['user-agent'];\n const ips = headers['x-forwarded-for'];\n const httpVersion = request.httpVersion;\n const host = headers.host as string | undefined;\n const hostname = host?.replace(/^(.*)(:[0-9]{1,5})/, '$1') || 'localhost';\n\n const tracer = client.tracer;\n const scheme = fullUrl.startsWith('https') ? 'https' : 'http';\n\n const method = normalizedRequest.method || request.method?.toUpperCase() || 'GET';\n const httpTargetWithoutQueryFragment = urlObj ? urlObj.pathname : stripUrlQueryAndFragment(fullUrl);\n const bestEffortTransactionName = `${method} ${httpTargetWithoutQueryFragment}`;\n\n // We use the plain tracer.startSpan here so we can pass the span kind\n const span = tracer.startSpan(bestEffortTransactionName, {\n kind: SpanKind.SERVER,\n attributes: {\n // Sentry specific attributes\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'http.server',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.otel.http',\n 'sentry.http.prefetch': isKnownPrefetchRequest(request) || undefined,\n // Old Semantic Conventions attributes - added for compatibility with what `@opentelemetry/instrumentation-http` output before\n 'http.url': fullUrl,\n 'http.method': normalizedRequest.method,\n 'http.target': urlObj ? `${urlObj.pathname}${urlObj.search}` : httpTargetWithoutQueryFragment,\n 'http.host': host,\n 'net.host.name': hostname,\n 'http.client_ip': typeof ips === 'string' ? ips.split(',')[0] : undefined,\n 'http.user_agent': userAgent,\n 'http.scheme': scheme,\n 'http.flavor': httpVersion,\n 'net.transport': httpVersion?.toUpperCase() === 'QUIC' ? 'ip_udp' : 'ip_tcp',\n ...getRequestContentLengthAttribute(request),\n ...httpHeadersToSpanAttributes(\n normalizedRequest.headers || {},\n client.getOptions().sendDefaultPii ?? false,\n ),\n },\n });\n\n // TODO v11: Remove the following three hooks, only onSpanCreated should remain\n requestHook?.(span, request);\n responseHook?.(span, response);\n applyCustomAttributesOnSpan?.(span, request, response);\n onSpanCreated?.(span, request, response);\n\n const rpcMetadata: RPCMetadata = {\n type: RPCType.HTTP,\n span,\n };\n\n return context.with(setRPCMetadata(trace.setSpan(context.active(), span), rpcMetadata), () => {\n context.bind(context.active(), request);\n context.bind(context.active(), response);\n\n // Ensure we only end the span once\n // E.g. error can be emitted before close is emitted\n let isEnded = false;\n function endSpan(status: SpanStatus): void {\n if (isEnded) {\n return;\n }\n\n isEnded = true;\n\n const newAttributes = getIncomingRequestAttributesOnResponse(request, response);\n span.setAttributes(newAttributes);\n span.setStatus(status);\n span.end();\n\n // Update the transaction name if the route has changed\n const route = newAttributes['http.route'];\n if (route) {\n getIsolationScope().setTransactionName(`${request.method?.toUpperCase() || 'GET'} ${route}`);\n }\n }\n\n response.on('close', () => {\n endSpan(getSpanStatusFromHttpCode(response.statusCode));\n });\n response.on(errorMonitor, () => {\n const httpStatus = getSpanStatusFromHttpCode(response.statusCode);\n // Ensure we def. have an error status here\n endSpan(httpStatus.code === SPAN_STATUS_ERROR ? httpStatus : { code: SPAN_STATUS_ERROR });\n });\n\n return next();\n });\n };\n\n addStartSpanCallback(request, startSpan);\n });\n },\n processEvent(event) {\n // Drop transaction if it has a status code that should be ignored\n if (event.type === 'transaction') {\n const statusCode = event.contexts?.trace?.data?.['http.response.status_code'];\n if (typeof statusCode === 'number') {\n const shouldDrop = shouldFilterStatusCode(statusCode, ignoreStatusCodes);\n if (shouldDrop) {\n DEBUG_BUILD && debug.log('Dropping transaction due to status code', statusCode);\n return null;\n }\n }\n }\n\n return event;\n },\n afterAllSetup(client) {\n if (!DEBUG_BUILD) {\n return;\n }\n\n if (client.getIntegrationByName('Http')) {\n debug.warn(\n 'It seems that you have manually added `httpServerSpansIntegration` while `httpIntegration` is also present. Make sure to remove `httpIntegration` when adding `httpServerSpansIntegration`.',\n );\n }\n\n if (!client.getIntegrationByName('Http.Server')) {\n debug.error(\n 'It seems that you have manually added `httpServerSpansIntegration` without adding `httpServerIntegration`. This is a requiement for spans to be created - please add the `httpServerIntegration` integration.',\n );\n }\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * This integration emits spans for incoming requests handled via the node `http` module.\n * It requires the `httpServerIntegration` to be present.\n */\nexport const httpServerSpansIntegration = _httpServerSpansIntegration as (\n options?: HttpServerSpansIntegrationOptions,\n) => Integration & {\n name: 'HttpServerSpans';\n setup: (client: NodeClient) => void;\n processEvent: (event: Event) => Event | null;\n};\n\nfunction isKnownPrefetchRequest(req: HttpIncomingMessage): boolean {\n // Currently only handles Next.js prefetch requests but may check other frameworks in the future.\n return req.headers['next-router-prefetch'] === '1';\n}\n\n/**\n * Check if a request is for a common static asset that should be ignored by default.\n *\n * Only exported for tests.\n */\nexport function isStaticAssetRequest(urlPath: string): boolean {\n const path = stripUrlQueryAndFragment(urlPath);\n // Common static file extensions\n if (path.match(/\\.(ico|png|jpg|jpeg|gif|svg|css|js|woff|woff2|ttf|eot|webp|avif)$/)) {\n return true;\n }\n\n // Common metadata files\n if (path.match(/^\\/(robots\\.txt|sitemap\\.xml|manifest\\.json|browserconfig\\.xml)$/)) {\n return true;\n }\n\n return false;\n}\n\nfunction shouldIgnoreSpansForIncomingRequest(\n request: HttpIncomingMessage,\n {\n ignoreStaticAssets,\n ignoreIncomingRequests,\n }: {\n ignoreStaticAssets?: boolean;\n ignoreIncomingRequests?: (urlPath: string, request: HttpIncomingMessage) => boolean;\n },\n): boolean {\n if (isTracingSuppressed(context.active())) {\n return true;\n }\n\n // request.url is the only property that holds any information about the url\n // it only consists of the URL path and query string (if any)\n const urlPath = request.url;\n\n const method = request.method?.toUpperCase();\n // We do not capture OPTIONS/HEAD requests as spans\n if (method === 'OPTIONS' || method === 'HEAD' || !urlPath) {\n return true;\n }\n\n // Default static asset filtering\n if (ignoreStaticAssets && method === 'GET' && isStaticAssetRequest(urlPath)) {\n return true;\n }\n\n if (ignoreIncomingRequests?.(urlPath, request)) {\n return true;\n }\n\n return false;\n}\n\nfunction getRequestContentLengthAttribute(request: HttpIncomingMessage): SpanAttributes {\n const length = getContentLength(request.headers);\n if (length == null) {\n return {};\n }\n\n if (isCompressed(request.headers)) {\n return {\n ['http.request_content_length']: length,\n };\n } else {\n return {\n ['http.request_content_length_uncompressed']: length,\n };\n }\n}\n\nfunction getContentLength(headers: IncomingHttpHeaders): number | null {\n const contentLengthHeader = headers['content-length'];\n if (contentLengthHeader === undefined) return null;\n\n const contentLength = parseInt(contentLengthHeader, 10);\n if (isNaN(contentLength)) return null;\n\n return contentLength;\n}\n\nfunction isCompressed(headers: IncomingHttpHeaders): boolean {\n const encoding = headers['content-encoding'];\n\n return !!encoding && encoding !== 'identity';\n}\n\nfunction getIncomingRequestAttributesOnResponse(\n request: HttpIncomingMessage,\n response: HttpServerResponse,\n): SpanAttributes {\n // take socket from the request,\n // since it may be detached from the response object in keep-alive mode\n const { socket } = request;\n const { statusCode, statusMessage } = response;\n\n const newAttributes: SpanAttributes = {\n [ATTR_HTTP_RESPONSE_STATUS_CODE]: statusCode,\n // eslint-disable-next-line deprecation/deprecation\n [SEMATTRS_HTTP_STATUS_CODE]: statusCode,\n 'http.status_text': statusMessage?.toUpperCase(),\n };\n\n const rpcMetadata = getRPCMetadata(context.active());\n if (socket) {\n const { localAddress, localPort, remoteAddress, remotePort } = socket;\n // eslint-disable-next-line deprecation/deprecation\n newAttributes[SEMATTRS_NET_HOST_IP] = localAddress;\n // eslint-disable-next-line deprecation/deprecation\n newAttributes[SEMATTRS_NET_HOST_PORT] = localPort;\n // eslint-disable-next-line deprecation/deprecation\n newAttributes[SEMATTRS_NET_PEER_IP] = remoteAddress;\n newAttributes['net.peer.port'] = remotePort;\n }\n // eslint-disable-next-line deprecation/deprecation\n newAttributes[SEMATTRS_HTTP_STATUS_CODE] = statusCode;\n newAttributes['http.status_text'] = (statusMessage || '').toUpperCase();\n\n if (rpcMetadata?.type === RPCType.HTTP && rpcMetadata.route !== undefined) {\n const routeName = rpcMetadata.route;\n newAttributes[ATTR_HTTP_ROUTE] = routeName;\n }\n\n return newAttributes;\n}\n\n/**\n * If the given status code should be filtered for the given list of status codes/ranges.\n */\nfunction shouldFilterStatusCode(statusCode: number, dropForStatusCodes: (number | [number, number])[]): boolean {\n return dropForStatusCodes.some(code => {\n if (typeof code === 'number') {\n return code === statusCode;\n }\n\n const [min, max] = code;\n return statusCode >= min && statusCode <= max;\n });\n}\n"],"names":["DEBUG_BUILD","debug","parseStringToURLObject","stripUrlQueryAndFragment","SpanKind","SEMANTIC_ATTRIBUTE_SENTRY_OP","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","httpHeadersToSpanAttributes","RPCType","context","setRPCMetadata","trace","getIsolationScope","getSpanStatusFromHttpCode","errorMonitor","SPAN_STATUS_ERROR","addStartSpanCallback","isTracingSuppressed","ATTR_HTTP_RESPONSE_STATUS_CODE","SEMATTRS_HTTP_STATUS_CODE","getRPCMetadata","SEMATTRS_NET_HOST_IP","SEMATTRS_NET_HOST_PORT","SEMATTRS_NET_PEER_IP","ATTR_HTTP_ROUTE"],"mappings":"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"} | ||
| {"version":3,"file":"httpServerSpansIntegration.js","sources":["../../../../src/integrations/http/httpServerSpansIntegration.ts"],"sourcesContent":["import { errorMonitor } from 'node:events';\nimport type { IncomingHttpHeaders } from 'node:http';\nimport { context, SpanKind, trace } from '@opentelemetry/api';\nimport type { RPCMetadata } from '@opentelemetry/core';\nimport { getRPCMetadata, isTracingSuppressed, RPCType, setRPCMetadata } from '@opentelemetry/core';\nimport {\n ATTR_HTTP_RESPONSE_STATUS_CODE,\n ATTR_HTTP_ROUTE,\n SEMATTRS_HTTP_STATUS_CODE,\n SEMATTRS_NET_HOST_IP,\n SEMATTRS_NET_HOST_PORT,\n SEMATTRS_NET_PEER_IP,\n} from '@opentelemetry/semantic-conventions';\nimport type {\n Event,\n HttpClientRequest,\n HttpIncomingMessage,\n HttpServerResponse,\n Integration,\n IntegrationFn,\n Span,\n SpanAttributes,\n SpanStatus,\n} from '@sentry/core';\nimport {\n debug,\n getIsolationScope,\n getSpanStatusFromHttpCode,\n httpHeadersToSpanAttributes,\n parseStringToURLObject,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_STATUS_ERROR,\n stripUrlQueryAndFragment,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport type { NodeClient } from '../../sdk/client';\nimport { addStartSpanCallback } from './httpServerIntegration';\n\nconst INTEGRATION_NAME = 'Http.ServerSpans';\n\n// Tree-shakable guard to remove all code related to tracing\ndeclare const __SENTRY_TRACING__: boolean;\n\nexport interface HttpServerSpansIntegrationOptions {\n /**\n * Do not capture spans for incoming HTTP requests to URLs where the given callback returns `true`.\n * Spans will be non recording if tracing is disabled.\n *\n * The `urlPath` param consists of the URL path and query string (if any) of the incoming request.\n * For example: `'/users/details?id=123'`\n *\n * The `request` param contains the original {@type IncomingMessage} object of the incoming request.\n * You can use it to filter on additional properties like method, headers, etc.\n */\n ignoreIncomingRequests?: (urlPath: string, request: HttpIncomingMessage) => boolean;\n\n /**\n * Whether to automatically ignore common static asset requests like favicon.ico, robots.txt, etc.\n * This helps reduce noise in your transactions.\n *\n * @default `true`\n */\n ignoreStaticAssets?: boolean;\n\n /**\n * Do not capture spans for incoming HTTP requests with the given status codes.\n * By default, spans with some 3xx and 4xx status codes are ignored (see @default).\n * Expects an array of status codes or a range of status codes, e.g. [[300,399], 404] would ignore 3xx and 404 status codes.\n *\n * @default `[[401, 404], [301, 303], [305, 399]]`\n */\n ignoreStatusCodes?: (number | [number, number])[];\n\n /**\n * @deprecated This is deprecated in favor of `incomingRequestSpanHook`.\n */\n instrumentation?: {\n requestHook?: (span: Span, req: HttpClientRequest | HttpIncomingMessage) => void;\n responseHook?: (span: Span, response: HttpIncomingMessage | HttpServerResponse) => void;\n applyCustomAttributesOnSpan?: (\n span: Span,\n request: HttpClientRequest | HttpIncomingMessage,\n response: HttpIncomingMessage | HttpServerResponse,\n ) => void;\n };\n\n /**\n * A hook that can be used to mutate the span for incoming requests.\n * This is triggered after the span is created, but before it is recorded.\n */\n onSpanCreated?: (span: Span, request: HttpIncomingMessage, response: HttpServerResponse) => void;\n}\n\nconst _httpServerSpansIntegration = ((options: HttpServerSpansIntegrationOptions = {}) => {\n const ignoreStaticAssets = options.ignoreStaticAssets ?? true;\n const ignoreIncomingRequests = options.ignoreIncomingRequests;\n const ignoreStatusCodes = options.ignoreStatusCodes ?? [\n [401, 404],\n // 300 and 304 are possibly valid status codes we do not want to filter\n [301, 303],\n [305, 399],\n ];\n\n const { onSpanCreated } = options;\n // eslint-disable-next-line deprecation/deprecation\n const { requestHook, responseHook, applyCustomAttributesOnSpan } = options.instrumentation ?? {};\n\n return {\n name: INTEGRATION_NAME,\n setup(client: NodeClient) {\n // If no tracing, we can just skip everything here\n if (typeof __SENTRY_TRACING__ !== 'undefined' && !__SENTRY_TRACING__) {\n return;\n }\n\n client.on('httpServerRequest', (_request, _response, normalizedRequest) => {\n // Type-casting this here because we do not want to put the node types into core\n const request = _request as HttpIncomingMessage;\n const response = _response as HttpServerResponse;\n\n const startSpan = (next: () => boolean): boolean => {\n if (\n shouldIgnoreSpansForIncomingRequest(request, {\n ignoreStaticAssets,\n ignoreIncomingRequests,\n })\n ) {\n DEBUG_BUILD && debug.log(INTEGRATION_NAME, 'Skipping span creation for incoming request', request.url);\n return next();\n }\n\n const fullUrl = normalizedRequest.url || request.url || '/';\n const urlObj = parseStringToURLObject(fullUrl);\n\n const headers = request.headers;\n const userAgent = headers['user-agent'];\n const ips = headers['x-forwarded-for'];\n const httpVersion = request.httpVersion;\n const host = headers.host as string | undefined;\n const hostname = host?.replace(/^(.*)(:[0-9]{1,5})/, '$1') || 'localhost';\n\n const tracer = client.tracer;\n const scheme = fullUrl.startsWith('https') ? 'https' : 'http';\n\n const method = normalizedRequest.method || request.method?.toUpperCase() || 'GET';\n const httpTargetWithoutQueryFragment = urlObj ? urlObj.pathname : stripUrlQueryAndFragment(fullUrl);\n const bestEffortTransactionName = `${method} ${httpTargetWithoutQueryFragment}`;\n\n // We use the plain tracer.startSpan here so we can pass the span kind\n const span = tracer.startSpan(bestEffortTransactionName, {\n kind: SpanKind.SERVER,\n attributes: {\n // Sentry specific attributes\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'http.server',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.otel.http',\n 'sentry.http.prefetch': isKnownPrefetchRequest(request) || undefined,\n // Old Semantic Conventions attributes - added for compatibility with what `@opentelemetry/instrumentation-http` output before\n 'http.url': fullUrl,\n 'http.method': normalizedRequest.method,\n 'http.target': urlObj ? `${urlObj.pathname}${urlObj.search}` : httpTargetWithoutQueryFragment,\n 'http.host': host,\n 'net.host.name': hostname,\n 'http.client_ip': typeof ips === 'string' ? ips.split(',')[0] : undefined,\n 'http.user_agent': userAgent,\n 'http.scheme': scheme,\n 'http.flavor': httpVersion,\n 'net.transport': httpVersion?.toUpperCase() === 'QUIC' ? 'ip_udp' : 'ip_tcp',\n ...getRequestContentLengthAttribute(request),\n ...httpHeadersToSpanAttributes(\n normalizedRequest.headers || {},\n client.getOptions().sendDefaultPii ?? false,\n ),\n },\n });\n\n // TODO v11: Remove the following three hooks, only onSpanCreated should remain\n requestHook?.(span, request);\n responseHook?.(span, response);\n applyCustomAttributesOnSpan?.(span, request, response);\n onSpanCreated?.(span, request, response);\n\n const rpcMetadata: RPCMetadata = {\n type: RPCType.HTTP,\n span,\n };\n\n return context.with(setRPCMetadata(trace.setSpan(context.active(), span), rpcMetadata), () => {\n context.bind(context.active(), request);\n context.bind(context.active(), response);\n\n // Ensure we only end the span once\n // E.g. error can be emitted before close is emitted\n let isEnded = false;\n function endSpan(status: SpanStatus): void {\n if (isEnded) {\n return;\n }\n\n isEnded = true;\n\n const newAttributes = getIncomingRequestAttributesOnResponse(request, response);\n span.setAttributes(newAttributes);\n span.setStatus(status);\n span.end();\n\n // Update the transaction name if the route has changed\n const route = newAttributes['http.route'];\n if (route) {\n getIsolationScope().setTransactionName(`${request.method?.toUpperCase() || 'GET'} ${route}`);\n }\n }\n\n response.on('close', () => {\n endSpan(getSpanStatusFromHttpCode(response.statusCode));\n });\n response.on(errorMonitor, () => {\n const httpStatus = getSpanStatusFromHttpCode(response.statusCode);\n // Ensure we def. have an error status here\n endSpan(httpStatus.code === SPAN_STATUS_ERROR ? httpStatus : { code: SPAN_STATUS_ERROR });\n });\n\n return next();\n });\n };\n\n addStartSpanCallback(request, startSpan);\n });\n },\n processEvent(event) {\n // Drop transaction if it has a status code that should be ignored\n if (event.type === 'transaction') {\n const statusCode = event.contexts?.trace?.data?.['http.response.status_code'];\n if (typeof statusCode === 'number') {\n const shouldDrop = shouldFilterStatusCode(statusCode, ignoreStatusCodes);\n if (shouldDrop) {\n DEBUG_BUILD && debug.log('Dropping transaction due to status code', statusCode);\n return null;\n }\n }\n }\n\n return event;\n },\n afterAllSetup(client) {\n if (!DEBUG_BUILD) {\n return;\n }\n\n if (client.getIntegrationByName('Http')) {\n debug.warn(\n 'It seems that you have manually added `httpServerSpansIntegration` while `httpIntegration` is also present. Make sure to remove `httpIntegration` when adding `httpServerSpansIntegration`.',\n );\n }\n\n if (!client.getIntegrationByName('Http.Server')) {\n debug.error(\n 'It seems that you have manually added `httpServerSpansIntegration` without adding `httpServerIntegration`. This is a requiement for spans to be created - please add the `httpServerIntegration` integration.',\n );\n }\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * This integration emits spans for incoming requests handled via the node `http` module.\n * It requires the `httpServerIntegration` to be present.\n */\nexport const httpServerSpansIntegration = _httpServerSpansIntegration as (\n options?: HttpServerSpansIntegrationOptions,\n) => Integration & {\n name: 'HttpServerSpans';\n setup: (client: NodeClient) => void;\n processEvent: (event: Event) => Event | null;\n};\n\nfunction isKnownPrefetchRequest(req: HttpIncomingMessage): boolean {\n // Currently only handles Next.js prefetch requests but may check other frameworks in the future.\n return req.headers['next-router-prefetch'] === '1';\n}\n\n/**\n * Check if a request is for a common static asset that should be ignored by default.\n *\n * Only exported for tests.\n */\nexport function isStaticAssetRequest(urlPath: string): boolean {\n const path = stripUrlQueryAndFragment(urlPath);\n // Common static file extensions\n if (path.match(/\\.(ico|png|jpg|jpeg|gif|svg|css|js|woff|woff2|ttf|eot|webp|avif)$/)) {\n return true;\n }\n\n // Common metadata files\n if (path.match(/^\\/(robots\\.txt|sitemap\\.xml|manifest\\.json|browserconfig\\.xml)$/)) {\n return true;\n }\n\n return false;\n}\n\nfunction shouldIgnoreSpansForIncomingRequest(\n request: HttpIncomingMessage,\n {\n ignoreStaticAssets,\n ignoreIncomingRequests,\n }: {\n ignoreStaticAssets?: boolean;\n ignoreIncomingRequests?: (urlPath: string, request: HttpIncomingMessage) => boolean;\n },\n): boolean {\n if (isTracingSuppressed(context.active())) {\n return true;\n }\n\n // request.url is the only property that holds any information about the url\n // it only consists of the URL path and query string (if any)\n const urlPath = request.url;\n\n const method = request.method?.toUpperCase();\n // We do not capture OPTIONS/HEAD requests as spans\n if (method === 'OPTIONS' || method === 'HEAD' || !urlPath) {\n return true;\n }\n\n // Default static asset filtering\n if (ignoreStaticAssets && method === 'GET' && isStaticAssetRequest(urlPath)) {\n return true;\n }\n\n if (ignoreIncomingRequests?.(urlPath, request)) {\n return true;\n }\n\n return false;\n}\n\nfunction getRequestContentLengthAttribute(request: HttpIncomingMessage): SpanAttributes {\n const length = getContentLength(request.headers);\n if (length == null) {\n return {};\n }\n\n if (isCompressed(request.headers)) {\n return {\n ['http.request_content_length']: length,\n };\n } else {\n return {\n ['http.request_content_length_uncompressed']: length,\n };\n }\n}\n\nfunction getContentLength(headers: IncomingHttpHeaders): number | null {\n const contentLengthHeader = headers['content-length'];\n if (contentLengthHeader === undefined) return null;\n\n const contentLength = parseInt(contentLengthHeader, 10);\n if (isNaN(contentLength)) return null;\n\n return contentLength;\n}\n\nfunction isCompressed(headers: IncomingHttpHeaders): boolean {\n const encoding = headers['content-encoding'];\n\n return !!encoding && encoding !== 'identity';\n}\n\nfunction getIncomingRequestAttributesOnResponse(\n request: HttpIncomingMessage,\n response: HttpServerResponse,\n): SpanAttributes {\n // take socket from the request,\n // since it may be detached from the response object in keep-alive mode\n const { socket } = request;\n const { statusCode, statusMessage } = response;\n\n const newAttributes: SpanAttributes = {\n [ATTR_HTTP_RESPONSE_STATUS_CODE]: statusCode,\n // eslint-disable-next-line deprecation/deprecation\n [SEMATTRS_HTTP_STATUS_CODE]: statusCode,\n 'http.status_text': statusMessage?.toUpperCase(),\n };\n\n const rpcMetadata = getRPCMetadata(context.active());\n if (socket) {\n const { localAddress, localPort, remoteAddress, remotePort } = socket;\n // eslint-disable-next-line deprecation/deprecation\n newAttributes[SEMATTRS_NET_HOST_IP] = localAddress;\n // eslint-disable-next-line deprecation/deprecation\n newAttributes[SEMATTRS_NET_HOST_PORT] = localPort;\n // eslint-disable-next-line deprecation/deprecation\n newAttributes[SEMATTRS_NET_PEER_IP] = remoteAddress;\n newAttributes['net.peer.port'] = remotePort;\n }\n // eslint-disable-next-line deprecation/deprecation\n newAttributes[SEMATTRS_HTTP_STATUS_CODE] = statusCode;\n newAttributes['http.status_text'] = (statusMessage || '').toUpperCase();\n\n if (rpcMetadata?.type === RPCType.HTTP && rpcMetadata.route !== undefined) {\n const routeName = rpcMetadata.route;\n newAttributes[ATTR_HTTP_ROUTE] = routeName;\n }\n\n return newAttributes;\n}\n\n/**\n * If the given status code should be filtered for the given list of status codes/ranges.\n */\nfunction shouldFilterStatusCode(statusCode: number, dropForStatusCodes: (number | [number, number])[]): boolean {\n return dropForStatusCodes.some(code => {\n if (typeof code === 'number') {\n return code === statusCode;\n }\n\n const [min, max] = code;\n return statusCode >= min && statusCode <= max;\n });\n}\n"],"names":["DEBUG_BUILD","debug","parseStringToURLObject","stripUrlQueryAndFragment","SpanKind","SEMANTIC_ATTRIBUTE_SENTRY_OP","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","httpHeadersToSpanAttributes","RPCType","context","setRPCMetadata","trace","getIsolationScope","getSpanStatusFromHttpCode","errorMonitor","SPAN_STATUS_ERROR","addStartSpanCallback","isTracingSuppressed","ATTR_HTTP_RESPONSE_STATUS_CODE","SEMATTRS_HTTP_STATUS_CODE","getRPCMetadata","SEMATTRS_NET_HOST_IP","SEMATTRS_NET_HOST_PORT","SEMATTRS_NET_PEER_IP","ATTR_HTTP_ROUTE"],"mappings":"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"} |
@@ -9,15 +9,9 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const INTEGRATION_NAME = 'Http'; | ||
| const INTEGRATION_NAME = "Http"; | ||
| const instrumentSentryHttp = instrument.generateInstrumentOnce( | ||
| `${INTEGRATION_NAME}.sentry`, | ||
| options => { | ||
| (options) => { | ||
| return new SentryHttpInstrumentation.SentryHttpInstrumentation(options); | ||
| }, | ||
| } | ||
| ); | ||
| /** | ||
| * The http integration instruments Node's internal http and https modules. | ||
| * It creates breadcrumbs for outgoing HTTP requests which will be attached to the currently active span. | ||
| */ | ||
| const httpIntegration = core.defineIntegration((options = {}) => { | ||
@@ -28,26 +22,19 @@ const serverOptions = { | ||
| ignoreRequestBody: options.ignoreIncomingRequestBody, | ||
| maxRequestBodySize: options.maxIncomingRequestBodySize, | ||
| maxRequestBodySize: options.maxIncomingRequestBodySize | ||
| }; | ||
| const serverSpansOptions = { | ||
| ignoreIncomingRequests: options.ignoreIncomingRequests, | ||
| ignoreStaticAssets: options.ignoreStaticAssets, | ||
| ignoreStatusCodes: options.dropSpansForIncomingRequestStatusCodes, | ||
| ignoreStatusCodes: options.dropSpansForIncomingRequestStatusCodes | ||
| }; | ||
| const httpInstrumentationOptions = { | ||
| breadcrumbs: options.breadcrumbs, | ||
| propagateTraceInOutgoingRequests: options.tracePropagation ?? true, | ||
| ignoreOutgoingRequests: options.ignoreOutgoingRequests, | ||
| ignoreOutgoingRequests: options.ignoreOutgoingRequests | ||
| }; | ||
| const server = httpServerIntegration.httpServerIntegration(serverOptions); | ||
| const serverSpans = httpServerSpansIntegration.httpServerSpansIntegration(serverSpansOptions); | ||
| // In node-core, for now we disable incoming requests spans by default | ||
| // we may revisit this in a future release | ||
| const spans = options.spans ?? false; | ||
| const disableIncomingRequestSpans = options.disableIncomingRequestSpans ?? false; | ||
| const enabledServerSpans = spans && !disableIncomingRequestSpans; | ||
| return { | ||
@@ -62,11 +49,7 @@ name: INTEGRATION_NAME, | ||
| server.setupOnce(); | ||
| instrumentSentryHttp(httpInstrumentationOptions); | ||
| }, | ||
| processEvent(event) { | ||
| // Note: We always run this, even if spans are disabled | ||
| // The reason being that e.g. the remix integration disables span creation here but still wants to use the ignore status codes option | ||
| return serverSpans.processEvent(event); | ||
| }, | ||
| } | ||
| }; | ||
@@ -73,0 +56,0 @@ }); |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.js","sources":["../../../../src/integrations/http/index.ts"],"sourcesContent":["import type { RequestOptions } from 'node:http';\nimport type { HttpIncomingMessage } from '@sentry/core';\nimport { defineIntegration } from '@sentry/core';\nimport { generateInstrumentOnce } from '../../otel/instrument';\nimport type { NodeClient } from '../../sdk/client';\nimport type { HttpServerIntegrationOptions } from './httpServerIntegration';\nimport { httpServerIntegration } from './httpServerIntegration';\nimport type { HttpServerSpansIntegrationOptions } from './httpServerSpansIntegration';\nimport { httpServerSpansIntegration } from './httpServerSpansIntegration';\nimport type { SentryHttpInstrumentationOptions } from './SentryHttpInstrumentation';\nimport { SentryHttpInstrumentation } from './SentryHttpInstrumentation';\n\nconst INTEGRATION_NAME = 'Http';\n\ninterface HttpOptions {\n /**\n * Whether breadcrumbs should be recorded for outgoing requests.\n * Defaults to true\n */\n breadcrumbs?: boolean;\n\n /**\n * Whether to create spans for requests or not.\n * As of now, creates spans for incoming requests, but not outgoing requests.\n *\n * @default `true`\n */\n spans?: boolean;\n\n /**\n * Whether the integration should create [Sessions](https://docs.sentry.io/product/releases/health/#sessions) for incoming requests to track the health and crash-free rate of your releases in Sentry.\n * Read more about Release Health: https://docs.sentry.io/product/releases/health/\n *\n * Defaults to `true`.\n */\n trackIncomingRequestsAsSessions?: boolean;\n\n /**\n * Number of milliseconds until sessions tracked with `trackIncomingRequestsAsSessions` will be flushed as a session aggregate.\n *\n * Defaults to `60000` (60s).\n */\n sessionFlushingDelayMS?: number;\n\n /**\n * Whether to inject trace propagation headers (sentry-trace, baggage, traceparent) into outgoing HTTP requests.\n *\n * When set to `false`, Sentry will not inject any trace propagation headers, but will still create breadcrumbs\n * (if `breadcrumbs` is enabled).\n *\n * @default `true`\n */\n tracePropagation?: boolean;\n\n /**\n * Do not capture spans or breadcrumbs for outgoing HTTP requests to URLs where the given callback returns `true`.\n * This controls both span & breadcrumb creation - spans will be non recording if tracing is disabled.\n *\n * The `url` param contains the entire URL, including query string (if any), protocol, host, etc. of the outgoing request.\n * For example: `'https://someService.com/users/details?id=123'`\n *\n * The `request` param contains the original {@type RequestOptions} object used to make the outgoing request.\n * You can use it to filter on additional properties like method, headers, etc.\n */\n ignoreOutgoingRequests?: (url: string, request: RequestOptions) => boolean;\n\n /**\n * Do not capture spans for incoming HTTP requests to URLs where the given callback returns `true`.\n * Spans will be non recording if tracing is disabled.\n *\n * The `urlPath` param consists of the URL path and query string (if any) of the incoming request.\n * For example: `'/users/details?id=123'`\n *\n * The `request` param contains the original {@type IncomingMessage} object of the incoming request.\n * You can use it to filter on additional properties like method, headers, etc.\n */\n ignoreIncomingRequests?: (urlPath: string, request: HttpIncomingMessage) => boolean;\n\n /**\n * Do not capture spans for incoming HTTP requests with the given status codes.\n * By default, spans with some 3xx and 4xx status codes are ignored (see @default).\n * Expects an array of status codes or a range of status codes, e.g. [[300,399], 404] would ignore 3xx and 404 status codes.\n *\n * @default `[[401, 404], [301, 303], [305, 399]]`\n */\n dropSpansForIncomingRequestStatusCodes?: (number | [number, number])[];\n\n /**\n * Do not capture the request body for incoming HTTP requests to URLs where the given callback returns `true`.\n * This can be useful for long running requests where the body is not needed and we want to avoid capturing it.\n *\n * @param url Contains the entire URL, including query string (if any), protocol, host, etc. of the incoming request.\n * @param request Contains the {@type RequestOptions} object used to make the incoming request.\n */\n ignoreIncomingRequestBody?: (url: string, request: RequestOptions) => boolean;\n\n /**\n * Whether to automatically ignore common static asset requests like favicon.ico, robots.txt, etc.\n * This helps reduce noise in your transactions.\n *\n * @default `true`\n */\n ignoreStaticAssets?: boolean;\n\n /**\n * Controls the maximum size of incoming HTTP request bodies attached to events.\n *\n * Available options:\n * - 'none': No request bodies will be attached\n * - 'small': Request bodies up to 1,000 bytes will be attached\n * - 'medium': Request bodies up to 10,000 bytes will be attached (default)\n * - 'always': Request bodies will always be attached\n *\n * Note that even with 'always' setting, bodies exceeding 1MB will never be attached\n * for performance and security reasons.\n *\n * @default 'medium'\n */\n maxIncomingRequestBodySize?: 'none' | 'small' | 'medium' | 'always';\n\n /**\n * If true, do not generate spans for incoming requests at all.\n * This is used by Remix to avoid generating spans for incoming requests, as it generates its own spans.\n */\n disableIncomingRequestSpans?: boolean;\n}\n\nexport const instrumentSentryHttp = generateInstrumentOnce<SentryHttpInstrumentationOptions>(\n `${INTEGRATION_NAME}.sentry`,\n options => {\n return new SentryHttpInstrumentation(options);\n },\n);\n\n/**\n * The http integration instruments Node's internal http and https modules.\n * It creates breadcrumbs for outgoing HTTP requests which will be attached to the currently active span.\n */\nexport const httpIntegration = defineIntegration((options: HttpOptions = {}) => {\n const serverOptions: HttpServerIntegrationOptions = {\n sessions: options.trackIncomingRequestsAsSessions,\n sessionFlushingDelayMS: options.sessionFlushingDelayMS,\n ignoreRequestBody: options.ignoreIncomingRequestBody,\n maxRequestBodySize: options.maxIncomingRequestBodySize,\n };\n\n const serverSpansOptions: HttpServerSpansIntegrationOptions = {\n ignoreIncomingRequests: options.ignoreIncomingRequests,\n ignoreStaticAssets: options.ignoreStaticAssets,\n ignoreStatusCodes: options.dropSpansForIncomingRequestStatusCodes,\n };\n\n const httpInstrumentationOptions: SentryHttpInstrumentationOptions = {\n breadcrumbs: options.breadcrumbs,\n propagateTraceInOutgoingRequests: options.tracePropagation ?? true,\n ignoreOutgoingRequests: options.ignoreOutgoingRequests,\n };\n\n const server = httpServerIntegration(serverOptions);\n const serverSpans = httpServerSpansIntegration(serverSpansOptions);\n\n // In node-core, for now we disable incoming requests spans by default\n // we may revisit this in a future release\n const spans = options.spans ?? false;\n const disableIncomingRequestSpans = options.disableIncomingRequestSpans ?? false;\n const enabledServerSpans = spans && !disableIncomingRequestSpans;\n\n return {\n name: INTEGRATION_NAME,\n setup(client: NodeClient) {\n if (enabledServerSpans) {\n serverSpans.setup(client);\n }\n },\n setupOnce() {\n server.setupOnce();\n\n instrumentSentryHttp(httpInstrumentationOptions);\n },\n\n processEvent(event) {\n // Note: We always run this, even if spans are disabled\n // The reason being that e.g. the remix integration disables span creation here but still wants to use the ignore status codes option\n return serverSpans.processEvent(event);\n },\n };\n});\n"],"names":["generateInstrumentOnce","SentryHttpInstrumentation","defineIntegration","httpServerIntegration","httpServerSpansIntegration"],"mappings":";;;;;;;;AAYA,MAAM,gBAAA,GAAmB,MAAM;;AAmHxB,MAAM,oBAAA,GAAuBA,iCAAsB;AAC1D,EAAE,CAAC,EAAA,gBAAA,CAAA,OAAA,CAAA;AACA,EAAA,OAAA,IAAA;AACA,IAAA,OAAA,IAAAC,mDAAA,CAAA,OAAA,CAAA;AACA,EAAA,CAAA;AACA;;AAEA;AACA;AACA;AACA;AACA,MAAA,eAAA,GAAAC,sBAAA,CAAA,CAAA,OAAA,GAAA,EAAA,KAAA;AACA,EAAA,MAAA,aAAA,GAAA;AACA,IAAA,QAAA,EAAA,OAAA,CAAA,+BAAA;AACA,IAAA,sBAAA,EAAA,OAAA,CAAA,sBAAA;AACA,IAAA,iBAAA,EAAA,OAAA,CAAA,yBAAA;AACA,IAAA,kBAAA,EAAA,OAAA,CAAA,0BAAA;AACA,GAAA;;AAEA,EAAA,MAAA,kBAAA,GAAA;AACA,IAAA,sBAAA,EAAA,OAAA,CAAA,sBAAA;AACA,IAAA,kBAAA,EAAA,OAAA,CAAA,kBAAA;AACA,IAAA,iBAAA,EAAA,OAAA,CAAA,sCAAA;AACA,GAAA;;AAEA,EAAA,MAAA,0BAAA,GAAA;AACA,IAAA,WAAA,EAAA,OAAA,CAAA,WAAA;AACA,IAAA,gCAAA,EAAA,OAAA,CAAA,gBAAA,IAAA,IAAA;AACA,IAAA,sBAAA,EAAA,OAAA,CAAA,sBAAA;AACA,GAAA;;AAEA,EAAA,MAAA,MAAA,GAAAC,2CAAA,CAAA,aAAA,CAAA;AACA,EAAA,MAAA,WAAA,GAAAC,qDAAA,CAAA,kBAAA,CAAA;;AAEA;AACA;AACA,EAAA,MAAA,KAAA,GAAA,OAAA,CAAA,KAAA,IAAA,KAAA;AACA,EAAA,MAAA,2BAAA,GAAA,OAAA,CAAA,2BAAA,IAAA,KAAA;AACA,EAAA,MAAA,kBAAA,GAAA,KAAA,IAAA,CAAA,2BAAA;;AAEA,EAAA,OAAA;AACA,IAAA,IAAA,EAAA,gBAAA;AACA,IAAA,KAAA,CAAA,MAAA,EAAA;AACA,MAAA,IAAA,kBAAA,EAAA;AACA,QAAA,WAAA,CAAA,KAAA,CAAA,MAAA,CAAA;AACA,MAAA;AACA,IAAA,CAAA;AACA,IAAA,SAAA,GAAA;AACA,MAAA,MAAA,CAAA,SAAA,EAAA;;AAEA,MAAA,oBAAA,CAAA,0BAAA,CAAA;AACA,IAAA,CAAA;;AAEA,IAAA,YAAA,CAAA,KAAA,EAAA;AACA;AACA;AACA,MAAA,OAAA,WAAA,CAAA,YAAA,CAAA,KAAA,CAAA;AACA,IAAA,CAAA;AACA,GAAA;AACA,CAAA;;;;;"} | ||
| {"version":3,"file":"index.js","sources":["../../../../src/integrations/http/index.ts"],"sourcesContent":["import type { RequestOptions } from 'node:http';\nimport type { HttpIncomingMessage } from '@sentry/core';\nimport { defineIntegration } from '@sentry/core';\nimport { generateInstrumentOnce } from '../../otel/instrument';\nimport type { NodeClient } from '../../sdk/client';\nimport type { HttpServerIntegrationOptions } from './httpServerIntegration';\nimport { httpServerIntegration } from './httpServerIntegration';\nimport type { HttpServerSpansIntegrationOptions } from './httpServerSpansIntegration';\nimport { httpServerSpansIntegration } from './httpServerSpansIntegration';\nimport type { SentryHttpInstrumentationOptions } from './SentryHttpInstrumentation';\nimport { SentryHttpInstrumentation } from './SentryHttpInstrumentation';\n\nconst INTEGRATION_NAME = 'Http';\n\ninterface HttpOptions {\n /**\n * Whether breadcrumbs should be recorded for outgoing requests.\n * Defaults to true\n */\n breadcrumbs?: boolean;\n\n /**\n * Whether to create spans for requests or not.\n * As of now, creates spans for incoming requests, but not outgoing requests.\n *\n * @default `true`\n */\n spans?: boolean;\n\n /**\n * Whether the integration should create [Sessions](https://docs.sentry.io/product/releases/health/#sessions) for incoming requests to track the health and crash-free rate of your releases in Sentry.\n * Read more about Release Health: https://docs.sentry.io/product/releases/health/\n *\n * Defaults to `true`.\n */\n trackIncomingRequestsAsSessions?: boolean;\n\n /**\n * Number of milliseconds until sessions tracked with `trackIncomingRequestsAsSessions` will be flushed as a session aggregate.\n *\n * Defaults to `60000` (60s).\n */\n sessionFlushingDelayMS?: number;\n\n /**\n * Whether to inject trace propagation headers (sentry-trace, baggage, traceparent) into outgoing HTTP requests.\n *\n * When set to `false`, Sentry will not inject any trace propagation headers, but will still create breadcrumbs\n * (if `breadcrumbs` is enabled).\n *\n * @default `true`\n */\n tracePropagation?: boolean;\n\n /**\n * Do not capture spans or breadcrumbs for outgoing HTTP requests to URLs where the given callback returns `true`.\n * This controls both span & breadcrumb creation - spans will be non recording if tracing is disabled.\n *\n * The `url` param contains the entire URL, including query string (if any), protocol, host, etc. of the outgoing request.\n * For example: `'https://someService.com/users/details?id=123'`\n *\n * The `request` param contains the original {@type RequestOptions} object used to make the outgoing request.\n * You can use it to filter on additional properties like method, headers, etc.\n */\n ignoreOutgoingRequests?: (url: string, request: RequestOptions) => boolean;\n\n /**\n * Do not capture spans for incoming HTTP requests to URLs where the given callback returns `true`.\n * Spans will be non recording if tracing is disabled.\n *\n * The `urlPath` param consists of the URL path and query string (if any) of the incoming request.\n * For example: `'/users/details?id=123'`\n *\n * The `request` param contains the original {@type IncomingMessage} object of the incoming request.\n * You can use it to filter on additional properties like method, headers, etc.\n */\n ignoreIncomingRequests?: (urlPath: string, request: HttpIncomingMessage) => boolean;\n\n /**\n * Do not capture spans for incoming HTTP requests with the given status codes.\n * By default, spans with some 3xx and 4xx status codes are ignored (see @default).\n * Expects an array of status codes or a range of status codes, e.g. [[300,399], 404] would ignore 3xx and 404 status codes.\n *\n * @default `[[401, 404], [301, 303], [305, 399]]`\n */\n dropSpansForIncomingRequestStatusCodes?: (number | [number, number])[];\n\n /**\n * Do not capture the request body for incoming HTTP requests to URLs where the given callback returns `true`.\n * This can be useful for long running requests where the body is not needed and we want to avoid capturing it.\n *\n * @param url Contains the entire URL, including query string (if any), protocol, host, etc. of the incoming request.\n * @param request Contains the {@type RequestOptions} object used to make the incoming request.\n */\n ignoreIncomingRequestBody?: (url: string, request: RequestOptions) => boolean;\n\n /**\n * Whether to automatically ignore common static asset requests like favicon.ico, robots.txt, etc.\n * This helps reduce noise in your transactions.\n *\n * @default `true`\n */\n ignoreStaticAssets?: boolean;\n\n /**\n * Controls the maximum size of incoming HTTP request bodies attached to events.\n *\n * Available options:\n * - 'none': No request bodies will be attached\n * - 'small': Request bodies up to 1,000 bytes will be attached\n * - 'medium': Request bodies up to 10,000 bytes will be attached (default)\n * - 'always': Request bodies will always be attached\n *\n * Note that even with 'always' setting, bodies exceeding 1MB will never be attached\n * for performance and security reasons.\n *\n * @default 'medium'\n */\n maxIncomingRequestBodySize?: 'none' | 'small' | 'medium' | 'always';\n\n /**\n * If true, do not generate spans for incoming requests at all.\n * This is used by Remix to avoid generating spans for incoming requests, as it generates its own spans.\n */\n disableIncomingRequestSpans?: boolean;\n}\n\nexport const instrumentSentryHttp = generateInstrumentOnce<SentryHttpInstrumentationOptions>(\n `${INTEGRATION_NAME}.sentry`,\n options => {\n return new SentryHttpInstrumentation(options);\n },\n);\n\n/**\n * The http integration instruments Node's internal http and https modules.\n * It creates breadcrumbs for outgoing HTTP requests which will be attached to the currently active span.\n */\nexport const httpIntegration = defineIntegration((options: HttpOptions = {}) => {\n const serverOptions: HttpServerIntegrationOptions = {\n sessions: options.trackIncomingRequestsAsSessions,\n sessionFlushingDelayMS: options.sessionFlushingDelayMS,\n ignoreRequestBody: options.ignoreIncomingRequestBody,\n maxRequestBodySize: options.maxIncomingRequestBodySize,\n };\n\n const serverSpansOptions: HttpServerSpansIntegrationOptions = {\n ignoreIncomingRequests: options.ignoreIncomingRequests,\n ignoreStaticAssets: options.ignoreStaticAssets,\n ignoreStatusCodes: options.dropSpansForIncomingRequestStatusCodes,\n };\n\n const httpInstrumentationOptions: SentryHttpInstrumentationOptions = {\n breadcrumbs: options.breadcrumbs,\n propagateTraceInOutgoingRequests: options.tracePropagation ?? true,\n ignoreOutgoingRequests: options.ignoreOutgoingRequests,\n };\n\n const server = httpServerIntegration(serverOptions);\n const serverSpans = httpServerSpansIntegration(serverSpansOptions);\n\n // In node-core, for now we disable incoming requests spans by default\n // we may revisit this in a future release\n const spans = options.spans ?? false;\n const disableIncomingRequestSpans = options.disableIncomingRequestSpans ?? false;\n const enabledServerSpans = spans && !disableIncomingRequestSpans;\n\n return {\n name: INTEGRATION_NAME,\n setup(client: NodeClient) {\n if (enabledServerSpans) {\n serverSpans.setup(client);\n }\n },\n setupOnce() {\n server.setupOnce();\n\n instrumentSentryHttp(httpInstrumentationOptions);\n },\n\n processEvent(event) {\n // Note: We always run this, even if spans are disabled\n // The reason being that e.g. the remix integration disables span creation here but still wants to use the ignore status codes option\n return serverSpans.processEvent(event);\n },\n };\n});\n"],"names":["generateInstrumentOnce","SentryHttpInstrumentation","defineIntegration","httpServerIntegration","httpServerSpansIntegration"],"mappings":";;;;;;;;AAYA,MAAM,gBAAA,GAAmB,MAAA;AAmHlB,MAAM,oBAAA,GAAuBA,iCAAA;AAAA,EAClC,GAAG,gBAAgB,CAAA,OAAA,CAAA;AAAA,EACnB,CAAA,OAAA,KAAW;AACT,IAAA,OAAO,IAAIC,oDAA0B,OAAO,CAAA;AAAA,EAC9C;AACF;AAMO,MAAM,eAAA,GAAkBC,sBAAA,CAAkB,CAAC,OAAA,GAAuB,EAAC,KAAM;AAC9E,EAAA,MAAM,aAAA,GAA8C;AAAA,IAClD,UAAU,OAAA,CAAQ,+BAAA;AAAA,IAClB,wBAAwB,OAAA,CAAQ,sBAAA;AAAA,IAChC,mBAAmB,OAAA,CAAQ,yBAAA;AAAA,IAC3B,oBAAoB,OAAA,CAAQ;AAAA,GAC9B;AAEA,EAAA,MAAM,kBAAA,GAAwD;AAAA,IAC5D,wBAAwB,OAAA,CAAQ,sBAAA;AAAA,IAChC,oBAAoB,OAAA,CAAQ,kBAAA;AAAA,IAC5B,mBAAmB,OAAA,CAAQ;AAAA,GAC7B;AAEA,EAAA,MAAM,0BAAA,GAA+D;AAAA,IACnE,aAAa,OAAA,CAAQ,WAAA;AAAA,IACrB,gCAAA,EAAkC,QAAQ,gBAAA,IAAoB,IAAA;AAAA,IAC9D,wBAAwB,OAAA,CAAQ;AAAA,GAClC;AAEA,EAAA,MAAM,MAAA,GAASC,4CAAsB,aAAa,CAAA;AAClD,EAAA,MAAM,WAAA,GAAcC,sDAA2B,kBAAkB,CAAA;AAIjE,EAAA,MAAM,KAAA,GAAQ,QAAQ,KAAA,IAAS,KAAA;AAC/B,EAAA,MAAM,2BAAA,GAA8B,QAAQ,2BAAA,IAA+B,KAAA;AAC3E,EAAA,MAAM,kBAAA,GAAqB,SAAS,CAAC,2BAAA;AAErC,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,MAAM,MAAA,EAAoB;AACxB,MAAA,IAAI,kBAAA,EAAoB;AACtB,QAAA,WAAA,CAAY,MAAM,MAAM,CAAA;AAAA,MAC1B;AAAA,IACF,CAAA;AAAA,IACA,SAAA,GAAY;AACV,MAAA,MAAA,CAAO,SAAA,EAAU;AAEjB,MAAA,oBAAA,CAAqB,0BAA0B,CAAA;AAAA,IACjD,CAAA;AAAA,IAEA,aAAa,KAAA,EAAO;AAGlB,MAAA,OAAO,WAAA,CAAY,aAAa,KAAK,CAAA;AAAA,IACvC;AAAA,GACF;AACF,CAAC;;;;;"} |
@@ -14,30 +14,9 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const FULLY_SUPPORTS_HTTP_DIAGNOSTICS_CHANNEL = | ||
| (nodeVersion.NODE_VERSION.major === 22 && nodeVersion.NODE_VERSION.minor >= 12) || | ||
| (nodeVersion.NODE_VERSION.major === 23 && nodeVersion.NODE_VERSION.minor >= 2) || | ||
| nodeVersion.NODE_VERSION.major >= 24; | ||
| /** | ||
| * This custom HTTP instrumentation handles outgoing HTTP requests. | ||
| * | ||
| * It provides: | ||
| * - Breadcrumbs for all outgoing requests | ||
| * - Trace propagation headers (when enabled) | ||
| * - Span creation for outgoing requests (when createSpansForOutgoingRequests is enabled) | ||
| * | ||
| * Span creation requires Node 22+ and uses diagnostic channels to avoid monkey-patching. | ||
| * By default, this is only enabled in the node SDK, not in node-core or other runtime SDKs. | ||
| * | ||
| * Important note: Contrary to other OTEL instrumentation, this one cannot be unwrapped. | ||
| * | ||
| * This is heavily inspired & adapted from: | ||
| * https://github.com/open-telemetry/opentelemetry-js/blob/f8ab5592ddea5cba0a3b33bf8d74f27872c0367f/experimental/packages/opentelemetry-instrumentation-http/src/http.ts | ||
| */ | ||
| const FULLY_SUPPORTS_HTTP_DIAGNOSTICS_CHANNEL = nodeVersion.NODE_VERSION.major === 22 && nodeVersion.NODE_VERSION.minor >= 12 || nodeVersion.NODE_VERSION.major === 23 && nodeVersion.NODE_VERSION.minor >= 2 || nodeVersion.NODE_VERSION.major >= 24; | ||
| class SentryHttpInstrumentation extends instrumentation.InstrumentationBase { | ||
| constructor(config = {}) { | ||
| constructor(config = {}) { | ||
| super(constants.INSTRUMENTATION_NAME, core.SDK_VERSION, config); | ||
| } | ||
| /** @inheritdoc */ | ||
| init() { | ||
| init() { | ||
| const { outgoingRequestApplyCustomAttributes: applyCustomAttributesOnSpan, ...options } = this.getConfig(); | ||
@@ -50,31 +29,20 @@ const patchOptions = { | ||
| ignoreOutgoingRequests(url, request) { | ||
| return ( | ||
| core$1.isTracingSuppressed(api.context.active()) || | ||
| !!options.ignoreOutgoingRequests?.(url, core.getRequestOptions(request )) | ||
| ); | ||
| return core$1.isTracingSuppressed(api.context.active()) || !!options.ignoreOutgoingRequests?.(url, core.getRequestOptions(request)); | ||
| }, | ||
| outgoingRequestHook(span, request) { | ||
| options.outgoingRequestHook?.(span, request); | ||
| // We monkey-patch `req.once('response'), which is used to trigger | ||
| // the callback of the request, so that it runs in the active context | ||
| // eslint-disable-next-line @typescript-eslint/unbound-method, deprecation/deprecation | ||
| const originalOnce = request.once; | ||
| const newOnce = new Proxy(originalOnce, { | ||
| apply(target, thisArg, args) { | ||
| const [event] = args; | ||
| if (event !== 'response') { | ||
| if (event !== "response") { | ||
| return target.apply(thisArg, args); | ||
| } | ||
| const parentContext = api.context.active(); | ||
| const requestContext = api.trace.setSpan(parentContext, span); | ||
| return api.context.with(requestContext, () => { | ||
| return target.apply(thisArg, args); | ||
| }); | ||
| }, | ||
| } | ||
| }); | ||
| // eslint-disable-next-line deprecation/deprecation | ||
| request.once = newOnce; | ||
@@ -89,37 +57,18 @@ }, | ||
| http, | ||
| https, | ||
| https | ||
| }; | ||
| // only generate the subscriber function if we'll actually use it | ||
| const { [core.HTTP_ON_CLIENT_REQUEST]: onHttpClientRequestCreated } = FULLY_SUPPORTS_HTTP_DIAGNOSTICS_CHANNEL | ||
| ? core.getHttpClientSubscriptions(patchOptions) | ||
| : {}; | ||
| // guard because we cover both http and https with the same subscribers | ||
| const { [core.HTTP_ON_CLIENT_REQUEST]: onHttpClientRequestCreated } = FULLY_SUPPORTS_HTTP_DIAGNOSTICS_CHANNEL ? core.getHttpClientSubscriptions(patchOptions) : {}; | ||
| let hasRegisteredHandlers = false; | ||
| const sub = onHttpClientRequestCreated | ||
| ? (moduleExports) => { | ||
| if (!hasRegisteredHandlers && onHttpClientRequestCreated) { | ||
| hasRegisteredHandlers = true; | ||
| diagnosticsChannel.subscribe(core.HTTP_ON_CLIENT_REQUEST, onHttpClientRequestCreated); | ||
| } | ||
| return moduleExports; | ||
| } | ||
| : undefined; | ||
| const sub = onHttpClientRequestCreated ? (moduleExports) => { | ||
| if (!hasRegisteredHandlers && onHttpClientRequestCreated) { | ||
| hasRegisteredHandlers = true; | ||
| diagnosticsChannel.subscribe(core.HTTP_ON_CLIENT_REQUEST, onHttpClientRequestCreated); | ||
| } | ||
| return moduleExports; | ||
| } : void 0; | ||
| const wrapHttp = sub ?? ((moduleExports) => core.patchHttpModuleClient(moduleExports, patchOptions)); | ||
| const wrapHttps = sub ?? ((moduleExports) => core.patchHttpModuleClient(moduleExports, patchOptions)); | ||
| /** | ||
| * You may be wondering why we register these diagnostics-channel listeners | ||
| * in such a convoluted way (as InstrumentationNodeModuleDefinition...)˝, | ||
| * instead of simply subscribing to the events once in here. | ||
| * The reason for this is timing semantics: These functions are called once the http or https module is loaded. | ||
| * If we'd subscribe before that, there seem to be conflicts with the OTEL native instrumentation in some scenarios, | ||
| * especially the "import-on-top" pattern of setting up ESM applications. | ||
| */ | ||
| return [ | ||
| new instrumentation.InstrumentationNodeModuleDefinition('http', ['*'], wrapHttp), | ||
| new instrumentation.InstrumentationNodeModuleDefinition('https', ['*'], wrapHttps), | ||
| new instrumentation.InstrumentationNodeModuleDefinition("http", ["*"], wrapHttp), | ||
| new instrumentation.InstrumentationNodeModuleDefinition("https", ["*"], wrapHttps) | ||
| ]; | ||
@@ -126,0 +75,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"SentryHttpInstrumentation.js","sources":["../../../../src/integrations/http/SentryHttpInstrumentation.ts"],"sourcesContent":["import { subscribe } from 'node:diagnostics_channel';\nimport { context, trace } from '@opentelemetry/api';\nimport { isTracingSuppressed } from '@opentelemetry/core';\nimport type { InstrumentationConfig } from '@opentelemetry/instrumentation';\nimport { InstrumentationBase, InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation';\nimport type { ClientRequest, IncomingMessage, ServerResponse } from 'node:http';\nimport type {\n HttpClientRequest,\n HttpIncomingMessage,\n HttpInstrumentationOptions,\n HttpModuleExport,\n Span,\n} from '@sentry/core';\nimport { getHttpClientSubscriptions, patchHttpModuleClient, SDK_VERSION, getRequestOptions } from '@sentry/core';\nimport { INSTRUMENTATION_NAME } from './constants';\nimport { HTTP_ON_CLIENT_REQUEST } from '@sentry/core';\nimport { NODE_VERSION } from '../../nodeVersion';\nimport { errorMonitor } from 'node:events';\nimport * as http from 'node:http';\nimport * as https from 'node:https';\n\nconst FULLY_SUPPORTS_HTTP_DIAGNOSTICS_CHANNEL =\n (NODE_VERSION.major === 22 && NODE_VERSION.minor >= 12) ||\n (NODE_VERSION.major === 23 && NODE_VERSION.minor >= 2) ||\n NODE_VERSION.major >= 24;\n\nexport type SentryHttpInstrumentationOptions = InstrumentationConfig & {\n /**\n * Whether breadcrumbs should be recorded for outgoing requests.\n *\n * @default `true`\n */\n breadcrumbs?: boolean;\n\n /**\n * Whether to propagate Sentry trace headers in outgoing requests.\n * By default this is done by the HttpInstrumentation, but if that is not added (e.g. because tracing is disabled)\n * then this instrumentation can take over.\n *\n * @default `false`\n */\n propagateTraceInOutgoingRequests?: boolean;\n\n /**\n * Whether to enable the capability to create spans for outgoing requests via diagnostic channels.\n * If enabled, spans will only be created if the `spans` option is also enabled (default: true).\n *\n * This is a feature flag that should be enabled by SDKs when the runtime supports it (Node 22.12+).\n * Individual users should not need to configure this directly.\n *\n * @default `false`\n */\n createSpansForOutgoingRequests?: boolean;\n\n /**\n * Whether to create spans for outgoing requests (user preference).\n * This only takes effect if `createSpansForOutgoingRequests` is also enabled.\n * If `createSpansForOutgoingRequests` is not enabled, this option is ignored.\n *\n * @default `true`\n */\n spans?: boolean;\n\n /**\n * Do not capture breadcrumbs for outgoing HTTP requests to URLs where the given callback returns `true`.\n * For the scope of this instrumentation, this callback only controls breadcrumb creation.\n * The same option can be passed to the top-level httpIntegration where it controls both, breadcrumb and\n * span creation.\n *\n * @param url Contains the entire URL, including query string (if any), protocol, host, etc. of the outgoing request.\n * @param request Contains the {@type RequestOptions} object used to make the outgoing request.\n */\n ignoreOutgoingRequests?: (url: string, request: http.RequestOptions) => boolean;\n\n /**\n * Hooks for outgoing request spans, called when `createSpansForOutgoingRequests` is enabled.\n * These mirror the OTEL HttpInstrumentation hooks for backwards compatibility.\n */\n outgoingRequestHook?: (span: Span, request: ClientRequest | HttpClientRequest) => void;\n outgoingResponseHook?: (span: Span, response: IncomingMessage | HttpIncomingMessage) => void;\n outgoingRequestApplyCustomAttributes?: (\n span: Span,\n request: HttpClientRequest,\n response: HttpIncomingMessage,\n ) => void;\n\n // All options below do not do anything anymore in this instrumentation, and will be removed in the future.\n // They are only kept here for backwards compatibility - the respective functionality is now handled by the httpServerIntegration/httpServerSpansIntegration.\n\n /**\n * @deprecated This no longer does anything.\n */\n extractIncomingTraceFromHeader?: boolean;\n\n /**\n * @deprecated This no longer does anything.\n */\n ignoreStaticAssets?: boolean;\n\n /**\n * @deprecated This no longer does anything.\n */\n disableIncomingRequestSpans?: boolean;\n\n /**\n * @deprecated This no longer does anything.\n */\n ignoreSpansForIncomingRequests?: (urlPath: string, request: IncomingMessage) => boolean;\n\n /**\n * @deprecated This no longer does anything.\n */\n ignoreIncomingRequestBody?: (url: string, request: http.RequestOptions) => boolean;\n\n /**\n * @deprecated This no longer does anything.\n */\n maxIncomingRequestBodySize?: 'none' | 'small' | 'medium' | 'always';\n\n /**\n * @deprecated This no longer does anything.\n */\n trackIncomingRequestsAsSessions?: boolean;\n\n /**\n * @deprecated This no longer does anything.\n */\n instrumentation?: {\n requestHook?: (span: Span, req: ClientRequest | IncomingMessage) => void;\n responseHook?: (span: Span, response: IncomingMessage | ServerResponse) => void;\n applyCustomAttributesOnSpan?: (\n span: Span,\n request: ClientRequest | IncomingMessage,\n response: IncomingMessage | ServerResponse,\n ) => void;\n };\n\n /**\n * @deprecated This no longer does anything.\n */\n sessionFlushingDelayMS?: number;\n};\n\n/**\n * This custom HTTP instrumentation handles outgoing HTTP requests.\n *\n * It provides:\n * - Breadcrumbs for all outgoing requests\n * - Trace propagation headers (when enabled)\n * - Span creation for outgoing requests (when createSpansForOutgoingRequests is enabled)\n *\n * Span creation requires Node 22+ and uses diagnostic channels to avoid monkey-patching.\n * By default, this is only enabled in the node SDK, not in node-core or other runtime SDKs.\n *\n * Important note: Contrary to other OTEL instrumentation, this one cannot be unwrapped.\n *\n * This is heavily inspired & adapted from:\n * https://github.com/open-telemetry/opentelemetry-js/blob/f8ab5592ddea5cba0a3b33bf8d74f27872c0367f/experimental/packages/opentelemetry-instrumentation-http/src/http.ts\n */\nexport class SentryHttpInstrumentation extends InstrumentationBase<SentryHttpInstrumentationOptions> {\n public constructor(config: SentryHttpInstrumentationOptions = {}) {\n super(INSTRUMENTATION_NAME, SDK_VERSION, config);\n }\n\n /** @inheritdoc */\n public init(): [InstrumentationNodeModuleDefinition, InstrumentationNodeModuleDefinition] {\n const { outgoingRequestApplyCustomAttributes: applyCustomAttributesOnSpan, ...options } = this.getConfig();\n const patchOptions: HttpInstrumentationOptions = {\n propagateTrace: options.propagateTraceInOutgoingRequests,\n applyCustomAttributesOnSpan,\n ...options,\n spans: options.createSpansForOutgoingRequests && (options.spans ?? true),\n ignoreOutgoingRequests(url, request) {\n return (\n isTracingSuppressed(context.active()) ||\n !!options.ignoreOutgoingRequests?.(url, getRequestOptions(request as ClientRequest))\n );\n },\n outgoingRequestHook(span, request) {\n options.outgoingRequestHook?.(span, request);\n // We monkey-patch `req.once('response'), which is used to trigger\n // the callback of the request, so that it runs in the active context\n // eslint-disable-next-line @typescript-eslint/unbound-method, deprecation/deprecation\n const originalOnce = request.once;\n\n const newOnce = new Proxy(originalOnce, {\n apply(target, thisArg, args: Parameters<typeof originalOnce>) {\n const [event] = args;\n if (event !== 'response') {\n return target.apply(thisArg, args);\n }\n\n const parentContext = context.active();\n const requestContext = trace.setSpan(parentContext, span);\n\n return context.with(requestContext, () => {\n return target.apply(thisArg, args);\n });\n },\n });\n\n // eslint-disable-next-line deprecation/deprecation\n request.once = newOnce;\n },\n outgoingResponseHook(span, response) {\n options.outgoingResponseHook?.(span, response);\n context.bind(context.active(), response);\n },\n errorMonitor,\n // Pass these in to detect OTel double-wrapping if we're enabling spans\n http,\n https,\n };\n\n // only generate the subscriber function if we'll actually use it\n const { [HTTP_ON_CLIENT_REQUEST]: onHttpClientRequestCreated } = FULLY_SUPPORTS_HTTP_DIAGNOSTICS_CHANNEL\n ? getHttpClientSubscriptions(patchOptions)\n : {};\n\n // guard because we cover both http and https with the same subscribers\n let hasRegisteredHandlers = false;\n const sub = onHttpClientRequestCreated\n ? <T extends HttpModuleExport>(moduleExports: T): T => {\n if (!hasRegisteredHandlers && onHttpClientRequestCreated) {\n hasRegisteredHandlers = true;\n subscribe(HTTP_ON_CLIENT_REQUEST, onHttpClientRequestCreated);\n }\n return moduleExports;\n }\n : undefined;\n\n const wrapHttp = sub ?? ((moduleExports: HttpModuleExport) => patchHttpModuleClient(moduleExports, patchOptions));\n\n const wrapHttps = sub ?? ((moduleExports: HttpModuleExport) => patchHttpModuleClient(moduleExports, patchOptions));\n\n /**\n * You may be wondering why we register these diagnostics-channel listeners\n * in such a convoluted way (as InstrumentationNodeModuleDefinition...)˝,\n * instead of simply subscribing to the events once in here.\n * The reason for this is timing semantics: These functions are called once the http or https module is loaded.\n * If we'd subscribe before that, there seem to be conflicts with the OTEL native instrumentation in some scenarios,\n * especially the \"import-on-top\" pattern of setting up ESM applications.\n */\n return [\n new InstrumentationNodeModuleDefinition('http', ['*'], wrapHttp),\n new InstrumentationNodeModuleDefinition('https', ['*'], wrapHttps),\n ];\n }\n}\n"],"names":["NODE_VERSION","InstrumentationBase","INSTRUMENTATION_NAME","SDK_VERSION","isTracingSuppressed","context","getRequestOptions","trace","errorMonitor","HTTP_ON_CLIENT_REQUEST","getHttpClientSubscriptions","subscribe","patchHttpModuleClient","InstrumentationNodeModuleDefinition"],"mappings":"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"} | ||
| {"version":3,"file":"SentryHttpInstrumentation.js","sources":["../../../../src/integrations/http/SentryHttpInstrumentation.ts"],"sourcesContent":["import { subscribe } from 'node:diagnostics_channel';\nimport { context, trace } from '@opentelemetry/api';\nimport { isTracingSuppressed } from '@opentelemetry/core';\nimport type { InstrumentationConfig } from '@opentelemetry/instrumentation';\nimport { InstrumentationBase, InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation';\nimport type { ClientRequest, IncomingMessage, ServerResponse } from 'node:http';\nimport type {\n HttpClientRequest,\n HttpIncomingMessage,\n HttpInstrumentationOptions,\n HttpModuleExport,\n Span,\n} from '@sentry/core';\nimport { getHttpClientSubscriptions, patchHttpModuleClient, SDK_VERSION, getRequestOptions } from '@sentry/core';\nimport { INSTRUMENTATION_NAME } from './constants';\nimport { HTTP_ON_CLIENT_REQUEST } from '@sentry/core';\nimport { NODE_VERSION } from '../../nodeVersion';\nimport { errorMonitor } from 'node:events';\nimport * as http from 'node:http';\nimport * as https from 'node:https';\n\nconst FULLY_SUPPORTS_HTTP_DIAGNOSTICS_CHANNEL =\n (NODE_VERSION.major === 22 && NODE_VERSION.minor >= 12) ||\n (NODE_VERSION.major === 23 && NODE_VERSION.minor >= 2) ||\n NODE_VERSION.major >= 24;\n\nexport type SentryHttpInstrumentationOptions = InstrumentationConfig & {\n /**\n * Whether breadcrumbs should be recorded for outgoing requests.\n *\n * @default `true`\n */\n breadcrumbs?: boolean;\n\n /**\n * Whether to propagate Sentry trace headers in outgoing requests.\n * By default this is done by the HttpInstrumentation, but if that is not added (e.g. because tracing is disabled)\n * then this instrumentation can take over.\n *\n * @default `false`\n */\n propagateTraceInOutgoingRequests?: boolean;\n\n /**\n * Whether to enable the capability to create spans for outgoing requests via diagnostic channels.\n * If enabled, spans will only be created if the `spans` option is also enabled (default: true).\n *\n * This is a feature flag that should be enabled by SDKs when the runtime supports it (Node 22.12+).\n * Individual users should not need to configure this directly.\n *\n * @default `false`\n */\n createSpansForOutgoingRequests?: boolean;\n\n /**\n * Whether to create spans for outgoing requests (user preference).\n * This only takes effect if `createSpansForOutgoingRequests` is also enabled.\n * If `createSpansForOutgoingRequests` is not enabled, this option is ignored.\n *\n * @default `true`\n */\n spans?: boolean;\n\n /**\n * Do not capture breadcrumbs for outgoing HTTP requests to URLs where the given callback returns `true`.\n * For the scope of this instrumentation, this callback only controls breadcrumb creation.\n * The same option can be passed to the top-level httpIntegration where it controls both, breadcrumb and\n * span creation.\n *\n * @param url Contains the entire URL, including query string (if any), protocol, host, etc. of the outgoing request.\n * @param request Contains the {@type RequestOptions} object used to make the outgoing request.\n */\n ignoreOutgoingRequests?: (url: string, request: http.RequestOptions) => boolean;\n\n /**\n * Hooks for outgoing request spans, called when `createSpansForOutgoingRequests` is enabled.\n * These mirror the OTEL HttpInstrumentation hooks for backwards compatibility.\n */\n outgoingRequestHook?: (span: Span, request: ClientRequest | HttpClientRequest) => void;\n outgoingResponseHook?: (span: Span, response: IncomingMessage | HttpIncomingMessage) => void;\n outgoingRequestApplyCustomAttributes?: (\n span: Span,\n request: HttpClientRequest,\n response: HttpIncomingMessage,\n ) => void;\n\n // All options below do not do anything anymore in this instrumentation, and will be removed in the future.\n // They are only kept here for backwards compatibility - the respective functionality is now handled by the httpServerIntegration/httpServerSpansIntegration.\n\n /**\n * @deprecated This no longer does anything.\n */\n extractIncomingTraceFromHeader?: boolean;\n\n /**\n * @deprecated This no longer does anything.\n */\n ignoreStaticAssets?: boolean;\n\n /**\n * @deprecated This no longer does anything.\n */\n disableIncomingRequestSpans?: boolean;\n\n /**\n * @deprecated This no longer does anything.\n */\n ignoreSpansForIncomingRequests?: (urlPath: string, request: IncomingMessage) => boolean;\n\n /**\n * @deprecated This no longer does anything.\n */\n ignoreIncomingRequestBody?: (url: string, request: http.RequestOptions) => boolean;\n\n /**\n * @deprecated This no longer does anything.\n */\n maxIncomingRequestBodySize?: 'none' | 'small' | 'medium' | 'always';\n\n /**\n * @deprecated This no longer does anything.\n */\n trackIncomingRequestsAsSessions?: boolean;\n\n /**\n * @deprecated This no longer does anything.\n */\n instrumentation?: {\n requestHook?: (span: Span, req: ClientRequest | IncomingMessage) => void;\n responseHook?: (span: Span, response: IncomingMessage | ServerResponse) => void;\n applyCustomAttributesOnSpan?: (\n span: Span,\n request: ClientRequest | IncomingMessage,\n response: IncomingMessage | ServerResponse,\n ) => void;\n };\n\n /**\n * @deprecated This no longer does anything.\n */\n sessionFlushingDelayMS?: number;\n};\n\n/**\n * This custom HTTP instrumentation handles outgoing HTTP requests.\n *\n * It provides:\n * - Breadcrumbs for all outgoing requests\n * - Trace propagation headers (when enabled)\n * - Span creation for outgoing requests (when createSpansForOutgoingRequests is enabled)\n *\n * Span creation requires Node 22+ and uses diagnostic channels to avoid monkey-patching.\n * By default, this is only enabled in the node SDK, not in node-core or other runtime SDKs.\n *\n * Important note: Contrary to other OTEL instrumentation, this one cannot be unwrapped.\n *\n * This is heavily inspired & adapted from:\n * https://github.com/open-telemetry/opentelemetry-js/blob/f8ab5592ddea5cba0a3b33bf8d74f27872c0367f/experimental/packages/opentelemetry-instrumentation-http/src/http.ts\n */\nexport class SentryHttpInstrumentation extends InstrumentationBase<SentryHttpInstrumentationOptions> {\n public constructor(config: SentryHttpInstrumentationOptions = {}) {\n super(INSTRUMENTATION_NAME, SDK_VERSION, config);\n }\n\n /** @inheritdoc */\n public init(): [InstrumentationNodeModuleDefinition, InstrumentationNodeModuleDefinition] {\n const { outgoingRequestApplyCustomAttributes: applyCustomAttributesOnSpan, ...options } = this.getConfig();\n const patchOptions: HttpInstrumentationOptions = {\n propagateTrace: options.propagateTraceInOutgoingRequests,\n applyCustomAttributesOnSpan,\n ...options,\n spans: options.createSpansForOutgoingRequests && (options.spans ?? true),\n ignoreOutgoingRequests(url, request) {\n return (\n isTracingSuppressed(context.active()) ||\n !!options.ignoreOutgoingRequests?.(url, getRequestOptions(request as ClientRequest))\n );\n },\n outgoingRequestHook(span, request) {\n options.outgoingRequestHook?.(span, request);\n // We monkey-patch `req.once('response'), which is used to trigger\n // the callback of the request, so that it runs in the active context\n // eslint-disable-next-line @typescript-eslint/unbound-method, deprecation/deprecation\n const originalOnce = request.once;\n\n const newOnce = new Proxy(originalOnce, {\n apply(target, thisArg, args: Parameters<typeof originalOnce>) {\n const [event] = args;\n if (event !== 'response') {\n return target.apply(thisArg, args);\n }\n\n const parentContext = context.active();\n const requestContext = trace.setSpan(parentContext, span);\n\n return context.with(requestContext, () => {\n return target.apply(thisArg, args);\n });\n },\n });\n\n // eslint-disable-next-line deprecation/deprecation\n request.once = newOnce;\n },\n outgoingResponseHook(span, response) {\n options.outgoingResponseHook?.(span, response);\n context.bind(context.active(), response);\n },\n errorMonitor,\n // Pass these in to detect OTel double-wrapping if we're enabling spans\n http,\n https,\n };\n\n // only generate the subscriber function if we'll actually use it\n const { [HTTP_ON_CLIENT_REQUEST]: onHttpClientRequestCreated } = FULLY_SUPPORTS_HTTP_DIAGNOSTICS_CHANNEL\n ? getHttpClientSubscriptions(patchOptions)\n : {};\n\n // guard because we cover both http and https with the same subscribers\n let hasRegisteredHandlers = false;\n const sub = onHttpClientRequestCreated\n ? <T extends HttpModuleExport>(moduleExports: T): T => {\n if (!hasRegisteredHandlers && onHttpClientRequestCreated) {\n hasRegisteredHandlers = true;\n subscribe(HTTP_ON_CLIENT_REQUEST, onHttpClientRequestCreated);\n }\n return moduleExports;\n }\n : undefined;\n\n const wrapHttp = sub ?? ((moduleExports: HttpModuleExport) => patchHttpModuleClient(moduleExports, patchOptions));\n\n const wrapHttps = sub ?? ((moduleExports: HttpModuleExport) => patchHttpModuleClient(moduleExports, patchOptions));\n\n /**\n * You may be wondering why we register these diagnostics-channel listeners\n * in such a convoluted way (as InstrumentationNodeModuleDefinition...)˝,\n * instead of simply subscribing to the events once in here.\n * The reason for this is timing semantics: These functions are called once the http or https module is loaded.\n * If we'd subscribe before that, there seem to be conflicts with the OTEL native instrumentation in some scenarios,\n * especially the \"import-on-top\" pattern of setting up ESM applications.\n */\n return [\n new InstrumentationNodeModuleDefinition('http', ['*'], wrapHttp),\n new InstrumentationNodeModuleDefinition('https', ['*'], wrapHttps),\n ];\n }\n}\n"],"names":["NODE_VERSION","InstrumentationBase","INSTRUMENTATION_NAME","SDK_VERSION","isTracingSuppressed","context","getRequestOptions","trace","errorMonitor","HTTP_ON_CLIENT_REQUEST","getHttpClientSubscriptions","subscribe","patchHttpModuleClient","InstrumentationNodeModuleDefinition"],"mappings":"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"} |
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| /** | ||
| * The key used to store the local variables on the error object. | ||
| */ | ||
| const LOCAL_VARIABLES_KEY = '__SENTRY_ERROR_LOCAL_VARIABLES__'; | ||
| /** | ||
| * Creates a rate limiter that will call the disable callback when the rate limit is reached and the enable callback | ||
| * when a timeout has occurred. | ||
| * @param maxPerSecond Maximum number of calls per second | ||
| * @param enable Callback to enable capture | ||
| * @param disable Callback to disable capture | ||
| * @returns A function to call to increment the rate limiter count | ||
| */ | ||
| function createRateLimiter( | ||
| maxPerSecond, | ||
| enable, | ||
| disable, | ||
| ) { | ||
| const LOCAL_VARIABLES_KEY = "__SENTRY_ERROR_LOCAL_VARIABLES__"; | ||
| function createRateLimiter(maxPerSecond, enable, disable) { | ||
| let count = 0; | ||
| let retrySeconds = 5; | ||
| let disabledTimeout = 0; | ||
| setInterval(() => { | ||
@@ -30,4 +13,2 @@ if (disabledTimeout === 0) { | ||
| disable(retrySeconds); | ||
| // Cap at one day | ||
| if (retrySeconds > 86400) { | ||
@@ -40,3 +21,2 @@ retrySeconds = 86400; | ||
| disabledTimeout -= 1; | ||
| if (disabledTimeout === 0) { | ||
@@ -46,6 +26,4 @@ enable(); | ||
| } | ||
| count = 0; | ||
| }, 1000).unref(); | ||
| }, 1e3).unref(); | ||
| return () => { | ||
@@ -55,13 +33,7 @@ count += 1; | ||
| } | ||
| // Add types for the exception event data | ||
| /** Could this be an anonymous function? */ | ||
| function isAnonymous(name) { | ||
| return name !== undefined && (name.length === 0 || name === '?' || name === '<anonymous>'); | ||
| return name !== void 0 && (name.length === 0 || name === "?" || name === "<anonymous>"); | ||
| } | ||
| /** Do the function names appear to match? */ | ||
| function functionNamesMatch(a, b) { | ||
| return a === b || `Object.${a}` === b || a === `Object.${b}` || (isAnonymous(a) && isAnonymous(b)); | ||
| return a === b || `Object.${a}` === b || a === `Object.${b}` || isAnonymous(a) && isAnonymous(b); | ||
| } | ||
@@ -68,0 +40,0 @@ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"common.js","sources":["../../../../src/integrations/local-variables/common.ts"],"sourcesContent":["import type { Debugger } from 'node:inspector';\n\nexport type Variables = Record<string, unknown>;\n\nexport type RateLimitIncrement = () => void;\n\n/**\n * The key used to store the local variables on the error object.\n */\nexport const LOCAL_VARIABLES_KEY = '__SENTRY_ERROR_LOCAL_VARIABLES__';\n\n/**\n * Creates a rate limiter that will call the disable callback when the rate limit is reached and the enable callback\n * when a timeout has occurred.\n * @param maxPerSecond Maximum number of calls per second\n * @param enable Callback to enable capture\n * @param disable Callback to disable capture\n * @returns A function to call to increment the rate limiter count\n */\nexport function createRateLimiter(\n maxPerSecond: number,\n enable: () => void,\n disable: (seconds: number) => void,\n): RateLimitIncrement {\n let count = 0;\n let retrySeconds = 5;\n let disabledTimeout = 0;\n\n setInterval(() => {\n if (disabledTimeout === 0) {\n if (count > maxPerSecond) {\n retrySeconds *= 2;\n disable(retrySeconds);\n\n // Cap at one day\n if (retrySeconds > 86400) {\n retrySeconds = 86400;\n }\n disabledTimeout = retrySeconds;\n }\n } else {\n disabledTimeout -= 1;\n\n if (disabledTimeout === 0) {\n enable();\n }\n }\n\n count = 0;\n }, 1_000).unref();\n\n return () => {\n count += 1;\n };\n}\n\n// Add types for the exception event data\nexport type PausedExceptionEvent = Debugger.PausedEventDataType & {\n data: {\n // This contains error.stack\n description: string;\n objectId?: string;\n };\n};\n\n/** Could this be an anonymous function? */\nexport function isAnonymous(name: string | undefined): boolean {\n return name !== undefined && (name.length === 0 || name === '?' || name === '<anonymous>');\n}\n\n/** Do the function names appear to match? */\nexport function functionNamesMatch(a: string | undefined, b: string | undefined): boolean {\n return a === b || `Object.${a}` === b || a === `Object.${b}` || (isAnonymous(a) && isAnonymous(b));\n}\n\nexport interface FrameVariables {\n function: string;\n vars?: Variables;\n}\n\nexport interface LocalVariablesIntegrationOptions {\n /**\n * Capture local variables for both caught and uncaught exceptions\n *\n * - When false, only uncaught exceptions will have local variables\n * - When true, both caught and uncaught exceptions will have local variables.\n *\n * Defaults to `true`.\n *\n * Capturing local variables for all exceptions can be expensive since the debugger pauses for every throw to collect\n * local variables.\n *\n * To reduce the likelihood of this feature impacting app performance or throughput, this feature is rate-limited.\n * Once the rate limit is reached, local variables will only be captured for uncaught exceptions until a timeout has\n * been reached.\n */\n captureAllExceptions?: boolean;\n /**\n * Maximum number of exceptions to capture local variables for per second before rate limiting is triggered.\n */\n maxExceptionsPerSecond?: number;\n /**\n * When true, local variables will be captured for all frames, including those that are not in_app.\n *\n * Defaults to `false`.\n */\n includeOutOfAppFrames?: boolean;\n}\n\nexport interface LocalVariablesWorkerArgs extends LocalVariablesIntegrationOptions {\n /**\n * Whether to enable debug logging.\n */\n debug: boolean;\n /**\n * Base path used to calculate module name.\n *\n * Defaults to `dirname(process.argv[1])` and falls back to `process.cwd()`\n */\n basePath?: string;\n}\n"],"names":[],"mappings":";;AAMA;AACA;AACA;AACO,MAAM,mBAAA,GAAsB;;AAEnC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,iBAAiB;AACjC,EAAE,YAAY;AACd,EAAE,MAAM;AACR,EAAE,OAAO;AACT,EAAsB;AACtB,EAAE,IAAI,KAAA,GAAQ,CAAC;AACf,EAAE,IAAI,YAAA,GAAe,CAAC;AACtB,EAAE,IAAI,eAAA,GAAkB,CAAC;;AAEzB,EAAE,WAAW,CAAC,MAAM;AACpB,IAAI,IAAI,eAAA,KAAoB,CAAC,EAAE;AAC/B,MAAM,IAAI,KAAA,GAAQ,YAAY,EAAE;AAChC,QAAQ,YAAA,IAAgB,CAAC;AACzB,QAAQ,OAAO,CAAC,YAAY,CAAC;;AAE7B;AACA,QAAQ,IAAI,YAAA,GAAe,KAAK,EAAE;AAClC,UAAU,YAAA,GAAe,KAAK;AAC9B,QAAQ;AACR,QAAQ,eAAA,GAAkB,YAAY;AACtC,MAAM;AACN,IAAI,OAAO;AACX,MAAM,eAAA,IAAmB,CAAC;;AAE1B,MAAM,IAAI,eAAA,KAAoB,CAAC,EAAE;AACjC,QAAQ,MAAM,EAAE;AAChB,MAAM;AACN,IAAI;;AAEJ,IAAI,KAAA,GAAQ,CAAC;AACb,EAAE,CAAC,EAAE,IAAK,CAAC,CAAC,KAAK,EAAE;;AAEnB,EAAE,OAAO,MAAM;AACf,IAAI,KAAA,IAAS,CAAC;AACd,EAAE,CAAC;AACH;;AAEA;;AASA;AACO,SAAS,WAAW,CAAC,IAAI,EAA+B;AAC/D,EAAE,OAAO,IAAA,KAAS,cAAc,IAAI,CAAC,MAAA,KAAW,CAAA,IAAK,SAAS,GAAA,IAAO,IAAA,KAAS,aAAa,CAAC;AAC5F;;AAEA;AACO,SAAS,kBAAkB,CAAC,CAAC,EAAsB,CAAC,EAA+B;AAC1F,EAAE,OAAO,CAAA,KAAM,CAAA,IAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAA,KAAA,CAAA,IAAA,CAAA,KAAA,CAAA,OAAA,EAAA,CAAA,CAAA,CAAA,KAAA,WAAA,CAAA,CAAA,CAAA,IAAA,WAAA,CAAA,CAAA,CAAA,CAAA;AACA;;;;;;;"} | ||
| {"version":3,"file":"common.js","sources":["../../../../src/integrations/local-variables/common.ts"],"sourcesContent":["import type { Debugger } from 'node:inspector';\n\nexport type Variables = Record<string, unknown>;\n\nexport type RateLimitIncrement = () => void;\n\n/**\n * The key used to store the local variables on the error object.\n */\nexport const LOCAL_VARIABLES_KEY = '__SENTRY_ERROR_LOCAL_VARIABLES__';\n\n/**\n * Creates a rate limiter that will call the disable callback when the rate limit is reached and the enable callback\n * when a timeout has occurred.\n * @param maxPerSecond Maximum number of calls per second\n * @param enable Callback to enable capture\n * @param disable Callback to disable capture\n * @returns A function to call to increment the rate limiter count\n */\nexport function createRateLimiter(\n maxPerSecond: number,\n enable: () => void,\n disable: (seconds: number) => void,\n): RateLimitIncrement {\n let count = 0;\n let retrySeconds = 5;\n let disabledTimeout = 0;\n\n setInterval(() => {\n if (disabledTimeout === 0) {\n if (count > maxPerSecond) {\n retrySeconds *= 2;\n disable(retrySeconds);\n\n // Cap at one day\n if (retrySeconds > 86400) {\n retrySeconds = 86400;\n }\n disabledTimeout = retrySeconds;\n }\n } else {\n disabledTimeout -= 1;\n\n if (disabledTimeout === 0) {\n enable();\n }\n }\n\n count = 0;\n }, 1_000).unref();\n\n return () => {\n count += 1;\n };\n}\n\n// Add types for the exception event data\nexport type PausedExceptionEvent = Debugger.PausedEventDataType & {\n data: {\n // This contains error.stack\n description: string;\n objectId?: string;\n };\n};\n\n/** Could this be an anonymous function? */\nexport function isAnonymous(name: string | undefined): boolean {\n return name !== undefined && (name.length === 0 || name === '?' || name === '<anonymous>');\n}\n\n/** Do the function names appear to match? */\nexport function functionNamesMatch(a: string | undefined, b: string | undefined): boolean {\n return a === b || `Object.${a}` === b || a === `Object.${b}` || (isAnonymous(a) && isAnonymous(b));\n}\n\nexport interface FrameVariables {\n function: string;\n vars?: Variables;\n}\n\nexport interface LocalVariablesIntegrationOptions {\n /**\n * Capture local variables for both caught and uncaught exceptions\n *\n * - When false, only uncaught exceptions will have local variables\n * - When true, both caught and uncaught exceptions will have local variables.\n *\n * Defaults to `true`.\n *\n * Capturing local variables for all exceptions can be expensive since the debugger pauses for every throw to collect\n * local variables.\n *\n * To reduce the likelihood of this feature impacting app performance or throughput, this feature is rate-limited.\n * Once the rate limit is reached, local variables will only be captured for uncaught exceptions until a timeout has\n * been reached.\n */\n captureAllExceptions?: boolean;\n /**\n * Maximum number of exceptions to capture local variables for per second before rate limiting is triggered.\n */\n maxExceptionsPerSecond?: number;\n /**\n * When true, local variables will be captured for all frames, including those that are not in_app.\n *\n * Defaults to `false`.\n */\n includeOutOfAppFrames?: boolean;\n}\n\nexport interface LocalVariablesWorkerArgs extends LocalVariablesIntegrationOptions {\n /**\n * Whether to enable debug logging.\n */\n debug: boolean;\n /**\n * Base path used to calculate module name.\n *\n * Defaults to `dirname(process.argv[1])` and falls back to `process.cwd()`\n */\n basePath?: string;\n}\n"],"names":[],"mappings":";;AASO,MAAM,mBAAA,GAAsB;AAU5B,SAAS,iBAAA,CACd,YAAA,EACA,MAAA,EACA,OAAA,EACoB;AACpB,EAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,EAAA,IAAI,YAAA,GAAe,CAAA;AACnB,EAAA,IAAI,eAAA,GAAkB,CAAA;AAEtB,EAAA,WAAA,CAAY,MAAM;AAChB,IAAA,IAAI,oBAAoB,CAAA,EAAG;AACzB,MAAA,IAAI,QAAQ,YAAA,EAAc;AACxB,QAAA,YAAA,IAAgB,CAAA;AAChB,QAAA,OAAA,CAAQ,YAAY,CAAA;AAGpB,QAAA,IAAI,eAAe,KAAA,EAAO;AACxB,UAAA,YAAA,GAAe,KAAA;AAAA,QACjB;AACA,QAAA,eAAA,GAAkB,YAAA;AAAA,MACpB;AAAA,IACF,CAAA,MAAO;AACL,MAAA,eAAA,IAAmB,CAAA;AAEnB,MAAA,IAAI,oBAAoB,CAAA,EAAG;AACzB,QAAA,MAAA,EAAO;AAAA,MACT;AAAA,IACF;AAEA,IAAA,KAAA,GAAQ,CAAA;AAAA,EACV,CAAA,EAAG,GAAK,CAAA,CAAE,KAAA,EAAM;AAEhB,EAAA,OAAO,MAAM;AACX,IAAA,KAAA,IAAS,CAAA;AAAA,EACX,CAAA;AACF;AAYO,SAAS,YAAY,IAAA,EAAmC;AAC7D,EAAA,OAAO,SAAS,MAAA,KAAc,IAAA,CAAK,WAAW,CAAA,IAAK,IAAA,KAAS,OAAO,IAAA,KAAS,aAAA,CAAA;AAC9E;AAGO,SAAS,kBAAA,CAAmB,GAAuB,CAAA,EAAgC;AACxF,EAAA,OAAO,CAAA,KAAM,CAAA,IAAK,CAAA,OAAA,EAAU,CAAC,OAAO,CAAA,IAAK,CAAA,KAAM,CAAA,OAAA,EAAU,CAAC,CAAA,CAAA,IAAO,WAAA,CAAY,CAAC,CAAA,IAAK,YAAY,CAAC,CAAA;AAClG;;;;;;;"} |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.js","sources":["../../../../src/integrations/local-variables/index.ts"],"sourcesContent":["import type { Integration } from '@sentry/core';\nimport { NODE_VERSION } from '../../nodeVersion';\nimport type { LocalVariablesIntegrationOptions } from './common';\nimport { localVariablesAsyncIntegration } from './local-variables-async';\nimport { localVariablesSyncIntegration } from './local-variables-sync';\n\nexport const localVariablesIntegration = (options: LocalVariablesIntegrationOptions = {}): Integration => {\n return NODE_VERSION.major < 19 ? localVariablesSyncIntegration(options) : localVariablesAsyncIntegration(options);\n};\n"],"names":["NODE_VERSION","localVariablesSyncIntegration","localVariablesAsyncIntegration"],"mappings":";;;;;;AAMO,MAAM,4BAA4B,CAAC,OAAO,GAAqC,EAAE,KAAkB;AAC1G,EAAE,OAAOA,wBAAY,CAAC,KAAA,GAAQ,EAAA,GAAKC,gDAA6B,CAAC,OAAO,CAAA,GAAIC,kDAA8B,CAAC,OAAO,CAAC;AACnH;;;;"} | ||
| {"version":3,"file":"index.js","sources":["../../../../src/integrations/local-variables/index.ts"],"sourcesContent":["import type { Integration } from '@sentry/core';\nimport { NODE_VERSION } from '../../nodeVersion';\nimport type { LocalVariablesIntegrationOptions } from './common';\nimport { localVariablesAsyncIntegration } from './local-variables-async';\nimport { localVariablesSyncIntegration } from './local-variables-sync';\n\nexport const localVariablesIntegration = (options: LocalVariablesIntegrationOptions = {}): Integration => {\n return NODE_VERSION.major < 19 ? localVariablesSyncIntegration(options) : localVariablesAsyncIntegration(options);\n};\n"],"names":["NODE_VERSION","localVariablesSyncIntegration","localVariablesAsyncIntegration"],"mappings":";;;;;;AAMO,MAAM,yBAAA,GAA4B,CAAC,OAAA,GAA4C,EAAC,KAAmB;AACxG,EAAA,OAAOA,yBAAa,KAAA,GAAQ,EAAA,GAAKC,iDAA8B,OAAO,CAAA,GAAIC,mDAA+B,OAAO,CAAA;AAClH;;;;"} |
@@ -8,38 +8,20 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| // This string is a placeholder that gets overwritten with the worker code. | ||
| const base64WorkerScript = 'LyohIEBzZW50cnkvbm9kZS1jb3JlIDEwLjUzLjEgKGNkOTc0MDgpIHwgaHR0cHM6Ly9naXRodWIuY29tL2dldHNlbnRyeS9zZW50cnktamF2YXNjcmlwdCAqLwppbXBvcnR7U2Vzc2lvbiBhcyBlfWZyb20ibm9kZTppbnNwZWN0b3IvcHJvbWlzZXMiO2ltcG9ydHt3b3JrZXJEYXRhIGFzIHR9ZnJvbSJub2RlOndvcmtlcl90aHJlYWRzIjtjb25zdCBuPWdsb2JhbFRoaXMsaT17fTtjb25zdCBvPSJfX1NFTlRSWV9FUlJPUl9MT0NBTF9WQVJJQUJMRVNfXyI7Y29uc3QgYT10O2Z1bmN0aW9uIHMoLi4uZSl7YS5kZWJ1ZyYmZnVuY3Rpb24oZSl7aWYoISgiY29uc29sZSJpbiBuKSlyZXR1cm4gZSgpO2NvbnN0IHQ9bi5jb25zb2xlLG89e30sYT1PYmplY3Qua2V5cyhpKTthLmZvckVhY2goZT0+e2NvbnN0IG49aVtlXTtvW2VdPXRbZV0sdFtlXT1ufSk7dHJ5e3JldHVybiBlKCl9ZmluYWxseXthLmZvckVhY2goZT0+e3RbZV09b1tlXX0pfX0oKCk9PmNvbnNvbGUubG9nKCJbTG9jYWxWYXJpYWJsZXMgV29ya2VyXSIsLi4uZSkpfWFzeW5jIGZ1bmN0aW9uIGMoZSx0LG4saSl7Y29uc3Qgbz1hd2FpdCBlLnBvc3QoIlJ1bnRpbWUuZ2V0UHJvcGVydGllcyIse29iamVjdElkOnQsb3duUHJvcGVydGllczohMH0pO2lbbl09by5yZXN1bHQuZmlsdGVyKGU9PiJsZW5ndGgiIT09ZS5uYW1lJiYhaXNOYU4ocGFyc2VJbnQoZS5uYW1lLDEwKSkpLnNvcnQoKGUsdCk9PnBhcnNlSW50KGUubmFtZSwxMCktcGFyc2VJbnQodC5uYW1lLDEwKSkubWFwKGU9PmUudmFsdWU/LnZhbHVlKX1hc3luYyBmdW5jdGlvbiByKGUsdCxuLGkpe2NvbnN0IG89YXdhaXQgZS5wb3N0KCJSdW50aW1lLmdldFByb3BlcnRpZXMiLHtvYmplY3RJZDp0LG93blByb3BlcnRpZXM6ITB9KTtpW25dPW8ucmVzdWx0Lm1hcChlPT5bZS5uYW1lLGUudmFsdWU/LnZhbHVlXSkucmVkdWNlKChlLFt0LG5dKT0+KGVbdF09bixlKSx7fSl9ZnVuY3Rpb24gdShlLHQpe2UudmFsdWUmJigidmFsdWUiaW4gZS52YWx1ZT92b2lkIDA9PT1lLnZhbHVlLnZhbHVlfHxudWxsPT09ZS52YWx1ZS52YWx1ZT90W2UubmFtZV09YDwke2UudmFsdWUudmFsdWV9PmA6dFtlLm5hbWVdPWUudmFsdWUudmFsdWU6ImRlc2NyaXB0aW9uImluIGUudmFsdWUmJiJmdW5jdGlvbiIhPT1lLnZhbHVlLnR5cGU/dFtlLm5hbWVdPWA8JHtlLnZhbHVlLmRlc2NyaXB0aW9ufT5gOiJ1bmRlZmluZWQiPT09ZS52YWx1ZS50eXBlJiYodFtlLm5hbWVdPSI8dW5kZWZpbmVkPiIpKX1hc3luYyBmdW5jdGlvbiBsKGUsdCl7Y29uc3Qgbj1hd2FpdCBlLnBvc3QoIlJ1bnRpbWUuZ2V0UHJvcGVydGllcyIse29iamVjdElkOnQsb3duUHJvcGVydGllczohMH0pLGk9e307Zm9yKGNvbnN0IHQgb2Ygbi5yZXN1bHQpaWYodC52YWx1ZT8ub2JqZWN0SWQmJiJBcnJheSI9PT10LnZhbHVlLmNsYXNzTmFtZSl7Y29uc3Qgbj10LnZhbHVlLm9iamVjdElkO2F3YWl0IGMoZSxuLHQubmFtZSxpKX1lbHNlIGlmKHQudmFsdWU/Lm9iamVjdElkJiYiT2JqZWN0Ij09PXQudmFsdWUuY2xhc3NOYW1lKXtjb25zdCBuPXQudmFsdWUub2JqZWN0SWQ7YXdhaXQgcihlLG4sdC5uYW1lLGkpfWVsc2UgdC52YWx1ZSYmdSh0LGkpO3JldHVybiBpfWxldCBmOyhhc3luYyBmdW5jdGlvbigpe2NvbnN0IHQ9bmV3IGU7dC5jb25uZWN0VG9NYWluVGhyZWFkKCkscygiQ29ubmVjdGVkIHRvIG1haW4gdGhyZWFkIik7bGV0IG49ITE7dC5vbigiRGVidWdnZXIucmVzdW1lZCIsKCk9PntuPSExfSksdC5vbigiRGVidWdnZXIucGF1c2VkIixlPT57bj0hMCxhc3luYyBmdW5jdGlvbihlLHtyZWFzb246dCxkYXRhOntvYmplY3RJZDpufSxjYWxsRnJhbWVzOml9KXtpZigiZXhjZXB0aW9uIiE9PXQmJiJwcm9taXNlUmVqZWN0aW9uIiE9PXQpcmV0dXJuO2lmKGY/LigpLG51bGw9PW4pcmV0dXJuO2NvbnN0IGE9W107Zm9yKGxldCB0PTA7dDxpLmxlbmd0aDt0Kyspe2NvbnN0e3Njb3BlQ2hhaW46bixmdW5jdGlvbk5hbWU6byx0aGlzOnN9PWlbdF0sYz1uLmZpbmQoZT0+ImxvY2FsIj09PWUudHlwZSkscj0iZ2xvYmFsIiE9PXMuY2xhc3NOYW1lJiZzLmNsYXNzTmFtZT9gJHtzLmNsYXNzTmFtZX0uJHtvfWA6bztpZih2b2lkIDA9PT1jPy5vYmplY3Qub2JqZWN0SWQpYVt0XT17ZnVuY3Rpb246cn07ZWxzZXtjb25zdCBuPWF3YWl0IGwoZSxjLm9iamVjdC5vYmplY3RJZCk7YVt0XT17ZnVuY3Rpb246cix2YXJzOm59fX1hd2FpdCBlLnBvc3QoIlJ1bnRpbWUuY2FsbEZ1bmN0aW9uT24iLHtmdW5jdGlvbkRlY2xhcmF0aW9uOmBmdW5jdGlvbigpIHsgdGhpcy4ke299ID0gdGhpcy4ke299IHx8ICR7SlNPTi5zdHJpbmdpZnkoYSl9OyB9YCxzaWxlbnQ6ITAsb2JqZWN0SWQ6bn0pLGF3YWl0IGUucG9zdCgiUnVudGltZS5yZWxlYXNlT2JqZWN0Iix7b2JqZWN0SWQ6bn0pfSh0LGUucGFyYW1zKS50aGVuKGFzeW5jKCk9PntuJiZhd2FpdCB0LnBvc3QoIkRlYnVnZ2VyLnJlc3VtZSIpfSxhc3luYyBlPT57biYmYXdhaXQgdC5wb3N0KCJEZWJ1Z2dlci5yZXN1bWUiKX0pfSksYXdhaXQgdC5wb3N0KCJEZWJ1Z2dlci5lbmFibGUiKTtjb25zdCBpPSExIT09YS5jYXB0dXJlQWxsRXhjZXB0aW9ucztpZihhd2FpdCB0LnBvc3QoIkRlYnVnZ2VyLnNldFBhdXNlT25FeGNlcHRpb25zIix7c3RhdGU6aT8iYWxsIjoidW5jYXVnaHQifSksaSl7Y29uc3QgZT1hLm1heEV4Y2VwdGlvbnNQZXJTZWNvbmR8fDUwO2Y9ZnVuY3Rpb24oZSx0LG4pe2xldCBpPTAsbz01LGE9MDtyZXR1cm4gc2V0SW50ZXJ2YWwoKCk9PnswPT09YT9pPmUmJihvKj0yLG4obyksbz44NjQwMCYmKG89ODY0MDApLGE9byk6KGEtPTEsMD09PWEmJnQoKSksaT0wfSwxZTMpLnVucmVmKCksKCk9PntpKz0xfX0oZSxhc3luYygpPT57cygiUmF0ZS1saW1pdCBsaWZ0ZWQuIiksYXdhaXQgdC5wb3N0KCJEZWJ1Z2dlci5zZXRQYXVzZU9uRXhjZXB0aW9ucyIse3N0YXRlOiJhbGwifSl9LGFzeW5jIGU9PntzKGBSYXRlLWxpbWl0IGV4Y2VlZGVkLiBEaXNhYmxpbmcgY2FwdHVyaW5nIG9mIGNhdWdodCBleGNlcHRpb25zIGZvciAke2V9IHNlY29uZHMuYCksYXdhaXQgdC5wb3N0KCJEZWJ1Z2dlci5zZXRQYXVzZU9uRXhjZXB0aW9ucyIse3N0YXRlOiJ1bmNhdWdodCJ9KX0pfX0pKCkuY2F0Y2goZT0+e3MoIkZhaWxlZCB0byBzdGFydCBkZWJ1Z2dlciIsZSl9KSxzZXRJbnRlcnZhbCgoKT0+e30sMWU0KTs='; | ||
| const base64WorkerScript = "LyohIEBzZW50cnkvbm9kZS1jb3JlIDEwLjU0LjAgKGYzMTY4NmIpIHwgaHR0cHM6Ly9naXRodWIuY29tL2dldHNlbnRyeS9zZW50cnktamF2YXNjcmlwdCAqLwppbXBvcnR7U2Vzc2lvbiBhcyBlfWZyb20ibm9kZTppbnNwZWN0b3IvcHJvbWlzZXMiO2ltcG9ydHt3b3JrZXJEYXRhIGFzIHR9ZnJvbSJub2RlOndvcmtlcl90aHJlYWRzIjtjb25zdCBuPWdsb2JhbFRoaXMsaT17fTtjb25zdCBvPSJfX1NFTlRSWV9FUlJPUl9MT0NBTF9WQVJJQUJMRVNfXyI7Y29uc3QgYT10O2Z1bmN0aW9uIHMoLi4uZSl7YS5kZWJ1ZyYmZnVuY3Rpb24oZSl7aWYoISgiY29uc29sZSJpbiBuKSlyZXR1cm4gZSgpO2NvbnN0IHQ9bi5jb25zb2xlLG89e30sYT1PYmplY3Qua2V5cyhpKTthLmZvckVhY2goZT0+e2NvbnN0IG49aVtlXTtvW2VdPXRbZV0sdFtlXT1ufSk7dHJ5e3JldHVybiBlKCl9ZmluYWxseXthLmZvckVhY2goZT0+e3RbZV09b1tlXX0pfX0oKCk9PmNvbnNvbGUubG9nKCJbTG9jYWxWYXJpYWJsZXMgV29ya2VyXSIsLi4uZSkpfWFzeW5jIGZ1bmN0aW9uIGMoZSx0LG4saSl7Y29uc3Qgbz1hd2FpdCBlLnBvc3QoIlJ1bnRpbWUuZ2V0UHJvcGVydGllcyIse29iamVjdElkOnQsb3duUHJvcGVydGllczohMH0pO2lbbl09by5yZXN1bHQuZmlsdGVyKGU9PiJsZW5ndGgiIT09ZS5uYW1lJiYhaXNOYU4ocGFyc2VJbnQoZS5uYW1lLDEwKSkpLnNvcnQoKGUsdCk9PnBhcnNlSW50KGUubmFtZSwxMCktcGFyc2VJbnQodC5uYW1lLDEwKSkubWFwKGU9PmUudmFsdWU/LnZhbHVlKX1hc3luYyBmdW5jdGlvbiByKGUsdCxuLGkpe2NvbnN0IG89YXdhaXQgZS5wb3N0KCJSdW50aW1lLmdldFByb3BlcnRpZXMiLHtvYmplY3RJZDp0LG93blByb3BlcnRpZXM6ITB9KTtpW25dPW8ucmVzdWx0Lm1hcChlPT5bZS5uYW1lLGUudmFsdWU/LnZhbHVlXSkucmVkdWNlKChlLFt0LG5dKT0+KGVbdF09bixlKSx7fSl9ZnVuY3Rpb24gdShlLHQpe2UudmFsdWUmJigidmFsdWUiaW4gZS52YWx1ZT92b2lkIDA9PT1lLnZhbHVlLnZhbHVlfHxudWxsPT09ZS52YWx1ZS52YWx1ZT90W2UubmFtZV09YDwke2UudmFsdWUudmFsdWV9PmA6dFtlLm5hbWVdPWUudmFsdWUudmFsdWU6ImRlc2NyaXB0aW9uImluIGUudmFsdWUmJiJmdW5jdGlvbiIhPT1lLnZhbHVlLnR5cGU/dFtlLm5hbWVdPWA8JHtlLnZhbHVlLmRlc2NyaXB0aW9ufT5gOiJ1bmRlZmluZWQiPT09ZS52YWx1ZS50eXBlJiYodFtlLm5hbWVdPSI8dW5kZWZpbmVkPiIpKX1hc3luYyBmdW5jdGlvbiBsKGUsdCl7Y29uc3Qgbj1hd2FpdCBlLnBvc3QoIlJ1bnRpbWUuZ2V0UHJvcGVydGllcyIse29iamVjdElkOnQsb3duUHJvcGVydGllczohMH0pLGk9e307Zm9yKGNvbnN0IHQgb2Ygbi5yZXN1bHQpaWYodC52YWx1ZT8ub2JqZWN0SWQmJiJBcnJheSI9PT10LnZhbHVlLmNsYXNzTmFtZSl7Y29uc3Qgbj10LnZhbHVlLm9iamVjdElkO2F3YWl0IGMoZSxuLHQubmFtZSxpKX1lbHNlIGlmKHQudmFsdWU/Lm9iamVjdElkJiYiT2JqZWN0Ij09PXQudmFsdWUuY2xhc3NOYW1lKXtjb25zdCBuPXQudmFsdWUub2JqZWN0SWQ7YXdhaXQgcihlLG4sdC5uYW1lLGkpfWVsc2UgdC52YWx1ZSYmdSh0LGkpO3JldHVybiBpfWxldCBmOyhhc3luYyBmdW5jdGlvbigpe2NvbnN0IHQ9bmV3IGU7dC5jb25uZWN0VG9NYWluVGhyZWFkKCkscygiQ29ubmVjdGVkIHRvIG1haW4gdGhyZWFkIik7bGV0IG49ITE7dC5vbigiRGVidWdnZXIucmVzdW1lZCIsKCk9PntuPSExfSksdC5vbigiRGVidWdnZXIucGF1c2VkIixlPT57bj0hMCxhc3luYyBmdW5jdGlvbihlLHtyZWFzb246dCxkYXRhOntvYmplY3RJZDpufSxjYWxsRnJhbWVzOml9KXtpZigiZXhjZXB0aW9uIiE9PXQmJiJwcm9taXNlUmVqZWN0aW9uIiE9PXQpcmV0dXJuO2lmKGY/LigpLG51bGw9PW4pcmV0dXJuO2NvbnN0IGE9W107Zm9yKGxldCB0PTA7dDxpLmxlbmd0aDt0Kyspe2NvbnN0e3Njb3BlQ2hhaW46bixmdW5jdGlvbk5hbWU6byx0aGlzOnN9PWlbdF0sYz1uLmZpbmQoZT0+ImxvY2FsIj09PWUudHlwZSkscj0iZ2xvYmFsIiE9PXMuY2xhc3NOYW1lJiZzLmNsYXNzTmFtZT9gJHtzLmNsYXNzTmFtZX0uJHtvfWA6bztpZih2b2lkIDA9PT1jPy5vYmplY3Qub2JqZWN0SWQpYVt0XT17ZnVuY3Rpb246cn07ZWxzZXtjb25zdCBuPWF3YWl0IGwoZSxjLm9iamVjdC5vYmplY3RJZCk7YVt0XT17ZnVuY3Rpb246cix2YXJzOm59fX1hd2FpdCBlLnBvc3QoIlJ1bnRpbWUuY2FsbEZ1bmN0aW9uT24iLHtmdW5jdGlvbkRlY2xhcmF0aW9uOmBmdW5jdGlvbigpIHsgdGhpcy4ke299ID0gdGhpcy4ke299IHx8ICR7SlNPTi5zdHJpbmdpZnkoYSl9OyB9YCxzaWxlbnQ6ITAsb2JqZWN0SWQ6bn0pLGF3YWl0IGUucG9zdCgiUnVudGltZS5yZWxlYXNlT2JqZWN0Iix7b2JqZWN0SWQ6bn0pfSh0LGUucGFyYW1zKS50aGVuKGFzeW5jKCk9PntuJiZhd2FpdCB0LnBvc3QoIkRlYnVnZ2VyLnJlc3VtZSIpfSxhc3luYyBlPT57biYmYXdhaXQgdC5wb3N0KCJEZWJ1Z2dlci5yZXN1bWUiKX0pfSksYXdhaXQgdC5wb3N0KCJEZWJ1Z2dlci5lbmFibGUiKTtjb25zdCBpPSExIT09YS5jYXB0dXJlQWxsRXhjZXB0aW9ucztpZihhd2FpdCB0LnBvc3QoIkRlYnVnZ2VyLnNldFBhdXNlT25FeGNlcHRpb25zIix7c3RhdGU6aT8iYWxsIjoidW5jYXVnaHQifSksaSl7Y29uc3QgZT1hLm1heEV4Y2VwdGlvbnNQZXJTZWNvbmR8fDUwO2Y9ZnVuY3Rpb24oZSx0LG4pe2xldCBpPTAsbz01LGE9MDtyZXR1cm4gc2V0SW50ZXJ2YWwoKCk9PnswPT09YT9pPmUmJihvKj0yLG4obyksbz44NjQwMCYmKG89ODY0MDApLGE9byk6KGEtPTEsMD09PWEmJnQoKSksaT0wfSwxZTMpLnVucmVmKCksKCk9PntpKz0xfX0oZSxhc3luYygpPT57cygiUmF0ZS1saW1pdCBsaWZ0ZWQuIiksYXdhaXQgdC5wb3N0KCJEZWJ1Z2dlci5zZXRQYXVzZU9uRXhjZXB0aW9ucyIse3N0YXRlOiJhbGwifSl9LGFzeW5jIGU9PntzKGBSYXRlLWxpbWl0IGV4Y2VlZGVkLiBEaXNhYmxpbmcgY2FwdHVyaW5nIG9mIGNhdWdodCBleGNlcHRpb25zIGZvciAke2V9IHNlY29uZHMuYCksYXdhaXQgdC5wb3N0KCJEZWJ1Z2dlci5zZXRQYXVzZU9uRXhjZXB0aW9ucyIse3N0YXRlOiJ1bmNhdWdodCJ9KX0pfX0pKCkuY2F0Y2goZT0+e3MoIkZhaWxlZCB0byBzdGFydCBkZWJ1Z2dlciIsZSl9KSxzZXRJbnRlcnZhbCgoKT0+e30sMWU0KTs="; | ||
| function log(...args) { | ||
| core.debug.log('[LocalVariables]', ...args); | ||
| core.debug.log("[LocalVariables]", ...args); | ||
| } | ||
| /** | ||
| * Adds local variables to exception frames | ||
| */ | ||
| const localVariablesAsyncIntegration = core.defineIntegration((( | ||
| integrationOptions = {}, | ||
| ) => { | ||
| const localVariablesAsyncIntegration = core.defineIntegration(((integrationOptions = {}) => { | ||
| function addLocalVariablesToException(exception, localVariables) { | ||
| // Filter out frames where the function name is `new Promise` since these are in the error.stack frames | ||
| // but do not appear in the debugger call frames | ||
| const frames = (exception.stacktrace?.frames || []).filter(frame => frame.function !== 'new Promise'); | ||
| const frames = (exception.stacktrace?.frames || []).filter((frame) => frame.function !== "new Promise"); | ||
| for (let i = 0; i < frames.length; i++) { | ||
| // Sentry frames are in reverse order | ||
| const frameIndex = frames.length - i - 1; | ||
| const frameLocalVariables = localVariables[i]; | ||
| const frame = frames[frameIndex]; | ||
| if (!frame || !frameLocalVariables) { | ||
| // Drop out if we run out of frames to match up | ||
| break; | ||
| } | ||
| if ( | ||
| // We need to have vars to add | ||
| frameLocalVariables.vars === undefined || | ||
| // Only skip out-of-app frames if includeOutOfAppFrames is not true | ||
| (frame.in_app === false && integrationOptions.includeOutOfAppFrames !== true) || | ||
| // The function names need to match | ||
| frameLocalVariables.vars === void 0 || // Only skip out-of-app frames if includeOutOfAppFrames is not true | ||
| frame.in_app === false && integrationOptions.includeOutOfAppFrames !== true || // The function names need to match | ||
| !common.functionNamesMatch(frame.function, frameLocalVariables.function) | ||
@@ -49,26 +31,15 @@ ) { | ||
| } | ||
| frame.vars = frameLocalVariables.vars; | ||
| } | ||
| } | ||
| function addLocalVariablesToEvent(event, hint) { | ||
| if ( | ||
| hint.originalException && | ||
| typeof hint.originalException === 'object' && | ||
| common.LOCAL_VARIABLES_KEY in hint.originalException && | ||
| Array.isArray(hint.originalException[common.LOCAL_VARIABLES_KEY]) | ||
| ) { | ||
| if (hint.originalException && typeof hint.originalException === "object" && common.LOCAL_VARIABLES_KEY in hint.originalException && Array.isArray(hint.originalException[common.LOCAL_VARIABLES_KEY])) { | ||
| for (const exception of event.exception?.values || []) { | ||
| addLocalVariablesToException(exception, hint.originalException[common.LOCAL_VARIABLES_KEY]); | ||
| } | ||
| hint.originalException[common.LOCAL_VARIABLES_KEY] = undefined; | ||
| hint.originalException[common.LOCAL_VARIABLES_KEY] = void 0; | ||
| } | ||
| return event; | ||
| } | ||
| async function startInspector() { | ||
| // We load inspector dynamically because on some platforms Node is built without inspector support | ||
| const inspector = await import('node:inspector'); | ||
@@ -79,3 +50,2 @@ if (!inspector.url()) { | ||
| } | ||
| function startWorker(options) { | ||
@@ -86,41 +56,30 @@ const worker = new node_worker_threads.Worker(new URL(`data:application/javascript;base64,${base64WorkerScript}`), { | ||
| execArgv: [], | ||
| env: { ...process.env, NODE_OPTIONS: undefined }, | ||
| env: { ...process.env, NODE_OPTIONS: void 0 } | ||
| }); | ||
| process.on('exit', () => { | ||
| // eslint-disable-next-line @typescript-eslint/no-floating-promises | ||
| process.on("exit", () => { | ||
| worker.terminate(); | ||
| }); | ||
| worker.once('error', (err) => { | ||
| log('Worker error', err); | ||
| worker.once("error", (err) => { | ||
| log("Worker error", err); | ||
| }); | ||
| worker.once('exit', (code) => { | ||
| log('Worker exit', code); | ||
| worker.once("exit", (code) => { | ||
| log("Worker exit", code); | ||
| }); | ||
| // Ensure this thread can't block app exit | ||
| worker.unref(); | ||
| } | ||
| return { | ||
| name: 'LocalVariablesAsync', | ||
| name: "LocalVariablesAsync", | ||
| async setup(client) { | ||
| const clientOptions = client.getOptions(); | ||
| if (!clientOptions.includeLocalVariables) { | ||
| return; | ||
| } | ||
| if (await debug.isDebuggerEnabled()) { | ||
| core.debug.warn('Local variables capture has been disabled because the debugger was already enabled'); | ||
| core.debug.warn("Local variables capture has been disabled because the debugger was already enabled"); | ||
| return; | ||
| } | ||
| const options = { | ||
| ...integrationOptions, | ||
| debug: core.debug.isEnabled(), | ||
| debug: core.debug.isEnabled() | ||
| }; | ||
| startInspector().then( | ||
@@ -131,8 +90,8 @@ () => { | ||
| } catch (e) { | ||
| core.debug.error('Failed to start worker', e); | ||
| core.debug.error("Failed to start worker", e); | ||
| } | ||
| }, | ||
| e => { | ||
| core.debug.error('Failed to start inspector', e); | ||
| }, | ||
| (e) => { | ||
| core.debug.error("Failed to start inspector", e); | ||
| } | ||
| ); | ||
@@ -142,5 +101,5 @@ }, | ||
| return addLocalVariablesToEvent(event, hint); | ||
| }, | ||
| } | ||
| }; | ||
| }) ); | ||
| })); | ||
@@ -147,0 +106,0 @@ exports.base64WorkerScript = base64WorkerScript; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"local-variables-async.js","sources":["../../../../src/integrations/local-variables/local-variables-async.ts"],"sourcesContent":["import { Worker } from 'node:worker_threads';\nimport type { Event, EventHint, Exception, IntegrationFn } from '@sentry/core';\nimport { debug, defineIntegration } from '@sentry/core';\nimport type { NodeClient } from '../../sdk/client';\nimport { isDebuggerEnabled } from '../../utils/debug';\nimport type { FrameVariables, LocalVariablesIntegrationOptions, LocalVariablesWorkerArgs } from './common';\nimport { functionNamesMatch, LOCAL_VARIABLES_KEY } from './common';\n\n// This string is a placeholder that gets overwritten with the worker code.\nexport const base64WorkerScript = '###LocalVariablesWorkerScript###';\n\nfunction log(...args: unknown[]): void {\n debug.log('[LocalVariables]', ...args);\n}\n\n/**\n * Adds local variables to exception frames\n */\nexport const localVariablesAsyncIntegration = defineIntegration(((\n integrationOptions: LocalVariablesIntegrationOptions = {},\n) => {\n function addLocalVariablesToException(exception: Exception, localVariables: FrameVariables[]): void {\n // Filter out frames where the function name is `new Promise` since these are in the error.stack frames\n // but do not appear in the debugger call frames\n const frames = (exception.stacktrace?.frames || []).filter(frame => frame.function !== 'new Promise');\n\n for (let i = 0; i < frames.length; i++) {\n // Sentry frames are in reverse order\n const frameIndex = frames.length - i - 1;\n\n const frameLocalVariables = localVariables[i];\n const frame = frames[frameIndex];\n\n if (!frame || !frameLocalVariables) {\n // Drop out if we run out of frames to match up\n break;\n }\n\n if (\n // We need to have vars to add\n frameLocalVariables.vars === undefined ||\n // Only skip out-of-app frames if includeOutOfAppFrames is not true\n (frame.in_app === false && integrationOptions.includeOutOfAppFrames !== true) ||\n // The function names need to match\n !functionNamesMatch(frame.function, frameLocalVariables.function)\n ) {\n continue;\n }\n\n frame.vars = frameLocalVariables.vars;\n }\n }\n\n function addLocalVariablesToEvent(event: Event, hint: EventHint): Event {\n if (\n hint.originalException &&\n typeof hint.originalException === 'object' &&\n LOCAL_VARIABLES_KEY in hint.originalException &&\n Array.isArray(hint.originalException[LOCAL_VARIABLES_KEY])\n ) {\n for (const exception of event.exception?.values || []) {\n addLocalVariablesToException(exception, hint.originalException[LOCAL_VARIABLES_KEY]);\n }\n\n hint.originalException[LOCAL_VARIABLES_KEY] = undefined;\n }\n\n return event;\n }\n\n async function startInspector(): Promise<void> {\n // We load inspector dynamically because on some platforms Node is built without inspector support\n const inspector = await import('node:inspector');\n if (!inspector.url()) {\n inspector.open(0);\n }\n }\n\n function startWorker(options: LocalVariablesWorkerArgs): void {\n const worker = new Worker(new URL(`data:application/javascript;base64,${base64WorkerScript}`), {\n workerData: options,\n // We don't want any Node args to be passed to the worker\n execArgv: [],\n env: { ...process.env, NODE_OPTIONS: undefined },\n });\n\n process.on('exit', () => {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n worker.terminate();\n });\n\n worker.once('error', (err: Error) => {\n log('Worker error', err);\n });\n\n worker.once('exit', (code: number) => {\n log('Worker exit', code);\n });\n\n // Ensure this thread can't block app exit\n worker.unref();\n }\n\n return {\n name: 'LocalVariablesAsync',\n async setup(client: NodeClient) {\n const clientOptions = client.getOptions();\n\n if (!clientOptions.includeLocalVariables) {\n return;\n }\n\n if (await isDebuggerEnabled()) {\n debug.warn('Local variables capture has been disabled because the debugger was already enabled');\n return;\n }\n\n const options: LocalVariablesWorkerArgs = {\n ...integrationOptions,\n debug: debug.isEnabled(),\n };\n\n startInspector().then(\n () => {\n try {\n startWorker(options);\n } catch (e) {\n debug.error('Failed to start worker', e);\n }\n },\n e => {\n debug.error('Failed to start inspector', e);\n },\n );\n },\n processEvent(event: Event, hint: EventHint): Event {\n return addLocalVariablesToEvent(event, hint);\n },\n };\n}) satisfies IntegrationFn);\n"],"names":["debug","defineIntegration","functionNamesMatch","LOCAL_VARIABLES_KEY","Worker","isDebuggerEnabled"],"mappings":"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"} | ||
| {"version":3,"file":"local-variables-async.js","sources":["../../../../src/integrations/local-variables/local-variables-async.ts"],"sourcesContent":["import { Worker } from 'node:worker_threads';\nimport type { Event, EventHint, Exception, IntegrationFn } from '@sentry/core';\nimport { debug, defineIntegration } from '@sentry/core';\nimport type { NodeClient } from '../../sdk/client';\nimport { isDebuggerEnabled } from '../../utils/debug';\nimport type { FrameVariables, LocalVariablesIntegrationOptions, LocalVariablesWorkerArgs } from './common';\nimport { functionNamesMatch, LOCAL_VARIABLES_KEY } from './common';\n\n// This string is a placeholder that gets overwritten with the worker code.\nexport const base64WorkerScript = '###LocalVariablesWorkerScript###';\n\nfunction log(...args: unknown[]): void {\n debug.log('[LocalVariables]', ...args);\n}\n\n/**\n * Adds local variables to exception frames\n */\nexport const localVariablesAsyncIntegration = defineIntegration(((\n integrationOptions: LocalVariablesIntegrationOptions = {},\n) => {\n function addLocalVariablesToException(exception: Exception, localVariables: FrameVariables[]): void {\n // Filter out frames where the function name is `new Promise` since these are in the error.stack frames\n // but do not appear in the debugger call frames\n const frames = (exception.stacktrace?.frames || []).filter(frame => frame.function !== 'new Promise');\n\n for (let i = 0; i < frames.length; i++) {\n // Sentry frames are in reverse order\n const frameIndex = frames.length - i - 1;\n\n const frameLocalVariables = localVariables[i];\n const frame = frames[frameIndex];\n\n if (!frame || !frameLocalVariables) {\n // Drop out if we run out of frames to match up\n break;\n }\n\n if (\n // We need to have vars to add\n frameLocalVariables.vars === undefined ||\n // Only skip out-of-app frames if includeOutOfAppFrames is not true\n (frame.in_app === false && integrationOptions.includeOutOfAppFrames !== true) ||\n // The function names need to match\n !functionNamesMatch(frame.function, frameLocalVariables.function)\n ) {\n continue;\n }\n\n frame.vars = frameLocalVariables.vars;\n }\n }\n\n function addLocalVariablesToEvent(event: Event, hint: EventHint): Event {\n if (\n hint.originalException &&\n typeof hint.originalException === 'object' &&\n LOCAL_VARIABLES_KEY in hint.originalException &&\n Array.isArray(hint.originalException[LOCAL_VARIABLES_KEY])\n ) {\n for (const exception of event.exception?.values || []) {\n addLocalVariablesToException(exception, hint.originalException[LOCAL_VARIABLES_KEY]);\n }\n\n hint.originalException[LOCAL_VARIABLES_KEY] = undefined;\n }\n\n return event;\n }\n\n async function startInspector(): Promise<void> {\n // We load inspector dynamically because on some platforms Node is built without inspector support\n const inspector = await import('node:inspector');\n if (!inspector.url()) {\n inspector.open(0);\n }\n }\n\n function startWorker(options: LocalVariablesWorkerArgs): void {\n const worker = new Worker(new URL(`data:application/javascript;base64,${base64WorkerScript}`), {\n workerData: options,\n // We don't want any Node args to be passed to the worker\n execArgv: [],\n env: { ...process.env, NODE_OPTIONS: undefined },\n });\n\n process.on('exit', () => {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n worker.terminate();\n });\n\n worker.once('error', (err: Error) => {\n log('Worker error', err);\n });\n\n worker.once('exit', (code: number) => {\n log('Worker exit', code);\n });\n\n // Ensure this thread can't block app exit\n worker.unref();\n }\n\n return {\n name: 'LocalVariablesAsync',\n async setup(client: NodeClient) {\n const clientOptions = client.getOptions();\n\n if (!clientOptions.includeLocalVariables) {\n return;\n }\n\n if (await isDebuggerEnabled()) {\n debug.warn('Local variables capture has been disabled because the debugger was already enabled');\n return;\n }\n\n const options: LocalVariablesWorkerArgs = {\n ...integrationOptions,\n debug: debug.isEnabled(),\n };\n\n startInspector().then(\n () => {\n try {\n startWorker(options);\n } catch (e) {\n debug.error('Failed to start worker', e);\n }\n },\n e => {\n debug.error('Failed to start inspector', e);\n },\n );\n },\n processEvent(event: Event, hint: EventHint): Event {\n return addLocalVariablesToEvent(event, hint);\n },\n };\n}) satisfies IntegrationFn);\n"],"names":["debug","defineIntegration","functionNamesMatch","LOCAL_VARIABLES_KEY","Worker","isDebuggerEnabled"],"mappings":"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"} |
@@ -8,29 +8,16 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| /** Creates a unique hash from stack frames */ | ||
| function hashFrames(frames) { | ||
| if (frames === undefined) { | ||
| if (frames === void 0) { | ||
| return; | ||
| } | ||
| // Only hash the 10 most recent frames (ie. the last 10) | ||
| return frames.slice(-10).reduce((acc, frame) => `${acc},${frame.function},${frame.lineno},${frame.colno}`, ''); | ||
| return frames.slice(-10).reduce((acc, frame) => `${acc},${frame.function},${frame.lineno},${frame.colno}`, ""); | ||
| } | ||
| /** | ||
| * We use the stack parser to create a unique hash from the exception stack trace | ||
| * This is used to lookup vars when the exception passes through the event processor | ||
| */ | ||
| function hashFromStack(stackParser, stack) { | ||
| if (stack === undefined) { | ||
| return undefined; | ||
| if (stack === void 0) { | ||
| return void 0; | ||
| } | ||
| return hashFrames(stackParser(stack, 1)); | ||
| } | ||
| /** Creates a container for callbacks to be called sequentially */ | ||
| function createCallbackList(complete) { | ||
| // A collection of callbacks to be executed last to first | ||
| let callbacks = []; | ||
| let completedCalled = false; | ||
@@ -45,97 +32,69 @@ function checkedComplete(result) { | ||
| } | ||
| // complete should be called last | ||
| callbacks.push(checkedComplete); | ||
| function add(fn) { | ||
| callbacks.push(fn); | ||
| } | ||
| function next(result) { | ||
| const popped = callbacks.pop() || checkedComplete; | ||
| try { | ||
| popped(result); | ||
| } catch { | ||
| // If there is an error, we still want to call the complete callback | ||
| checkedComplete(result); | ||
| } | ||
| } | ||
| return { add, next }; | ||
| } | ||
| /** | ||
| * Promise API is available as `Experimental` and in Node 19 only. | ||
| * | ||
| * Callback-based API is `Stable` since v14 and `Experimental` since v8. | ||
| * Because of that, we are creating our own `AsyncSession` class. | ||
| * | ||
| * https://nodejs.org/docs/latest-v19.x/api/inspector.html#promises-api | ||
| * https://nodejs.org/docs/latest-v14.x/api/inspector.html | ||
| */ | ||
| class AsyncSession { | ||
| class AsyncSession { | ||
| /** Throws if inspector API is not available */ | ||
| constructor( _session) {this._session = _session; | ||
| // | ||
| constructor(_session) { | ||
| this._session = _session; | ||
| } | ||
| static async create(orDefault) { | ||
| static async create(orDefault) { | ||
| if (orDefault) { | ||
| return orDefault; | ||
| } | ||
| const inspector = await import('node:inspector'); | ||
| return new AsyncSession(new inspector.Session()); | ||
| } | ||
| /** @inheritdoc */ | ||
| configureAndConnect(onPause, captureAll) { | ||
| configureAndConnect(onPause, captureAll) { | ||
| this._session.connect(); | ||
| this._session.on('Debugger.paused', event => { | ||
| this._session.on("Debugger.paused", (event) => { | ||
| onPause(event, () => { | ||
| // After the pause work is complete, resume execution or the exception context memory is leaked | ||
| this._session.post('Debugger.resume'); | ||
| this._session.post("Debugger.resume"); | ||
| }); | ||
| }); | ||
| this._session.post('Debugger.enable'); | ||
| this._session.post('Debugger.setPauseOnExceptions', { state: captureAll ? 'all' : 'uncaught' }); | ||
| this._session.post("Debugger.enable"); | ||
| this._session.post("Debugger.setPauseOnExceptions", { state: captureAll ? "all" : "uncaught" }); | ||
| } | ||
| setPauseOnExceptions(captureAll) { | ||
| this._session.post('Debugger.setPauseOnExceptions', { state: captureAll ? 'all' : 'uncaught' }); | ||
| setPauseOnExceptions(captureAll) { | ||
| this._session.post("Debugger.setPauseOnExceptions", { state: captureAll ? "all" : "uncaught" }); | ||
| } | ||
| /** @inheritdoc */ | ||
| getLocalVariables(objectId, complete) { | ||
| this._getProperties(objectId, props => { | ||
| getLocalVariables(objectId, complete) { | ||
| this._getProperties(objectId, (props) => { | ||
| const { add, next } = createCallbackList(complete); | ||
| for (const prop of props) { | ||
| if (prop.value?.objectId && prop.value.className === 'Array') { | ||
| if (prop.value?.objectId && prop.value.className === "Array") { | ||
| const id = prop.value.objectId; | ||
| add(vars => this._unrollArray(id, prop.name, vars, next)); | ||
| } else if (prop.value?.objectId && prop.value.className === 'Object') { | ||
| add((vars) => this._unrollArray(id, prop.name, vars, next)); | ||
| } else if (prop.value?.objectId && prop.value.className === "Object") { | ||
| const id = prop.value.objectId; | ||
| add(vars => this._unrollObject(id, prop.name, vars, next)); | ||
| add((vars) => this._unrollObject(id, prop.name, vars, next)); | ||
| } else if (prop.value) { | ||
| add(vars => this._unrollOther(prop, vars, next)); | ||
| add((vars) => this._unrollOther(prop, vars, next)); | ||
| } | ||
| } | ||
| next({}); | ||
| }); | ||
| } | ||
| /** | ||
| * Gets all the PropertyDescriptors of an object | ||
| */ | ||
| _getProperties(objectId, next) { | ||
| _getProperties(objectId, next) { | ||
| this._session.post( | ||
| 'Runtime.getProperties', | ||
| "Runtime.getProperties", | ||
| { | ||
| objectId, | ||
| ownProperties: true, | ||
| ownProperties: true | ||
| }, | ||
@@ -148,43 +107,33 @@ (err, params) => { | ||
| } | ||
| }, | ||
| } | ||
| ); | ||
| } | ||
| /** | ||
| * Unrolls an array property | ||
| */ | ||
| _unrollArray(objectId, name, vars, next) { | ||
| this._getProperties(objectId, props => { | ||
| vars[name] = props | ||
| .filter(v => v.name !== 'length' && !isNaN(parseInt(v.name, 10))) | ||
| .sort((a, b) => parseInt(a.name, 10) - parseInt(b.name, 10)) | ||
| .map(v => v.value?.value); | ||
| _unrollArray(objectId, name, vars, next) { | ||
| this._getProperties(objectId, (props) => { | ||
| vars[name] = props.filter((v) => v.name !== "length" && !isNaN(parseInt(v.name, 10))).sort((a, b) => parseInt(a.name, 10) - parseInt(b.name, 10)).map((v) => v.value?.value); | ||
| next(vars); | ||
| }); | ||
| } | ||
| /** | ||
| * Unrolls an object property | ||
| */ | ||
| _unrollObject(objectId, name, vars, next) { | ||
| this._getProperties(objectId, props => { | ||
| vars[name] = props | ||
| .map(v => [v.name, v.value?.value]) | ||
| .reduce((obj, [key, val]) => { | ||
| obj[key] = val; | ||
| return obj; | ||
| }, {} ); | ||
| _unrollObject(objectId, name, vars, next) { | ||
| this._getProperties(objectId, (props) => { | ||
| vars[name] = props.map((v) => [v.name, v.value?.value]).reduce((obj, [key, val]) => { | ||
| obj[key] = val; | ||
| return obj; | ||
| }, {}); | ||
| next(vars); | ||
| }); | ||
| } | ||
| /** | ||
| * Unrolls other properties | ||
| */ | ||
| _unrollOther(prop, vars, next) { | ||
| _unrollOther(prop, vars, next) { | ||
| if (prop.value) { | ||
| if ('value' in prop.value) { | ||
| if (prop.value.value === undefined || prop.value.value === null) { | ||
| if ("value" in prop.value) { | ||
| if (prop.value.value === void 0 || prop.value.value === null) { | ||
| vars[prop.name] = `<${prop.value.value}>`; | ||
@@ -194,63 +143,37 @@ } else { | ||
| } | ||
| } else if ('description' in prop.value && prop.value.type !== 'function') { | ||
| } else if ("description" in prop.value && prop.value.type !== "function") { | ||
| vars[prop.name] = `<${prop.value.description}>`; | ||
| } else if (prop.value.type === 'undefined') { | ||
| vars[prop.name] = '<undefined>'; | ||
| } else if (prop.value.type === "undefined") { | ||
| vars[prop.name] = "<undefined>"; | ||
| } | ||
| } | ||
| next(vars); | ||
| } | ||
| } | ||
| const INTEGRATION_NAME = 'LocalVariables'; | ||
| /** | ||
| * Adds local variables to exception frames | ||
| */ | ||
| const _localVariablesSyncIntegration = (( | ||
| options = {}, | ||
| sessionOverride, | ||
| ) => { | ||
| const INTEGRATION_NAME = "LocalVariables"; | ||
| const _localVariablesSyncIntegration = ((options = {}, sessionOverride) => { | ||
| const cachedFrames = new core.LRUMap(20); | ||
| let rateLimiter; | ||
| let shouldProcessEvent = false; | ||
| function addLocalVariablesToException(exception) { | ||
| const hash = hashFrames(exception.stacktrace?.frames); | ||
| if (hash === undefined) { | ||
| if (hash === void 0) { | ||
| return; | ||
| } | ||
| // Check if we have local variables for an exception that matches the hash | ||
| // remove is identical to get but also removes the entry from the cache | ||
| const cachedFrame = cachedFrames.remove(hash); | ||
| if (cachedFrame === undefined) { | ||
| if (cachedFrame === void 0) { | ||
| return; | ||
| } | ||
| // Filter out frames where the function name is `new Promise` since these are in the error.stack frames | ||
| // but do not appear in the debugger call frames | ||
| const frames = (exception.stacktrace?.frames || []).filter(frame => frame.function !== 'new Promise'); | ||
| const frames = (exception.stacktrace?.frames || []).filter((frame) => frame.function !== "new Promise"); | ||
| for (let i = 0; i < frames.length; i++) { | ||
| // Sentry frames are in reverse order | ||
| const frameIndex = frames.length - i - 1; | ||
| const cachedFrameVariable = cachedFrame[i]; | ||
| const frameVariable = frames[frameIndex]; | ||
| // Drop out if we run out of frames to match up | ||
| if (!frameVariable || !cachedFrameVariable) { | ||
| break; | ||
| } | ||
| if ( | ||
| // We need to have vars to add | ||
| cachedFrameVariable.vars === undefined || | ||
| // Only skip out-of-app frames if includeOutOfAppFrames is not true | ||
| (frameVariable.in_app === false && options.includeOutOfAppFrames !== true) || | ||
| // The function names need to match | ||
| cachedFrameVariable.vars === void 0 || // Only skip out-of-app frames if includeOutOfAppFrames is not true | ||
| frameVariable.in_app === false && options.includeOutOfAppFrames !== true || // The function names need to match | ||
| !common.functionNamesMatch(frameVariable.function, cachedFrameVariable.function) | ||
@@ -260,7 +183,5 @@ ) { | ||
| } | ||
| frameVariable.vars = cachedFrameVariable.vars; | ||
| } | ||
| } | ||
| function addLocalVariablesToEvent(event) { | ||
@@ -270,71 +191,43 @@ for (const exception of event.exception?.values || []) { | ||
| } | ||
| return event; | ||
| } | ||
| let setupPromise; | ||
| async function setup() { | ||
| const client = core.getClient(); | ||
| const clientOptions = client?.getOptions(); | ||
| if (!clientOptions?.includeLocalVariables) { | ||
| return; | ||
| } | ||
| // Only setup this integration if the Node version is >= v18 | ||
| // https://github.com/getsentry/sentry-javascript/issues/7697 | ||
| const unsupportedNodeVersion = nodeVersion.NODE_MAJOR < 18; | ||
| if (unsupportedNodeVersion) { | ||
| core.debug.log('The `LocalVariables` integration is only supported on Node >= v18.'); | ||
| core.debug.log("The `LocalVariables` integration is only supported on Node >= v18."); | ||
| return; | ||
| } | ||
| if (await debug.isDebuggerEnabled()) { | ||
| core.debug.warn('Local variables capture has been disabled because the debugger was already enabled'); | ||
| core.debug.warn("Local variables capture has been disabled because the debugger was already enabled"); | ||
| return; | ||
| } | ||
| try { | ||
| const session = await AsyncSession.create(sessionOverride); | ||
| const handlePaused = ( | ||
| stackParser, | ||
| { params: { reason, data, callFrames } }, | ||
| complete, | ||
| ) => { | ||
| if (reason !== 'exception' && reason !== 'promiseRejection') { | ||
| const handlePaused = (stackParser, { params: { reason, data, callFrames } }, complete) => { | ||
| if (reason !== "exception" && reason !== "promiseRejection") { | ||
| complete(); | ||
| return; | ||
| } | ||
| rateLimiter?.(); | ||
| // data.description contains the original error.stack | ||
| const exceptionHash = hashFromStack(stackParser, data.description); | ||
| if (exceptionHash == undefined) { | ||
| if (exceptionHash == void 0) { | ||
| complete(); | ||
| return; | ||
| } | ||
| const { add, next } = createCallbackList(frames => { | ||
| const { add, next } = createCallbackList((frames) => { | ||
| cachedFrames.set(exceptionHash, frames); | ||
| complete(); | ||
| }); | ||
| // Because we're queuing up and making all these calls synchronously, we can potentially overflow the stack | ||
| // For this reason we only attempt to get local variables for the first 5 frames | ||
| for (let i = 0; i < Math.min(callFrames.length, 5); i++) { | ||
| // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
| const { scopeChain, functionName, this: obj } = callFrames[i]; | ||
| const localScope = scopeChain.find(scope => scope.type === 'local'); | ||
| // obj.className is undefined in ESM modules | ||
| const fn = obj.className === 'global' || !obj.className ? functionName : `${obj.className}.${functionName}`; | ||
| if (localScope?.object.objectId === undefined) { | ||
| add(frames => { | ||
| const localScope = scopeChain.find((scope) => scope.type === "local"); | ||
| const fn = obj.className === "global" || !obj.className ? functionName : `${obj.className}.${functionName}`; | ||
| if (localScope?.object.objectId === void 0) { | ||
| add((frames) => { | ||
| frames[i] = { function: fn }; | ||
@@ -345,46 +238,38 @@ next(frames); | ||
| const id = localScope.object.objectId; | ||
| add(frames => | ||
| session.getLocalVariables(id, vars => { | ||
| add( | ||
| (frames) => session.getLocalVariables(id, (vars) => { | ||
| frames[i] = { function: fn, vars }; | ||
| next(frames); | ||
| }), | ||
| }) | ||
| ); | ||
| } | ||
| } | ||
| next([]); | ||
| }; | ||
| const captureAll = options.captureAllExceptions !== false; | ||
| session.configureAndConnect( | ||
| (ev, complete) => | ||
| handlePaused(clientOptions.stackParser, ev , complete), | ||
| captureAll, | ||
| (ev, complete) => handlePaused(clientOptions.stackParser, ev, complete), | ||
| captureAll | ||
| ); | ||
| if (captureAll) { | ||
| const max = options.maxExceptionsPerSecond || 50; | ||
| rateLimiter = common.createRateLimiter( | ||
| max, | ||
| () => { | ||
| core.debug.log('Local variables rate-limit lifted.'); | ||
| core.debug.log("Local variables rate-limit lifted."); | ||
| session.setPauseOnExceptions(true); | ||
| }, | ||
| seconds => { | ||
| (seconds) => { | ||
| core.debug.log( | ||
| `Local variables rate-limit exceeded. Disabling capturing of caught exceptions for ${seconds} seconds.`, | ||
| `Local variables rate-limit exceeded. Disabling capturing of caught exceptions for ${seconds} seconds.` | ||
| ); | ||
| session.setPauseOnExceptions(false); | ||
| }, | ||
| } | ||
| ); | ||
| } | ||
| shouldProcessEvent = true; | ||
| } catch (error) { | ||
| core.debug.log('The `LocalVariables` integration failed to start.', error); | ||
| core.debug.log("The `LocalVariables` integration failed to start.", error); | ||
| } | ||
| } | ||
| return { | ||
@@ -397,7 +282,5 @@ name: INTEGRATION_NAME, | ||
| await setupPromise; | ||
| if (shouldProcessEvent) { | ||
| return addLocalVariablesToEvent(event); | ||
| } | ||
| return event; | ||
@@ -411,9 +294,5 @@ }, | ||
| return cachedFrames.values()[0]; | ||
| }, | ||
| } | ||
| }; | ||
| }) ; | ||
| /** | ||
| * Adds local variables to exception frames. | ||
| */ | ||
| }); | ||
| const localVariablesSyncIntegration = core.defineIntegration(_localVariablesSyncIntegration); | ||
@@ -420,0 +299,0 @@ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"local-variables-sync.js","sources":["../../../../src/integrations/local-variables/local-variables-sync.ts"],"sourcesContent":["import type { Debugger, InspectorNotification, Runtime, Session } from 'node:inspector';\nimport type { Event, Exception, IntegrationFn, StackFrame, StackParser } from '@sentry/core';\nimport { debug, defineIntegration, getClient, LRUMap } from '@sentry/core';\nimport { NODE_MAJOR } from '../../nodeVersion';\nimport type { NodeClient } from '../../sdk/client';\nimport { isDebuggerEnabled } from '../../utils/debug';\nimport type {\n FrameVariables,\n LocalVariablesIntegrationOptions,\n PausedExceptionEvent,\n RateLimitIncrement,\n Variables,\n} from './common';\nimport { createRateLimiter, functionNamesMatch } from './common';\n\n/** Creates a unique hash from stack frames */\nexport function hashFrames(frames: StackFrame[] | undefined): string | undefined {\n if (frames === undefined) {\n return;\n }\n\n // Only hash the 10 most recent frames (ie. the last 10)\n return frames.slice(-10).reduce((acc, frame) => `${acc},${frame.function},${frame.lineno},${frame.colno}`, '');\n}\n\n/**\n * We use the stack parser to create a unique hash from the exception stack trace\n * This is used to lookup vars when the exception passes through the event processor\n */\nexport function hashFromStack(stackParser: StackParser, stack: string | undefined): string | undefined {\n if (stack === undefined) {\n return undefined;\n }\n\n return hashFrames(stackParser(stack, 1));\n}\n\ntype OnPauseEvent = InspectorNotification<Debugger.PausedEventDataType>;\nexport interface DebugSession {\n /** Configures and connects to the debug session */\n configureAndConnect(onPause: (message: OnPauseEvent, complete: () => void) => void, captureAll: boolean): void;\n /** Updates which kind of exceptions to capture */\n setPauseOnExceptions(captureAll: boolean): void;\n /** Gets local variables for an objectId */\n getLocalVariables(objectId: string, callback: (vars: Variables) => void): void;\n}\n\ntype Next<T> = (result: T) => void;\ntype Add<T> = (fn: Next<T>) => void;\ntype CallbackWrapper<T> = { add: Add<T>; next: Next<T> };\n\n/** Creates a container for callbacks to be called sequentially */\nexport function createCallbackList<T>(complete: Next<T>): CallbackWrapper<T> {\n // A collection of callbacks to be executed last to first\n let callbacks: Next<T>[] = [];\n\n let completedCalled = false;\n function checkedComplete(result: T): void {\n callbacks = [];\n if (completedCalled) {\n return;\n }\n completedCalled = true;\n complete(result);\n }\n\n // complete should be called last\n callbacks.push(checkedComplete);\n\n function add(fn: Next<T>): void {\n callbacks.push(fn);\n }\n\n function next(result: T): void {\n const popped = callbacks.pop() || checkedComplete;\n\n try {\n popped(result);\n } catch {\n // If there is an error, we still want to call the complete callback\n checkedComplete(result);\n }\n }\n\n return { add, next };\n}\n\n/**\n * Promise API is available as `Experimental` and in Node 19 only.\n *\n * Callback-based API is `Stable` since v14 and `Experimental` since v8.\n * Because of that, we are creating our own `AsyncSession` class.\n *\n * https://nodejs.org/docs/latest-v19.x/api/inspector.html#promises-api\n * https://nodejs.org/docs/latest-v14.x/api/inspector.html\n */\nclass AsyncSession implements DebugSession {\n /** Throws if inspector API is not available */\n private constructor(private readonly _session: Session) {\n //\n }\n\n public static async create(orDefault?: DebugSession | undefined): Promise<DebugSession> {\n if (orDefault) {\n return orDefault;\n }\n\n const inspector = await import('node:inspector');\n return new AsyncSession(new inspector.Session());\n }\n\n /** @inheritdoc */\n public configureAndConnect(onPause: (event: OnPauseEvent, complete: () => void) => void, captureAll: boolean): void {\n this._session.connect();\n\n this._session.on('Debugger.paused', event => {\n onPause(event, () => {\n // After the pause work is complete, resume execution or the exception context memory is leaked\n this._session.post('Debugger.resume');\n });\n });\n\n this._session.post('Debugger.enable');\n this._session.post('Debugger.setPauseOnExceptions', { state: captureAll ? 'all' : 'uncaught' });\n }\n\n public setPauseOnExceptions(captureAll: boolean): void {\n this._session.post('Debugger.setPauseOnExceptions', { state: captureAll ? 'all' : 'uncaught' });\n }\n\n /** @inheritdoc */\n public getLocalVariables(objectId: string, complete: (vars: Variables) => void): void {\n this._getProperties(objectId, props => {\n const { add, next } = createCallbackList<Variables>(complete);\n\n for (const prop of props) {\n if (prop.value?.objectId && prop.value.className === 'Array') {\n const id = prop.value.objectId;\n add(vars => this._unrollArray(id, prop.name, vars, next));\n } else if (prop.value?.objectId && prop.value.className === 'Object') {\n const id = prop.value.objectId;\n add(vars => this._unrollObject(id, prop.name, vars, next));\n } else if (prop.value) {\n add(vars => this._unrollOther(prop, vars, next));\n }\n }\n\n next({});\n });\n }\n\n /**\n * Gets all the PropertyDescriptors of an object\n */\n private _getProperties(objectId: string, next: (result: Runtime.PropertyDescriptor[]) => void): void {\n this._session.post(\n 'Runtime.getProperties',\n {\n objectId,\n ownProperties: true,\n },\n (err, params) => {\n if (err) {\n next([]);\n } else {\n next(params.result);\n }\n },\n );\n }\n\n /**\n * Unrolls an array property\n */\n private _unrollArray(objectId: string, name: string, vars: Variables, next: (vars: Variables) => void): void {\n this._getProperties(objectId, props => {\n vars[name] = props\n .filter(v => v.name !== 'length' && !isNaN(parseInt(v.name, 10)))\n .sort((a, b) => parseInt(a.name, 10) - parseInt(b.name, 10))\n .map(v => v.value?.value);\n\n next(vars);\n });\n }\n\n /**\n * Unrolls an object property\n */\n private _unrollObject(objectId: string, name: string, vars: Variables, next: (obj: Variables) => void): void {\n this._getProperties(objectId, props => {\n vars[name] = props\n .map<[string, unknown]>(v => [v.name, v.value?.value])\n .reduce((obj, [key, val]) => {\n obj[key] = val;\n return obj;\n }, {} as Variables);\n\n next(vars);\n });\n }\n\n /**\n * Unrolls other properties\n */\n private _unrollOther(prop: Runtime.PropertyDescriptor, vars: Variables, next: (vars: Variables) => void): void {\n if (prop.value) {\n if ('value' in prop.value) {\n if (prop.value.value === undefined || prop.value.value === null) {\n vars[prop.name] = `<${prop.value.value}>`;\n } else {\n vars[prop.name] = prop.value.value;\n }\n } else if ('description' in prop.value && prop.value.type !== 'function') {\n vars[prop.name] = `<${prop.value.description}>`;\n } else if (prop.value.type === 'undefined') {\n vars[prop.name] = '<undefined>';\n }\n }\n\n next(vars);\n }\n}\n\nconst INTEGRATION_NAME = 'LocalVariables';\n\n/**\n * Adds local variables to exception frames\n */\nconst _localVariablesSyncIntegration = ((\n options: LocalVariablesIntegrationOptions = {},\n sessionOverride?: DebugSession,\n) => {\n const cachedFrames: LRUMap<string, FrameVariables[]> = new LRUMap(20);\n let rateLimiter: RateLimitIncrement | undefined;\n let shouldProcessEvent = false;\n\n function addLocalVariablesToException(exception: Exception): void {\n const hash = hashFrames(exception.stacktrace?.frames);\n\n if (hash === undefined) {\n return;\n }\n\n // Check if we have local variables for an exception that matches the hash\n // remove is identical to get but also removes the entry from the cache\n const cachedFrame = cachedFrames.remove(hash);\n\n if (cachedFrame === undefined) {\n return;\n }\n\n // Filter out frames where the function name is `new Promise` since these are in the error.stack frames\n // but do not appear in the debugger call frames\n const frames = (exception.stacktrace?.frames || []).filter(frame => frame.function !== 'new Promise');\n\n for (let i = 0; i < frames.length; i++) {\n // Sentry frames are in reverse order\n const frameIndex = frames.length - i - 1;\n\n const cachedFrameVariable = cachedFrame[i];\n const frameVariable = frames[frameIndex];\n\n // Drop out if we run out of frames to match up\n if (!frameVariable || !cachedFrameVariable) {\n break;\n }\n\n if (\n // We need to have vars to add\n cachedFrameVariable.vars === undefined ||\n // Only skip out-of-app frames if includeOutOfAppFrames is not true\n (frameVariable.in_app === false && options.includeOutOfAppFrames !== true) ||\n // The function names need to match\n !functionNamesMatch(frameVariable.function, cachedFrameVariable.function)\n ) {\n continue;\n }\n\n frameVariable.vars = cachedFrameVariable.vars;\n }\n }\n\n function addLocalVariablesToEvent(event: Event): Event {\n for (const exception of event.exception?.values || []) {\n addLocalVariablesToException(exception);\n }\n\n return event;\n }\n\n let setupPromise: Promise<void> | undefined;\n\n async function setup(): Promise<void> {\n const client = getClient<NodeClient>();\n const clientOptions = client?.getOptions();\n\n if (!clientOptions?.includeLocalVariables) {\n return;\n }\n\n // Only setup this integration if the Node version is >= v18\n // https://github.com/getsentry/sentry-javascript/issues/7697\n const unsupportedNodeVersion = NODE_MAJOR < 18;\n\n if (unsupportedNodeVersion) {\n debug.log('The `LocalVariables` integration is only supported on Node >= v18.');\n return;\n }\n\n if (await isDebuggerEnabled()) {\n debug.warn('Local variables capture has been disabled because the debugger was already enabled');\n return;\n }\n\n try {\n const session = await AsyncSession.create(sessionOverride);\n\n const handlePaused = (\n stackParser: StackParser,\n { params: { reason, data, callFrames } }: InspectorNotification<PausedExceptionEvent>,\n complete: () => void,\n ): void => {\n if (reason !== 'exception' && reason !== 'promiseRejection') {\n complete();\n return;\n }\n\n rateLimiter?.();\n\n // data.description contains the original error.stack\n const exceptionHash = hashFromStack(stackParser, data.description);\n\n if (exceptionHash == undefined) {\n complete();\n return;\n }\n\n const { add, next } = createCallbackList<FrameVariables[]>(frames => {\n cachedFrames.set(exceptionHash, frames);\n complete();\n });\n\n // Because we're queuing up and making all these calls synchronously, we can potentially overflow the stack\n // For this reason we only attempt to get local variables for the first 5 frames\n for (let i = 0; i < Math.min(callFrames.length, 5); i++) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const { scopeChain, functionName, this: obj } = callFrames[i]!;\n\n const localScope = scopeChain.find(scope => scope.type === 'local');\n\n // obj.className is undefined in ESM modules\n const fn = obj.className === 'global' || !obj.className ? functionName : `${obj.className}.${functionName}`;\n\n if (localScope?.object.objectId === undefined) {\n add(frames => {\n frames[i] = { function: fn };\n next(frames);\n });\n } else {\n const id = localScope.object.objectId;\n add(frames =>\n session.getLocalVariables(id, vars => {\n frames[i] = { function: fn, vars };\n next(frames);\n }),\n );\n }\n }\n\n next([]);\n };\n\n const captureAll = options.captureAllExceptions !== false;\n\n session.configureAndConnect(\n (ev, complete) =>\n handlePaused(clientOptions.stackParser, ev as InspectorNotification<PausedExceptionEvent>, complete),\n captureAll,\n );\n\n if (captureAll) {\n const max = options.maxExceptionsPerSecond || 50;\n\n rateLimiter = createRateLimiter(\n max,\n () => {\n debug.log('Local variables rate-limit lifted.');\n session.setPauseOnExceptions(true);\n },\n seconds => {\n debug.log(\n `Local variables rate-limit exceeded. Disabling capturing of caught exceptions for ${seconds} seconds.`,\n );\n session.setPauseOnExceptions(false);\n },\n );\n }\n\n shouldProcessEvent = true;\n } catch (error) {\n debug.log('The `LocalVariables` integration failed to start.', error);\n }\n }\n\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n setupPromise = setup();\n },\n async processEvent(event: Event): Promise<Event> {\n await setupPromise;\n\n if (shouldProcessEvent) {\n return addLocalVariablesToEvent(event);\n }\n\n return event;\n },\n // These are entirely for testing\n _getCachedFramesCount(): number {\n return cachedFrames.size;\n },\n _getFirstCachedFrame(): FrameVariables[] | undefined {\n return cachedFrames.values()[0];\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Adds local variables to exception frames.\n */\nexport const localVariablesSyncIntegration = defineIntegration(_localVariablesSyncIntegration);\n"],"names":["LRUMap","functionNamesMatch","getClient","NODE_MAJOR","debug","isDebuggerEnabled","createRateLimiter","defineIntegration"],"mappings":"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"} | ||
| {"version":3,"file":"local-variables-sync.js","sources":["../../../../src/integrations/local-variables/local-variables-sync.ts"],"sourcesContent":["import type { Debugger, InspectorNotification, Runtime, Session } from 'node:inspector';\nimport type { Event, Exception, IntegrationFn, StackFrame, StackParser } from '@sentry/core';\nimport { debug, defineIntegration, getClient, LRUMap } from '@sentry/core';\nimport { NODE_MAJOR } from '../../nodeVersion';\nimport type { NodeClient } from '../../sdk/client';\nimport { isDebuggerEnabled } from '../../utils/debug';\nimport type {\n FrameVariables,\n LocalVariablesIntegrationOptions,\n PausedExceptionEvent,\n RateLimitIncrement,\n Variables,\n} from './common';\nimport { createRateLimiter, functionNamesMatch } from './common';\n\n/** Creates a unique hash from stack frames */\nexport function hashFrames(frames: StackFrame[] | undefined): string | undefined {\n if (frames === undefined) {\n return;\n }\n\n // Only hash the 10 most recent frames (ie. the last 10)\n return frames.slice(-10).reduce((acc, frame) => `${acc},${frame.function},${frame.lineno},${frame.colno}`, '');\n}\n\n/**\n * We use the stack parser to create a unique hash from the exception stack trace\n * This is used to lookup vars when the exception passes through the event processor\n */\nexport function hashFromStack(stackParser: StackParser, stack: string | undefined): string | undefined {\n if (stack === undefined) {\n return undefined;\n }\n\n return hashFrames(stackParser(stack, 1));\n}\n\ntype OnPauseEvent = InspectorNotification<Debugger.PausedEventDataType>;\nexport interface DebugSession {\n /** Configures and connects to the debug session */\n configureAndConnect(onPause: (message: OnPauseEvent, complete: () => void) => void, captureAll: boolean): void;\n /** Updates which kind of exceptions to capture */\n setPauseOnExceptions(captureAll: boolean): void;\n /** Gets local variables for an objectId */\n getLocalVariables(objectId: string, callback: (vars: Variables) => void): void;\n}\n\ntype Next<T> = (result: T) => void;\ntype Add<T> = (fn: Next<T>) => void;\ntype CallbackWrapper<T> = { add: Add<T>; next: Next<T> };\n\n/** Creates a container for callbacks to be called sequentially */\nexport function createCallbackList<T>(complete: Next<T>): CallbackWrapper<T> {\n // A collection of callbacks to be executed last to first\n let callbacks: Next<T>[] = [];\n\n let completedCalled = false;\n function checkedComplete(result: T): void {\n callbacks = [];\n if (completedCalled) {\n return;\n }\n completedCalled = true;\n complete(result);\n }\n\n // complete should be called last\n callbacks.push(checkedComplete);\n\n function add(fn: Next<T>): void {\n callbacks.push(fn);\n }\n\n function next(result: T): void {\n const popped = callbacks.pop() || checkedComplete;\n\n try {\n popped(result);\n } catch {\n // If there is an error, we still want to call the complete callback\n checkedComplete(result);\n }\n }\n\n return { add, next };\n}\n\n/**\n * Promise API is available as `Experimental` and in Node 19 only.\n *\n * Callback-based API is `Stable` since v14 and `Experimental` since v8.\n * Because of that, we are creating our own `AsyncSession` class.\n *\n * https://nodejs.org/docs/latest-v19.x/api/inspector.html#promises-api\n * https://nodejs.org/docs/latest-v14.x/api/inspector.html\n */\nclass AsyncSession implements DebugSession {\n /** Throws if inspector API is not available */\n private constructor(private readonly _session: Session) {\n //\n }\n\n public static async create(orDefault?: DebugSession | undefined): Promise<DebugSession> {\n if (orDefault) {\n return orDefault;\n }\n\n const inspector = await import('node:inspector');\n return new AsyncSession(new inspector.Session());\n }\n\n /** @inheritdoc */\n public configureAndConnect(onPause: (event: OnPauseEvent, complete: () => void) => void, captureAll: boolean): void {\n this._session.connect();\n\n this._session.on('Debugger.paused', event => {\n onPause(event, () => {\n // After the pause work is complete, resume execution or the exception context memory is leaked\n this._session.post('Debugger.resume');\n });\n });\n\n this._session.post('Debugger.enable');\n this._session.post('Debugger.setPauseOnExceptions', { state: captureAll ? 'all' : 'uncaught' });\n }\n\n public setPauseOnExceptions(captureAll: boolean): void {\n this._session.post('Debugger.setPauseOnExceptions', { state: captureAll ? 'all' : 'uncaught' });\n }\n\n /** @inheritdoc */\n public getLocalVariables(objectId: string, complete: (vars: Variables) => void): void {\n this._getProperties(objectId, props => {\n const { add, next } = createCallbackList<Variables>(complete);\n\n for (const prop of props) {\n if (prop.value?.objectId && prop.value.className === 'Array') {\n const id = prop.value.objectId;\n add(vars => this._unrollArray(id, prop.name, vars, next));\n } else if (prop.value?.objectId && prop.value.className === 'Object') {\n const id = prop.value.objectId;\n add(vars => this._unrollObject(id, prop.name, vars, next));\n } else if (prop.value) {\n add(vars => this._unrollOther(prop, vars, next));\n }\n }\n\n next({});\n });\n }\n\n /**\n * Gets all the PropertyDescriptors of an object\n */\n private _getProperties(objectId: string, next: (result: Runtime.PropertyDescriptor[]) => void): void {\n this._session.post(\n 'Runtime.getProperties',\n {\n objectId,\n ownProperties: true,\n },\n (err, params) => {\n if (err) {\n next([]);\n } else {\n next(params.result);\n }\n },\n );\n }\n\n /**\n * Unrolls an array property\n */\n private _unrollArray(objectId: string, name: string, vars: Variables, next: (vars: Variables) => void): void {\n this._getProperties(objectId, props => {\n vars[name] = props\n .filter(v => v.name !== 'length' && !isNaN(parseInt(v.name, 10)))\n .sort((a, b) => parseInt(a.name, 10) - parseInt(b.name, 10))\n .map(v => v.value?.value);\n\n next(vars);\n });\n }\n\n /**\n * Unrolls an object property\n */\n private _unrollObject(objectId: string, name: string, vars: Variables, next: (obj: Variables) => void): void {\n this._getProperties(objectId, props => {\n vars[name] = props\n .map<[string, unknown]>(v => [v.name, v.value?.value])\n .reduce((obj, [key, val]) => {\n obj[key] = val;\n return obj;\n }, {} as Variables);\n\n next(vars);\n });\n }\n\n /**\n * Unrolls other properties\n */\n private _unrollOther(prop: Runtime.PropertyDescriptor, vars: Variables, next: (vars: Variables) => void): void {\n if (prop.value) {\n if ('value' in prop.value) {\n if (prop.value.value === undefined || prop.value.value === null) {\n vars[prop.name] = `<${prop.value.value}>`;\n } else {\n vars[prop.name] = prop.value.value;\n }\n } else if ('description' in prop.value && prop.value.type !== 'function') {\n vars[prop.name] = `<${prop.value.description}>`;\n } else if (prop.value.type === 'undefined') {\n vars[prop.name] = '<undefined>';\n }\n }\n\n next(vars);\n }\n}\n\nconst INTEGRATION_NAME = 'LocalVariables';\n\n/**\n * Adds local variables to exception frames\n */\nconst _localVariablesSyncIntegration = ((\n options: LocalVariablesIntegrationOptions = {},\n sessionOverride?: DebugSession,\n) => {\n const cachedFrames: LRUMap<string, FrameVariables[]> = new LRUMap(20);\n let rateLimiter: RateLimitIncrement | undefined;\n let shouldProcessEvent = false;\n\n function addLocalVariablesToException(exception: Exception): void {\n const hash = hashFrames(exception.stacktrace?.frames);\n\n if (hash === undefined) {\n return;\n }\n\n // Check if we have local variables for an exception that matches the hash\n // remove is identical to get but also removes the entry from the cache\n const cachedFrame = cachedFrames.remove(hash);\n\n if (cachedFrame === undefined) {\n return;\n }\n\n // Filter out frames where the function name is `new Promise` since these are in the error.stack frames\n // but do not appear in the debugger call frames\n const frames = (exception.stacktrace?.frames || []).filter(frame => frame.function !== 'new Promise');\n\n for (let i = 0; i < frames.length; i++) {\n // Sentry frames are in reverse order\n const frameIndex = frames.length - i - 1;\n\n const cachedFrameVariable = cachedFrame[i];\n const frameVariable = frames[frameIndex];\n\n // Drop out if we run out of frames to match up\n if (!frameVariable || !cachedFrameVariable) {\n break;\n }\n\n if (\n // We need to have vars to add\n cachedFrameVariable.vars === undefined ||\n // Only skip out-of-app frames if includeOutOfAppFrames is not true\n (frameVariable.in_app === false && options.includeOutOfAppFrames !== true) ||\n // The function names need to match\n !functionNamesMatch(frameVariable.function, cachedFrameVariable.function)\n ) {\n continue;\n }\n\n frameVariable.vars = cachedFrameVariable.vars;\n }\n }\n\n function addLocalVariablesToEvent(event: Event): Event {\n for (const exception of event.exception?.values || []) {\n addLocalVariablesToException(exception);\n }\n\n return event;\n }\n\n let setupPromise: Promise<void> | undefined;\n\n async function setup(): Promise<void> {\n const client = getClient<NodeClient>();\n const clientOptions = client?.getOptions();\n\n if (!clientOptions?.includeLocalVariables) {\n return;\n }\n\n // Only setup this integration if the Node version is >= v18\n // https://github.com/getsentry/sentry-javascript/issues/7697\n const unsupportedNodeVersion = NODE_MAJOR < 18;\n\n if (unsupportedNodeVersion) {\n debug.log('The `LocalVariables` integration is only supported on Node >= v18.');\n return;\n }\n\n if (await isDebuggerEnabled()) {\n debug.warn('Local variables capture has been disabled because the debugger was already enabled');\n return;\n }\n\n try {\n const session = await AsyncSession.create(sessionOverride);\n\n const handlePaused = (\n stackParser: StackParser,\n { params: { reason, data, callFrames } }: InspectorNotification<PausedExceptionEvent>,\n complete: () => void,\n ): void => {\n if (reason !== 'exception' && reason !== 'promiseRejection') {\n complete();\n return;\n }\n\n rateLimiter?.();\n\n // data.description contains the original error.stack\n const exceptionHash = hashFromStack(stackParser, data.description);\n\n if (exceptionHash == undefined) {\n complete();\n return;\n }\n\n const { add, next } = createCallbackList<FrameVariables[]>(frames => {\n cachedFrames.set(exceptionHash, frames);\n complete();\n });\n\n // Because we're queuing up and making all these calls synchronously, we can potentially overflow the stack\n // For this reason we only attempt to get local variables for the first 5 frames\n for (let i = 0; i < Math.min(callFrames.length, 5); i++) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const { scopeChain, functionName, this: obj } = callFrames[i]!;\n\n const localScope = scopeChain.find(scope => scope.type === 'local');\n\n // obj.className is undefined in ESM modules\n const fn = obj.className === 'global' || !obj.className ? functionName : `${obj.className}.${functionName}`;\n\n if (localScope?.object.objectId === undefined) {\n add(frames => {\n frames[i] = { function: fn };\n next(frames);\n });\n } else {\n const id = localScope.object.objectId;\n add(frames =>\n session.getLocalVariables(id, vars => {\n frames[i] = { function: fn, vars };\n next(frames);\n }),\n );\n }\n }\n\n next([]);\n };\n\n const captureAll = options.captureAllExceptions !== false;\n\n session.configureAndConnect(\n (ev, complete) =>\n handlePaused(clientOptions.stackParser, ev as InspectorNotification<PausedExceptionEvent>, complete),\n captureAll,\n );\n\n if (captureAll) {\n const max = options.maxExceptionsPerSecond || 50;\n\n rateLimiter = createRateLimiter(\n max,\n () => {\n debug.log('Local variables rate-limit lifted.');\n session.setPauseOnExceptions(true);\n },\n seconds => {\n debug.log(\n `Local variables rate-limit exceeded. Disabling capturing of caught exceptions for ${seconds} seconds.`,\n );\n session.setPauseOnExceptions(false);\n },\n );\n }\n\n shouldProcessEvent = true;\n } catch (error) {\n debug.log('The `LocalVariables` integration failed to start.', error);\n }\n }\n\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n setupPromise = setup();\n },\n async processEvent(event: Event): Promise<Event> {\n await setupPromise;\n\n if (shouldProcessEvent) {\n return addLocalVariablesToEvent(event);\n }\n\n return event;\n },\n // These are entirely for testing\n _getCachedFramesCount(): number {\n return cachedFrames.size;\n },\n _getFirstCachedFrame(): FrameVariables[] | undefined {\n return cachedFrames.values()[0];\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Adds local variables to exception frames.\n */\nexport const localVariablesSyncIntegration = defineIntegration(_localVariablesSyncIntegration);\n"],"names":["LRUMap","functionNamesMatch","getClient","NODE_MAJOR","debug","isDebuggerEnabled","createRateLimiter","defineIntegration"],"mappings":"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"} |
@@ -8,11 +8,4 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| let moduleCache; | ||
| const INTEGRATION_NAME = 'Modules'; | ||
| /** | ||
| * `__SENTRY_SERVER_MODULES__` can be replaced at build time with the modules loaded by the server. | ||
| * Right now, we leverage this in Next.js to circumvent the problem that we do not get access to these things at runtime. | ||
| */ | ||
| const SERVER_MODULES = typeof __SENTRY_SERVER_MODULES__ === 'undefined' ? {} : __SENTRY_SERVER_MODULES__; | ||
| const INTEGRATION_NAME = "Modules"; | ||
| const SERVER_MODULES = typeof __SENTRY_SERVER_MODULES__ === "undefined" ? {} : __SENTRY_SERVER_MODULES__; | ||
| const _modulesIntegration = (() => { | ||
@@ -24,23 +17,13 @@ return { | ||
| ...event.modules, | ||
| ..._getModules(), | ||
| ..._getModules() | ||
| }; | ||
| return event; | ||
| }, | ||
| getModules: _getModules, | ||
| getModules: _getModules | ||
| }; | ||
| }) ; | ||
| /** | ||
| * Add node modules / packages to the event. | ||
| * For this, multiple sources are used: | ||
| * - They can be injected at build time into the __SENTRY_SERVER_MODULES__ variable (e.g. in Next.js) | ||
| * - They are extracted from the dependencies & devDependencies in the package.json file | ||
| * - They are extracted from the require.cache (CJS only) | ||
| */ | ||
| }); | ||
| const modulesIntegration = _modulesIntegration; | ||
| function getRequireCachePaths() { | ||
| try { | ||
| return require.cache ? Object.keys(require.cache ) : []; | ||
| return require.cache ? Object.keys(require.cache) : []; | ||
| } catch { | ||
@@ -50,4 +33,2 @@ return []; | ||
| } | ||
| /** Extract information about package.json modules */ | ||
| function collectModules() { | ||
@@ -57,26 +38,17 @@ return { | ||
| ...getModulesFromPackageJson(), | ||
| ...(detection.isCjs() ? collectRequireModules() : {}), | ||
| ...detection.isCjs() ? collectRequireModules() : {} | ||
| }; | ||
| } | ||
| /** Extract information about package.json modules from require.cache */ | ||
| function collectRequireModules() { | ||
| const mainPaths = require.main?.paths || []; | ||
| const paths = getRequireCachePaths(); | ||
| // We start with the modules from package.json (if possible) | ||
| // These may be overwritten by more specific versions from the require.cache | ||
| const infos = {}; | ||
| const seen = new Set(); | ||
| paths.forEach(path => { | ||
| const seen = /* @__PURE__ */ new Set(); | ||
| paths.forEach((path) => { | ||
| let dir = path; | ||
| /** Traverse directories upward in the search of package.json file */ | ||
| const updir = () => { | ||
| const orig = dir; | ||
| dir = node_path.dirname(orig); | ||
| if (!dir || orig === dir || seen.has(orig)) { | ||
| return undefined; | ||
| return void 0; | ||
| } | ||
@@ -86,27 +58,17 @@ if (mainPaths.indexOf(dir) < 0) { | ||
| } | ||
| const pkgfile = node_path.join(orig, 'package.json'); | ||
| const pkgfile = node_path.join(orig, "package.json"); | ||
| seen.add(orig); | ||
| if (!node_fs.existsSync(pkgfile)) { | ||
| return updir(); | ||
| } | ||
| try { | ||
| const info = JSON.parse(node_fs.readFileSync(pkgfile, 'utf8')) | ||
| ; | ||
| const info = JSON.parse(node_fs.readFileSync(pkgfile, "utf8")); | ||
| infos[info.name] = info.version; | ||
| } catch { | ||
| // no-empty | ||
| } | ||
| }; | ||
| updir(); | ||
| }); | ||
| return infos; | ||
| } | ||
| /** Fetches the list of modules and the versions loaded by the entry file for your node.js app. */ | ||
| function _getModules() { | ||
@@ -118,8 +80,6 @@ if (!moduleCache) { | ||
| } | ||
| function getPackageJson() { | ||
| try { | ||
| const filePath = node_path.join(process.cwd(), 'package.json'); | ||
| const packageJson = JSON.parse(node_fs.readFileSync(filePath, 'utf8')) ; | ||
| const filePath = node_path.join(process.cwd(), "package.json"); | ||
| const packageJson = JSON.parse(node_fs.readFileSync(filePath, "utf8")); | ||
| return packageJson; | ||
@@ -130,9 +90,7 @@ } catch { | ||
| } | ||
| function getModulesFromPackageJson() { | ||
| const packageJson = getPackageJson(); | ||
| return { | ||
| ...packageJson.dependencies, | ||
| ...packageJson.devDependencies, | ||
| ...packageJson.devDependencies | ||
| }; | ||
@@ -139,0 +97,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"modules.js","sources":["../../../src/integrations/modules.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport type { IntegrationFn } from '@sentry/core';\nimport { isCjs } from '../utils/detection';\n\ntype ModuleInfo = Record<string, string>;\n\nlet moduleCache: ModuleInfo | undefined;\n\nconst INTEGRATION_NAME = 'Modules';\n\ndeclare const __SENTRY_SERVER_MODULES__: Record<string, string>;\n\n/**\n * `__SENTRY_SERVER_MODULES__` can be replaced at build time with the modules loaded by the server.\n * Right now, we leverage this in Next.js to circumvent the problem that we do not get access to these things at runtime.\n */\nconst SERVER_MODULES = typeof __SENTRY_SERVER_MODULES__ === 'undefined' ? {} : __SENTRY_SERVER_MODULES__;\n\nconst _modulesIntegration = (() => {\n return {\n name: INTEGRATION_NAME,\n processEvent(event) {\n event.modules = {\n ...event.modules,\n ..._getModules(),\n };\n\n return event;\n },\n getModules: _getModules,\n };\n}) satisfies IntegrationFn;\n\n/**\n * Add node modules / packages to the event.\n * For this, multiple sources are used:\n * - They can be injected at build time into the __SENTRY_SERVER_MODULES__ variable (e.g. in Next.js)\n * - They are extracted from the dependencies & devDependencies in the package.json file\n * - They are extracted from the require.cache (CJS only)\n */\nexport const modulesIntegration = _modulesIntegration;\n\nfunction getRequireCachePaths(): string[] {\n try {\n return require.cache ? Object.keys(require.cache as Record<string, unknown>) : [];\n } catch {\n return [];\n }\n}\n\n/** Extract information about package.json modules */\nfunction collectModules(): ModuleInfo {\n return {\n ...SERVER_MODULES,\n ...getModulesFromPackageJson(),\n ...(isCjs() ? collectRequireModules() : {}),\n };\n}\n\n/** Extract information about package.json modules from require.cache */\nfunction collectRequireModules(): ModuleInfo {\n const mainPaths = require.main?.paths || [];\n const paths = getRequireCachePaths();\n\n // We start with the modules from package.json (if possible)\n // These may be overwritten by more specific versions from the require.cache\n const infos: ModuleInfo = {};\n const seen = new Set<string>();\n\n paths.forEach(path => {\n let dir = path;\n\n /** Traverse directories upward in the search of package.json file */\n const updir = (): void | (() => void) => {\n const orig = dir;\n dir = dirname(orig);\n\n if (!dir || orig === dir || seen.has(orig)) {\n return undefined;\n }\n if (mainPaths.indexOf(dir) < 0) {\n return updir();\n }\n\n const pkgfile = join(orig, 'package.json');\n seen.add(orig);\n\n if (!existsSync(pkgfile)) {\n return updir();\n }\n\n try {\n const info = JSON.parse(readFileSync(pkgfile, 'utf8')) as {\n name: string;\n version: string;\n };\n infos[info.name] = info.version;\n } catch {\n // no-empty\n }\n };\n\n updir();\n });\n\n return infos;\n}\n\n/** Fetches the list of modules and the versions loaded by the entry file for your node.js app. */\nfunction _getModules(): ModuleInfo {\n if (!moduleCache) {\n moduleCache = collectModules();\n }\n return moduleCache;\n}\n\ninterface PackageJson {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n}\n\nfunction getPackageJson(): PackageJson {\n try {\n const filePath = join(process.cwd(), 'package.json');\n const packageJson = JSON.parse(readFileSync(filePath, 'utf8')) as PackageJson;\n\n return packageJson;\n } catch {\n return {};\n }\n}\n\nfunction getModulesFromPackageJson(): ModuleInfo {\n const packageJson = getPackageJson();\n\n return {\n ...packageJson.dependencies,\n ...packageJson.devDependencies,\n };\n}\n"],"names":["isCjs","dirname","join","existsSync","readFileSync"],"mappings":"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"} | ||
| {"version":3,"file":"modules.js","sources":["../../../src/integrations/modules.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport type { IntegrationFn } from '@sentry/core';\nimport { isCjs } from '../utils/detection';\n\ntype ModuleInfo = Record<string, string>;\n\nlet moduleCache: ModuleInfo | undefined;\n\nconst INTEGRATION_NAME = 'Modules';\n\ndeclare const __SENTRY_SERVER_MODULES__: Record<string, string>;\n\n/**\n * `__SENTRY_SERVER_MODULES__` can be replaced at build time with the modules loaded by the server.\n * Right now, we leverage this in Next.js to circumvent the problem that we do not get access to these things at runtime.\n */\nconst SERVER_MODULES = typeof __SENTRY_SERVER_MODULES__ === 'undefined' ? {} : __SENTRY_SERVER_MODULES__;\n\nconst _modulesIntegration = (() => {\n return {\n name: INTEGRATION_NAME,\n processEvent(event) {\n event.modules = {\n ...event.modules,\n ..._getModules(),\n };\n\n return event;\n },\n getModules: _getModules,\n };\n}) satisfies IntegrationFn;\n\n/**\n * Add node modules / packages to the event.\n * For this, multiple sources are used:\n * - They can be injected at build time into the __SENTRY_SERVER_MODULES__ variable (e.g. in Next.js)\n * - They are extracted from the dependencies & devDependencies in the package.json file\n * - They are extracted from the require.cache (CJS only)\n */\nexport const modulesIntegration = _modulesIntegration;\n\nfunction getRequireCachePaths(): string[] {\n try {\n return require.cache ? Object.keys(require.cache as Record<string, unknown>) : [];\n } catch {\n return [];\n }\n}\n\n/** Extract information about package.json modules */\nfunction collectModules(): ModuleInfo {\n return {\n ...SERVER_MODULES,\n ...getModulesFromPackageJson(),\n ...(isCjs() ? collectRequireModules() : {}),\n };\n}\n\n/** Extract information about package.json modules from require.cache */\nfunction collectRequireModules(): ModuleInfo {\n const mainPaths = require.main?.paths || [];\n const paths = getRequireCachePaths();\n\n // We start with the modules from package.json (if possible)\n // These may be overwritten by more specific versions from the require.cache\n const infos: ModuleInfo = {};\n const seen = new Set<string>();\n\n paths.forEach(path => {\n let dir = path;\n\n /** Traverse directories upward in the search of package.json file */\n const updir = (): void | (() => void) => {\n const orig = dir;\n dir = dirname(orig);\n\n if (!dir || orig === dir || seen.has(orig)) {\n return undefined;\n }\n if (mainPaths.indexOf(dir) < 0) {\n return updir();\n }\n\n const pkgfile = join(orig, 'package.json');\n seen.add(orig);\n\n if (!existsSync(pkgfile)) {\n return updir();\n }\n\n try {\n const info = JSON.parse(readFileSync(pkgfile, 'utf8')) as {\n name: string;\n version: string;\n };\n infos[info.name] = info.version;\n } catch {\n // no-empty\n }\n };\n\n updir();\n });\n\n return infos;\n}\n\n/** Fetches the list of modules and the versions loaded by the entry file for your node.js app. */\nfunction _getModules(): ModuleInfo {\n if (!moduleCache) {\n moduleCache = collectModules();\n }\n return moduleCache;\n}\n\ninterface PackageJson {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n}\n\nfunction getPackageJson(): PackageJson {\n try {\n const filePath = join(process.cwd(), 'package.json');\n const packageJson = JSON.parse(readFileSync(filePath, 'utf8')) as PackageJson;\n\n return packageJson;\n } catch {\n return {};\n }\n}\n\nfunction getModulesFromPackageJson(): ModuleInfo {\n const packageJson = getPackageJson();\n\n return {\n ...packageJson.dependencies,\n ...packageJson.devDependencies,\n };\n}\n"],"names":["isCjs","dirname","join","existsSync","readFileSync"],"mappings":"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"} |
@@ -7,4 +7,3 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const INTEGRATION_NAME = 'NodeFetch'; | ||
| const INTEGRATION_NAME = "NodeFetch"; | ||
| const instrumentSentryNodeFetch = instrument.generateInstrumentOnce( | ||
@@ -15,14 +14,12 @@ `${INTEGRATION_NAME}.sentry`, | ||
| return options; | ||
| }, | ||
| } | ||
| ); | ||
| const _nativeNodeFetchIntegration = ((options = {}) => { | ||
| return { | ||
| name: 'NodeFetch', | ||
| name: "NodeFetch", | ||
| setupOnce() { | ||
| instrumentSentryNodeFetch(options); | ||
| }, | ||
| } | ||
| }; | ||
| }) ; | ||
| }); | ||
| const nativeNodeFetchIntegration = core.defineIntegration(_nativeNodeFetchIntegration); | ||
@@ -29,0 +26,0 @@ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.js","sources":["../../../../src/integrations/node-fetch/index.ts"],"sourcesContent":["import type { IntegrationFn } from '@sentry/core';\nimport { defineIntegration } from '@sentry/core';\nimport { generateInstrumentOnce } from '../../otel/instrument';\nimport { SentryNodeFetchInstrumentation } from './SentryNodeFetchInstrumentation';\n\nconst INTEGRATION_NAME = 'NodeFetch';\n\ninterface NodeFetchOptions {\n /**\n * Whether breadcrumbs should be recorded for requests.\n * Defaults to true\n */\n breadcrumbs?: boolean;\n\n /**\n * Whether to inject trace propagation headers (sentry-trace, baggage, traceparent) into outgoing fetch requests.\n *\n * @default `true`\n */\n tracePropagation?: boolean;\n\n /**\n * Do not capture spans or breadcrumbs for outgoing fetch requests to URLs where the given callback returns `true`.\n * This controls both span & breadcrumb creation - spans will be non recording if tracing is disabled.\n */\n ignoreOutgoingRequests?: (url: string) => boolean;\n}\n\nconst instrumentSentryNodeFetch = generateInstrumentOnce(\n `${INTEGRATION_NAME}.sentry`,\n SentryNodeFetchInstrumentation,\n (options: NodeFetchOptions) => {\n return options;\n },\n);\n\nconst _nativeNodeFetchIntegration = ((options: NodeFetchOptions = {}) => {\n return {\n name: 'NodeFetch',\n setupOnce() {\n instrumentSentryNodeFetch(options);\n },\n };\n}) satisfies IntegrationFn;\n\nexport const nativeNodeFetchIntegration = defineIntegration(_nativeNodeFetchIntegration);\n"],"names":["generateInstrumentOnce","SentryNodeFetchInstrumentation","defineIntegration"],"mappings":";;;;;;AAKA,MAAM,gBAAA,GAAmB,WAAW;;AAuBpC,MAAM,yBAAA,GAA4BA,iCAAsB;AACxD,EAAE,CAAC,EAAA,gBAAA,CAAA,OAAA,CAAA;AACA,EAAAC,6DAAA;AACA,EAAA,CAAA,OAAA,KAAA;AACA,IAAA,OAAA,OAAA;AACA,EAAA,CAAA;AACA,CAAA;;AAEA,MAAA,2BAAA,IAAA,CAAA,OAAA,GAAA,EAAA,KAAA;AACA,EAAA,OAAA;AACA,IAAA,IAAA,EAAA,WAAA;AACA,IAAA,SAAA,GAAA;AACA,MAAA,yBAAA,CAAA,OAAA,CAAA;AACA,IAAA,CAAA;AACA,GAAA;AACA,CAAA,CAAA;;AAEA,MAAA,0BAAA,GAAAC,sBAAA,CAAA,2BAAA;;;;"} | ||
| {"version":3,"file":"index.js","sources":["../../../../src/integrations/node-fetch/index.ts"],"sourcesContent":["import type { IntegrationFn } from '@sentry/core';\nimport { defineIntegration } from '@sentry/core';\nimport { generateInstrumentOnce } from '../../otel/instrument';\nimport { SentryNodeFetchInstrumentation } from './SentryNodeFetchInstrumentation';\n\nconst INTEGRATION_NAME = 'NodeFetch';\n\ninterface NodeFetchOptions {\n /**\n * Whether breadcrumbs should be recorded for requests.\n * Defaults to true\n */\n breadcrumbs?: boolean;\n\n /**\n * Whether to inject trace propagation headers (sentry-trace, baggage, traceparent) into outgoing fetch requests.\n *\n * @default `true`\n */\n tracePropagation?: boolean;\n\n /**\n * Do not capture spans or breadcrumbs for outgoing fetch requests to URLs where the given callback returns `true`.\n * This controls both span & breadcrumb creation - spans will be non recording if tracing is disabled.\n */\n ignoreOutgoingRequests?: (url: string) => boolean;\n}\n\nconst instrumentSentryNodeFetch = generateInstrumentOnce(\n `${INTEGRATION_NAME}.sentry`,\n SentryNodeFetchInstrumentation,\n (options: NodeFetchOptions) => {\n return options;\n },\n);\n\nconst _nativeNodeFetchIntegration = ((options: NodeFetchOptions = {}) => {\n return {\n name: 'NodeFetch',\n setupOnce() {\n instrumentSentryNodeFetch(options);\n },\n };\n}) satisfies IntegrationFn;\n\nexport const nativeNodeFetchIntegration = defineIntegration(_nativeNodeFetchIntegration);\n"],"names":["generateInstrumentOnce","SentryNodeFetchInstrumentation","defineIntegration"],"mappings":";;;;;;AAKA,MAAM,gBAAA,GAAmB,WAAA;AAuBzB,MAAM,yBAAA,GAA4BA,iCAAA;AAAA,EAChC,GAAG,gBAAgB,CAAA,OAAA,CAAA;AAAA,EACnBC,6DAAA;AAAA,EACA,CAAC,OAAA,KAA8B;AAC7B,IAAA,OAAO,OAAA;AAAA,EACT;AACF,CAAA;AAEA,MAAM,2BAAA,IAA+B,CAAC,OAAA,GAA4B,EAAC,KAAM;AACvE,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,WAAA;AAAA,IACN,SAAA,GAAY;AACV,MAAA,yBAAA,CAA0B,OAAO,CAAA;AAAA,IACnC;AAAA,GACF;AACF,CAAA,CAAA;AAEO,MAAM,0BAAA,GAA6BC,uBAAkB,2BAA2B;;;;"} |
@@ -11,61 +11,29 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| /** | ||
| * This custom node-fetch instrumentation is used to instrument outgoing fetch requests. | ||
| * It does not emit any spans. | ||
| * | ||
| * The reason this is isolated from the OpenTelemetry instrumentation is that users may overwrite this, | ||
| * which would lead to Sentry not working as expected. | ||
| * | ||
| * This is heavily inspired & adapted from: | ||
| * https://github.com/open-telemetry/opentelemetry-js-contrib/blob/28e209a9da36bc4e1f8c2b0db7360170ed46cb80/plugins/node/instrumentation-undici/src/undici.ts | ||
| */ | ||
| class SentryNodeFetchInstrumentation extends instrumentation.InstrumentationBase { | ||
| // Keep ref to avoid https://github.com/nodejs/node/issues/42170 bug and for | ||
| // unsubscribing. | ||
| constructor(config = {}) { | ||
| super('@sentry/instrumentation-node-fetch', core.SDK_VERSION, config); | ||
| constructor(config = {}) { | ||
| super("@sentry/instrumentation-node-fetch", core.SDK_VERSION, config); | ||
| this._channelSubs = []; | ||
| this._propagationDecisionMap = new core.LRUMap(100); | ||
| this._ignoreOutgoingRequestsMap = new WeakMap(); | ||
| this._ignoreOutgoingRequestsMap = /* @__PURE__ */ new WeakMap(); | ||
| } | ||
| /** No need to instrument files/modules. */ | ||
| init() { | ||
| return undefined; | ||
| init() { | ||
| return void 0; | ||
| } | ||
| /** Disable the instrumentation. */ | ||
| disable() { | ||
| disable() { | ||
| super.disable(); | ||
| this._channelSubs.forEach(sub => sub.unsubscribe()); | ||
| this._channelSubs.forEach((sub) => sub.unsubscribe()); | ||
| this._channelSubs = []; | ||
| } | ||
| /** Enable the instrumentation. */ | ||
| enable() { | ||
| // "enabled" handling is currently a bit messy with InstrumentationBase. | ||
| // If constructed with `{enabled: false}`, this `.enable()` is still called, | ||
| // and `this.getConfig().enabled !== this.isEnabled()`, creating confusion. | ||
| // | ||
| // For now, this class will setup for instrumenting if `.enable()` is | ||
| // called, but use `this.getConfig().enabled` to determine if | ||
| // instrumentation should be generated. This covers the more likely common | ||
| // case of config being given a construction time, rather than later via | ||
| // `instance.enable()`, `.disable()`, or `.setConfig()` calls. | ||
| enable() { | ||
| super.enable(); | ||
| // This method is called by the super-class constructor before ours is | ||
| // called. So we need to ensure the property is initalized. | ||
| this._channelSubs = this._channelSubs || []; | ||
| // Avoid to duplicate subscriptions | ||
| if (this._channelSubs.length > 0) { | ||
| return; | ||
| } | ||
| this._subscribeToChannel('undici:request:create', this._onRequestCreated.bind(this)); | ||
| this._subscribeToChannel('undici:request:headers', this._onResponseHeaders.bind(this)); | ||
| this._subscribeToChannel("undici:request:create", this._onRequestCreated.bind(this)); | ||
| this._subscribeToChannel("undici:request:headers", this._onResponseHeaders.bind(this)); | ||
| } | ||
| /** | ||
@@ -75,19 +43,13 @@ * This method is called when a request is created. | ||
| */ | ||
| _onRequestCreated({ request }) { | ||
| _onRequestCreated({ request }) { | ||
| const config = this.getConfig(); | ||
| const enabled = config.enabled !== false; | ||
| if (!enabled) { | ||
| return; | ||
| } | ||
| const shouldIgnore = this._shouldIgnoreOutgoingRequest(request); | ||
| // We store this decisision for later so we do not need to re-evaluate it | ||
| // Additionally, the active context is not correct in _onResponseHeaders, so we need to make sure it is evaluated here | ||
| this._ignoreOutgoingRequestsMap.set(request, shouldIgnore); | ||
| if (shouldIgnore) { | ||
| return; | ||
| } | ||
| if (config.tracePropagation !== false) { | ||
@@ -97,19 +59,14 @@ outgoingFetchRequest.addTracePropagationHeadersToFetchRequest(request, this._propagationDecisionMap); | ||
| } | ||
| /** | ||
| * This method is called when a response is received. | ||
| */ | ||
| _onResponseHeaders({ request, response }) { | ||
| _onResponseHeaders({ request, response }) { | ||
| const config = this.getConfig(); | ||
| const enabled = config.enabled !== false; | ||
| if (!enabled) { | ||
| return; | ||
| } | ||
| const _breadcrumbs = config.breadcrumbs; | ||
| const breadCrumbsEnabled = typeof _breadcrumbs === 'undefined' ? true : _breadcrumbs; | ||
| const breadCrumbsEnabled = typeof _breadcrumbs === "undefined" ? true : _breadcrumbs; | ||
| const shouldIgnore = this._ignoreOutgoingRequestsMap.get(request); | ||
| if (breadCrumbsEnabled && !shouldIgnore) { | ||
@@ -119,12 +76,5 @@ outgoingFetchRequest.addFetchRequestBreadcrumb(request, response); | ||
| } | ||
| /** Subscribe to a diagnostics channel. */ | ||
| _subscribeToChannel( | ||
| diagnosticChannel, | ||
| onMessage, | ||
| ) { | ||
| // `diagnostics_channel` had a ref counting bug until v18.19.0. | ||
| // https://github.com/nodejs/node/pull/47520 | ||
| const useNewSubscribe = nodeVersion.NODE_MAJOR > 18 || (nodeVersion.NODE_MAJOR === 18 && nodeVersion.NODE_MINOR >= 19); | ||
| _subscribeToChannel(diagnosticChannel, onMessage) { | ||
| const useNewSubscribe = nodeVersion.NODE_MAJOR > 18 || nodeVersion.NODE_MAJOR === 18 && nodeVersion.NODE_MINOR >= 19; | ||
| let unsubscribe; | ||
@@ -139,25 +89,19 @@ if (useNewSubscribe) { | ||
| } | ||
| this._channelSubs.push({ | ||
| name: diagnosticChannel, | ||
| unsubscribe, | ||
| unsubscribe | ||
| }); | ||
| } | ||
| /** | ||
| * Check if the given outgoing request should be ignored. | ||
| */ | ||
| _shouldIgnoreOutgoingRequest(request) { | ||
| _shouldIgnoreOutgoingRequest(request) { | ||
| if (core$1.isTracingSuppressed(api.context.active())) { | ||
| return true; | ||
| } | ||
| // Add trace propagation headers | ||
| const url = outgoingFetchRequest.getAbsoluteUrl(request.origin, request.path); | ||
| const ignoreOutgoingRequests = this.getConfig().ignoreOutgoingRequests; | ||
| if (typeof ignoreOutgoingRequests !== 'function' || !url) { | ||
| if (typeof ignoreOutgoingRequests !== "function" || !url) { | ||
| return false; | ||
| } | ||
| return ignoreOutgoingRequests(url); | ||
@@ -164,0 +108,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"SentryNodeFetchInstrumentation.js","sources":["../../../../src/integrations/node-fetch/SentryNodeFetchInstrumentation.ts"],"sourcesContent":["import { context } from '@opentelemetry/api';\nimport { isTracingSuppressed } from '@opentelemetry/core';\nimport type { InstrumentationConfig } from '@opentelemetry/instrumentation';\nimport { InstrumentationBase } from '@opentelemetry/instrumentation';\nimport { LRUMap, SDK_VERSION } from '@sentry/core';\nimport * as diagch from 'diagnostics_channel';\nimport { NODE_MAJOR, NODE_MINOR } from '../../nodeVersion';\nimport {\n addFetchRequestBreadcrumb,\n addTracePropagationHeadersToFetchRequest,\n getAbsoluteUrl,\n} from '../../utils/outgoingFetchRequest';\nimport type { UndiciRequest, UndiciResponse } from './types';\n\nexport type SentryNodeFetchInstrumentationOptions = InstrumentationConfig & {\n /**\n * Whether breadcrumbs should be recorded for requests.\n *\n * @default `true`\n */\n breadcrumbs?: boolean;\n\n /**\n * Whether to inject trace propagation headers (sentry-trace, baggage, traceparent) into outgoing fetch requests.\n *\n * When set to `false`, Sentry will not inject any trace propagation headers, but will still create breadcrumbs\n * (if `breadcrumbs` is enabled). This is useful when `skipOpenTelemetrySetup: true` is configured and you want\n * to avoid duplicate trace headers being injected by both Sentry and OpenTelemetry's UndiciInstrumentation.\n *\n * @default `true`\n */\n tracePropagation?: boolean;\n\n /**\n * Do not capture breadcrumbs or inject headers for outgoing fetch requests to URLs where the given callback returns `true`.\n * The same option can be passed to the top-level httpIntegration where it controls both, breadcrumb and\n * span creation.\n *\n * @param url Contains the entire URL, including query string (if any), protocol, host, etc. of the outgoing request.\n */\n ignoreOutgoingRequests?: (url: string) => boolean;\n};\n\ninterface ListenerRecord {\n name: string;\n unsubscribe: () => void;\n}\n\n/**\n * This custom node-fetch instrumentation is used to instrument outgoing fetch requests.\n * It does not emit any spans.\n *\n * The reason this is isolated from the OpenTelemetry instrumentation is that users may overwrite this,\n * which would lead to Sentry not working as expected.\n *\n * This is heavily inspired & adapted from:\n * https://github.com/open-telemetry/opentelemetry-js-contrib/blob/28e209a9da36bc4e1f8c2b0db7360170ed46cb80/plugins/node/instrumentation-undici/src/undici.ts\n */\nexport class SentryNodeFetchInstrumentation extends InstrumentationBase<SentryNodeFetchInstrumentationOptions> {\n // Keep ref to avoid https://github.com/nodejs/node/issues/42170 bug and for\n // unsubscribing.\n private _channelSubs: Array<ListenerRecord>;\n private _propagationDecisionMap: LRUMap<string, boolean>;\n private _ignoreOutgoingRequestsMap: WeakMap<UndiciRequest, boolean>;\n\n public constructor(config: SentryNodeFetchInstrumentationOptions = {}) {\n super('@sentry/instrumentation-node-fetch', SDK_VERSION, config);\n this._channelSubs = [];\n this._propagationDecisionMap = new LRUMap<string, boolean>(100);\n this._ignoreOutgoingRequestsMap = new WeakMap<UndiciRequest, boolean>();\n }\n\n /** No need to instrument files/modules. */\n public init(): void {\n return undefined;\n }\n\n /** Disable the instrumentation. */\n public disable(): void {\n super.disable();\n this._channelSubs.forEach(sub => sub.unsubscribe());\n this._channelSubs = [];\n }\n\n /** Enable the instrumentation. */\n public enable(): void {\n // \"enabled\" handling is currently a bit messy with InstrumentationBase.\n // If constructed with `{enabled: false}`, this `.enable()` is still called,\n // and `this.getConfig().enabled !== this.isEnabled()`, creating confusion.\n //\n // For now, this class will setup for instrumenting if `.enable()` is\n // called, but use `this.getConfig().enabled` to determine if\n // instrumentation should be generated. This covers the more likely common\n // case of config being given a construction time, rather than later via\n // `instance.enable()`, `.disable()`, or `.setConfig()` calls.\n super.enable();\n\n // This method is called by the super-class constructor before ours is\n // called. So we need to ensure the property is initalized.\n this._channelSubs = this._channelSubs || [];\n\n // Avoid to duplicate subscriptions\n if (this._channelSubs.length > 0) {\n return;\n }\n\n this._subscribeToChannel('undici:request:create', this._onRequestCreated.bind(this));\n this._subscribeToChannel('undici:request:headers', this._onResponseHeaders.bind(this));\n }\n\n /**\n * This method is called when a request is created.\n * You can still mutate the request here before it is sent.\n */\n private _onRequestCreated({ request }: { request: UndiciRequest }): void {\n const config = this.getConfig();\n const enabled = config.enabled !== false;\n\n if (!enabled) {\n return;\n }\n\n const shouldIgnore = this._shouldIgnoreOutgoingRequest(request);\n // We store this decisision for later so we do not need to re-evaluate it\n // Additionally, the active context is not correct in _onResponseHeaders, so we need to make sure it is evaluated here\n this._ignoreOutgoingRequestsMap.set(request, shouldIgnore);\n\n if (shouldIgnore) {\n return;\n }\n\n if (config.tracePropagation !== false) {\n addTracePropagationHeadersToFetchRequest(request, this._propagationDecisionMap);\n }\n }\n\n /**\n * This method is called when a response is received.\n */\n private _onResponseHeaders({ request, response }: { request: UndiciRequest; response: UndiciResponse }): void {\n const config = this.getConfig();\n const enabled = config.enabled !== false;\n\n if (!enabled) {\n return;\n }\n\n const _breadcrumbs = config.breadcrumbs;\n const breadCrumbsEnabled = typeof _breadcrumbs === 'undefined' ? true : _breadcrumbs;\n\n const shouldIgnore = this._ignoreOutgoingRequestsMap.get(request);\n\n if (breadCrumbsEnabled && !shouldIgnore) {\n addFetchRequestBreadcrumb(request, response);\n }\n }\n\n /** Subscribe to a diagnostics channel. */\n private _subscribeToChannel(\n diagnosticChannel: string,\n onMessage: (message: unknown, name: string | symbol) => void,\n ): void {\n // `diagnostics_channel` had a ref counting bug until v18.19.0.\n // https://github.com/nodejs/node/pull/47520\n const useNewSubscribe = NODE_MAJOR > 18 || (NODE_MAJOR === 18 && NODE_MINOR >= 19);\n\n let unsubscribe: () => void;\n if (useNewSubscribe) {\n diagch.subscribe?.(diagnosticChannel, onMessage);\n unsubscribe = () => diagch.unsubscribe?.(diagnosticChannel, onMessage);\n } else {\n const channel = diagch.channel(diagnosticChannel);\n channel.subscribe(onMessage);\n unsubscribe = () => channel.unsubscribe(onMessage);\n }\n\n this._channelSubs.push({\n name: diagnosticChannel,\n unsubscribe,\n });\n }\n\n /**\n * Check if the given outgoing request should be ignored.\n */\n private _shouldIgnoreOutgoingRequest(request: UndiciRequest): boolean {\n if (isTracingSuppressed(context.active())) {\n return true;\n }\n\n // Add trace propagation headers\n const url = getAbsoluteUrl(request.origin, request.path);\n const ignoreOutgoingRequests = this.getConfig().ignoreOutgoingRequests;\n\n if (typeof ignoreOutgoingRequests !== 'function' || !url) {\n return false;\n }\n\n return ignoreOutgoingRequests(url);\n }\n}\n"],"names":["InstrumentationBase","SDK_VERSION","LRUMap","addTracePropagationHeadersToFetchRequest","addFetchRequestBreadcrumb","NODE_MAJOR","NODE_MINOR","isTracingSuppressed","context","getAbsoluteUrl"],"mappings":"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"} | ||
| {"version":3,"file":"SentryNodeFetchInstrumentation.js","sources":["../../../../src/integrations/node-fetch/SentryNodeFetchInstrumentation.ts"],"sourcesContent":["import { context } from '@opentelemetry/api';\nimport { isTracingSuppressed } from '@opentelemetry/core';\nimport type { InstrumentationConfig } from '@opentelemetry/instrumentation';\nimport { InstrumentationBase } from '@opentelemetry/instrumentation';\nimport { LRUMap, SDK_VERSION } from '@sentry/core';\nimport * as diagch from 'diagnostics_channel';\nimport { NODE_MAJOR, NODE_MINOR } from '../../nodeVersion';\nimport {\n addFetchRequestBreadcrumb,\n addTracePropagationHeadersToFetchRequest,\n getAbsoluteUrl,\n} from '../../utils/outgoingFetchRequest';\nimport type { UndiciRequest, UndiciResponse } from './types';\n\nexport type SentryNodeFetchInstrumentationOptions = InstrumentationConfig & {\n /**\n * Whether breadcrumbs should be recorded for requests.\n *\n * @default `true`\n */\n breadcrumbs?: boolean;\n\n /**\n * Whether to inject trace propagation headers (sentry-trace, baggage, traceparent) into outgoing fetch requests.\n *\n * When set to `false`, Sentry will not inject any trace propagation headers, but will still create breadcrumbs\n * (if `breadcrumbs` is enabled). This is useful when `skipOpenTelemetrySetup: true` is configured and you want\n * to avoid duplicate trace headers being injected by both Sentry and OpenTelemetry's UndiciInstrumentation.\n *\n * @default `true`\n */\n tracePropagation?: boolean;\n\n /**\n * Do not capture breadcrumbs or inject headers for outgoing fetch requests to URLs where the given callback returns `true`.\n * The same option can be passed to the top-level httpIntegration where it controls both, breadcrumb and\n * span creation.\n *\n * @param url Contains the entire URL, including query string (if any), protocol, host, etc. of the outgoing request.\n */\n ignoreOutgoingRequests?: (url: string) => boolean;\n};\n\ninterface ListenerRecord {\n name: string;\n unsubscribe: () => void;\n}\n\n/**\n * This custom node-fetch instrumentation is used to instrument outgoing fetch requests.\n * It does not emit any spans.\n *\n * The reason this is isolated from the OpenTelemetry instrumentation is that users may overwrite this,\n * which would lead to Sentry not working as expected.\n *\n * This is heavily inspired & adapted from:\n * https://github.com/open-telemetry/opentelemetry-js-contrib/blob/28e209a9da36bc4e1f8c2b0db7360170ed46cb80/plugins/node/instrumentation-undici/src/undici.ts\n */\nexport class SentryNodeFetchInstrumentation extends InstrumentationBase<SentryNodeFetchInstrumentationOptions> {\n // Keep ref to avoid https://github.com/nodejs/node/issues/42170 bug and for\n // unsubscribing.\n private _channelSubs: Array<ListenerRecord>;\n private _propagationDecisionMap: LRUMap<string, boolean>;\n private _ignoreOutgoingRequestsMap: WeakMap<UndiciRequest, boolean>;\n\n public constructor(config: SentryNodeFetchInstrumentationOptions = {}) {\n super('@sentry/instrumentation-node-fetch', SDK_VERSION, config);\n this._channelSubs = [];\n this._propagationDecisionMap = new LRUMap<string, boolean>(100);\n this._ignoreOutgoingRequestsMap = new WeakMap<UndiciRequest, boolean>();\n }\n\n /** No need to instrument files/modules. */\n public init(): void {\n return undefined;\n }\n\n /** Disable the instrumentation. */\n public disable(): void {\n super.disable();\n this._channelSubs.forEach(sub => sub.unsubscribe());\n this._channelSubs = [];\n }\n\n /** Enable the instrumentation. */\n public enable(): void {\n // \"enabled\" handling is currently a bit messy with InstrumentationBase.\n // If constructed with `{enabled: false}`, this `.enable()` is still called,\n // and `this.getConfig().enabled !== this.isEnabled()`, creating confusion.\n //\n // For now, this class will setup for instrumenting if `.enable()` is\n // called, but use `this.getConfig().enabled` to determine if\n // instrumentation should be generated. This covers the more likely common\n // case of config being given a construction time, rather than later via\n // `instance.enable()`, `.disable()`, or `.setConfig()` calls.\n super.enable();\n\n // This method is called by the super-class constructor before ours is\n // called. So we need to ensure the property is initalized.\n this._channelSubs = this._channelSubs || [];\n\n // Avoid to duplicate subscriptions\n if (this._channelSubs.length > 0) {\n return;\n }\n\n this._subscribeToChannel('undici:request:create', this._onRequestCreated.bind(this));\n this._subscribeToChannel('undici:request:headers', this._onResponseHeaders.bind(this));\n }\n\n /**\n * This method is called when a request is created.\n * You can still mutate the request here before it is sent.\n */\n private _onRequestCreated({ request }: { request: UndiciRequest }): void {\n const config = this.getConfig();\n const enabled = config.enabled !== false;\n\n if (!enabled) {\n return;\n }\n\n const shouldIgnore = this._shouldIgnoreOutgoingRequest(request);\n // We store this decisision for later so we do not need to re-evaluate it\n // Additionally, the active context is not correct in _onResponseHeaders, so we need to make sure it is evaluated here\n this._ignoreOutgoingRequestsMap.set(request, shouldIgnore);\n\n if (shouldIgnore) {\n return;\n }\n\n if (config.tracePropagation !== false) {\n addTracePropagationHeadersToFetchRequest(request, this._propagationDecisionMap);\n }\n }\n\n /**\n * This method is called when a response is received.\n */\n private _onResponseHeaders({ request, response }: { request: UndiciRequest; response: UndiciResponse }): void {\n const config = this.getConfig();\n const enabled = config.enabled !== false;\n\n if (!enabled) {\n return;\n }\n\n const _breadcrumbs = config.breadcrumbs;\n const breadCrumbsEnabled = typeof _breadcrumbs === 'undefined' ? true : _breadcrumbs;\n\n const shouldIgnore = this._ignoreOutgoingRequestsMap.get(request);\n\n if (breadCrumbsEnabled && !shouldIgnore) {\n addFetchRequestBreadcrumb(request, response);\n }\n }\n\n /** Subscribe to a diagnostics channel. */\n private _subscribeToChannel(\n diagnosticChannel: string,\n onMessage: (message: unknown, name: string | symbol) => void,\n ): void {\n // `diagnostics_channel` had a ref counting bug until v18.19.0.\n // https://github.com/nodejs/node/pull/47520\n const useNewSubscribe = NODE_MAJOR > 18 || (NODE_MAJOR === 18 && NODE_MINOR >= 19);\n\n let unsubscribe: () => void;\n if (useNewSubscribe) {\n diagch.subscribe?.(diagnosticChannel, onMessage);\n unsubscribe = () => diagch.unsubscribe?.(diagnosticChannel, onMessage);\n } else {\n const channel = diagch.channel(diagnosticChannel);\n channel.subscribe(onMessage);\n unsubscribe = () => channel.unsubscribe(onMessage);\n }\n\n this._channelSubs.push({\n name: diagnosticChannel,\n unsubscribe,\n });\n }\n\n /**\n * Check if the given outgoing request should be ignored.\n */\n private _shouldIgnoreOutgoingRequest(request: UndiciRequest): boolean {\n if (isTracingSuppressed(context.active())) {\n return true;\n }\n\n // Add trace propagation headers\n const url = getAbsoluteUrl(request.origin, request.path);\n const ignoreOutgoingRequests = this.getConfig().ignoreOutgoingRequests;\n\n if (typeof ignoreOutgoingRequests !== 'function' || !url) {\n return false;\n }\n\n return ignoreOutgoingRequests(url);\n }\n}\n"],"names":["InstrumentationBase","SDK_VERSION","LRUMap","addTracePropagationHeadersToFetchRequest","addFetchRequestBreadcrumb","NODE_MAJOR","NODE_MINOR","isTracingSuppressed","context","getAbsoluteUrl"],"mappings":"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"} |
@@ -6,22 +6,10 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const INTEGRATION_NAME = 'NodeRuntimeMetrics'; | ||
| const DEFAULT_INTERVAL_MS = 30000; | ||
| const MIN_COLLECTION_INTERVAL_MS = 1000; | ||
| const INTEGRATION_NAME = "NodeRuntimeMetrics"; | ||
| const DEFAULT_INTERVAL_MS = 3e4; | ||
| const MIN_COLLECTION_INTERVAL_MS = 1e3; | ||
| const EVENT_LOOP_DELAY_RESOLUTION_MS = 10; | ||
| /** | ||
| * Normalizes a `collectionIntervalMs` value, enforcing a minimum of 1000ms. | ||
| * - Non-finite values (NaN, Infinity): warns and falls back to `defaultInterval`. | ||
| * - Values below the minimum: warns and clamps to 1000ms. | ||
| * @internal | ||
| */ | ||
| function _INTERNAL_normalizeCollectionInterval( | ||
| rawInterval, | ||
| integrationName, | ||
| defaultInterval, | ||
| ) { | ||
| function _INTERNAL_normalizeCollectionInterval(rawInterval, integrationName, defaultInterval) { | ||
| if (!Number.isFinite(rawInterval)) { | ||
| // eslint-disable-next-line no-console | ||
| console.warn( | ||
| `[Sentry] ${integrationName}: collectionIntervalMs (${rawInterval}) is invalid. Using default of ${defaultInterval}ms.`, | ||
| `[Sentry] ${integrationName}: collectionIntervalMs (${rawInterval}) is invalid. Using default of ${defaultInterval}ms.` | ||
| ); | ||
@@ -31,5 +19,4 @@ return defaultInterval; | ||
| if (rawInterval < MIN_COLLECTION_INTERVAL_MS) { | ||
| // eslint-disable-next-line no-console | ||
| console.warn( | ||
| `[Sentry] ${integrationName}: collectionIntervalMs (${rawInterval}) is below the minimum of ${MIN_COLLECTION_INTERVAL_MS}ms. Using minimum of ${MIN_COLLECTION_INTERVAL_MS}ms.`, | ||
| `[Sentry] ${integrationName}: collectionIntervalMs (${rawInterval}) is below the minimum of ${MIN_COLLECTION_INTERVAL_MS}ms. Using minimum of ${MIN_COLLECTION_INTERVAL_MS}ms.` | ||
| ); | ||
@@ -40,15 +27,2 @@ return MIN_COLLECTION_INTERVAL_MS; | ||
| } | ||
| /** | ||
| * Automatically collects Node.js runtime metrics and emits them to Sentry. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * Sentry.init({ | ||
| * integrations: [ | ||
| * Sentry.nodeRuntimeMetricsIntegration(), | ||
| * ], | ||
| * }); | ||
| * ``` | ||
| */ | ||
| const nodeRuntimeMetricsIntegration = core.defineIntegration((options = {}) => { | ||
@@ -58,3 +32,3 @@ const collectionIntervalMs = _INTERNAL_normalizeCollectionInterval( | ||
| INTEGRATION_NAME, | ||
| DEFAULT_INTERVAL_MS, | ||
| DEFAULT_INTERVAL_MS | ||
| ); | ||
@@ -78,15 +52,6 @@ const collect = { | ||
| eventLoopDelayP90: false, | ||
| ...options.collect, | ||
| ...options.collect | ||
| }; | ||
| const needsEventLoopDelay = | ||
| collect.eventLoopDelayP99 || | ||
| collect.eventLoopDelayMin || | ||
| collect.eventLoopDelayMax || | ||
| collect.eventLoopDelayMean || | ||
| collect.eventLoopDelayP50 || | ||
| collect.eventLoopDelayP90; | ||
| const needsEventLoopDelay = collect.eventLoopDelayP99 || collect.eventLoopDelayMin || collect.eventLoopDelayMax || collect.eventLoopDelayMean || collect.eventLoopDelayP50 || collect.eventLoopDelayP90; | ||
| const needsCpu = collect.cpuUtilization || collect.cpuTime; | ||
| let intervalId; | ||
@@ -97,57 +62,47 @@ let prevCpuUsage; | ||
| let eventLoopDelayHistogram; | ||
| const resolutionNs = EVENT_LOOP_DELAY_RESOLUTION_MS * 1e6; | ||
| const nsToS = (ns) => Math.max(0, (ns - resolutionNs) / 1e9); | ||
| const METRIC_ATTRIBUTES = { attributes: { 'sentry.origin': 'auto.node.runtime_metrics' } }; | ||
| const METRIC_ATTRIBUTES_BYTE = { unit: 'byte', attributes: { 'sentry.origin': 'auto.node.runtime_metrics' } }; | ||
| const METRIC_ATTRIBUTES_SECOND = { unit: 'second', attributes: { 'sentry.origin': 'auto.node.runtime_metrics' } }; | ||
| const METRIC_ATTRIBUTES = { attributes: { "sentry.origin": "auto.node.runtime_metrics" } }; | ||
| const METRIC_ATTRIBUTES_BYTE = { unit: "byte", attributes: { "sentry.origin": "auto.node.runtime_metrics" } }; | ||
| const METRIC_ATTRIBUTES_SECOND = { unit: "second", attributes: { "sentry.origin": "auto.node.runtime_metrics" } }; | ||
| function collectMetrics() { | ||
| const now = core._INTERNAL_safeDateNow(); | ||
| const elapsed = now - prevFlushTime; | ||
| if (needsCpu && prevCpuUsage !== undefined) { | ||
| if (needsCpu && prevCpuUsage !== void 0) { | ||
| const delta = process.cpuUsage(prevCpuUsage); | ||
| if (collect.cpuTime) { | ||
| core.metrics.gauge('node.runtime.cpu.user', delta.user / 1e6, METRIC_ATTRIBUTES_SECOND); | ||
| core.metrics.gauge('node.runtime.cpu.system', delta.system / 1e6, METRIC_ATTRIBUTES_SECOND); | ||
| core.metrics.gauge("node.runtime.cpu.user", delta.user / 1e6, METRIC_ATTRIBUTES_SECOND); | ||
| core.metrics.gauge("node.runtime.cpu.system", delta.system / 1e6, METRIC_ATTRIBUTES_SECOND); | ||
| } | ||
| if (collect.cpuUtilization && elapsed > 0) { | ||
| // Ratio of CPU time to wall-clock time. Can exceed 1.0 on multi-core systems. | ||
| // TODO: In cluster mode, add a runtime_id/process_id attribute to disambiguate per-worker metrics. | ||
| core.metrics.gauge( | ||
| 'node.runtime.cpu.utilization', | ||
| (delta.user + delta.system) / (elapsed * 1000), | ||
| METRIC_ATTRIBUTES, | ||
| "node.runtime.cpu.utilization", | ||
| (delta.user + delta.system) / (elapsed * 1e3), | ||
| METRIC_ATTRIBUTES | ||
| ); | ||
| } | ||
| prevCpuUsage = process.cpuUsage(); | ||
| } | ||
| if (collect.memRss || collect.memHeapUsed || collect.memHeapTotal || collect.memExternal) { | ||
| const mem = process.memoryUsage(); | ||
| if (collect.memRss) { | ||
| core.metrics.gauge('node.runtime.mem.rss', mem.rss, METRIC_ATTRIBUTES_BYTE); | ||
| core.metrics.gauge("node.runtime.mem.rss", mem.rss, METRIC_ATTRIBUTES_BYTE); | ||
| } | ||
| if (collect.memHeapUsed) { | ||
| core.metrics.gauge('node.runtime.mem.heap_used', mem.heapUsed, METRIC_ATTRIBUTES_BYTE); | ||
| core.metrics.gauge("node.runtime.mem.heap_used", mem.heapUsed, METRIC_ATTRIBUTES_BYTE); | ||
| } | ||
| if (collect.memHeapTotal) { | ||
| core.metrics.gauge('node.runtime.mem.heap_total', mem.heapTotal, METRIC_ATTRIBUTES_BYTE); | ||
| core.metrics.gauge("node.runtime.mem.heap_total", mem.heapTotal, METRIC_ATTRIBUTES_BYTE); | ||
| } | ||
| if (collect.memExternal) { | ||
| core.metrics.gauge('node.runtime.mem.external', mem.external, METRIC_ATTRIBUTES_BYTE); | ||
| core.metrics.gauge('node.runtime.mem.array_buffers', mem.arrayBuffers, METRIC_ATTRIBUTES_BYTE); | ||
| core.metrics.gauge("node.runtime.mem.external", mem.external, METRIC_ATTRIBUTES_BYTE); | ||
| core.metrics.gauge("node.runtime.mem.array_buffers", mem.arrayBuffers, METRIC_ATTRIBUTES_BYTE); | ||
| } | ||
| } | ||
| if (needsEventLoopDelay && eventLoopDelayHistogram) { | ||
| if (collect.eventLoopDelayMin) { | ||
| core.metrics.gauge( | ||
| 'node.runtime.event_loop.delay.min', | ||
| "node.runtime.event_loop.delay.min", | ||
| nsToS(eventLoopDelayHistogram.min), | ||
| METRIC_ATTRIBUTES_SECOND, | ||
| METRIC_ATTRIBUTES_SECOND | ||
| ); | ||
@@ -157,5 +112,5 @@ } | ||
| core.metrics.gauge( | ||
| 'node.runtime.event_loop.delay.max', | ||
| "node.runtime.event_loop.delay.max", | ||
| nsToS(eventLoopDelayHistogram.max), | ||
| METRIC_ATTRIBUTES_SECOND, | ||
| METRIC_ATTRIBUTES_SECOND | ||
| ); | ||
@@ -165,5 +120,5 @@ } | ||
| core.metrics.gauge( | ||
| 'node.runtime.event_loop.delay.mean', | ||
| "node.runtime.event_loop.delay.mean", | ||
| nsToS(eventLoopDelayHistogram.mean), | ||
| METRIC_ATTRIBUTES_SECOND, | ||
| METRIC_ATTRIBUTES_SECOND | ||
| ); | ||
@@ -173,5 +128,5 @@ } | ||
| core.metrics.gauge( | ||
| 'node.runtime.event_loop.delay.p50', | ||
| "node.runtime.event_loop.delay.p50", | ||
| nsToS(eventLoopDelayHistogram.percentile(50)), | ||
| METRIC_ATTRIBUTES_SECOND, | ||
| METRIC_ATTRIBUTES_SECOND | ||
| ); | ||
@@ -181,5 +136,5 @@ } | ||
| core.metrics.gauge( | ||
| 'node.runtime.event_loop.delay.p90', | ||
| "node.runtime.event_loop.delay.p90", | ||
| nsToS(eventLoopDelayHistogram.percentile(90)), | ||
| METRIC_ATTRIBUTES_SECOND, | ||
| METRIC_ATTRIBUTES_SECOND | ||
| ); | ||
@@ -189,31 +144,24 @@ } | ||
| core.metrics.gauge( | ||
| 'node.runtime.event_loop.delay.p99', | ||
| "node.runtime.event_loop.delay.p99", | ||
| nsToS(eventLoopDelayHistogram.percentile(99)), | ||
| METRIC_ATTRIBUTES_SECOND, | ||
| METRIC_ATTRIBUTES_SECOND | ||
| ); | ||
| } | ||
| eventLoopDelayHistogram.reset(); | ||
| } | ||
| if (collect.eventLoopUtilization && prevElu !== undefined) { | ||
| if (collect.eventLoopUtilization && prevElu !== void 0) { | ||
| const currentElu = perf_hooks.performance.eventLoopUtilization(); | ||
| const delta = perf_hooks.performance.eventLoopUtilization(currentElu, prevElu); | ||
| core.metrics.gauge('node.runtime.event_loop.utilization', delta.utilization, METRIC_ATTRIBUTES); | ||
| core.metrics.gauge("node.runtime.event_loop.utilization", delta.utilization, METRIC_ATTRIBUTES); | ||
| prevElu = currentElu; | ||
| } | ||
| if (collect.uptime && elapsed > 0) { | ||
| core.metrics.count('node.runtime.process.uptime', elapsed / 1000, METRIC_ATTRIBUTES_SECOND); | ||
| core.metrics.count("node.runtime.process.uptime", elapsed / 1e3, METRIC_ATTRIBUTES_SECOND); | ||
| } | ||
| prevFlushTime = now; | ||
| } | ||
| return { | ||
| name: INTEGRATION_NAME, | ||
| setup() { | ||
| if (needsEventLoopDelay) { | ||
| // Disable any previous histogram before overwriting (prevents native resource leak on re-init). | ||
| eventLoopDelayHistogram?.disable(); | ||
@@ -224,8 +172,5 @@ try { | ||
| } catch { | ||
| // Not available in all runtimes (e.g. Bun throws NotImplementedError). | ||
| eventLoopDelayHistogram = undefined; | ||
| eventLoopDelayHistogram = void 0; | ||
| } | ||
| } | ||
| // Prime baselines before the first collection interval. | ||
| if (needsCpu) { | ||
@@ -238,4 +183,2 @@ prevCpuUsage = process.cpuUsage(); | ||
| prevFlushTime = core._INTERNAL_safeDateNow(); | ||
| // Guard against double setup (e.g. re-init). | ||
| if (intervalId) { | ||
@@ -245,3 +188,3 @@ clearInterval(intervalId); | ||
| intervalId = core._INTERNAL_safeUnref(setInterval(collectMetrics, collectionIntervalMs)); | ||
| }, | ||
| } | ||
| }; | ||
@@ -248,0 +191,0 @@ }); |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"nodeRuntimeMetrics.js","sources":["../../../src/integrations/nodeRuntimeMetrics.ts"],"sourcesContent":["import { monitorEventLoopDelay, performance } from 'perf_hooks';\nimport { _INTERNAL_safeDateNow, _INTERNAL_safeUnref, defineIntegration, metrics } from '@sentry/core';\n\nconst INTEGRATION_NAME = 'NodeRuntimeMetrics';\nconst DEFAULT_INTERVAL_MS = 30_000;\nconst MIN_COLLECTION_INTERVAL_MS = 1_000;\nconst EVENT_LOOP_DELAY_RESOLUTION_MS = 10;\n\n/**\n * Normalizes a `collectionIntervalMs` value, enforcing a minimum of 1000ms.\n * - Non-finite values (NaN, Infinity): warns and falls back to `defaultInterval`.\n * - Values below the minimum: warns and clamps to 1000ms.\n * @internal\n */\nexport function _INTERNAL_normalizeCollectionInterval(\n rawInterval: number,\n integrationName: string,\n defaultInterval: number,\n): number {\n if (!Number.isFinite(rawInterval)) {\n // eslint-disable-next-line no-console\n console.warn(\n `[Sentry] ${integrationName}: collectionIntervalMs (${rawInterval}) is invalid. Using default of ${defaultInterval}ms.`,\n );\n return defaultInterval;\n }\n if (rawInterval < MIN_COLLECTION_INTERVAL_MS) {\n // eslint-disable-next-line no-console\n console.warn(\n `[Sentry] ${integrationName}: collectionIntervalMs (${rawInterval}) is below the minimum of ${MIN_COLLECTION_INTERVAL_MS}ms. Using minimum of ${MIN_COLLECTION_INTERVAL_MS}ms.`,\n );\n return MIN_COLLECTION_INTERVAL_MS;\n }\n return rawInterval;\n}\n\nexport interface NodeRuntimeMetricsOptions {\n /**\n * Which metrics to collect.\n *\n * Default on (8 metrics):\n * - `cpuUtilization` — CPU utilization ratio\n * - `memRss` — Resident Set Size (actual memory footprint)\n * - `memHeapUsed` — V8 heap currently in use\n * - `memHeapTotal` — total V8 heap allocated (headroom paired with `memHeapUsed`)\n * - `eventLoopDelayP50` — median event loop delay (baseline latency)\n * - `eventLoopDelayP99` — 99th percentile event loop delay (tail latency / spikes)\n * - `eventLoopUtilization` — fraction of time the event loop was active\n * - `uptime` — process uptime (detect restarts/crashes)\n *\n * Default off (opt-in):\n * - `cpuTime` — raw user/system CPU time in seconds\n * - `memExternal` — external/ArrayBuffer memory (relevant for native addons)\n * - `eventLoopDelayMin` / `eventLoopDelayMax` / `eventLoopDelayMean` / `eventLoopDelayP90`\n */\n collect?: {\n // Default on\n cpuUtilization?: boolean;\n memHeapUsed?: boolean;\n memRss?: boolean;\n eventLoopDelayP99?: boolean;\n eventLoopUtilization?: boolean;\n uptime?: boolean;\n // Default off\n cpuTime?: boolean;\n memHeapTotal?: boolean;\n memExternal?: boolean;\n eventLoopDelayMin?: boolean;\n eventLoopDelayMax?: boolean;\n eventLoopDelayMean?: boolean;\n eventLoopDelayP50?: boolean;\n eventLoopDelayP90?: boolean;\n };\n /**\n * How often to collect metrics, in milliseconds.\n * Minimum allowed value is 1000ms.\n * @default 30000\n * @minimum 1000\n */\n collectionIntervalMs?: number;\n}\n\n/**\n * Automatically collects Node.js runtime metrics and emits them to Sentry.\n *\n * @example\n * ```ts\n * Sentry.init({\n * integrations: [\n * Sentry.nodeRuntimeMetricsIntegration(),\n * ],\n * });\n * ```\n */\nexport const nodeRuntimeMetricsIntegration = defineIntegration((options: NodeRuntimeMetricsOptions = {}) => {\n const collectionIntervalMs = _INTERNAL_normalizeCollectionInterval(\n options.collectionIntervalMs ?? DEFAULT_INTERVAL_MS,\n INTEGRATION_NAME,\n DEFAULT_INTERVAL_MS,\n );\n const collect = {\n // Default on\n cpuUtilization: true,\n memHeapUsed: true,\n memHeapTotal: true,\n memRss: true,\n eventLoopDelayP50: true,\n eventLoopDelayP99: true,\n eventLoopUtilization: true,\n uptime: true,\n // Default off\n cpuTime: false,\n memExternal: false,\n eventLoopDelayMin: false,\n eventLoopDelayMax: false,\n eventLoopDelayMean: false,\n eventLoopDelayP90: false,\n ...options.collect,\n };\n\n const needsEventLoopDelay =\n collect.eventLoopDelayP99 ||\n collect.eventLoopDelayMin ||\n collect.eventLoopDelayMax ||\n collect.eventLoopDelayMean ||\n collect.eventLoopDelayP50 ||\n collect.eventLoopDelayP90;\n\n const needsCpu = collect.cpuUtilization || collect.cpuTime;\n\n let intervalId: ReturnType<typeof setInterval> | undefined;\n let prevCpuUsage: NodeJS.CpuUsage | undefined;\n let prevElu: ReturnType<typeof performance.eventLoopUtilization> | undefined;\n let prevFlushTime: number = 0;\n let eventLoopDelayHistogram: ReturnType<typeof monitorEventLoopDelay> | undefined;\n\n const resolutionNs = EVENT_LOOP_DELAY_RESOLUTION_MS * 1e6;\n const nsToS = (ns: number): number => Math.max(0, (ns - resolutionNs) / 1e9);\n\n const METRIC_ATTRIBUTES = { attributes: { 'sentry.origin': 'auto.node.runtime_metrics' } };\n const METRIC_ATTRIBUTES_BYTE = { unit: 'byte', attributes: { 'sentry.origin': 'auto.node.runtime_metrics' } };\n const METRIC_ATTRIBUTES_SECOND = { unit: 'second', attributes: { 'sentry.origin': 'auto.node.runtime_metrics' } };\n\n function collectMetrics(): void {\n const now = _INTERNAL_safeDateNow();\n const elapsed = now - prevFlushTime;\n\n if (needsCpu && prevCpuUsage !== undefined) {\n const delta = process.cpuUsage(prevCpuUsage);\n\n if (collect.cpuTime) {\n metrics.gauge('node.runtime.cpu.user', delta.user / 1e6, METRIC_ATTRIBUTES_SECOND);\n metrics.gauge('node.runtime.cpu.system', delta.system / 1e6, METRIC_ATTRIBUTES_SECOND);\n }\n if (collect.cpuUtilization && elapsed > 0) {\n // Ratio of CPU time to wall-clock time. Can exceed 1.0 on multi-core systems.\n // TODO: In cluster mode, add a runtime_id/process_id attribute to disambiguate per-worker metrics.\n metrics.gauge(\n 'node.runtime.cpu.utilization',\n (delta.user + delta.system) / (elapsed * 1000),\n METRIC_ATTRIBUTES,\n );\n }\n\n prevCpuUsage = process.cpuUsage();\n }\n\n if (collect.memRss || collect.memHeapUsed || collect.memHeapTotal || collect.memExternal) {\n const mem = process.memoryUsage();\n if (collect.memRss) {\n metrics.gauge('node.runtime.mem.rss', mem.rss, METRIC_ATTRIBUTES_BYTE);\n }\n if (collect.memHeapUsed) {\n metrics.gauge('node.runtime.mem.heap_used', mem.heapUsed, METRIC_ATTRIBUTES_BYTE);\n }\n if (collect.memHeapTotal) {\n metrics.gauge('node.runtime.mem.heap_total', mem.heapTotal, METRIC_ATTRIBUTES_BYTE);\n }\n if (collect.memExternal) {\n metrics.gauge('node.runtime.mem.external', mem.external, METRIC_ATTRIBUTES_BYTE);\n metrics.gauge('node.runtime.mem.array_buffers', mem.arrayBuffers, METRIC_ATTRIBUTES_BYTE);\n }\n }\n\n if (needsEventLoopDelay && eventLoopDelayHistogram) {\n if (collect.eventLoopDelayMin) {\n metrics.gauge(\n 'node.runtime.event_loop.delay.min',\n nsToS(eventLoopDelayHistogram.min),\n METRIC_ATTRIBUTES_SECOND,\n );\n }\n if (collect.eventLoopDelayMax) {\n metrics.gauge(\n 'node.runtime.event_loop.delay.max',\n nsToS(eventLoopDelayHistogram.max),\n METRIC_ATTRIBUTES_SECOND,\n );\n }\n if (collect.eventLoopDelayMean) {\n metrics.gauge(\n 'node.runtime.event_loop.delay.mean',\n nsToS(eventLoopDelayHistogram.mean),\n METRIC_ATTRIBUTES_SECOND,\n );\n }\n if (collect.eventLoopDelayP50) {\n metrics.gauge(\n 'node.runtime.event_loop.delay.p50',\n nsToS(eventLoopDelayHistogram.percentile(50)),\n METRIC_ATTRIBUTES_SECOND,\n );\n }\n if (collect.eventLoopDelayP90) {\n metrics.gauge(\n 'node.runtime.event_loop.delay.p90',\n nsToS(eventLoopDelayHistogram.percentile(90)),\n METRIC_ATTRIBUTES_SECOND,\n );\n }\n if (collect.eventLoopDelayP99) {\n metrics.gauge(\n 'node.runtime.event_loop.delay.p99',\n nsToS(eventLoopDelayHistogram.percentile(99)),\n METRIC_ATTRIBUTES_SECOND,\n );\n }\n\n eventLoopDelayHistogram.reset();\n }\n\n if (collect.eventLoopUtilization && prevElu !== undefined) {\n const currentElu = performance.eventLoopUtilization();\n const delta = performance.eventLoopUtilization(currentElu, prevElu);\n metrics.gauge('node.runtime.event_loop.utilization', delta.utilization, METRIC_ATTRIBUTES);\n prevElu = currentElu;\n }\n\n if (collect.uptime && elapsed > 0) {\n metrics.count('node.runtime.process.uptime', elapsed / 1000, METRIC_ATTRIBUTES_SECOND);\n }\n\n prevFlushTime = now;\n }\n\n return {\n name: INTEGRATION_NAME,\n\n setup(): void {\n if (needsEventLoopDelay) {\n // Disable any previous histogram before overwriting (prevents native resource leak on re-init).\n eventLoopDelayHistogram?.disable();\n try {\n eventLoopDelayHistogram = monitorEventLoopDelay({ resolution: EVENT_LOOP_DELAY_RESOLUTION_MS });\n eventLoopDelayHistogram.enable();\n } catch {\n // Not available in all runtimes (e.g. Bun throws NotImplementedError).\n eventLoopDelayHistogram = undefined;\n }\n }\n\n // Prime baselines before the first collection interval.\n if (needsCpu) {\n prevCpuUsage = process.cpuUsage();\n }\n if (collect.eventLoopUtilization) {\n prevElu = performance.eventLoopUtilization();\n }\n prevFlushTime = _INTERNAL_safeDateNow();\n\n // Guard against double setup (e.g. re-init).\n if (intervalId) {\n clearInterval(intervalId);\n }\n intervalId = _INTERNAL_safeUnref(setInterval(collectMetrics, collectionIntervalMs));\n },\n };\n});\n"],"names":["defineIntegration","_INTERNAL_safeDateNow","metrics","performance","monitorEventLoopDelay","_INTERNAL_safeUnref"],"mappings":"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"} | ||
| {"version":3,"file":"nodeRuntimeMetrics.js","sources":["../../../src/integrations/nodeRuntimeMetrics.ts"],"sourcesContent":["import { monitorEventLoopDelay, performance } from 'perf_hooks';\nimport { _INTERNAL_safeDateNow, _INTERNAL_safeUnref, defineIntegration, metrics } from '@sentry/core';\n\nconst INTEGRATION_NAME = 'NodeRuntimeMetrics';\nconst DEFAULT_INTERVAL_MS = 30_000;\nconst MIN_COLLECTION_INTERVAL_MS = 1_000;\nconst EVENT_LOOP_DELAY_RESOLUTION_MS = 10;\n\n/**\n * Normalizes a `collectionIntervalMs` value, enforcing a minimum of 1000ms.\n * - Non-finite values (NaN, Infinity): warns and falls back to `defaultInterval`.\n * - Values below the minimum: warns and clamps to 1000ms.\n * @internal\n */\nexport function _INTERNAL_normalizeCollectionInterval(\n rawInterval: number,\n integrationName: string,\n defaultInterval: number,\n): number {\n if (!Number.isFinite(rawInterval)) {\n // eslint-disable-next-line no-console\n console.warn(\n `[Sentry] ${integrationName}: collectionIntervalMs (${rawInterval}) is invalid. Using default of ${defaultInterval}ms.`,\n );\n return defaultInterval;\n }\n if (rawInterval < MIN_COLLECTION_INTERVAL_MS) {\n // eslint-disable-next-line no-console\n console.warn(\n `[Sentry] ${integrationName}: collectionIntervalMs (${rawInterval}) is below the minimum of ${MIN_COLLECTION_INTERVAL_MS}ms. Using minimum of ${MIN_COLLECTION_INTERVAL_MS}ms.`,\n );\n return MIN_COLLECTION_INTERVAL_MS;\n }\n return rawInterval;\n}\n\nexport interface NodeRuntimeMetricsOptions {\n /**\n * Which metrics to collect.\n *\n * Default on (8 metrics):\n * - `cpuUtilization` — CPU utilization ratio\n * - `memRss` — Resident Set Size (actual memory footprint)\n * - `memHeapUsed` — V8 heap currently in use\n * - `memHeapTotal` — total V8 heap allocated (headroom paired with `memHeapUsed`)\n * - `eventLoopDelayP50` — median event loop delay (baseline latency)\n * - `eventLoopDelayP99` — 99th percentile event loop delay (tail latency / spikes)\n * - `eventLoopUtilization` — fraction of time the event loop was active\n * - `uptime` — process uptime (detect restarts/crashes)\n *\n * Default off (opt-in):\n * - `cpuTime` — raw user/system CPU time in seconds\n * - `memExternal` — external/ArrayBuffer memory (relevant for native addons)\n * - `eventLoopDelayMin` / `eventLoopDelayMax` / `eventLoopDelayMean` / `eventLoopDelayP90`\n */\n collect?: {\n // Default on\n cpuUtilization?: boolean;\n memHeapUsed?: boolean;\n memRss?: boolean;\n eventLoopDelayP99?: boolean;\n eventLoopUtilization?: boolean;\n uptime?: boolean;\n // Default off\n cpuTime?: boolean;\n memHeapTotal?: boolean;\n memExternal?: boolean;\n eventLoopDelayMin?: boolean;\n eventLoopDelayMax?: boolean;\n eventLoopDelayMean?: boolean;\n eventLoopDelayP50?: boolean;\n eventLoopDelayP90?: boolean;\n };\n /**\n * How often to collect metrics, in milliseconds.\n * Minimum allowed value is 1000ms.\n * @default 30000\n * @minimum 1000\n */\n collectionIntervalMs?: number;\n}\n\n/**\n * Automatically collects Node.js runtime metrics and emits them to Sentry.\n *\n * @example\n * ```ts\n * Sentry.init({\n * integrations: [\n * Sentry.nodeRuntimeMetricsIntegration(),\n * ],\n * });\n * ```\n */\nexport const nodeRuntimeMetricsIntegration = defineIntegration((options: NodeRuntimeMetricsOptions = {}) => {\n const collectionIntervalMs = _INTERNAL_normalizeCollectionInterval(\n options.collectionIntervalMs ?? DEFAULT_INTERVAL_MS,\n INTEGRATION_NAME,\n DEFAULT_INTERVAL_MS,\n );\n const collect = {\n // Default on\n cpuUtilization: true,\n memHeapUsed: true,\n memHeapTotal: true,\n memRss: true,\n eventLoopDelayP50: true,\n eventLoopDelayP99: true,\n eventLoopUtilization: true,\n uptime: true,\n // Default off\n cpuTime: false,\n memExternal: false,\n eventLoopDelayMin: false,\n eventLoopDelayMax: false,\n eventLoopDelayMean: false,\n eventLoopDelayP90: false,\n ...options.collect,\n };\n\n const needsEventLoopDelay =\n collect.eventLoopDelayP99 ||\n collect.eventLoopDelayMin ||\n collect.eventLoopDelayMax ||\n collect.eventLoopDelayMean ||\n collect.eventLoopDelayP50 ||\n collect.eventLoopDelayP90;\n\n const needsCpu = collect.cpuUtilization || collect.cpuTime;\n\n let intervalId: ReturnType<typeof setInterval> | undefined;\n let prevCpuUsage: NodeJS.CpuUsage | undefined;\n let prevElu: ReturnType<typeof performance.eventLoopUtilization> | undefined;\n let prevFlushTime: number = 0;\n let eventLoopDelayHistogram: ReturnType<typeof monitorEventLoopDelay> | undefined;\n\n const resolutionNs = EVENT_LOOP_DELAY_RESOLUTION_MS * 1e6;\n const nsToS = (ns: number): number => Math.max(0, (ns - resolutionNs) / 1e9);\n\n const METRIC_ATTRIBUTES = { attributes: { 'sentry.origin': 'auto.node.runtime_metrics' } };\n const METRIC_ATTRIBUTES_BYTE = { unit: 'byte', attributes: { 'sentry.origin': 'auto.node.runtime_metrics' } };\n const METRIC_ATTRIBUTES_SECOND = { unit: 'second', attributes: { 'sentry.origin': 'auto.node.runtime_metrics' } };\n\n function collectMetrics(): void {\n const now = _INTERNAL_safeDateNow();\n const elapsed = now - prevFlushTime;\n\n if (needsCpu && prevCpuUsage !== undefined) {\n const delta = process.cpuUsage(prevCpuUsage);\n\n if (collect.cpuTime) {\n metrics.gauge('node.runtime.cpu.user', delta.user / 1e6, METRIC_ATTRIBUTES_SECOND);\n metrics.gauge('node.runtime.cpu.system', delta.system / 1e6, METRIC_ATTRIBUTES_SECOND);\n }\n if (collect.cpuUtilization && elapsed > 0) {\n // Ratio of CPU time to wall-clock time. Can exceed 1.0 on multi-core systems.\n // TODO: In cluster mode, add a runtime_id/process_id attribute to disambiguate per-worker metrics.\n metrics.gauge(\n 'node.runtime.cpu.utilization',\n (delta.user + delta.system) / (elapsed * 1000),\n METRIC_ATTRIBUTES,\n );\n }\n\n prevCpuUsage = process.cpuUsage();\n }\n\n if (collect.memRss || collect.memHeapUsed || collect.memHeapTotal || collect.memExternal) {\n const mem = process.memoryUsage();\n if (collect.memRss) {\n metrics.gauge('node.runtime.mem.rss', mem.rss, METRIC_ATTRIBUTES_BYTE);\n }\n if (collect.memHeapUsed) {\n metrics.gauge('node.runtime.mem.heap_used', mem.heapUsed, METRIC_ATTRIBUTES_BYTE);\n }\n if (collect.memHeapTotal) {\n metrics.gauge('node.runtime.mem.heap_total', mem.heapTotal, METRIC_ATTRIBUTES_BYTE);\n }\n if (collect.memExternal) {\n metrics.gauge('node.runtime.mem.external', mem.external, METRIC_ATTRIBUTES_BYTE);\n metrics.gauge('node.runtime.mem.array_buffers', mem.arrayBuffers, METRIC_ATTRIBUTES_BYTE);\n }\n }\n\n if (needsEventLoopDelay && eventLoopDelayHistogram) {\n if (collect.eventLoopDelayMin) {\n metrics.gauge(\n 'node.runtime.event_loop.delay.min',\n nsToS(eventLoopDelayHistogram.min),\n METRIC_ATTRIBUTES_SECOND,\n );\n }\n if (collect.eventLoopDelayMax) {\n metrics.gauge(\n 'node.runtime.event_loop.delay.max',\n nsToS(eventLoopDelayHistogram.max),\n METRIC_ATTRIBUTES_SECOND,\n );\n }\n if (collect.eventLoopDelayMean) {\n metrics.gauge(\n 'node.runtime.event_loop.delay.mean',\n nsToS(eventLoopDelayHistogram.mean),\n METRIC_ATTRIBUTES_SECOND,\n );\n }\n if (collect.eventLoopDelayP50) {\n metrics.gauge(\n 'node.runtime.event_loop.delay.p50',\n nsToS(eventLoopDelayHistogram.percentile(50)),\n METRIC_ATTRIBUTES_SECOND,\n );\n }\n if (collect.eventLoopDelayP90) {\n metrics.gauge(\n 'node.runtime.event_loop.delay.p90',\n nsToS(eventLoopDelayHistogram.percentile(90)),\n METRIC_ATTRIBUTES_SECOND,\n );\n }\n if (collect.eventLoopDelayP99) {\n metrics.gauge(\n 'node.runtime.event_loop.delay.p99',\n nsToS(eventLoopDelayHistogram.percentile(99)),\n METRIC_ATTRIBUTES_SECOND,\n );\n }\n\n eventLoopDelayHistogram.reset();\n }\n\n if (collect.eventLoopUtilization && prevElu !== undefined) {\n const currentElu = performance.eventLoopUtilization();\n const delta = performance.eventLoopUtilization(currentElu, prevElu);\n metrics.gauge('node.runtime.event_loop.utilization', delta.utilization, METRIC_ATTRIBUTES);\n prevElu = currentElu;\n }\n\n if (collect.uptime && elapsed > 0) {\n metrics.count('node.runtime.process.uptime', elapsed / 1000, METRIC_ATTRIBUTES_SECOND);\n }\n\n prevFlushTime = now;\n }\n\n return {\n name: INTEGRATION_NAME,\n\n setup(): void {\n if (needsEventLoopDelay) {\n // Disable any previous histogram before overwriting (prevents native resource leak on re-init).\n eventLoopDelayHistogram?.disable();\n try {\n eventLoopDelayHistogram = monitorEventLoopDelay({ resolution: EVENT_LOOP_DELAY_RESOLUTION_MS });\n eventLoopDelayHistogram.enable();\n } catch {\n // Not available in all runtimes (e.g. Bun throws NotImplementedError).\n eventLoopDelayHistogram = undefined;\n }\n }\n\n // Prime baselines before the first collection interval.\n if (needsCpu) {\n prevCpuUsage = process.cpuUsage();\n }\n if (collect.eventLoopUtilization) {\n prevElu = performance.eventLoopUtilization();\n }\n prevFlushTime = _INTERNAL_safeDateNow();\n\n // Guard against double setup (e.g. re-init).\n if (intervalId) {\n clearInterval(intervalId);\n }\n intervalId = _INTERNAL_safeUnref(setInterval(collectMetrics, collectionIntervalMs));\n },\n };\n});\n"],"names":["defineIntegration","_INTERNAL_safeDateNow","metrics","performance","monitorEventLoopDelay","_INTERNAL_safeUnref"],"mappings":"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"} |
@@ -8,30 +8,20 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const INTEGRATION_NAME = 'OnUncaughtException'; | ||
| /** | ||
| * Add a global exception handler. | ||
| */ | ||
| const INTEGRATION_NAME = "OnUncaughtException"; | ||
| const onUncaughtExceptionIntegration = core.defineIntegration((options = {}) => { | ||
| const optionsWithDefaults = { | ||
| exitEvenIfOtherHandlersAreRegistered: false, | ||
| ...options, | ||
| ...options | ||
| }; | ||
| return { | ||
| name: INTEGRATION_NAME, | ||
| setup(client) { | ||
| // errors in worker threads are already handled by the childProcessIntegration | ||
| // also we don't want to exit the Node process on worker thread errors | ||
| if (!worker_threads.isMainThread) { | ||
| return; | ||
| } | ||
| global.process.on('uncaughtException', makeErrorHandler(client, optionsWithDefaults)); | ||
| }, | ||
| global.process.on("uncaughtException", makeErrorHandler(client, optionsWithDefaults)); | ||
| } | ||
| }; | ||
| }); | ||
| /** Exported only for tests */ | ||
| function makeErrorHandler(client, options) { | ||
| const timeout = 2000; | ||
| const timeout = 2e3; | ||
| let caughtFirstError = false; | ||
@@ -41,44 +31,26 @@ let caughtSecondError = false; | ||
| let firstError; | ||
| const clientOptions = client.getOptions(); | ||
| return Object.assign( | ||
| (error) => { | ||
| let onFatalError = errorhandling.logAndExitProcess; | ||
| if (options.onFatalError) { | ||
| onFatalError = options.onFatalError; | ||
| } else if (clientOptions.onFatalError) { | ||
| onFatalError = clientOptions.onFatalError ; | ||
| onFatalError = clientOptions.onFatalError; | ||
| } | ||
| // Attaching a listener to `uncaughtException` will prevent the node process from exiting. We generally do not | ||
| // want to alter this behaviour so we check for other listeners that users may have attached themselves and adjust | ||
| // exit behaviour of the SDK accordingly: | ||
| // - If other listeners are attached, do not exit. | ||
| // - If the only listener attached is ours, exit. | ||
| const userProvidedListenersCount = (global.process.listeners('uncaughtException') ).filter( | ||
| listener => { | ||
| // There are 3 listeners we ignore: | ||
| const userProvidedListenersCount = global.process.listeners("uncaughtException").filter( | ||
| (listener) => { | ||
| return ( | ||
| // as soon as we're using domains this listener is attached by node itself | ||
| listener.name !== 'domainUncaughtExceptionClear' && | ||
| // the handler we register for tracing | ||
| listener.tag !== 'sentry_tracingErrorCallback' && | ||
| // the handler we register in this integration | ||
| (listener )._errorHandler !== true | ||
| listener.name !== "domainUncaughtExceptionClear" && // the handler we register for tracing | ||
| listener.tag !== "sentry_tracingErrorCallback" && // the handler we register in this integration | ||
| listener._errorHandler !== true | ||
| ); | ||
| }, | ||
| } | ||
| ).length; | ||
| const processWouldExit = userProvidedListenersCount === 0; | ||
| const shouldApplyFatalHandlingLogic = options.exitEvenIfOtherHandlersAreRegistered || processWouldExit; | ||
| if (!caughtFirstError) { | ||
| // this is the first uncaught error and the ultimate reason for shutting down | ||
| // we want to do absolutely everything possible to ensure it gets captured | ||
| // also we want to make sure we don't go recursion crazy if more errors happen after this one | ||
| firstError = error; | ||
| caughtFirstError = true; | ||
| if (core.getClient() === client) { | ||
@@ -88,11 +60,10 @@ core.captureException(error, { | ||
| captureContext: { | ||
| level: 'fatal', | ||
| level: "fatal" | ||
| }, | ||
| mechanism: { | ||
| handled: false, | ||
| type: 'auto.node.onuncaughtexception', | ||
| }, | ||
| type: "auto.node.onuncaughtexception" | ||
| } | ||
| }); | ||
| } | ||
| if (!calledFatalError && shouldApplyFatalHandlingLogic) { | ||
@@ -105,31 +76,14 @@ calledFatalError = true; | ||
| if (calledFatalError) { | ||
| // we hit an error *after* calling onFatalError - pretty boned at this point, just shut it down | ||
| debugBuild.DEBUG_BUILD && | ||
| core.debug.warn( | ||
| 'uncaught exception after calling fatal error shutdown callback - this is bad! forcing shutdown', | ||
| ); | ||
| debugBuild.DEBUG_BUILD && core.debug.warn( | ||
| "uncaught exception after calling fatal error shutdown callback - this is bad! forcing shutdown" | ||
| ); | ||
| errorhandling.logAndExitProcess(error); | ||
| } else if (!caughtSecondError) { | ||
| // two cases for how we can hit this branch: | ||
| // - capturing of first error blew up and we just caught the exception from that | ||
| // - quit trying to capture, proceed with shutdown | ||
| // - a second independent error happened while waiting for first error to capture | ||
| // - want to avoid causing premature shutdown before first error capture finishes | ||
| // it's hard to immediately tell case 1 from case 2 without doing some fancy/questionable domain stuff | ||
| // so let's instead just delay a bit before we proceed with our action here | ||
| // in case 1, we just wait a bit unnecessarily but ultimately do the same thing | ||
| // in case 2, the delay hopefully made us wait long enough for the capture to finish | ||
| // two potential nonideal outcomes: | ||
| // nonideal case 1: capturing fails fast, we sit around for a few seconds unnecessarily before proceeding correctly by calling onFatalError | ||
| // nonideal case 2: case 2 happens, 1st error is captured but slowly, timeout completes before capture and we treat second error as the sendErr of (nonexistent) failure from trying to capture first error | ||
| // note that after hitting this branch, we might catch more errors where (caughtSecondError && !calledFatalError) | ||
| // we ignore them - they don't matter to us, we're just waiting for the second error timeout to finish | ||
| caughtSecondError = true; | ||
| setTimeout(() => { | ||
| if (!calledFatalError) { | ||
| // it was probably case 1, let's treat err as the sendErr and call onFatalError | ||
| calledFatalError = true; | ||
| onFatalError(firstError, error); | ||
| } | ||
| }, timeout); // capturing could take at least sendTimeout to fail, plus an arbitrary second for how long it takes to collect surrounding source etc | ||
| }, timeout); | ||
| } | ||
@@ -139,3 +93,3 @@ } | ||
| }, | ||
| { _errorHandler: true }, | ||
| { _errorHandler: true } | ||
| ); | ||
@@ -142,0 +96,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"onuncaughtexception.js","sources":["../../../src/integrations/onuncaughtexception.ts"],"sourcesContent":["import { captureException, debug, defineIntegration, getClient } from '@sentry/core';\nimport { isMainThread } from 'worker_threads';\nimport { DEBUG_BUILD } from '../debug-build';\nimport type { NodeClient } from '../sdk/client';\nimport { logAndExitProcess } from '../utils/errorhandling';\n\ntype OnFatalErrorHandler = (firstError: Error, secondError?: Error) => void;\n\ntype TaggedListener = NodeJS.UncaughtExceptionListener & {\n tag?: string;\n};\n\ninterface OnUncaughtExceptionOptions {\n /**\n * Controls if the SDK should register a handler to exit the process on uncaught errors:\n * - `true`: The SDK will exit the process on all uncaught errors.\n * - `false`: The SDK will only exit the process when there are no other `uncaughtException` handlers attached.\n *\n * Default: `false`\n */\n exitEvenIfOtherHandlersAreRegistered: boolean;\n\n /**\n * This is called when an uncaught error would cause the process to exit.\n *\n * @param firstError Uncaught error causing the process to exit\n * @param secondError Will be set if the handler was called multiple times. This can happen either because\n * `onFatalError` itself threw, or because an independent error happened somewhere else while `onFatalError`\n * was running.\n */\n onFatalError?(this: void, firstError: Error, secondError?: Error): void;\n}\n\nconst INTEGRATION_NAME = 'OnUncaughtException';\n\n/**\n * Add a global exception handler.\n */\nexport const onUncaughtExceptionIntegration = defineIntegration((options: Partial<OnUncaughtExceptionOptions> = {}) => {\n const optionsWithDefaults = {\n exitEvenIfOtherHandlersAreRegistered: false,\n ...options,\n };\n\n return {\n name: INTEGRATION_NAME,\n setup(client: NodeClient) {\n // errors in worker threads are already handled by the childProcessIntegration\n // also we don't want to exit the Node process on worker thread errors\n if (!isMainThread) {\n return;\n }\n\n global.process.on('uncaughtException', makeErrorHandler(client, optionsWithDefaults));\n },\n };\n});\n\ntype ErrorHandler = { _errorHandler: boolean } & ((error: Error) => void);\n\n/** Exported only for tests */\nexport function makeErrorHandler(client: NodeClient, options: OnUncaughtExceptionOptions): ErrorHandler {\n const timeout = 2000;\n let caughtFirstError: boolean = false;\n let caughtSecondError: boolean = false;\n let calledFatalError: boolean = false;\n let firstError: Error;\n\n const clientOptions = client.getOptions();\n\n return Object.assign(\n (error: Error): void => {\n let onFatalError: OnFatalErrorHandler = logAndExitProcess;\n\n if (options.onFatalError) {\n onFatalError = options.onFatalError;\n } else if (clientOptions.onFatalError) {\n onFatalError = clientOptions.onFatalError as OnFatalErrorHandler;\n }\n\n // Attaching a listener to `uncaughtException` will prevent the node process from exiting. We generally do not\n // want to alter this behaviour so we check for other listeners that users may have attached themselves and adjust\n // exit behaviour of the SDK accordingly:\n // - If other listeners are attached, do not exit.\n // - If the only listener attached is ours, exit.\n const userProvidedListenersCount = (global.process.listeners('uncaughtException') as TaggedListener[]).filter(\n listener => {\n // There are 3 listeners we ignore:\n return (\n // as soon as we're using domains this listener is attached by node itself\n listener.name !== 'domainUncaughtExceptionClear' &&\n // the handler we register for tracing\n listener.tag !== 'sentry_tracingErrorCallback' &&\n // the handler we register in this integration\n (listener as ErrorHandler)._errorHandler !== true\n );\n },\n ).length;\n\n const processWouldExit = userProvidedListenersCount === 0;\n const shouldApplyFatalHandlingLogic = options.exitEvenIfOtherHandlersAreRegistered || processWouldExit;\n\n if (!caughtFirstError) {\n // this is the first uncaught error and the ultimate reason for shutting down\n // we want to do absolutely everything possible to ensure it gets captured\n // also we want to make sure we don't go recursion crazy if more errors happen after this one\n firstError = error;\n caughtFirstError = true;\n\n if (getClient() === client) {\n captureException(error, {\n originalException: error,\n captureContext: {\n level: 'fatal',\n },\n mechanism: {\n handled: false,\n type: 'auto.node.onuncaughtexception',\n },\n });\n }\n\n if (!calledFatalError && shouldApplyFatalHandlingLogic) {\n calledFatalError = true;\n onFatalError(error);\n }\n } else {\n if (shouldApplyFatalHandlingLogic) {\n if (calledFatalError) {\n // we hit an error *after* calling onFatalError - pretty boned at this point, just shut it down\n DEBUG_BUILD &&\n debug.warn(\n 'uncaught exception after calling fatal error shutdown callback - this is bad! forcing shutdown',\n );\n logAndExitProcess(error);\n } else if (!caughtSecondError) {\n // two cases for how we can hit this branch:\n // - capturing of first error blew up and we just caught the exception from that\n // - quit trying to capture, proceed with shutdown\n // - a second independent error happened while waiting for first error to capture\n // - want to avoid causing premature shutdown before first error capture finishes\n // it's hard to immediately tell case 1 from case 2 without doing some fancy/questionable domain stuff\n // so let's instead just delay a bit before we proceed with our action here\n // in case 1, we just wait a bit unnecessarily but ultimately do the same thing\n // in case 2, the delay hopefully made us wait long enough for the capture to finish\n // two potential nonideal outcomes:\n // nonideal case 1: capturing fails fast, we sit around for a few seconds unnecessarily before proceeding correctly by calling onFatalError\n // nonideal case 2: case 2 happens, 1st error is captured but slowly, timeout completes before capture and we treat second error as the sendErr of (nonexistent) failure from trying to capture first error\n // note that after hitting this branch, we might catch more errors where (caughtSecondError && !calledFatalError)\n // we ignore them - they don't matter to us, we're just waiting for the second error timeout to finish\n caughtSecondError = true;\n setTimeout(() => {\n if (!calledFatalError) {\n // it was probably case 1, let's treat err as the sendErr and call onFatalError\n calledFatalError = true;\n onFatalError(firstError, error);\n } else {\n // it was probably case 2, our first error finished capturing while we waited, cool, do nothing\n }\n }, timeout); // capturing could take at least sendTimeout to fail, plus an arbitrary second for how long it takes to collect surrounding source etc\n }\n }\n }\n },\n { _errorHandler: true },\n );\n}\n"],"names":["defineIntegration","isMainThread","logAndExitProcess","getClient","captureException","DEBUG_BUILD","debug"],"mappings":"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"} | ||
| {"version":3,"file":"onuncaughtexception.js","sources":["../../../src/integrations/onuncaughtexception.ts"],"sourcesContent":["import { captureException, debug, defineIntegration, getClient } from '@sentry/core';\nimport { isMainThread } from 'worker_threads';\nimport { DEBUG_BUILD } from '../debug-build';\nimport type { NodeClient } from '../sdk/client';\nimport { logAndExitProcess } from '../utils/errorhandling';\n\ntype OnFatalErrorHandler = (firstError: Error, secondError?: Error) => void;\n\ntype TaggedListener = NodeJS.UncaughtExceptionListener & {\n tag?: string;\n};\n\ninterface OnUncaughtExceptionOptions {\n /**\n * Controls if the SDK should register a handler to exit the process on uncaught errors:\n * - `true`: The SDK will exit the process on all uncaught errors.\n * - `false`: The SDK will only exit the process when there are no other `uncaughtException` handlers attached.\n *\n * Default: `false`\n */\n exitEvenIfOtherHandlersAreRegistered: boolean;\n\n /**\n * This is called when an uncaught error would cause the process to exit.\n *\n * @param firstError Uncaught error causing the process to exit\n * @param secondError Will be set if the handler was called multiple times. This can happen either because\n * `onFatalError` itself threw, or because an independent error happened somewhere else while `onFatalError`\n * was running.\n */\n onFatalError?(this: void, firstError: Error, secondError?: Error): void;\n}\n\nconst INTEGRATION_NAME = 'OnUncaughtException';\n\n/**\n * Add a global exception handler.\n */\nexport const onUncaughtExceptionIntegration = defineIntegration((options: Partial<OnUncaughtExceptionOptions> = {}) => {\n const optionsWithDefaults = {\n exitEvenIfOtherHandlersAreRegistered: false,\n ...options,\n };\n\n return {\n name: INTEGRATION_NAME,\n setup(client: NodeClient) {\n // errors in worker threads are already handled by the childProcessIntegration\n // also we don't want to exit the Node process on worker thread errors\n if (!isMainThread) {\n return;\n }\n\n global.process.on('uncaughtException', makeErrorHandler(client, optionsWithDefaults));\n },\n };\n});\n\ntype ErrorHandler = { _errorHandler: boolean } & ((error: Error) => void);\n\n/** Exported only for tests */\nexport function makeErrorHandler(client: NodeClient, options: OnUncaughtExceptionOptions): ErrorHandler {\n const timeout = 2000;\n let caughtFirstError: boolean = false;\n let caughtSecondError: boolean = false;\n let calledFatalError: boolean = false;\n let firstError: Error;\n\n const clientOptions = client.getOptions();\n\n return Object.assign(\n (error: Error): void => {\n let onFatalError: OnFatalErrorHandler = logAndExitProcess;\n\n if (options.onFatalError) {\n onFatalError = options.onFatalError;\n } else if (clientOptions.onFatalError) {\n onFatalError = clientOptions.onFatalError as OnFatalErrorHandler;\n }\n\n // Attaching a listener to `uncaughtException` will prevent the node process from exiting. We generally do not\n // want to alter this behaviour so we check for other listeners that users may have attached themselves and adjust\n // exit behaviour of the SDK accordingly:\n // - If other listeners are attached, do not exit.\n // - If the only listener attached is ours, exit.\n const userProvidedListenersCount = (global.process.listeners('uncaughtException') as TaggedListener[]).filter(\n listener => {\n // There are 3 listeners we ignore:\n return (\n // as soon as we're using domains this listener is attached by node itself\n listener.name !== 'domainUncaughtExceptionClear' &&\n // the handler we register for tracing\n listener.tag !== 'sentry_tracingErrorCallback' &&\n // the handler we register in this integration\n (listener as ErrorHandler)._errorHandler !== true\n );\n },\n ).length;\n\n const processWouldExit = userProvidedListenersCount === 0;\n const shouldApplyFatalHandlingLogic = options.exitEvenIfOtherHandlersAreRegistered || processWouldExit;\n\n if (!caughtFirstError) {\n // this is the first uncaught error and the ultimate reason for shutting down\n // we want to do absolutely everything possible to ensure it gets captured\n // also we want to make sure we don't go recursion crazy if more errors happen after this one\n firstError = error;\n caughtFirstError = true;\n\n if (getClient() === client) {\n captureException(error, {\n originalException: error,\n captureContext: {\n level: 'fatal',\n },\n mechanism: {\n handled: false,\n type: 'auto.node.onuncaughtexception',\n },\n });\n }\n\n if (!calledFatalError && shouldApplyFatalHandlingLogic) {\n calledFatalError = true;\n onFatalError(error);\n }\n } else {\n if (shouldApplyFatalHandlingLogic) {\n if (calledFatalError) {\n // we hit an error *after* calling onFatalError - pretty boned at this point, just shut it down\n DEBUG_BUILD &&\n debug.warn(\n 'uncaught exception after calling fatal error shutdown callback - this is bad! forcing shutdown',\n );\n logAndExitProcess(error);\n } else if (!caughtSecondError) {\n // two cases for how we can hit this branch:\n // - capturing of first error blew up and we just caught the exception from that\n // - quit trying to capture, proceed with shutdown\n // - a second independent error happened while waiting for first error to capture\n // - want to avoid causing premature shutdown before first error capture finishes\n // it's hard to immediately tell case 1 from case 2 without doing some fancy/questionable domain stuff\n // so let's instead just delay a bit before we proceed with our action here\n // in case 1, we just wait a bit unnecessarily but ultimately do the same thing\n // in case 2, the delay hopefully made us wait long enough for the capture to finish\n // two potential nonideal outcomes:\n // nonideal case 1: capturing fails fast, we sit around for a few seconds unnecessarily before proceeding correctly by calling onFatalError\n // nonideal case 2: case 2 happens, 1st error is captured but slowly, timeout completes before capture and we treat second error as the sendErr of (nonexistent) failure from trying to capture first error\n // note that after hitting this branch, we might catch more errors where (caughtSecondError && !calledFatalError)\n // we ignore them - they don't matter to us, we're just waiting for the second error timeout to finish\n caughtSecondError = true;\n setTimeout(() => {\n if (!calledFatalError) {\n // it was probably case 1, let's treat err as the sendErr and call onFatalError\n calledFatalError = true;\n onFatalError(firstError, error);\n } else {\n // it was probably case 2, our first error finished capturing while we waited, cool, do nothing\n }\n }, timeout); // capturing could take at least sendTimeout to fail, plus an arbitrary second for how long it takes to collect surrounding source etc\n }\n }\n }\n },\n { _errorHandler: true },\n );\n}\n"],"names":["defineIntegration","isMainThread","logAndExitProcess","getClient","captureException","DEBUG_BUILD","debug"],"mappings":"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"} |
@@ -6,86 +6,55 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const INTEGRATION_NAME = 'OnUnhandledRejection'; | ||
| const INTEGRATION_NAME = "OnUnhandledRejection"; | ||
| const DEFAULT_IGNORES = [ | ||
| { | ||
| name: 'AI_NoOutputGeneratedError', // When stream aborts in Vercel AI SDK V5, Vercel flush() fails with an error | ||
| name: "AI_NoOutputGeneratedError" | ||
| // When stream aborts in Vercel AI SDK V5, Vercel flush() fails with an error | ||
| }, | ||
| { | ||
| name: 'AbortError', // When stream aborts in Vercel AI SDK V6 | ||
| }, | ||
| name: "AbortError" | ||
| // When stream aborts in Vercel AI SDK V6 | ||
| } | ||
| ]; | ||
| const _onUnhandledRejectionIntegration = ((options = {}) => { | ||
| const opts = { | ||
| mode: options.mode ?? 'warn', | ||
| ignore: [...DEFAULT_IGNORES, ...(options.ignore ?? [])], | ||
| mode: options.mode ?? "warn", | ||
| ignore: [...DEFAULT_IGNORES, ...options.ignore ?? []] | ||
| }; | ||
| return { | ||
| name: INTEGRATION_NAME, | ||
| setup(client) { | ||
| global.process.on('unhandledRejection', makeUnhandledPromiseHandler(client, opts)); | ||
| }, | ||
| global.process.on("unhandledRejection", makeUnhandledPromiseHandler(client, opts)); | ||
| } | ||
| }; | ||
| }) ; | ||
| }); | ||
| const onUnhandledRejectionIntegration = core.defineIntegration(_onUnhandledRejectionIntegration); | ||
| /** Extract error info safely */ | ||
| function extractErrorInfo(reason) { | ||
| // Check if reason is an object (including Error instances, not just plain objects) | ||
| if (typeof reason !== 'object' || reason === null) { | ||
| return { name: '', message: String(reason ?? '') }; | ||
| if (typeof reason !== "object" || reason === null) { | ||
| return { name: "", message: String(reason ?? "") }; | ||
| } | ||
| const errorLike = reason ; | ||
| const name = typeof errorLike.name === 'string' ? errorLike.name : ''; | ||
| const message = typeof errorLike.message === 'string' ? errorLike.message : String(reason); | ||
| const errorLike = reason; | ||
| const name = typeof errorLike.name === "string" ? errorLike.name : ""; | ||
| const message = typeof errorLike.message === "string" ? errorLike.message : String(reason); | ||
| return { name, message }; | ||
| } | ||
| /** Check if a matcher matches the reason */ | ||
| function isMatchingReason(matcher, errorInfo) { | ||
| // name/message matcher | ||
| const nameMatches = matcher.name === undefined || core.isMatchingPattern(errorInfo.name, matcher.name, true); | ||
| const messageMatches = matcher.message === undefined || core.isMatchingPattern(errorInfo.message, matcher.message); | ||
| const nameMatches = matcher.name === void 0 || core.isMatchingPattern(errorInfo.name, matcher.name, true); | ||
| const messageMatches = matcher.message === void 0 || core.isMatchingPattern(errorInfo.message, matcher.message); | ||
| return nameMatches && messageMatches; | ||
| } | ||
| /** Match helper */ | ||
| function matchesIgnore(list, reason) { | ||
| const errorInfo = extractErrorInfo(reason); | ||
| return list.some(matcher => isMatchingReason(matcher, errorInfo)); | ||
| return list.some((matcher) => isMatchingReason(matcher, errorInfo)); | ||
| } | ||
| /** Core handler */ | ||
| function makeUnhandledPromiseHandler( | ||
| client, | ||
| options, | ||
| ) { | ||
| function makeUnhandledPromiseHandler(client, options) { | ||
| return function sendUnhandledPromise(reason, _promise) { | ||
| // Only handle for the active client | ||
| if (core.getClient() !== client) { | ||
| return; | ||
| } | ||
| // Skip if configured to ignore | ||
| if (matchesIgnore(options.ignore ?? [], reason)) { | ||
| return; | ||
| } | ||
| const level = options.mode === 'strict' ? 'fatal' : 'error'; | ||
| // this can be set in places where we cannot reliably get access to the active span/error | ||
| // when the error bubbles up to this handler, we can use this to set the active span | ||
| const activeSpanForError = | ||
| reason && typeof reason === 'object' ? (reason )._sentry_active_span : undefined; | ||
| const activeSpanWrapper = activeSpanForError | ||
| ? (fn) => core.withActiveSpan(activeSpanForError, fn) | ||
| : (fn) => fn(); | ||
| const level = options.mode === "strict" ? "fatal" : "error"; | ||
| const activeSpanForError = reason && typeof reason === "object" ? reason._sentry_active_span : void 0; | ||
| const activeSpanWrapper = activeSpanForError ? (fn) => core.withActiveSpan(activeSpanForError, fn) : (fn) => fn(); | ||
| activeSpanWrapper(() => { | ||
@@ -96,33 +65,21 @@ core.captureException(reason, { | ||
| extra: { unhandledPromiseRejection: true }, | ||
| level, | ||
| level | ||
| }, | ||
| mechanism: { | ||
| handled: false, | ||
| type: 'auto.node.onunhandledrejection', | ||
| }, | ||
| type: "auto.node.onunhandledrejection" | ||
| } | ||
| }); | ||
| }); | ||
| handleRejection(reason, options.mode); | ||
| }; | ||
| } | ||
| /** | ||
| * Handler for `mode` option | ||
| */ | ||
| function handleRejection(reason, mode) { | ||
| // https://github.com/nodejs/node/blob/7cf6f9e964aa00772965391c23acda6d71972a9a/lib/internal/process/promises.js#L234-L240 | ||
| const rejectionWarning = | ||
| 'This error originated either by ' + | ||
| 'throwing inside of an async function without a catch block, ' + | ||
| 'or by rejecting a promise which was not handled with .catch().' + | ||
| ' The promise rejected with the reason:'; | ||
| /* eslint-disable no-console */ | ||
| if (mode === 'warn') { | ||
| const rejectionWarning = "This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason:"; | ||
| if (mode === "warn") { | ||
| core.consoleSandbox(() => { | ||
| console.warn(rejectionWarning); | ||
| console.error(reason && typeof reason === 'object' && 'stack' in reason ? reason.stack : reason); | ||
| console.error(reason && typeof reason === "object" && "stack" in reason ? reason.stack : reason); | ||
| }); | ||
| } else if (mode === 'strict') { | ||
| } else if (mode === "strict") { | ||
| core.consoleSandbox(() => { | ||
@@ -133,3 +90,2 @@ console.warn(rejectionWarning); | ||
| } | ||
| /* eslint-enable no-console */ | ||
| } | ||
@@ -136,0 +92,0 @@ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"onunhandledrejection.js","sources":["../../../src/integrations/onunhandledrejection.ts"],"sourcesContent":["import type { Client, IntegrationFn, SeverityLevel, Span } from '@sentry/core';\nimport {\n captureException,\n consoleSandbox,\n defineIntegration,\n getClient,\n isMatchingPattern,\n withActiveSpan,\n} from '@sentry/core';\nimport { logAndExitProcess } from '../utils/errorhandling';\n\ntype UnhandledRejectionMode = 'none' | 'warn' | 'strict';\n\ntype IgnoreMatcher = { name?: string | RegExp; message?: string | RegExp };\n\ninterface OnUnhandledRejectionOptions {\n /**\n * Option deciding what to do after capturing unhandledRejection,\n * that mimicks behavior of node's --unhandled-rejection flag.\n */\n mode: UnhandledRejectionMode;\n /** Rejection Errors to ignore (don't capture or warn). */\n ignore?: IgnoreMatcher[];\n}\n\nconst INTEGRATION_NAME = 'OnUnhandledRejection';\n\nconst DEFAULT_IGNORES: IgnoreMatcher[] = [\n {\n name: 'AI_NoOutputGeneratedError', // When stream aborts in Vercel AI SDK V5, Vercel flush() fails with an error\n },\n {\n name: 'AbortError', // When stream aborts in Vercel AI SDK V6\n },\n];\n\nconst _onUnhandledRejectionIntegration = ((options: Partial<OnUnhandledRejectionOptions> = {}) => {\n const opts: OnUnhandledRejectionOptions = {\n mode: options.mode ?? 'warn',\n ignore: [...DEFAULT_IGNORES, ...(options.ignore ?? [])],\n };\n\n return {\n name: INTEGRATION_NAME,\n setup(client) {\n global.process.on('unhandledRejection', makeUnhandledPromiseHandler(client, opts));\n },\n };\n}) satisfies IntegrationFn;\n\nexport const onUnhandledRejectionIntegration = defineIntegration(_onUnhandledRejectionIntegration);\n\n/** Extract error info safely */\nfunction extractErrorInfo(reason: unknown): { name: string; message: string } {\n // Check if reason is an object (including Error instances, not just plain objects)\n if (typeof reason !== 'object' || reason === null) {\n return { name: '', message: String(reason ?? '') };\n }\n\n const errorLike = reason as Record<string, unknown>;\n const name = typeof errorLike.name === 'string' ? errorLike.name : '';\n const message = typeof errorLike.message === 'string' ? errorLike.message : String(reason);\n\n return { name, message };\n}\n\n/** Check if a matcher matches the reason */\nfunction isMatchingReason(matcher: IgnoreMatcher, errorInfo: ReturnType<typeof extractErrorInfo>): boolean {\n // name/message matcher\n const nameMatches = matcher.name === undefined || isMatchingPattern(errorInfo.name, matcher.name, true);\n\n const messageMatches = matcher.message === undefined || isMatchingPattern(errorInfo.message, matcher.message);\n\n return nameMatches && messageMatches;\n}\n\n/** Match helper */\nfunction matchesIgnore(list: IgnoreMatcher[], reason: unknown): boolean {\n const errorInfo = extractErrorInfo(reason);\n return list.some(matcher => isMatchingReason(matcher, errorInfo));\n}\n\n/** Core handler */\nexport function makeUnhandledPromiseHandler(\n client: Client,\n options: OnUnhandledRejectionOptions,\n): (reason: unknown, promise: unknown) => void {\n return function sendUnhandledPromise(reason: unknown, _promise: unknown): void {\n // Only handle for the active client\n if (getClient() !== client) {\n return;\n }\n\n // Skip if configured to ignore\n if (matchesIgnore(options.ignore ?? [], reason)) {\n return;\n }\n\n const level: SeverityLevel = options.mode === 'strict' ? 'fatal' : 'error';\n\n // this can be set in places where we cannot reliably get access to the active span/error\n // when the error bubbles up to this handler, we can use this to set the active span\n const activeSpanForError =\n reason && typeof reason === 'object' ? (reason as { _sentry_active_span?: Span })._sentry_active_span : undefined;\n\n const activeSpanWrapper = activeSpanForError\n ? (fn: () => void) => withActiveSpan(activeSpanForError, fn)\n : (fn: () => void) => fn();\n\n activeSpanWrapper(() => {\n captureException(reason, {\n originalException: reason,\n captureContext: {\n extra: { unhandledPromiseRejection: true },\n level,\n },\n mechanism: {\n handled: false,\n type: 'auto.node.onunhandledrejection',\n },\n });\n });\n\n handleRejection(reason, options.mode);\n };\n}\n\n/**\n * Handler for `mode` option\n */\nfunction handleRejection(reason: unknown, mode: UnhandledRejectionMode): void {\n // https://github.com/nodejs/node/blob/7cf6f9e964aa00772965391c23acda6d71972a9a/lib/internal/process/promises.js#L234-L240\n const rejectionWarning =\n 'This error originated either by ' +\n 'throwing inside of an async function without a catch block, ' +\n 'or by rejecting a promise which was not handled with .catch().' +\n ' The promise rejected with the reason:';\n\n /* eslint-disable no-console */\n if (mode === 'warn') {\n consoleSandbox(() => {\n console.warn(rejectionWarning);\n console.error(reason && typeof reason === 'object' && 'stack' in reason ? reason.stack : reason);\n });\n } else if (mode === 'strict') {\n consoleSandbox(() => {\n console.warn(rejectionWarning);\n });\n logAndExitProcess(reason);\n }\n /* eslint-enable no-console */\n}\n"],"names":["defineIntegration","isMatchingPattern","getClient","withActiveSpan","captureException","consoleSandbox","logAndExitProcess"],"mappings":"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"} | ||
| {"version":3,"file":"onunhandledrejection.js","sources":["../../../src/integrations/onunhandledrejection.ts"],"sourcesContent":["import type { Client, IntegrationFn, SeverityLevel, Span } from '@sentry/core';\nimport {\n captureException,\n consoleSandbox,\n defineIntegration,\n getClient,\n isMatchingPattern,\n withActiveSpan,\n} from '@sentry/core';\nimport { logAndExitProcess } from '../utils/errorhandling';\n\ntype UnhandledRejectionMode = 'none' | 'warn' | 'strict';\n\ntype IgnoreMatcher = { name?: string | RegExp; message?: string | RegExp };\n\ninterface OnUnhandledRejectionOptions {\n /**\n * Option deciding what to do after capturing unhandledRejection,\n * that mimicks behavior of node's --unhandled-rejection flag.\n */\n mode: UnhandledRejectionMode;\n /** Rejection Errors to ignore (don't capture or warn). */\n ignore?: IgnoreMatcher[];\n}\n\nconst INTEGRATION_NAME = 'OnUnhandledRejection';\n\nconst DEFAULT_IGNORES: IgnoreMatcher[] = [\n {\n name: 'AI_NoOutputGeneratedError', // When stream aborts in Vercel AI SDK V5, Vercel flush() fails with an error\n },\n {\n name: 'AbortError', // When stream aborts in Vercel AI SDK V6\n },\n];\n\nconst _onUnhandledRejectionIntegration = ((options: Partial<OnUnhandledRejectionOptions> = {}) => {\n const opts: OnUnhandledRejectionOptions = {\n mode: options.mode ?? 'warn',\n ignore: [...DEFAULT_IGNORES, ...(options.ignore ?? [])],\n };\n\n return {\n name: INTEGRATION_NAME,\n setup(client) {\n global.process.on('unhandledRejection', makeUnhandledPromiseHandler(client, opts));\n },\n };\n}) satisfies IntegrationFn;\n\nexport const onUnhandledRejectionIntegration = defineIntegration(_onUnhandledRejectionIntegration);\n\n/** Extract error info safely */\nfunction extractErrorInfo(reason: unknown): { name: string; message: string } {\n // Check if reason is an object (including Error instances, not just plain objects)\n if (typeof reason !== 'object' || reason === null) {\n return { name: '', message: String(reason ?? '') };\n }\n\n const errorLike = reason as Record<string, unknown>;\n const name = typeof errorLike.name === 'string' ? errorLike.name : '';\n const message = typeof errorLike.message === 'string' ? errorLike.message : String(reason);\n\n return { name, message };\n}\n\n/** Check if a matcher matches the reason */\nfunction isMatchingReason(matcher: IgnoreMatcher, errorInfo: ReturnType<typeof extractErrorInfo>): boolean {\n // name/message matcher\n const nameMatches = matcher.name === undefined || isMatchingPattern(errorInfo.name, matcher.name, true);\n\n const messageMatches = matcher.message === undefined || isMatchingPattern(errorInfo.message, matcher.message);\n\n return nameMatches && messageMatches;\n}\n\n/** Match helper */\nfunction matchesIgnore(list: IgnoreMatcher[], reason: unknown): boolean {\n const errorInfo = extractErrorInfo(reason);\n return list.some(matcher => isMatchingReason(matcher, errorInfo));\n}\n\n/** Core handler */\nexport function makeUnhandledPromiseHandler(\n client: Client,\n options: OnUnhandledRejectionOptions,\n): (reason: unknown, promise: unknown) => void {\n return function sendUnhandledPromise(reason: unknown, _promise: unknown): void {\n // Only handle for the active client\n if (getClient() !== client) {\n return;\n }\n\n // Skip if configured to ignore\n if (matchesIgnore(options.ignore ?? [], reason)) {\n return;\n }\n\n const level: SeverityLevel = options.mode === 'strict' ? 'fatal' : 'error';\n\n // this can be set in places where we cannot reliably get access to the active span/error\n // when the error bubbles up to this handler, we can use this to set the active span\n const activeSpanForError =\n reason && typeof reason === 'object' ? (reason as { _sentry_active_span?: Span })._sentry_active_span : undefined;\n\n const activeSpanWrapper = activeSpanForError\n ? (fn: () => void) => withActiveSpan(activeSpanForError, fn)\n : (fn: () => void) => fn();\n\n activeSpanWrapper(() => {\n captureException(reason, {\n originalException: reason,\n captureContext: {\n extra: { unhandledPromiseRejection: true },\n level,\n },\n mechanism: {\n handled: false,\n type: 'auto.node.onunhandledrejection',\n },\n });\n });\n\n handleRejection(reason, options.mode);\n };\n}\n\n/**\n * Handler for `mode` option\n */\nfunction handleRejection(reason: unknown, mode: UnhandledRejectionMode): void {\n // https://github.com/nodejs/node/blob/7cf6f9e964aa00772965391c23acda6d71972a9a/lib/internal/process/promises.js#L234-L240\n const rejectionWarning =\n 'This error originated either by ' +\n 'throwing inside of an async function without a catch block, ' +\n 'or by rejecting a promise which was not handled with .catch().' +\n ' The promise rejected with the reason:';\n\n /* eslint-disable no-console */\n if (mode === 'warn') {\n consoleSandbox(() => {\n console.warn(rejectionWarning);\n console.error(reason && typeof reason === 'object' && 'stack' in reason ? reason.stack : reason);\n });\n } else if (mode === 'strict') {\n consoleSandbox(() => {\n console.warn(rejectionWarning);\n });\n logAndExitProcess(reason);\n }\n /* eslint-enable no-console */\n}\n"],"names":["defineIntegration","isMatchingPattern","getClient","withActiveSpan","captureException","consoleSandbox","logAndExitProcess"],"mappings":"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"} |
@@ -6,8 +6,3 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const SENTRY_TRACK_SYMBOL = Symbol('sentry-track-pino-logger'); | ||
| /** | ||
| * Gets a custom Pino key from a logger instance by searching for the symbol. | ||
| * Pino uses non-global symbols like Symbol('pino.messageKey'): https://github.com/pinojs/pino/blob/8a816c0b1f72de5ae9181f3bb402109b66f7d812/lib/symbols.js | ||
| */ | ||
| const SENTRY_TRACK_SYMBOL = /* @__PURE__ */ Symbol("sentry-track-pino-logger"); | ||
| function getPinoKey(logger, symbolName, defaultKey) { | ||
@@ -19,3 +14,3 @@ const symbols = Object.getOwnPropertySymbols(logger); | ||
| const value = logger[sym]; | ||
| return typeof value === 'string' ? value : defaultKey; | ||
| return typeof value === "string" ? value : defaultKey; | ||
| } | ||
@@ -25,14 +20,10 @@ } | ||
| } | ||
| const DEFAULT_OPTIONS = { | ||
| error: { levels: [], handled: true }, | ||
| log: { levels: ['trace', 'debug', 'info', 'warn', 'error', 'fatal'] }, | ||
| log: { levels: ["trace", "debug", "info", "warn", "error", "fatal"] } | ||
| }; | ||
| function stripIgnoredFields(result) { | ||
| // eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
| const { level, time, pid, hostname, ...rest } = result; | ||
| return rest; | ||
| } | ||
| const _pinoIntegration = core.defineIntegration((userOptions = {}) => { | ||
@@ -42,17 +33,13 @@ const options = { | ||
| error: { ...DEFAULT_OPTIONS.error, ...userOptions.error }, | ||
| log: { ...DEFAULT_OPTIONS.log, ...userOptions.log }, | ||
| log: { ...DEFAULT_OPTIONS.log, ...userOptions.log } | ||
| }; | ||
| function shouldTrackLogger(logger) { | ||
| const override = logger[SENTRY_TRACK_SYMBOL]; | ||
| return override === 'track' || (override !== 'ignore' && options.autoInstrument); | ||
| return override === "track" || override !== "ignore" && options.autoInstrument; | ||
| } | ||
| return { | ||
| name: 'Pino', | ||
| setup: client => { | ||
| name: "Pino", | ||
| setup: (client) => { | ||
| const enableLogs = !!client.getOptions().enableLogs; | ||
| const integratedChannel = diagnosticsChannel.tracingChannel('pino_asJson'); | ||
| const integratedChannel = diagnosticsChannel.tracingChannel("pino_asJson"); | ||
| function onPinoStart(self, args, result) { | ||
@@ -62,38 +49,29 @@ if (!shouldTrackLogger(self)) { | ||
| } | ||
| const resultObj = stripIgnoredFields(result); | ||
| const [captureObj, message, levelNumber] = args; | ||
| const level = self?.levels?.labels?.[levelNumber] || 'info'; | ||
| const messageKey = getPinoKey(self, 'pino.messageKey', 'msg'); | ||
| const logMessage = message || (resultObj?.[messageKey] ) || ''; | ||
| const level = self?.levels?.labels?.[levelNumber] || "info"; | ||
| const messageKey = getPinoKey(self, "pino.messageKey", "msg"); | ||
| const logMessage = message || resultObj?.[messageKey] || ""; | ||
| if (enableLogs && options.log.levels.includes(level)) { | ||
| const attributes = { | ||
| ...resultObj, | ||
| 'sentry.origin': 'auto.log.pino', | ||
| 'pino.logger.level': levelNumber, | ||
| "sentry.origin": "auto.log.pino", | ||
| "pino.logger.level": levelNumber | ||
| }; | ||
| core._INTERNAL_captureLog({ level, message: logMessage, attributes }); | ||
| } | ||
| if (options.error.levels.includes(level)) { | ||
| const captureContext = { | ||
| level: core.severityLevelFromString(level), | ||
| level: core.severityLevelFromString(level) | ||
| }; | ||
| core.withScope(scope => { | ||
| scope.addEventProcessor(event => { | ||
| event.logger = 'pino'; | ||
| core.withScope((scope) => { | ||
| scope.addEventProcessor((event) => { | ||
| event.logger = "pino"; | ||
| core.addExceptionMechanism(event, { | ||
| handled: options.error.handled, | ||
| type: 'auto.log.pino', | ||
| type: "auto.log.pino" | ||
| }); | ||
| return event; | ||
| }); | ||
| const error = captureObj[getPinoKey(self, 'pino.errorKey', 'err')]; | ||
| const error = captureObj[getPinoKey(self, "pino.errorKey", "err")]; | ||
| if (error) { | ||
@@ -103,3 +81,2 @@ core.captureException(error, captureContext); | ||
| } | ||
| core.captureMessage(logMessage, captureContext); | ||
@@ -109,39 +86,27 @@ }); | ||
| } | ||
| integratedChannel.end.subscribe(data => { | ||
| integratedChannel.end.subscribe((data) => { | ||
| const { | ||
| instance, | ||
| arguments: args, | ||
| result, | ||
| } = data ; | ||
| result | ||
| } = data; | ||
| onPinoStart(instance, args, JSON.parse(result)); | ||
| }); | ||
| }, | ||
| } | ||
| }; | ||
| }) ; | ||
| /** | ||
| * Integration for Pino logging library. | ||
| * Captures Pino logs as Sentry logs and optionally captures some log levels as events. | ||
| * | ||
| * By default, all Pino loggers will be captured. To ignore a specific logger, use `pinoIntegration.untrackLogger(logger)`. | ||
| * | ||
| * If you disable automatic instrumentation with `autoInstrument: false`, you can mark specific loggers to be tracked with `pinoIntegration.trackLogger(logger)`. | ||
| * | ||
| * Requires Pino >=v8.0.0 and Node >=20.6.0 or >=18.19.0 | ||
| */ | ||
| }); | ||
| const pinoIntegration = Object.assign(_pinoIntegration, { | ||
| trackLogger(logger) { | ||
| if (logger && typeof logger === 'object' && 'levels' in logger) { | ||
| (logger )[SENTRY_TRACK_SYMBOL] = 'track'; | ||
| if (logger && typeof logger === "object" && "levels" in logger) { | ||
| logger[SENTRY_TRACK_SYMBOL] = "track"; | ||
| } | ||
| }, | ||
| untrackLogger(logger) { | ||
| if (logger && typeof logger === 'object' && 'levels' in logger) { | ||
| (logger )[SENTRY_TRACK_SYMBOL] = 'ignore'; | ||
| if (logger && typeof logger === "object" && "levels" in logger) { | ||
| logger[SENTRY_TRACK_SYMBOL] = "ignore"; | ||
| } | ||
| }, | ||
| }) ; | ||
| } | ||
| }); | ||
| exports.pinoIntegration = pinoIntegration; | ||
| //# sourceMappingURL=pino.js.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"pino.js","sources":["../../../src/integrations/pino.ts"],"sourcesContent":["import * as diagnosticsChannel from 'node:diagnostics_channel';\nimport type { Integration, IntegrationFn, LogSeverityLevel } from '@sentry/core';\nimport {\n _INTERNAL_captureLog,\n addExceptionMechanism,\n captureException,\n captureMessage,\n defineIntegration,\n severityLevelFromString,\n withScope,\n} from '@sentry/core';\n\nconst SENTRY_TRACK_SYMBOL = Symbol('sentry-track-pino-logger');\n\ntype LevelMapping = {\n // Fortunately pino uses the same levels as Sentry\n labels: { [level: number]: LogSeverityLevel };\n};\n\ntype Pino = {\n [key: symbol]: unknown;\n levels: LevelMapping;\n [SENTRY_TRACK_SYMBOL]?: 'track' | 'ignore';\n};\n\n/**\n * Gets a custom Pino key from a logger instance by searching for the symbol.\n * Pino uses non-global symbols like Symbol('pino.messageKey'): https://github.com/pinojs/pino/blob/8a816c0b1f72de5ae9181f3bb402109b66f7d812/lib/symbols.js\n */\nfunction getPinoKey(logger: Pino, symbolName: string, defaultKey: string): string {\n const symbols = Object.getOwnPropertySymbols(logger);\n const symbolString = `Symbol(${symbolName})`;\n for (const sym of symbols) {\n if (sym.toString() === symbolString) {\n const value = logger[sym];\n return typeof value === 'string' ? value : defaultKey;\n }\n }\n return defaultKey;\n}\n\ntype MergeObject = {\n [key: string]: unknown;\n err?: Error;\n};\n\ntype PinoHookArgs = [MergeObject, string, number];\n\ntype PinoOptions = {\n /**\n * Automatically instrument all Pino loggers.\n *\n * When set to `false`, only loggers marked with `pinoIntegration.trackLogger(logger)` will be captured.\n *\n * @default true\n */\n autoInstrument: boolean;\n /**\n * Options to enable capturing of error events.\n */\n error: {\n /**\n * Levels that trigger capturing of events.\n *\n * @default []\n */\n levels: LogSeverityLevel[];\n /**\n * By default, Sentry will mark captured errors as handled.\n * Set this to `false` if you want to mark them as unhandled instead.\n *\n * @default true\n */\n handled: boolean;\n };\n /**\n * Options to enable capturing of logs.\n */\n log: {\n /**\n * Levels that trigger capturing of logs. Logs are only captured if\n * `enableLogs` is enabled.\n *\n * @default [\"trace\", \"debug\", \"info\", \"warn\", \"error\", \"fatal\"]\n */\n levels: LogSeverityLevel[];\n };\n};\n\nconst DEFAULT_OPTIONS: PinoOptions = {\n autoInstrument: true,\n error: { levels: [], handled: true },\n log: { levels: ['trace', 'debug', 'info', 'warn', 'error', 'fatal'] },\n};\n\ntype DeepPartial<T> = {\n [P in keyof T]?: T[P] extends object ? Partial<T[P]> : T[P];\n};\n\ntype PinoResult = {\n level?: string;\n time?: string;\n pid?: number;\n hostname?: string;\n} & Record<string, unknown>;\n\nfunction stripIgnoredFields(result: PinoResult): PinoResult {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { level, time, pid, hostname, ...rest } = result;\n return rest;\n}\n\nconst _pinoIntegration = defineIntegration((userOptions: DeepPartial<PinoOptions> = {}) => {\n const options: PinoOptions = {\n autoInstrument: userOptions.autoInstrument !== false,\n error: { ...DEFAULT_OPTIONS.error, ...userOptions.error },\n log: { ...DEFAULT_OPTIONS.log, ...userOptions.log },\n };\n\n function shouldTrackLogger(logger: Pino): boolean {\n const override = logger[SENTRY_TRACK_SYMBOL];\n return override === 'track' || (override !== 'ignore' && options.autoInstrument);\n }\n\n return {\n name: 'Pino',\n setup: client => {\n const enableLogs = !!client.getOptions().enableLogs;\n\n const integratedChannel = diagnosticsChannel.tracingChannel('pino_asJson');\n\n function onPinoStart(self: Pino, args: PinoHookArgs, result: PinoResult): void {\n if (!shouldTrackLogger(self)) {\n return;\n }\n\n const resultObj = stripIgnoredFields(result);\n\n const [captureObj, message, levelNumber] = args;\n const level = self?.levels?.labels?.[levelNumber] || 'info';\n const messageKey = getPinoKey(self, 'pino.messageKey', 'msg');\n const logMessage = message || (resultObj?.[messageKey] as string | undefined) || '';\n\n if (enableLogs && options.log.levels.includes(level)) {\n const attributes: Record<string, unknown> = {\n ...resultObj,\n 'sentry.origin': 'auto.log.pino',\n 'pino.logger.level': levelNumber,\n };\n\n _INTERNAL_captureLog({ level, message: logMessage, attributes });\n }\n\n if (options.error.levels.includes(level)) {\n const captureContext = {\n level: severityLevelFromString(level),\n };\n\n withScope(scope => {\n scope.addEventProcessor(event => {\n event.logger = 'pino';\n\n addExceptionMechanism(event, {\n handled: options.error.handled,\n type: 'auto.log.pino',\n });\n\n return event;\n });\n\n const error = captureObj[getPinoKey(self, 'pino.errorKey', 'err')];\n if (error) {\n captureException(error, captureContext);\n return;\n }\n\n captureMessage(logMessage, captureContext);\n });\n }\n }\n\n integratedChannel.end.subscribe(data => {\n const {\n instance,\n arguments: args,\n result,\n } = data as { instance: Pino; arguments: PinoHookArgs; result: string };\n onPinoStart(instance, args, JSON.parse(result));\n });\n },\n };\n}) satisfies IntegrationFn;\n\ninterface PinoIntegrationFunction {\n (userOptions?: DeepPartial<PinoOptions>): Integration;\n /**\n * Marks a Pino logger to be tracked by the Pino integration.\n *\n * @param logger A Pino logger instance.\n */\n trackLogger(logger: unknown): void;\n /**\n * Marks a Pino logger to be ignored by the Pino integration.\n *\n * @param logger A Pino logger instance.\n */\n untrackLogger(logger: unknown): void;\n}\n\n/**\n * Integration for Pino logging library.\n * Captures Pino logs as Sentry logs and optionally captures some log levels as events.\n *\n * By default, all Pino loggers will be captured. To ignore a specific logger, use `pinoIntegration.untrackLogger(logger)`.\n *\n * If you disable automatic instrumentation with `autoInstrument: false`, you can mark specific loggers to be tracked with `pinoIntegration.trackLogger(logger)`.\n *\n * Requires Pino >=v8.0.0 and Node >=20.6.0 or >=18.19.0\n */\nexport const pinoIntegration = Object.assign(_pinoIntegration, {\n trackLogger(logger: unknown): void {\n if (logger && typeof logger === 'object' && 'levels' in logger) {\n (logger as Pino)[SENTRY_TRACK_SYMBOL] = 'track';\n }\n },\n untrackLogger(logger: unknown): void {\n if (logger && typeof logger === 'object' && 'levels' in logger) {\n (logger as Pino)[SENTRY_TRACK_SYMBOL] = 'ignore';\n }\n },\n}) as PinoIntegrationFunction;\n"],"names":["defineIntegration","_INTERNAL_captureLog","severityLevelFromString","withScope","addExceptionMechanism","captureException","captureMessage"],"mappings":"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"} | ||
| {"version":3,"file":"pino.js","sources":["../../../src/integrations/pino.ts"],"sourcesContent":["import * as diagnosticsChannel from 'node:diagnostics_channel';\nimport type { Integration, IntegrationFn, LogSeverityLevel } from '@sentry/core';\nimport {\n _INTERNAL_captureLog,\n addExceptionMechanism,\n captureException,\n captureMessage,\n defineIntegration,\n severityLevelFromString,\n withScope,\n} from '@sentry/core';\n\nconst SENTRY_TRACK_SYMBOL = Symbol('sentry-track-pino-logger');\n\ntype LevelMapping = {\n // Fortunately pino uses the same levels as Sentry\n labels: { [level: number]: LogSeverityLevel };\n};\n\ntype Pino = {\n [key: symbol]: unknown;\n levels: LevelMapping;\n [SENTRY_TRACK_SYMBOL]?: 'track' | 'ignore';\n};\n\n/**\n * Gets a custom Pino key from a logger instance by searching for the symbol.\n * Pino uses non-global symbols like Symbol('pino.messageKey'): https://github.com/pinojs/pino/blob/8a816c0b1f72de5ae9181f3bb402109b66f7d812/lib/symbols.js\n */\nfunction getPinoKey(logger: Pino, symbolName: string, defaultKey: string): string {\n const symbols = Object.getOwnPropertySymbols(logger);\n const symbolString = `Symbol(${symbolName})`;\n for (const sym of symbols) {\n if (sym.toString() === symbolString) {\n const value = logger[sym];\n return typeof value === 'string' ? value : defaultKey;\n }\n }\n return defaultKey;\n}\n\ntype MergeObject = {\n [key: string]: unknown;\n err?: Error;\n};\n\ntype PinoHookArgs = [MergeObject, string, number];\n\ntype PinoOptions = {\n /**\n * Automatically instrument all Pino loggers.\n *\n * When set to `false`, only loggers marked with `pinoIntegration.trackLogger(logger)` will be captured.\n *\n * @default true\n */\n autoInstrument: boolean;\n /**\n * Options to enable capturing of error events.\n */\n error: {\n /**\n * Levels that trigger capturing of events.\n *\n * @default []\n */\n levels: LogSeverityLevel[];\n /**\n * By default, Sentry will mark captured errors as handled.\n * Set this to `false` if you want to mark them as unhandled instead.\n *\n * @default true\n */\n handled: boolean;\n };\n /**\n * Options to enable capturing of logs.\n */\n log: {\n /**\n * Levels that trigger capturing of logs. Logs are only captured if\n * `enableLogs` is enabled.\n *\n * @default [\"trace\", \"debug\", \"info\", \"warn\", \"error\", \"fatal\"]\n */\n levels: LogSeverityLevel[];\n };\n};\n\nconst DEFAULT_OPTIONS: PinoOptions = {\n autoInstrument: true,\n error: { levels: [], handled: true },\n log: { levels: ['trace', 'debug', 'info', 'warn', 'error', 'fatal'] },\n};\n\ntype DeepPartial<T> = {\n [P in keyof T]?: T[P] extends object ? Partial<T[P]> : T[P];\n};\n\ntype PinoResult = {\n level?: string;\n time?: string;\n pid?: number;\n hostname?: string;\n} & Record<string, unknown>;\n\nfunction stripIgnoredFields(result: PinoResult): PinoResult {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { level, time, pid, hostname, ...rest } = result;\n return rest;\n}\n\nconst _pinoIntegration = defineIntegration((userOptions: DeepPartial<PinoOptions> = {}) => {\n const options: PinoOptions = {\n autoInstrument: userOptions.autoInstrument !== false,\n error: { ...DEFAULT_OPTIONS.error, ...userOptions.error },\n log: { ...DEFAULT_OPTIONS.log, ...userOptions.log },\n };\n\n function shouldTrackLogger(logger: Pino): boolean {\n const override = logger[SENTRY_TRACK_SYMBOL];\n return override === 'track' || (override !== 'ignore' && options.autoInstrument);\n }\n\n return {\n name: 'Pino',\n setup: client => {\n const enableLogs = !!client.getOptions().enableLogs;\n\n const integratedChannel = diagnosticsChannel.tracingChannel('pino_asJson');\n\n function onPinoStart(self: Pino, args: PinoHookArgs, result: PinoResult): void {\n if (!shouldTrackLogger(self)) {\n return;\n }\n\n const resultObj = stripIgnoredFields(result);\n\n const [captureObj, message, levelNumber] = args;\n const level = self?.levels?.labels?.[levelNumber] || 'info';\n const messageKey = getPinoKey(self, 'pino.messageKey', 'msg');\n const logMessage = message || (resultObj?.[messageKey] as string | undefined) || '';\n\n if (enableLogs && options.log.levels.includes(level)) {\n const attributes: Record<string, unknown> = {\n ...resultObj,\n 'sentry.origin': 'auto.log.pino',\n 'pino.logger.level': levelNumber,\n };\n\n _INTERNAL_captureLog({ level, message: logMessage, attributes });\n }\n\n if (options.error.levels.includes(level)) {\n const captureContext = {\n level: severityLevelFromString(level),\n };\n\n withScope(scope => {\n scope.addEventProcessor(event => {\n event.logger = 'pino';\n\n addExceptionMechanism(event, {\n handled: options.error.handled,\n type: 'auto.log.pino',\n });\n\n return event;\n });\n\n const error = captureObj[getPinoKey(self, 'pino.errorKey', 'err')];\n if (error) {\n captureException(error, captureContext);\n return;\n }\n\n captureMessage(logMessage, captureContext);\n });\n }\n }\n\n integratedChannel.end.subscribe(data => {\n const {\n instance,\n arguments: args,\n result,\n } = data as { instance: Pino; arguments: PinoHookArgs; result: string };\n onPinoStart(instance, args, JSON.parse(result));\n });\n },\n };\n}) satisfies IntegrationFn;\n\ninterface PinoIntegrationFunction {\n (userOptions?: DeepPartial<PinoOptions>): Integration;\n /**\n * Marks a Pino logger to be tracked by the Pino integration.\n *\n * @param logger A Pino logger instance.\n */\n trackLogger(logger: unknown): void;\n /**\n * Marks a Pino logger to be ignored by the Pino integration.\n *\n * @param logger A Pino logger instance.\n */\n untrackLogger(logger: unknown): void;\n}\n\n/**\n * Integration for Pino logging library.\n * Captures Pino logs as Sentry logs and optionally captures some log levels as events.\n *\n * By default, all Pino loggers will be captured. To ignore a specific logger, use `pinoIntegration.untrackLogger(logger)`.\n *\n * If you disable automatic instrumentation with `autoInstrument: false`, you can mark specific loggers to be tracked with `pinoIntegration.trackLogger(logger)`.\n *\n * Requires Pino >=v8.0.0 and Node >=20.6.0 or >=18.19.0\n */\nexport const pinoIntegration = Object.assign(_pinoIntegration, {\n trackLogger(logger: unknown): void {\n if (logger && typeof logger === 'object' && 'levels' in logger) {\n (logger as Pino)[SENTRY_TRACK_SYMBOL] = 'track';\n }\n },\n untrackLogger(logger: unknown): void {\n if (logger && typeof logger === 'object' && 'levels' in logger) {\n (logger as Pino)[SENTRY_TRACK_SYMBOL] = 'ignore';\n }\n },\n}) as PinoIntegrationFunction;\n"],"names":["defineIntegration","_INTERNAL_captureLog","severityLevelFromString","withScope","addExceptionMechanism","captureException","captureMessage"],"mappings":"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"} |
@@ -5,7 +5,3 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const INTEGRATION_NAME = 'ProcessSession'; | ||
| /** | ||
| * Records a Session for the current process to track release health. | ||
| */ | ||
| const INTEGRATION_NAME = "ProcessSession"; | ||
| const processSessionIntegration = core.defineIntegration(() => { | ||
@@ -16,19 +12,9 @@ return { | ||
| core.startSession(); | ||
| // Emitted in the case of healthy sessions, error of `mechanism.handled: true` and unhandledrejections because | ||
| // The 'beforeExit' event is not emitted for conditions causing explicit termination, | ||
| // such as calling process.exit() or uncaught exceptions. | ||
| // Ref: https://nodejs.org/api/process.html#process_event_beforeexit | ||
| process.on('beforeExit', () => { | ||
| process.on("beforeExit", () => { | ||
| const session = core.getIsolationScope().getSession(); | ||
| // Only call endSession, if the Session exists on Scope and SessionStatus is not a | ||
| // Terminal Status i.e. Exited or Crashed because | ||
| // "When a session is moved away from ok it must not be updated anymore." | ||
| // Ref: https://develop.sentry.dev/sdk/sessions/ | ||
| if (session?.status !== 'ok') { | ||
| if (session?.status !== "ok") { | ||
| core.endSession(); | ||
| } | ||
| }); | ||
| }, | ||
| } | ||
| }; | ||
@@ -35,0 +21,0 @@ }); |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"processSession.js","sources":["../../../src/integrations/processSession.ts"],"sourcesContent":["import { defineIntegration, endSession, getIsolationScope, startSession } from '@sentry/core';\n\nconst INTEGRATION_NAME = 'ProcessSession';\n\n/**\n * Records a Session for the current process to track release health.\n */\nexport const processSessionIntegration = defineIntegration(() => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n startSession();\n\n // Emitted in the case of healthy sessions, error of `mechanism.handled: true` and unhandledrejections because\n // The 'beforeExit' event is not emitted for conditions causing explicit termination,\n // such as calling process.exit() or uncaught exceptions.\n // Ref: https://nodejs.org/api/process.html#process_event_beforeexit\n process.on('beforeExit', () => {\n const session = getIsolationScope().getSession();\n\n // Only call endSession, if the Session exists on Scope and SessionStatus is not a\n // Terminal Status i.e. Exited or Crashed because\n // \"When a session is moved away from ok it must not be updated anymore.\"\n // Ref: https://develop.sentry.dev/sdk/sessions/\n if (session?.status !== 'ok') {\n endSession();\n }\n });\n },\n };\n});\n"],"names":["defineIntegration","startSession","getIsolationScope","endSession"],"mappings":";;;;AAEA,MAAM,gBAAA,GAAmB,gBAAgB;;AAEzC;AACA;AACA;MACa,yBAAA,GAA4BA,sBAAiB,CAAC,MAAM;AACjE,EAAE,OAAO;AACT,IAAI,IAAI,EAAE,gBAAgB;AAC1B,IAAI,SAAS,GAAG;AAChB,MAAMC,iBAAY,EAAE;;AAEpB;AACA;AACA;AACA;AACA,MAAM,OAAO,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM;AACrC,QAAQ,MAAM,UAAUC,sBAAiB,EAAE,CAAC,UAAU,EAAE;;AAExD;AACA;AACA;AACA;AACA,QAAQ,IAAI,OAAO,EAAE,MAAA,KAAW,IAAI,EAAE;AACtC,UAAUC,eAAU,EAAE;AACtB,QAAQ;AACR,MAAM,CAAC,CAAC;AACR,IAAI,CAAC;AACL,GAAG;AACH,CAAC;;;;"} | ||
| {"version":3,"file":"processSession.js","sources":["../../../src/integrations/processSession.ts"],"sourcesContent":["import { defineIntegration, endSession, getIsolationScope, startSession } from '@sentry/core';\n\nconst INTEGRATION_NAME = 'ProcessSession';\n\n/**\n * Records a Session for the current process to track release health.\n */\nexport const processSessionIntegration = defineIntegration(() => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n startSession();\n\n // Emitted in the case of healthy sessions, error of `mechanism.handled: true` and unhandledrejections because\n // The 'beforeExit' event is not emitted for conditions causing explicit termination,\n // such as calling process.exit() or uncaught exceptions.\n // Ref: https://nodejs.org/api/process.html#process_event_beforeexit\n process.on('beforeExit', () => {\n const session = getIsolationScope().getSession();\n\n // Only call endSession, if the Session exists on Scope and SessionStatus is not a\n // Terminal Status i.e. Exited or Crashed because\n // \"When a session is moved away from ok it must not be updated anymore.\"\n // Ref: https://develop.sentry.dev/sdk/sessions/\n if (session?.status !== 'ok') {\n endSession();\n }\n });\n },\n };\n});\n"],"names":["defineIntegration","startSession","getIsolationScope","endSession"],"mappings":";;;;AAEA,MAAM,gBAAA,GAAmB,gBAAA;AAKlB,MAAM,yBAAA,GAA4BA,uBAAkB,MAAM;AAC/D,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,SAAA,GAAY;AACV,MAAAC,iBAAA,EAAa;AAMb,MAAA,OAAA,CAAQ,EAAA,CAAG,cAAc,MAAM;AAC7B,QAAA,MAAM,OAAA,GAAUC,sBAAA,EAAkB,CAAE,UAAA,EAAW;AAM/C,QAAA,IAAI,OAAA,EAAS,WAAW,IAAA,EAAM;AAC5B,UAAAC,eAAA,EAAW;AAAA,QACb;AAAA,MACF,CAAC,CAAA;AAAA,IACH;AAAA,GACF;AACF,CAAC;;;;"} |
@@ -6,9 +6,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const INTEGRATION_NAME = 'Spotlight'; | ||
| const INTEGRATION_NAME = "Spotlight"; | ||
| const _spotlightIntegration = ((options = {}) => { | ||
| const _options = { | ||
| sidecarUrl: options.sidecarUrl || 'http://localhost:8969/stream', | ||
| sidecarUrl: options.sidecarUrl || "http://localhost:8969/stream" | ||
| }; | ||
| return { | ||
@@ -18,22 +16,12 @@ name: INTEGRATION_NAME, | ||
| try { | ||
| if (process.env.NODE_ENV && process.env.NODE_ENV !== 'development') { | ||
| if (process.env.NODE_ENV && process.env.NODE_ENV !== "development") { | ||
| core.debug.warn("[Spotlight] It seems you're not in dev mode. Do you really want to have Spotlight enabled?"); | ||
| } | ||
| } catch { | ||
| // ignore | ||
| } | ||
| connectToSpotlight(client, _options); | ||
| }, | ||
| } | ||
| }; | ||
| }) ; | ||
| /** | ||
| * Use this integration to send errors and transactions to Spotlight. | ||
| * | ||
| * Learn more about spotlight at https://spotlightjs.com | ||
| * | ||
| * Important: This integration only works with Node 18 or newer. | ||
| */ | ||
| }); | ||
| const spotlightIntegration = core.defineIntegration(_spotlightIntegration); | ||
| function connectToSpotlight(client, options) { | ||
@@ -44,11 +32,8 @@ const spotlightUrl = parseSidecarUrl(options.sidecarUrl); | ||
| } | ||
| let failedRequests = 0; | ||
| client.on('beforeEnvelope', (envelope) => { | ||
| client.on("beforeEnvelope", (envelope) => { | ||
| if (failedRequests > 3) { | ||
| core.debug.warn('[Spotlight] Disabled Sentry -> Spotlight integration due to too many failed requests'); | ||
| core.debug.warn("[Spotlight] Disabled Sentry -> Spotlight integration due to too many failed requests"); | ||
| return; | ||
| } | ||
| const serializedEnvelope = core.serializeEnvelope(envelope); | ||
@@ -58,3 +43,3 @@ core.suppressTracing(() => { | ||
| { | ||
| method: 'POST', | ||
| method: "POST", | ||
| path: spotlightUrl.pathname, | ||
@@ -64,24 +49,19 @@ hostname: spotlightUrl.hostname, | ||
| headers: { | ||
| 'Content-Type': 'application/x-sentry-envelope', | ||
| }, | ||
| "Content-Type": "application/x-sentry-envelope" | ||
| } | ||
| }, | ||
| res => { | ||
| (res) => { | ||
| if (res.statusCode && res.statusCode >= 200 && res.statusCode < 400) { | ||
| // Reset failed requests counter on success | ||
| failedRequests = 0; | ||
| } | ||
| res.on('data', () => { | ||
| // Drain socket | ||
| res.on("data", () => { | ||
| }); | ||
| res.on('end', () => { | ||
| // Drain socket | ||
| res.on("end", () => { | ||
| }); | ||
| res.setEncoding('utf8'); | ||
| }, | ||
| res.setEncoding("utf8"); | ||
| } | ||
| ); | ||
| req.on('error', () => { | ||
| req.on("error", () => { | ||
| failedRequests++; | ||
| core.debug.warn('[Spotlight] Failed to send envelope to Spotlight Sidecar'); | ||
| core.debug.warn("[Spotlight] Failed to send envelope to Spotlight Sidecar"); | ||
| }); | ||
@@ -93,3 +73,2 @@ req.write(serializedEnvelope); | ||
| } | ||
| function parseSidecarUrl(url) { | ||
@@ -100,3 +79,3 @@ try { | ||
| core.debug.warn(`[Spotlight] Invalid sidecar URL: ${url}`); | ||
| return undefined; | ||
| return void 0; | ||
| } | ||
@@ -103,0 +82,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"spotlight.js","sources":["../../../src/integrations/spotlight.ts"],"sourcesContent":["import * as http from 'node:http';\nimport type { Client, Envelope, IntegrationFn } from '@sentry/core';\nimport { debug, defineIntegration, serializeEnvelope, suppressTracing } from '@sentry/core';\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';\n\nconst _spotlightIntegration = ((options: Partial<SpotlightConnectionOptions> = {}) => {\n const _options = {\n sidecarUrl: options.sidecarUrl || 'http://localhost:8969/stream',\n };\n\n return {\n name: INTEGRATION_NAME,\n setup(client) {\n try {\n if (process.env.NODE_ENV && process.env.NODE_ENV !== 'development') {\n debug.warn(\"[Spotlight] It seems you're not in dev mode. Do you really want to have Spotlight enabled?\");\n }\n } catch {\n // ignore\n }\n connectToSpotlight(client, _options);\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 only works with Node 18 or newer.\n */\nexport const spotlightIntegration = defineIntegration(_spotlightIntegration);\n\nfunction connectToSpotlight(client: Client, options: Required<SpotlightConnectionOptions>): void {\n const spotlightUrl = parseSidecarUrl(options.sidecarUrl);\n if (!spotlightUrl) {\n return;\n }\n\n let failedRequests = 0;\n\n client.on('beforeEnvelope', (envelope: Envelope) => {\n if (failedRequests > 3) {\n debug.warn('[Spotlight] Disabled Sentry -> Spotlight integration due to too many failed requests');\n return;\n }\n\n const serializedEnvelope = serializeEnvelope(envelope);\n suppressTracing(() => {\n const req = http.request(\n {\n method: 'POST',\n path: spotlightUrl.pathname,\n hostname: spotlightUrl.hostname,\n port: spotlightUrl.port,\n headers: {\n 'Content-Type': 'application/x-sentry-envelope',\n },\n },\n res => {\n if (res.statusCode && res.statusCode >= 200 && res.statusCode < 400) {\n // Reset failed requests counter on success\n failedRequests = 0;\n }\n res.on('data', () => {\n // Drain socket\n });\n\n res.on('end', () => {\n // Drain socket\n });\n res.setEncoding('utf8');\n },\n );\n\n req.on('error', () => {\n failedRequests++;\n debug.warn('[Spotlight] Failed to send envelope to Spotlight Sidecar');\n });\n req.write(serializedEnvelope);\n req.end();\n });\n });\n}\n\nfunction parseSidecarUrl(url: string): URL | undefined {\n try {\n return new URL(`${url}`);\n } catch {\n debug.warn(`[Spotlight] Invalid sidecar URL: ${url}`);\n return undefined;\n }\n}\n"],"names":["debug","defineIntegration","serializeEnvelope","suppressTracing"],"mappings":"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"} | ||
| {"version":3,"file":"spotlight.js","sources":["../../../src/integrations/spotlight.ts"],"sourcesContent":["import * as http from 'node:http';\nimport type { Client, Envelope, IntegrationFn } from '@sentry/core';\nimport { debug, defineIntegration, serializeEnvelope, suppressTracing } from '@sentry/core';\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';\n\nconst _spotlightIntegration = ((options: Partial<SpotlightConnectionOptions> = {}) => {\n const _options = {\n sidecarUrl: options.sidecarUrl || 'http://localhost:8969/stream',\n };\n\n return {\n name: INTEGRATION_NAME,\n setup(client) {\n try {\n if (process.env.NODE_ENV && process.env.NODE_ENV !== 'development') {\n debug.warn(\"[Spotlight] It seems you're not in dev mode. Do you really want to have Spotlight enabled?\");\n }\n } catch {\n // ignore\n }\n connectToSpotlight(client, _options);\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 only works with Node 18 or newer.\n */\nexport const spotlightIntegration = defineIntegration(_spotlightIntegration);\n\nfunction connectToSpotlight(client: Client, options: Required<SpotlightConnectionOptions>): void {\n const spotlightUrl = parseSidecarUrl(options.sidecarUrl);\n if (!spotlightUrl) {\n return;\n }\n\n let failedRequests = 0;\n\n client.on('beforeEnvelope', (envelope: Envelope) => {\n if (failedRequests > 3) {\n debug.warn('[Spotlight] Disabled Sentry -> Spotlight integration due to too many failed requests');\n return;\n }\n\n const serializedEnvelope = serializeEnvelope(envelope);\n suppressTracing(() => {\n const req = http.request(\n {\n method: 'POST',\n path: spotlightUrl.pathname,\n hostname: spotlightUrl.hostname,\n port: spotlightUrl.port,\n headers: {\n 'Content-Type': 'application/x-sentry-envelope',\n },\n },\n res => {\n if (res.statusCode && res.statusCode >= 200 && res.statusCode < 400) {\n // Reset failed requests counter on success\n failedRequests = 0;\n }\n res.on('data', () => {\n // Drain socket\n });\n\n res.on('end', () => {\n // Drain socket\n });\n res.setEncoding('utf8');\n },\n );\n\n req.on('error', () => {\n failedRequests++;\n debug.warn('[Spotlight] Failed to send envelope to Spotlight Sidecar');\n });\n req.write(serializedEnvelope);\n req.end();\n });\n });\n}\n\nfunction parseSidecarUrl(url: string): URL | undefined {\n try {\n return new URL(`${url}`);\n } catch {\n debug.warn(`[Spotlight] Invalid sidecar URL: ${url}`);\n return undefined;\n }\n}\n"],"names":["debug","defineIntegration","serializeEnvelope","suppressTracing"],"mappings":";;;;;AAYO,MAAM,gBAAA,GAAmB;AAEhC,MAAM,qBAAA,IAAyB,CAAC,OAAA,GAA+C,EAAC,KAAM;AACpF,EAAA,MAAM,QAAA,GAAW;AAAA,IACf,UAAA,EAAY,QAAQ,UAAA,IAAc;AAAA,GACpC;AAEA,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,MAAM,MAAA,EAAQ;AACZ,MAAA,IAAI;AACF,QAAA,IAAI,QAAQ,GAAA,CAAI,QAAA,IAAY,OAAA,CAAQ,GAAA,CAAI,aAAa,aAAA,EAAe;AAClE,UAAAA,UAAA,CAAM,KAAK,4FAA4F,CAAA;AAAA,QACzG;AAAA,MACF,CAAA,CAAA,MAAQ;AAAA,MAER;AACA,MAAA,kBAAA,CAAmB,QAAQ,QAAQ,CAAA;AAAA,IACrC;AAAA,GACF;AACF,CAAA,CAAA;AASO,MAAM,oBAAA,GAAuBC,uBAAkB,qBAAqB;AAE3E,SAAS,kBAAA,CAAmB,QAAgB,OAAA,EAAqD;AAC/F,EAAA,MAAM,YAAA,GAAe,eAAA,CAAgB,OAAA,CAAQ,UAAU,CAAA;AACvD,EAAA,IAAI,CAAC,YAAA,EAAc;AACjB,IAAA;AAAA,EACF;AAEA,EAAA,IAAI,cAAA,GAAiB,CAAA;AAErB,EAAA,MAAA,CAAO,EAAA,CAAG,gBAAA,EAAkB,CAAC,QAAA,KAAuB;AAClD,IAAA,IAAI,iBAAiB,CAAA,EAAG;AACtB,MAAAD,UAAA,CAAM,KAAK,sFAAsF,CAAA;AACjG,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,kBAAA,GAAqBE,uBAAkB,QAAQ,CAAA;AACrD,IAAAC,oBAAA,CAAgB,MAAM;AACpB,MAAA,MAAM,MAAM,IAAA,CAAK,OAAA;AAAA,QACf;AAAA,UACE,MAAA,EAAQ,MAAA;AAAA,UACR,MAAM,YAAA,CAAa,QAAA;AAAA,UACnB,UAAU,YAAA,CAAa,QAAA;AAAA,UACvB,MAAM,YAAA,CAAa,IAAA;AAAA,UACnB,OAAA,EAAS;AAAA,YACP,cAAA,EAAgB;AAAA;AAClB,SACF;AAAA,QACA,CAAA,GAAA,KAAO;AACL,UAAA,IAAI,IAAI,UAAA,IAAc,GAAA,CAAI,cAAc,GAAA,IAAO,GAAA,CAAI,aAAa,GAAA,EAAK;AAEnE,YAAA,cAAA,GAAiB,CAAA;AAAA,UACnB;AACA,UAAA,GAAA,CAAI,EAAA,CAAG,QAAQ,MAAM;AAAA,UAErB,CAAC,CAAA;AAED,UAAA,GAAA,CAAI,EAAA,CAAG,OAAO,MAAM;AAAA,UAEpB,CAAC,CAAA;AACD,UAAA,GAAA,CAAI,YAAY,MAAM,CAAA;AAAA,QACxB;AAAA,OACF;AAEA,MAAA,GAAA,CAAI,EAAA,CAAG,SAAS,MAAM;AACpB,QAAA,cAAA,EAAA;AACA,QAAAH,UAAA,CAAM,KAAK,0DAA0D,CAAA;AAAA,MACvE,CAAC,CAAA;AACD,MAAA,GAAA,CAAI,MAAM,kBAAkB,CAAA;AAC5B,MAAA,GAAA,CAAI,GAAA,EAAI;AAAA,IACV,CAAC,CAAA;AAAA,EACH,CAAC,CAAA;AACH;AAEA,SAAS,gBAAgB,GAAA,EAA8B;AACrD,EAAA,IAAI;AACF,IAAA,OAAO,IAAI,GAAA,CAAI,CAAA,EAAG,GAAG,CAAA,CAAE,CAAA;AAAA,EACzB,CAAA,CAAA,MAAQ;AACN,IAAAA,UAAA,CAAM,IAAA,CAAK,CAAA,iCAAA,EAAoC,GAAG,CAAA,CAAE,CAAA;AACpD,IAAA,OAAO,MAAA;AAAA,EACT;AACF;;;;;"} |
@@ -6,4 +6,3 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const INTEGRATION_NAME = 'NodeSystemError'; | ||
| const INTEGRATION_NAME = "NodeSystemError"; | ||
| function isSystemError(error) { | ||
@@ -13,22 +12,10 @@ if (!(error instanceof Error)) { | ||
| } | ||
| if (!('errno' in error) || typeof error.errno !== 'number') { | ||
| if (!("errno" in error) || typeof error.errno !== "number") { | ||
| return false; | ||
| } | ||
| // Workaround for Bun where getSystemErrorMap doesn't exist | ||
| // Can be removed once Bun supports getSystemErrorMap | ||
| // https://github.com/oven-sh/bun/issues/22872 | ||
| if (typeof util.getSystemErrorMap !== 'function') { | ||
| if (typeof util.getSystemErrorMap !== "function") { | ||
| return false; | ||
| } | ||
| // Appears this is the recommended way to check for Node.js SystemError | ||
| // https://github.com/nodejs/node/issues/46869 | ||
| return util.getSystemErrorMap().has(error.errno); | ||
| } | ||
| /** | ||
| * Captures context for Node.js SystemError errors. | ||
| */ | ||
| const systemErrorIntegration = core.defineIntegration((options = {}) => { | ||
@@ -41,9 +28,6 @@ return { | ||
| } | ||
| const error = hint.originalException; | ||
| const errorContext = { | ||
| ...(error ), | ||
| ...error | ||
| }; | ||
| if (!client.getOptions().sendDefaultPii && options.includePaths !== true) { | ||
@@ -53,21 +37,18 @@ delete errorContext.path; | ||
| } | ||
| event.contexts = { | ||
| ...event.contexts, | ||
| node_system_error: errorContext, | ||
| node_system_error: errorContext | ||
| }; | ||
| for (const exception of event.exception?.values || []) { | ||
| if (exception.value) { | ||
| if (error.path && exception.value.includes(error.path)) { | ||
| exception.value = exception.value.replace(`'${error.path}'`, '').trim(); | ||
| exception.value = exception.value.replace(`'${error.path}'`, "").trim(); | ||
| } | ||
| if (error.dest && exception.value.includes(error.dest)) { | ||
| exception.value = exception.value.replace(`'${error.dest}'`, '').trim(); | ||
| exception.value = exception.value.replace(`'${error.dest}'`, "").trim(); | ||
| } | ||
| } | ||
| } | ||
| return event; | ||
| }, | ||
| } | ||
| }; | ||
@@ -74,0 +55,0 @@ }); |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"systemError.js","sources":["../../../src/integrations/systemError.ts"],"sourcesContent":["import * as util from 'node:util';\nimport { defineIntegration } from '@sentry/core';\n\nconst INTEGRATION_NAME = 'NodeSystemError';\n\ntype SystemErrorContext = {\n dest?: string; // If present, the file path destination when reporting a file system error\n errno: number; // The system-provided error number\n path?: string; // If present, the file path when reporting a file system error\n};\n\ntype SystemError = Error & SystemErrorContext;\n\nfunction isSystemError(error: unknown): error is SystemError {\n if (!(error instanceof Error)) {\n return false;\n }\n\n if (!('errno' in error) || typeof error.errno !== 'number') {\n return false;\n }\n\n // Workaround for Bun where getSystemErrorMap doesn't exist\n // Can be removed once Bun supports getSystemErrorMap\n // https://github.com/oven-sh/bun/issues/22872\n if (typeof util.getSystemErrorMap !== 'function') {\n return false;\n }\n\n // Appears this is the recommended way to check for Node.js SystemError\n // https://github.com/nodejs/node/issues/46869\n return util.getSystemErrorMap().has(error.errno);\n}\n\ntype Options = {\n /**\n * If true, includes the `path` and `dest` properties in the error context.\n */\n includePaths?: boolean;\n};\n\n/**\n * Captures context for Node.js SystemError errors.\n */\nexport const systemErrorIntegration = defineIntegration((options: Options = {}) => {\n return {\n name: INTEGRATION_NAME,\n processEvent: (event, hint, client) => {\n if (!isSystemError(hint.originalException)) {\n return event;\n }\n\n const error = hint.originalException;\n\n const errorContext: SystemErrorContext = {\n ...(error as SystemErrorContext),\n };\n\n if (!client.getOptions().sendDefaultPii && options.includePaths !== true) {\n delete errorContext.path;\n delete errorContext.dest;\n }\n\n event.contexts = {\n ...event.contexts,\n node_system_error: errorContext,\n };\n\n for (const exception of event.exception?.values || []) {\n if (exception.value) {\n if (error.path && exception.value.includes(error.path)) {\n exception.value = exception.value.replace(`'${error.path}'`, '').trim();\n }\n if (error.dest && exception.value.includes(error.dest)) {\n exception.value = exception.value.replace(`'${error.dest}'`, '').trim();\n }\n }\n }\n\n return event;\n },\n };\n});\n"],"names":["defineIntegration"],"mappings":";;;;;AAGA,MAAM,gBAAA,GAAmB,iBAAiB;;AAU1C,SAAS,aAAa,CAAC,KAAK,EAAiC;AAC7D,EAAE,IAAI,EAAE,iBAAiB,KAAK,CAAC,EAAE;AACjC,IAAI,OAAO,KAAK;AAChB,EAAE;;AAEF,EAAE,IAAI,EAAE,WAAW,KAAK,CAAA,IAAK,OAAO,KAAK,CAAC,KAAA,KAAU,QAAQ,EAAE;AAC9D,IAAI,OAAO,KAAK;AAChB,EAAE;;AAEF;AACA;AACA;AACA,EAAE,IAAI,OAAO,IAAI,CAAC,iBAAA,KAAsB,UAAU,EAAE;AACpD,IAAI,OAAO,KAAK;AAChB,EAAE;;AAEF;AACA;AACA,EAAE,OAAO,IAAI,CAAC,iBAAiB,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC;AAClD;;AASA;AACA;AACA;AACO,MAAM,sBAAA,GAAyBA,sBAAiB,CAAC,CAAC,OAAO,GAAY,EAAE,KAAK;AACnF,EAAE,OAAO;AACT,IAAI,IAAI,EAAE,gBAAgB;AAC1B,IAAI,YAAY,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,MAAM,KAAK;AAC3C,MAAM,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,iBAAiB,CAAC,EAAE;AAClD,QAAQ,OAAO,KAAK;AACpB,MAAM;;AAEN,MAAM,MAAM,KAAA,GAAQ,IAAI,CAAC,iBAAiB;;AAE1C,MAAM,MAAM,YAAY,GAAuB;AAC/C,QAAQ,IAAI,KAAA,EAA4B;AACxC,OAAO;;AAEP,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,cAAA,IAAkB,OAAO,CAAC,YAAA,KAAiB,IAAI,EAAE;AAChF,QAAQ,OAAO,YAAY,CAAC,IAAI;AAChC,QAAQ,OAAO,YAAY,CAAC,IAAI;AAChC,MAAM;;AAEN,MAAM,KAAK,CAAC,QAAA,GAAW;AACvB,QAAQ,GAAG,KAAK,CAAC,QAAQ;AACzB,QAAQ,iBAAiB,EAAE,YAAY;AACvC,OAAO;;AAEP,MAAM,KAAK,MAAM,SAAA,IAAa,KAAK,CAAC,SAAS,EAAE,MAAA,IAAU,EAAE,EAAE;AAC7D,QAAQ,IAAI,SAAS,CAAC,KAAK,EAAE;AAC7B,UAAU,IAAI,KAAK,CAAC,IAAA,IAAQ,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE;AAClE,YAAY,SAAS,CAAC,KAAA,GAAQ,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE;AACnF,UAAU;AACV,UAAU,IAAI,KAAK,CAAC,IAAA,IAAQ,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE;AAClE,YAAY,SAAS,CAAC,KAAA,GAAQ,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE;AACnF,UAAU;AACV,QAAQ;AACR,MAAM;;AAEN,MAAM,OAAO,KAAK;AAClB,IAAI,CAAC;AACL,GAAG;AACH,CAAC;;;;"} | ||
| {"version":3,"file":"systemError.js","sources":["../../../src/integrations/systemError.ts"],"sourcesContent":["import * as util from 'node:util';\nimport { defineIntegration } from '@sentry/core';\n\nconst INTEGRATION_NAME = 'NodeSystemError';\n\ntype SystemErrorContext = {\n dest?: string; // If present, the file path destination when reporting a file system error\n errno: number; // The system-provided error number\n path?: string; // If present, the file path when reporting a file system error\n};\n\ntype SystemError = Error & SystemErrorContext;\n\nfunction isSystemError(error: unknown): error is SystemError {\n if (!(error instanceof Error)) {\n return false;\n }\n\n if (!('errno' in error) || typeof error.errno !== 'number') {\n return false;\n }\n\n // Workaround for Bun where getSystemErrorMap doesn't exist\n // Can be removed once Bun supports getSystemErrorMap\n // https://github.com/oven-sh/bun/issues/22872\n if (typeof util.getSystemErrorMap !== 'function') {\n return false;\n }\n\n // Appears this is the recommended way to check for Node.js SystemError\n // https://github.com/nodejs/node/issues/46869\n return util.getSystemErrorMap().has(error.errno);\n}\n\ntype Options = {\n /**\n * If true, includes the `path` and `dest` properties in the error context.\n */\n includePaths?: boolean;\n};\n\n/**\n * Captures context for Node.js SystemError errors.\n */\nexport const systemErrorIntegration = defineIntegration((options: Options = {}) => {\n return {\n name: INTEGRATION_NAME,\n processEvent: (event, hint, client) => {\n if (!isSystemError(hint.originalException)) {\n return event;\n }\n\n const error = hint.originalException;\n\n const errorContext: SystemErrorContext = {\n ...(error as SystemErrorContext),\n };\n\n if (!client.getOptions().sendDefaultPii && options.includePaths !== true) {\n delete errorContext.path;\n delete errorContext.dest;\n }\n\n event.contexts = {\n ...event.contexts,\n node_system_error: errorContext,\n };\n\n for (const exception of event.exception?.values || []) {\n if (exception.value) {\n if (error.path && exception.value.includes(error.path)) {\n exception.value = exception.value.replace(`'${error.path}'`, '').trim();\n }\n if (error.dest && exception.value.includes(error.dest)) {\n exception.value = exception.value.replace(`'${error.dest}'`, '').trim();\n }\n }\n }\n\n return event;\n },\n };\n});\n"],"names":["defineIntegration"],"mappings":";;;;;AAGA,MAAM,gBAAA,GAAmB,iBAAA;AAUzB,SAAS,cAAc,KAAA,EAAsC;AAC3D,EAAA,IAAI,EAAE,iBAAiB,KAAA,CAAA,EAAQ;AAC7B,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,IAAI,EAAE,OAAA,IAAW,KAAA,CAAA,IAAU,OAAO,KAAA,CAAM,UAAU,QAAA,EAAU;AAC1D,IAAA,OAAO,KAAA;AAAA,EACT;AAKA,EAAA,IAAI,OAAO,IAAA,CAAK,iBAAA,KAAsB,UAAA,EAAY;AAChD,IAAA,OAAO,KAAA;AAAA,EACT;AAIA,EAAA,OAAO,IAAA,CAAK,iBAAA,EAAkB,CAAE,GAAA,CAAI,MAAM,KAAK,CAAA;AACjD;AAYO,MAAM,sBAAA,GAAyBA,sBAAA,CAAkB,CAAC,OAAA,GAAmB,EAAC,KAAM;AACjF,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,YAAA,EAAc,CAAC,KAAA,EAAO,IAAA,EAAM,MAAA,KAAW;AACrC,MAAA,IAAI,CAAC,aAAA,CAAc,IAAA,CAAK,iBAAiB,CAAA,EAAG;AAC1C,QAAA,OAAO,KAAA;AAAA,MACT;AAEA,MAAA,MAAM,QAAQ,IAAA,CAAK,iBAAA;AAEnB,MAAA,MAAM,YAAA,GAAmC;AAAA,QACvC,GAAI;AAAA,OACN;AAEA,MAAA,IAAI,CAAC,MAAA,CAAO,UAAA,GAAa,cAAA,IAAkB,OAAA,CAAQ,iBAAiB,IAAA,EAAM;AACxE,QAAA,OAAO,YAAA,CAAa,IAAA;AACpB,QAAA,OAAO,YAAA,CAAa,IAAA;AAAA,MACtB;AAEA,MAAA,KAAA,CAAM,QAAA,GAAW;AAAA,QACf,GAAG,KAAA,CAAM,QAAA;AAAA,QACT,iBAAA,EAAmB;AAAA,OACrB;AAEA,MAAA,KAAA,MAAW,SAAA,IAAa,KAAA,CAAM,SAAA,EAAW,MAAA,IAAU,EAAC,EAAG;AACrD,QAAA,IAAI,UAAU,KAAA,EAAO;AACnB,UAAA,IAAI,MAAM,IAAA,IAAQ,SAAA,CAAU,MAAM,QAAA,CAAS,KAAA,CAAM,IAAI,CAAA,EAAG;AACtD,YAAA,SAAA,CAAU,KAAA,GAAQ,SAAA,CAAU,KAAA,CAAM,OAAA,CAAQ,CAAA,CAAA,EAAI,MAAM,IAAI,CAAA,CAAA,CAAA,EAAK,EAAE,CAAA,CAAE,IAAA,EAAK;AAAA,UACxE;AACA,UAAA,IAAI,MAAM,IAAA,IAAQ,SAAA,CAAU,MAAM,QAAA,CAAS,KAAA,CAAM,IAAI,CAAA,EAAG;AACtD,YAAA,SAAA,CAAU,KAAA,GAAQ,SAAA,CAAU,KAAA,CAAM,OAAA,CAAQ,CAAA,CAAA,EAAI,MAAM,IAAI,CAAA,CAAA,CAAA,EAAK,EAAE,CAAA,CAAE,IAAA,EAAK;AAAA,UACxE;AAAA,QACF;AAAA,MACF;AAEA,MAAA,OAAO,KAAA;AAAA,IACT;AAAA,GACF;AACF,CAAC;;;;"} |
@@ -7,90 +7,44 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const DEFAULT_CAPTURED_LEVELS = ['trace', 'debug', 'info', 'warn', 'error', 'fatal']; | ||
| // See: https://github.com/winstonjs/triple-beam | ||
| const LEVEL_SYMBOL = Symbol.for('level'); | ||
| const MESSAGE_SYMBOL = Symbol.for('message'); | ||
| const SPLAT_SYMBOL = Symbol.for('splat'); | ||
| /** | ||
| * Options for the Sentry Winston transport. | ||
| */ | ||
| /** | ||
| * Creates a new Sentry Winston transport that fowards logs to Sentry. Requires the `enableLogs` option to be enabled. | ||
| * | ||
| * Supports Winston 3.x.x. | ||
| * | ||
| * @param TransportClass - The Winston transport class to extend. | ||
| * @returns The extended transport class. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * const winston = require('winston'); | ||
| * const Transport = require('winston-transport'); | ||
| * | ||
| * const SentryWinstonTransport = Sentry.createSentryWinstonTransport(Transport); | ||
| * | ||
| * const logger = winston.createLogger({ | ||
| * transports: [new SentryWinstonTransport()], | ||
| * }); | ||
| * ``` | ||
| */ | ||
| function createSentryWinstonTransport( | ||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| TransportClass, | ||
| sentryWinstonOptions, | ||
| ) { | ||
| // @ts-ignore - We know this is safe because SentryWinstonTransport extends TransportClass | ||
| const DEFAULT_CAPTURED_LEVELS = ["trace", "debug", "info", "warn", "error", "fatal"]; | ||
| const LEVEL_SYMBOL = /* @__PURE__ */ Symbol.for("level"); | ||
| const MESSAGE_SYMBOL = /* @__PURE__ */ Symbol.for("message"); | ||
| const SPLAT_SYMBOL = /* @__PURE__ */ Symbol.for("splat"); | ||
| function createSentryWinstonTransport(TransportClass, sentryWinstonOptions) { | ||
| class SentryWinstonTransport extends TransportClass { | ||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| constructor(options) { | ||
| constructor(options) { | ||
| super(options); | ||
| this._levels = new Set(sentryWinstonOptions?.levels ?? DEFAULT_CAPTURED_LEVELS); | ||
| } | ||
| /** | ||
| * Forwards a winston log to the Sentry SDK. | ||
| */ | ||
| log(info, callback) { | ||
| log(info, callback) { | ||
| try { | ||
| setImmediate(() => { | ||
| // @ts-ignore - We know this is safe because SentryWinstonTransport extends TransportClass | ||
| this.emit('logged', info); | ||
| this.emit("logged", info); | ||
| }); | ||
| if (!isObject(info)) { | ||
| return; | ||
| } | ||
| const levelFromSymbol = info[LEVEL_SYMBOL]; | ||
| // See: https://github.com/winstonjs/winston?tab=readme-ov-file#streams-objectmode-and-info-objects | ||
| // eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
| const { level, message, timestamp, ...attributes } = info; | ||
| // Remove all symbols from the remaining attributes | ||
| attributes[LEVEL_SYMBOL] = undefined; | ||
| attributes[MESSAGE_SYMBOL] = undefined; | ||
| attributes[SPLAT_SYMBOL] = undefined; | ||
| const customLevel = sentryWinstonOptions?.customLevelMap?.[levelFromSymbol ]; | ||
| const winstonLogLevel = WINSTON_LEVEL_TO_LOG_SEVERITY_LEVEL_MAP[levelFromSymbol ]; | ||
| const logSeverityLevel = customLevel ?? winstonLogLevel ?? 'info'; | ||
| attributes[LEVEL_SYMBOL] = void 0; | ||
| attributes[MESSAGE_SYMBOL] = void 0; | ||
| attributes[SPLAT_SYMBOL] = void 0; | ||
| const customLevel = sentryWinstonOptions?.customLevelMap?.[levelFromSymbol]; | ||
| const winstonLogLevel = WINSTON_LEVEL_TO_LOG_SEVERITY_LEVEL_MAP[levelFromSymbol]; | ||
| const logSeverityLevel = customLevel ?? winstonLogLevel ?? "info"; | ||
| if (this._levels.has(logSeverityLevel)) { | ||
| capture.captureLog(logSeverityLevel, message , { | ||
| capture.captureLog(logSeverityLevel, message, { | ||
| ...attributes, | ||
| 'sentry.origin': 'auto.log.winston', | ||
| "sentry.origin": "auto.log.winston" | ||
| }); | ||
| } else if (!customLevel && !winstonLogLevel) { | ||
| debugBuild.DEBUG_BUILD && | ||
| core.debug.log( | ||
| `Winston log level ${levelFromSymbol} is not captured by Sentry. Please add ${levelFromSymbol} to the "customLevelMap" option of the Sentry Winston transport.`, | ||
| ); | ||
| debugBuild.DEBUG_BUILD && core.debug.log( | ||
| `Winston log level ${levelFromSymbol} is not captured by Sentry. Please add ${levelFromSymbol} to the "customLevelMap" option of the Sentry Winston transport.` | ||
| ); | ||
| } | ||
| } catch { | ||
| // do nothing | ||
| } | ||
| if (callback) { | ||
@@ -101,57 +55,32 @@ callback(); | ||
| } | ||
| return SentryWinstonTransport ; | ||
| return SentryWinstonTransport; | ||
| } | ||
| function isObject(anything) { | ||
| return typeof anything === 'object' && anything != null; | ||
| return typeof anything === "object" && anything != null; | ||
| } | ||
| // npm | ||
| // { | ||
| // error: 0, | ||
| // warn: 1, | ||
| // info: 2, | ||
| // http: 3, | ||
| // verbose: 4, | ||
| // debug: 5, | ||
| // silly: 6 | ||
| // } | ||
| // | ||
| // syslog | ||
| // { | ||
| // emerg: 0, | ||
| // alert: 1, | ||
| // crit: 2, | ||
| // error: 3, | ||
| // warning: 4, | ||
| // notice: 5, | ||
| // info: 6, | ||
| // debug: 7, | ||
| // } | ||
| const WINSTON_LEVEL_TO_LOG_SEVERITY_LEVEL_MAP = { | ||
| // npm | ||
| silly: 'trace', | ||
| silly: "trace", | ||
| // npm and syslog | ||
| debug: 'debug', | ||
| debug: "debug", | ||
| // npm | ||
| verbose: 'debug', | ||
| verbose: "debug", | ||
| // npm | ||
| http: 'debug', | ||
| http: "debug", | ||
| // npm and syslog | ||
| info: 'info', | ||
| info: "info", | ||
| // syslog | ||
| notice: 'info', | ||
| notice: "info", | ||
| // npm | ||
| warn: 'warn', | ||
| warn: "warn", | ||
| // syslog | ||
| warning: 'warn', | ||
| warning: "warn", | ||
| // npm and syslog | ||
| error: 'error', | ||
| error: "error", | ||
| // syslog | ||
| emerg: 'fatal', | ||
| emerg: "fatal", | ||
| // syslog | ||
| alert: 'fatal', | ||
| alert: "fatal", | ||
| // syslog | ||
| crit: 'fatal', | ||
| crit: "fatal" | ||
| }; | ||
@@ -158,0 +87,0 @@ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"winston.js","sources":["../../../src/integrations/winston.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/ban-ts-comment */\nimport type { LogSeverityLevel } from '@sentry/core';\nimport { debug } from '@sentry/core';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { captureLog } from '../logs/capture';\n\nconst DEFAULT_CAPTURED_LEVELS: Array<LogSeverityLevel> = ['trace', 'debug', 'info', 'warn', 'error', 'fatal'];\n\n// See: https://github.com/winstonjs/triple-beam\nconst LEVEL_SYMBOL = Symbol.for('level');\nconst MESSAGE_SYMBOL = Symbol.for('message');\nconst SPLAT_SYMBOL = Symbol.for('splat');\n\n/**\n * Options for the Sentry Winston transport.\n */\ninterface WinstonTransportOptions {\n /**\n * Use this option to filter which levels should be captured. By default, all levels are captured.\n *\n * @example\n * ```ts\n * const SentryWinstonTransport = Sentry.createSentryWinstonTransport(Transport, {\n * // Only capture error and warn logs\n * levels: ['error', 'warn'],\n * });\n * ```\n */\n levels?: Array<LogSeverityLevel>;\n\n /**\n * Use this option to map custom levels to Sentry log severity levels.\n *\n * @example\n * ```ts\n * const SentryWinstonTransport = Sentry.createSentryWinstonTransport(Transport, {\n * customLevelMap: {\n * myCustomLevel: 'info',\n * customError: 'error',\n * },\n * });\n * ```\n */\n customLevelMap?: Record<string, LogSeverityLevel>;\n}\n\n/**\n * Creates a new Sentry Winston transport that fowards logs to Sentry. Requires the `enableLogs` option to be enabled.\n *\n * Supports Winston 3.x.x.\n *\n * @param TransportClass - The Winston transport class to extend.\n * @returns The extended transport class.\n *\n * @example\n * ```ts\n * const winston = require('winston');\n * const Transport = require('winston-transport');\n *\n * const SentryWinstonTransport = Sentry.createSentryWinstonTransport(Transport);\n *\n * const logger = winston.createLogger({\n * transports: [new SentryWinstonTransport()],\n * });\n * ```\n */\nexport function createSentryWinstonTransport<TransportStreamInstance extends object>(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n TransportClass: new (options?: any) => TransportStreamInstance,\n sentryWinstonOptions?: WinstonTransportOptions,\n): typeof TransportClass {\n // @ts-ignore - We know this is safe because SentryWinstonTransport extends TransportClass\n class SentryWinstonTransport extends TransportClass {\n private _levels: Set<LogSeverityLevel>;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n public constructor(options?: any) {\n super(options);\n this._levels = new Set(sentryWinstonOptions?.levels ?? DEFAULT_CAPTURED_LEVELS);\n }\n\n /**\n * Forwards a winston log to the Sentry SDK.\n */\n public log(info: unknown, callback: () => void): void {\n try {\n setImmediate(() => {\n // @ts-ignore - We know this is safe because SentryWinstonTransport extends TransportClass\n this.emit('logged', info);\n });\n\n if (!isObject(info)) {\n return;\n }\n\n const levelFromSymbol = info[LEVEL_SYMBOL];\n\n // See: https://github.com/winstonjs/winston?tab=readme-ov-file#streams-objectmode-and-info-objects\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { level, message, timestamp, ...attributes } = info;\n // Remove all symbols from the remaining attributes\n attributes[LEVEL_SYMBOL] = undefined;\n attributes[MESSAGE_SYMBOL] = undefined;\n attributes[SPLAT_SYMBOL] = undefined;\n\n const customLevel = sentryWinstonOptions?.customLevelMap?.[levelFromSymbol as string];\n const winstonLogLevel = WINSTON_LEVEL_TO_LOG_SEVERITY_LEVEL_MAP[levelFromSymbol as string];\n const logSeverityLevel = customLevel ?? winstonLogLevel ?? 'info';\n\n if (this._levels.has(logSeverityLevel)) {\n captureLog(logSeverityLevel, message as string, {\n ...attributes,\n 'sentry.origin': 'auto.log.winston',\n });\n } else if (!customLevel && !winstonLogLevel) {\n DEBUG_BUILD &&\n debug.log(\n `Winston log level ${levelFromSymbol} is not captured by Sentry. Please add ${levelFromSymbol} to the \"customLevelMap\" option of the Sentry Winston transport.`,\n );\n }\n } catch {\n // do nothing\n }\n\n if (callback) {\n callback();\n }\n }\n }\n\n return SentryWinstonTransport as typeof TransportClass;\n}\n\nfunction isObject(anything: unknown): anything is Record<string | symbol, unknown> {\n return typeof anything === 'object' && anything != null;\n}\n\n// npm\n// {\n// error: 0,\n// warn: 1,\n// info: 2,\n// http: 3,\n// verbose: 4,\n// debug: 5,\n// silly: 6\n// }\n//\n// syslog\n// {\n// emerg: 0,\n// alert: 1,\n// crit: 2,\n// error: 3,\n// warning: 4,\n// notice: 5,\n// info: 6,\n// debug: 7,\n// }\nconst WINSTON_LEVEL_TO_LOG_SEVERITY_LEVEL_MAP: Record<string, LogSeverityLevel> = {\n // npm\n silly: 'trace',\n // npm and syslog\n debug: 'debug',\n // npm\n verbose: 'debug',\n // npm\n http: 'debug',\n // npm and syslog\n info: 'info',\n // syslog\n notice: 'info',\n // npm\n warn: 'warn',\n // syslog\n warning: 'warn',\n // npm and syslog\n error: 'error',\n // syslog\n emerg: 'fatal',\n // syslog\n alert: 'fatal',\n // syslog\n crit: 'fatal',\n};\n"],"names":["captureLog","DEBUG_BUILD","debug"],"mappings":";;;;;;AAMA,MAAM,uBAAuB,GAA4B,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC;;AAE7G;AACA,MAAM,eAAe,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC;AACxC,MAAM,iBAAiB,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC;AAC5C,MAAM,eAAe,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC;;AAExC;AACA;AACA;;AA+BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,4BAA4B;AAC5C;AACA,EAAE,cAAc;AAChB,EAAE,oBAAoB;AACtB,EAAyB;AACzB;AACA,EAAE,MAAM,sBAAA,SAA+B,cAAA,CAAe;;AAEtD;AACA,KAAW,WAAW,CAAC,OAAO,EAAQ;AACtC,MAAM,KAAK,CAAC,OAAO,CAAC;AACpB,MAAM,IAAI,CAAC,OAAA,GAAU,IAAI,GAAG,CAAC,oBAAoB,EAAE,MAAA,IAAU,uBAAuB,CAAC;AACrF,IAAI;;AAEJ;AACA;AACA;AACA,KAAW,GAAG,CAAC,IAAI,EAAW,QAAQ,EAAoB;AAC1D,MAAM,IAAI;AACV,QAAQ,YAAY,CAAC,MAAM;AAC3B;AACA,UAAU,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC;AACnC,QAAQ,CAAC,CAAC;;AAEV,QAAQ,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE;AAC7B,UAAU;AACV,QAAQ;;AAER,QAAQ,MAAM,eAAA,GAAkB,IAAI,CAAC,YAAY,CAAC;;AAElD;AACA;AACA,QAAQ,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,UAAA,EAAW,GAAI,IAAI;AACjE;AACA,QAAQ,UAAU,CAAC,YAAY,CAAA,GAAI,SAAS;AAC5C,QAAQ,UAAU,CAAC,cAAc,CAAA,GAAI,SAAS;AAC9C,QAAQ,UAAU,CAAC,YAAY,CAAA,GAAI,SAAS;;AAE5C,QAAQ,MAAM,cAAc,oBAAoB,EAAE,cAAc,GAAG,eAAA,EAA0B;AAC7F,QAAQ,MAAM,eAAA,GAAkB,uCAAuC,CAAC,iBAA0B;AAClG,QAAQ,MAAM,gBAAA,GAAmB,eAAe,eAAA,IAAmB,MAAM;;AAEzE,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,EAAE;AAChD,UAAUA,kBAAU,CAAC,gBAAgB,EAAE,UAAmB;AAC1D,YAAY,GAAG,UAAU;AACzB,YAAY,eAAe,EAAE,kBAAkB;AAC/C,WAAW,CAAC;AACZ,QAAQ,CAAA,MAAO,IAAI,CAAC,WAAA,IAAe,CAAC,eAAe,EAAE;AACrD,UAAUC,sBAAA;AACV,YAAYC,UAAK,CAAC,GAAG;AACrB,cAAc,CAAC,kBAAkB,EAAE,eAAe,CAAC,uCAAuC,EAAE,eAAe,CAAC,gEAAgE,CAAC;AAC7K,aAAa;AACb,QAAQ;AACR,MAAM,EAAE,MAAM;AACd;AACA,MAAM;;AAEN,MAAM,IAAI,QAAQ,EAAE;AACpB,QAAQ,QAAQ,EAAE;AAClB,MAAM;AACN,IAAI;AACJ;;AAEA,EAAE,OAAO,sBAAA;AACT;;AAEA,SAAS,QAAQ,CAAC,QAAQ,EAAyD;AACnF,EAAE,OAAO,OAAO,QAAA,KAAa,YAAY,QAAA,IAAY,IAAI;AACzD;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM,uCAAuC,GAAqC;AAClF;AACA,EAAE,KAAK,EAAE,OAAO;AAChB;AACA,EAAE,KAAK,EAAE,OAAO;AAChB;AACA,EAAE,OAAO,EAAE,OAAO;AAClB;AACA,EAAE,IAAI,EAAE,OAAO;AACf;AACA,EAAE,IAAI,EAAE,MAAM;AACd;AACA,EAAE,MAAM,EAAE,MAAM;AAChB;AACA,EAAE,IAAI,EAAE,MAAM;AACd;AACA,EAAE,OAAO,EAAE,MAAM;AACjB;AACA,EAAE,KAAK,EAAE,OAAO;AAChB;AACA,EAAE,KAAK,EAAE,OAAO;AAChB;AACA,EAAE,KAAK,EAAE,OAAO;AAChB;AACA,EAAE,IAAI,EAAE,OAAO;AACf,CAAC;;;;"} | ||
| {"version":3,"file":"winston.js","sources":["../../../src/integrations/winston.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/ban-ts-comment */\nimport type { LogSeverityLevel } from '@sentry/core';\nimport { debug } from '@sentry/core';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { captureLog } from '../logs/capture';\n\nconst DEFAULT_CAPTURED_LEVELS: Array<LogSeverityLevel> = ['trace', 'debug', 'info', 'warn', 'error', 'fatal'];\n\n// See: https://github.com/winstonjs/triple-beam\nconst LEVEL_SYMBOL = Symbol.for('level');\nconst MESSAGE_SYMBOL = Symbol.for('message');\nconst SPLAT_SYMBOL = Symbol.for('splat');\n\n/**\n * Options for the Sentry Winston transport.\n */\ninterface WinstonTransportOptions {\n /**\n * Use this option to filter which levels should be captured. By default, all levels are captured.\n *\n * @example\n * ```ts\n * const SentryWinstonTransport = Sentry.createSentryWinstonTransport(Transport, {\n * // Only capture error and warn logs\n * levels: ['error', 'warn'],\n * });\n * ```\n */\n levels?: Array<LogSeverityLevel>;\n\n /**\n * Use this option to map custom levels to Sentry log severity levels.\n *\n * @example\n * ```ts\n * const SentryWinstonTransport = Sentry.createSentryWinstonTransport(Transport, {\n * customLevelMap: {\n * myCustomLevel: 'info',\n * customError: 'error',\n * },\n * });\n * ```\n */\n customLevelMap?: Record<string, LogSeverityLevel>;\n}\n\n/**\n * Creates a new Sentry Winston transport that fowards logs to Sentry. Requires the `enableLogs` option to be enabled.\n *\n * Supports Winston 3.x.x.\n *\n * @param TransportClass - The Winston transport class to extend.\n * @returns The extended transport class.\n *\n * @example\n * ```ts\n * const winston = require('winston');\n * const Transport = require('winston-transport');\n *\n * const SentryWinstonTransport = Sentry.createSentryWinstonTransport(Transport);\n *\n * const logger = winston.createLogger({\n * transports: [new SentryWinstonTransport()],\n * });\n * ```\n */\nexport function createSentryWinstonTransport<TransportStreamInstance extends object>(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n TransportClass: new (options?: any) => TransportStreamInstance,\n sentryWinstonOptions?: WinstonTransportOptions,\n): typeof TransportClass {\n // @ts-ignore - We know this is safe because SentryWinstonTransport extends TransportClass\n class SentryWinstonTransport extends TransportClass {\n private _levels: Set<LogSeverityLevel>;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n public constructor(options?: any) {\n super(options);\n this._levels = new Set(sentryWinstonOptions?.levels ?? DEFAULT_CAPTURED_LEVELS);\n }\n\n /**\n * Forwards a winston log to the Sentry SDK.\n */\n public log(info: unknown, callback: () => void): void {\n try {\n setImmediate(() => {\n // @ts-ignore - We know this is safe because SentryWinstonTransport extends TransportClass\n this.emit('logged', info);\n });\n\n if (!isObject(info)) {\n return;\n }\n\n const levelFromSymbol = info[LEVEL_SYMBOL];\n\n // See: https://github.com/winstonjs/winston?tab=readme-ov-file#streams-objectmode-and-info-objects\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { level, message, timestamp, ...attributes } = info;\n // Remove all symbols from the remaining attributes\n attributes[LEVEL_SYMBOL] = undefined;\n attributes[MESSAGE_SYMBOL] = undefined;\n attributes[SPLAT_SYMBOL] = undefined;\n\n const customLevel = sentryWinstonOptions?.customLevelMap?.[levelFromSymbol as string];\n const winstonLogLevel = WINSTON_LEVEL_TO_LOG_SEVERITY_LEVEL_MAP[levelFromSymbol as string];\n const logSeverityLevel = customLevel ?? winstonLogLevel ?? 'info';\n\n if (this._levels.has(logSeverityLevel)) {\n captureLog(logSeverityLevel, message as string, {\n ...attributes,\n 'sentry.origin': 'auto.log.winston',\n });\n } else if (!customLevel && !winstonLogLevel) {\n DEBUG_BUILD &&\n debug.log(\n `Winston log level ${levelFromSymbol} is not captured by Sentry. Please add ${levelFromSymbol} to the \"customLevelMap\" option of the Sentry Winston transport.`,\n );\n }\n } catch {\n // do nothing\n }\n\n if (callback) {\n callback();\n }\n }\n }\n\n return SentryWinstonTransport as typeof TransportClass;\n}\n\nfunction isObject(anything: unknown): anything is Record<string | symbol, unknown> {\n return typeof anything === 'object' && anything != null;\n}\n\n// npm\n// {\n// error: 0,\n// warn: 1,\n// info: 2,\n// http: 3,\n// verbose: 4,\n// debug: 5,\n// silly: 6\n// }\n//\n// syslog\n// {\n// emerg: 0,\n// alert: 1,\n// crit: 2,\n// error: 3,\n// warning: 4,\n// notice: 5,\n// info: 6,\n// debug: 7,\n// }\nconst WINSTON_LEVEL_TO_LOG_SEVERITY_LEVEL_MAP: Record<string, LogSeverityLevel> = {\n // npm\n silly: 'trace',\n // npm and syslog\n debug: 'debug',\n // npm\n verbose: 'debug',\n // npm\n http: 'debug',\n // npm and syslog\n info: 'info',\n // syslog\n notice: 'info',\n // npm\n warn: 'warn',\n // syslog\n warning: 'warn',\n // npm and syslog\n error: 'error',\n // syslog\n emerg: 'fatal',\n // syslog\n alert: 'fatal',\n // syslog\n crit: 'fatal',\n};\n"],"names":["captureLog","DEBUG_BUILD","debug"],"mappings":"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"} |
@@ -6,28 +6,14 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| /** | ||
| * Sets the async context strategy to use AsyncLocalStorage. | ||
| * | ||
| * This is a lightweight alternative to the OpenTelemetry-based strategy. | ||
| * It uses Node's native AsyncLocalStorage directly without any OpenTelemetry dependencies. | ||
| */ | ||
| function setAsyncLocalStorageAsyncContextStrategy() { | ||
| const asyncStorage = new node_async_hooks.AsyncLocalStorage | ||
| (); | ||
| const asyncStorage = new node_async_hooks.AsyncLocalStorage(); | ||
| function getScopes() { | ||
| const scopes = asyncStorage.getStore(); | ||
| if (scopes) { | ||
| return scopes; | ||
| } | ||
| // fallback behavior: | ||
| // if, for whatever reason, we can't find scopes on the context here, we have to fix this somehow | ||
| return { | ||
| scope: core.getDefaultCurrentScope(), | ||
| isolationScope: core.getDefaultIsolationScope(), | ||
| isolationScope: core.getDefaultIsolationScope() | ||
| }; | ||
| } | ||
| function withScope(callback) { | ||
@@ -40,3 +26,2 @@ const scope = getScopes().scope.clone(); | ||
| } | ||
| function withSetScope(scope, callback) { | ||
@@ -48,3 +33,2 @@ const isolationScope = getScopes().isolationScope.clone(); | ||
| } | ||
| function withIsolationScope(callback) { | ||
@@ -57,3 +41,2 @@ const scope = getScopes().scope.clone(); | ||
| } | ||
| function withSetIsolationScope(isolationScope, callback) { | ||
@@ -65,6 +48,4 @@ const scope = getScopes().scope.clone(); | ||
| } | ||
| // In contrast to the browser, we can rely on async context isolation here | ||
| function suppressTracing(callback) { | ||
| return withScope(scope => { | ||
| return withScope((scope) => { | ||
| scope.setSDKProcessingMetadata({ [core.SUPPRESS_TRACING_KEY]: true }); | ||
@@ -74,3 +55,2 @@ return callback(); | ||
| } | ||
| core.setAsyncContextStrategy({ | ||
@@ -83,3 +63,3 @@ suppressTracing, | ||
| getCurrentScope: () => getScopes().scope, | ||
| getIsolationScope: () => getScopes().isolationScope, | ||
| getIsolationScope: () => getScopes().isolationScope | ||
| }); | ||
@@ -86,0 +66,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"asyncLocalStorageStrategy.js","sources":["../../../src/light/asyncLocalStorageStrategy.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks';\nimport type { Scope } from '@sentry/core';\nimport {\n getDefaultCurrentScope,\n getDefaultIsolationScope,\n setAsyncContextStrategy,\n SUPPRESS_TRACING_KEY,\n} from '@sentry/core';\n\n/**\n * Sets the async context strategy to use AsyncLocalStorage.\n *\n * This is a lightweight alternative to the OpenTelemetry-based strategy.\n * It uses Node's native AsyncLocalStorage directly without any OpenTelemetry dependencies.\n */\nexport function setAsyncLocalStorageAsyncContextStrategy(): void {\n const asyncStorage = new AsyncLocalStorage<{\n scope: Scope;\n isolationScope: Scope;\n }>();\n\n function getScopes(): { scope: Scope; isolationScope: Scope } {\n const scopes = asyncStorage.getStore();\n\n if (scopes) {\n return scopes;\n }\n\n // fallback behavior:\n // if, for whatever reason, we can't find scopes on the context here, we have to fix this somehow\n return {\n scope: getDefaultCurrentScope(),\n isolationScope: getDefaultIsolationScope(),\n };\n }\n\n function withScope<T>(callback: (scope: Scope) => T): T {\n const scope = getScopes().scope.clone();\n const isolationScope = getScopes().isolationScope;\n return asyncStorage.run({ scope, isolationScope }, () => {\n return callback(scope);\n });\n }\n\n function withSetScope<T>(scope: Scope, callback: (scope: Scope) => T): T {\n const isolationScope = getScopes().isolationScope.clone();\n return asyncStorage.run({ scope, isolationScope }, () => {\n return callback(scope);\n });\n }\n\n function withIsolationScope<T>(callback: (isolationScope: Scope) => T): T {\n const scope = getScopes().scope.clone();\n const isolationScope = getScopes().isolationScope.clone();\n return asyncStorage.run({ scope, isolationScope }, () => {\n return callback(isolationScope);\n });\n }\n\n function withSetIsolationScope<T>(isolationScope: Scope, callback: (isolationScope: Scope) => T): T {\n const scope = getScopes().scope.clone();\n return asyncStorage.run({ scope, isolationScope }, () => {\n return callback(isolationScope);\n });\n }\n\n // In contrast to the browser, we can rely on async context isolation here\n function suppressTracing<T>(callback: () => T): T {\n return withScope(scope => {\n scope.setSDKProcessingMetadata({ [SUPPRESS_TRACING_KEY]: true });\n return callback();\n });\n }\n\n setAsyncContextStrategy({\n suppressTracing,\n withScope,\n withSetScope,\n withIsolationScope,\n withSetIsolationScope,\n getCurrentScope: () => getScopes().scope,\n getIsolationScope: () => getScopes().isolationScope,\n });\n}\n"],"names":["AsyncLocalStorage","getDefaultCurrentScope","getDefaultIsolationScope","SUPPRESS_TRACING_KEY","setAsyncContextStrategy"],"mappings":";;;;;AASA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,wCAAwC,GAAS;AACjE,EAAE,MAAM,YAAA,GAAe,IAAIA;;AAGzB,EAAI;;AAEN,EAAE,SAAS,SAAS,GAA4C;AAChE,IAAI,MAAM,MAAA,GAAS,YAAY,CAAC,QAAQ,EAAE;;AAE1C,IAAI,IAAI,MAAM,EAAE;AAChB,MAAM,OAAO,MAAM;AACnB,IAAI;;AAEJ;AACA;AACA,IAAI,OAAO;AACX,MAAM,KAAK,EAAEC,2BAAsB,EAAE;AACrC,MAAM,cAAc,EAAEC,6BAAwB,EAAE;AAChD,KAAK;AACL,EAAE;;AAEF,EAAE,SAAS,SAAS,CAAI,QAAQ,EAA0B;AAC1D,IAAI,MAAM,KAAA,GAAQ,SAAS,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE;AAC3C,IAAI,MAAM,cAAA,GAAiB,SAAS,EAAE,CAAC,cAAc;AACrD,IAAI,OAAO,YAAY,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,cAAA,EAAgB,EAAE,MAAM;AAC7D,MAAM,OAAO,QAAQ,CAAC,KAAK,CAAC;AAC5B,IAAI,CAAC,CAAC;AACN,EAAE;;AAEF,EAAE,SAAS,YAAY,CAAI,KAAK,EAAS,QAAQ,EAA0B;AAC3E,IAAI,MAAM,cAAA,GAAiB,SAAS,EAAE,CAAC,cAAc,CAAC,KAAK,EAAE;AAC7D,IAAI,OAAO,YAAY,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,cAAA,EAAgB,EAAE,MAAM;AAC7D,MAAM,OAAO,QAAQ,CAAC,KAAK,CAAC;AAC5B,IAAI,CAAC,CAAC;AACN,EAAE;;AAEF,EAAE,SAAS,kBAAkB,CAAI,QAAQ,EAAmC;AAC5E,IAAI,MAAM,KAAA,GAAQ,SAAS,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE;AAC3C,IAAI,MAAM,cAAA,GAAiB,SAAS,EAAE,CAAC,cAAc,CAAC,KAAK,EAAE;AAC7D,IAAI,OAAO,YAAY,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,cAAA,EAAgB,EAAE,MAAM;AAC7D,MAAM,OAAO,QAAQ,CAAC,cAAc,CAAC;AACrC,IAAI,CAAC,CAAC;AACN,EAAE;;AAEF,EAAE,SAAS,qBAAqB,CAAI,cAAc,EAAS,QAAQ,EAAmC;AACtG,IAAI,MAAM,KAAA,GAAQ,SAAS,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE;AAC3C,IAAI,OAAO,YAAY,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,cAAA,EAAgB,EAAE,MAAM;AAC7D,MAAM,OAAO,QAAQ,CAAC,cAAc,CAAC;AACrC,IAAI,CAAC,CAAC;AACN,EAAE;;AAEF;AACA,EAAE,SAAS,eAAe,CAAI,QAAQ,EAAc;AACpD,IAAI,OAAO,SAAS,CAAC,KAAA,IAAS;AAC9B,MAAM,KAAK,CAAC,wBAAwB,CAAC,EAAE,CAACC,yBAAoB,GAAG,IAAA,EAAM,CAAC;AACtE,MAAM,OAAO,QAAQ,EAAE;AACvB,IAAI,CAAC,CAAC;AACN,EAAE;;AAEF,EAAEC,4BAAuB,CAAC;AAC1B,IAAI,eAAe;AACnB,IAAI,SAAS;AACb,IAAI,YAAY;AAChB,IAAI,kBAAkB;AACtB,IAAI,qBAAqB;AACzB,IAAI,eAAe,EAAE,MAAM,SAAS,EAAE,CAAC,KAAK;AAC5C,IAAI,iBAAiB,EAAE,MAAM,SAAS,EAAE,CAAC,cAAc;AACvD,GAAG,CAAC;AACJ;;;;"} | ||
| {"version":3,"file":"asyncLocalStorageStrategy.js","sources":["../../../src/light/asyncLocalStorageStrategy.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks';\nimport type { Scope } from '@sentry/core';\nimport {\n getDefaultCurrentScope,\n getDefaultIsolationScope,\n setAsyncContextStrategy,\n SUPPRESS_TRACING_KEY,\n} from '@sentry/core';\n\n/**\n * Sets the async context strategy to use AsyncLocalStorage.\n *\n * This is a lightweight alternative to the OpenTelemetry-based strategy.\n * It uses Node's native AsyncLocalStorage directly without any OpenTelemetry dependencies.\n */\nexport function setAsyncLocalStorageAsyncContextStrategy(): void {\n const asyncStorage = new AsyncLocalStorage<{\n scope: Scope;\n isolationScope: Scope;\n }>();\n\n function getScopes(): { scope: Scope; isolationScope: Scope } {\n const scopes = asyncStorage.getStore();\n\n if (scopes) {\n return scopes;\n }\n\n // fallback behavior:\n // if, for whatever reason, we can't find scopes on the context here, we have to fix this somehow\n return {\n scope: getDefaultCurrentScope(),\n isolationScope: getDefaultIsolationScope(),\n };\n }\n\n function withScope<T>(callback: (scope: Scope) => T): T {\n const scope = getScopes().scope.clone();\n const isolationScope = getScopes().isolationScope;\n return asyncStorage.run({ scope, isolationScope }, () => {\n return callback(scope);\n });\n }\n\n function withSetScope<T>(scope: Scope, callback: (scope: Scope) => T): T {\n const isolationScope = getScopes().isolationScope.clone();\n return asyncStorage.run({ scope, isolationScope }, () => {\n return callback(scope);\n });\n }\n\n function withIsolationScope<T>(callback: (isolationScope: Scope) => T): T {\n const scope = getScopes().scope.clone();\n const isolationScope = getScopes().isolationScope.clone();\n return asyncStorage.run({ scope, isolationScope }, () => {\n return callback(isolationScope);\n });\n }\n\n function withSetIsolationScope<T>(isolationScope: Scope, callback: (isolationScope: Scope) => T): T {\n const scope = getScopes().scope.clone();\n return asyncStorage.run({ scope, isolationScope }, () => {\n return callback(isolationScope);\n });\n }\n\n // In contrast to the browser, we can rely on async context isolation here\n function suppressTracing<T>(callback: () => T): T {\n return withScope(scope => {\n scope.setSDKProcessingMetadata({ [SUPPRESS_TRACING_KEY]: true });\n return callback();\n });\n }\n\n setAsyncContextStrategy({\n suppressTracing,\n withScope,\n withSetScope,\n withIsolationScope,\n withSetIsolationScope,\n getCurrentScope: () => getScopes().scope,\n getIsolationScope: () => getScopes().isolationScope,\n });\n}\n"],"names":["AsyncLocalStorage","getDefaultCurrentScope","getDefaultIsolationScope","SUPPRESS_TRACING_KEY","setAsyncContextStrategy"],"mappings":";;;;;AAeO,SAAS,wCAAA,GAAiD;AAC/D,EAAA,MAAM,YAAA,GAAe,IAAIA,kCAAA,EAGtB;AAEH,EAAA,SAAS,SAAA,GAAqD;AAC5D,IAAA,MAAM,MAAA,GAAS,aAAa,QAAA,EAAS;AAErC,IAAA,IAAI,MAAA,EAAQ;AACV,MAAA,OAAO,MAAA;AAAA,IACT;AAIA,IAAA,OAAO;AAAA,MACL,OAAOC,2BAAA,EAAuB;AAAA,MAC9B,gBAAgBC,6BAAA;AAAyB,KAC3C;AAAA,EACF;AAEA,EAAA,SAAS,UAAa,QAAA,EAAkC;AACtD,IAAA,MAAM,KAAA,GAAQ,SAAA,EAAU,CAAE,KAAA,CAAM,KAAA,EAAM;AACtC,IAAA,MAAM,cAAA,GAAiB,WAAU,CAAE,cAAA;AACnC,IAAA,OAAO,aAAa,GAAA,CAAI,EAAE,KAAA,EAAO,cAAA,IAAkB,MAAM;AACvD,MAAA,OAAO,SAAS,KAAK,CAAA;AAAA,IACvB,CAAC,CAAA;AAAA,EACH;AAEA,EAAA,SAAS,YAAA,CAAgB,OAAc,QAAA,EAAkC;AACvE,IAAA,MAAM,cAAA,GAAiB,SAAA,EAAU,CAAE,cAAA,CAAe,KAAA,EAAM;AACxD,IAAA,OAAO,aAAa,GAAA,CAAI,EAAE,KAAA,EAAO,cAAA,IAAkB,MAAM;AACvD,MAAA,OAAO,SAAS,KAAK,CAAA;AAAA,IACvB,CAAC,CAAA;AAAA,EACH;AAEA,EAAA,SAAS,mBAAsB,QAAA,EAA2C;AACxE,IAAA,MAAM,KAAA,GAAQ,SAAA,EAAU,CAAE,KAAA,CAAM,KAAA,EAAM;AACtC,IAAA,MAAM,cAAA,GAAiB,SAAA,EAAU,CAAE,cAAA,CAAe,KAAA,EAAM;AACxD,IAAA,OAAO,aAAa,GAAA,CAAI,EAAE,KAAA,EAAO,cAAA,IAAkB,MAAM;AACvD,MAAA,OAAO,SAAS,cAAc,CAAA;AAAA,IAChC,CAAC,CAAA;AAAA,EACH;AAEA,EAAA,SAAS,qBAAA,CAAyB,gBAAuB,QAAA,EAA2C;AAClG,IAAA,MAAM,KAAA,GAAQ,SAAA,EAAU,CAAE,KAAA,CAAM,KAAA,EAAM;AACtC,IAAA,OAAO,aAAa,GAAA,CAAI,EAAE,KAAA,EAAO,cAAA,IAAkB,MAAM;AACvD,MAAA,OAAO,SAAS,cAAc,CAAA;AAAA,IAChC,CAAC,CAAA;AAAA,EACH;AAGA,EAAA,SAAS,gBAAmB,QAAA,EAAsB;AAChD,IAAA,OAAO,UAAU,CAAA,KAAA,KAAS;AACxB,MAAA,KAAA,CAAM,yBAAyB,EAAE,CAACC,yBAAoB,GAAG,MAAM,CAAA;AAC/D,MAAA,OAAO,QAAA,EAAS;AAAA,IAClB,CAAC,CAAA;AAAA,EACH;AAEA,EAAAC,4BAAA,CAAwB;AAAA,IACtB,eAAA;AAAA,IACA,SAAA;AAAA,IACA,YAAA;AAAA,IACA,kBAAA;AAAA,IACA,qBAAA;AAAA,IACA,eAAA,EAAiB,MAAM,SAAA,EAAU,CAAE,KAAA;AAAA,IACnC,iBAAA,EAAmB,MAAM,SAAA,EAAU,CAAE;AAAA,GACtC,CAAA;AACH;;;;"} |
@@ -8,26 +8,15 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const DEFAULT_CLIENT_REPORT_FLUSH_INTERVAL_MS = 60000; // 60s was chosen arbitrarily | ||
| /** A lightweight client for using Sentry with Node without OpenTelemetry. */ | ||
| const DEFAULT_CLIENT_REPORT_FLUSH_INTERVAL_MS = 6e4; | ||
| class LightNodeClient extends core.ServerRuntimeClient { | ||
| constructor(options) { | ||
| const serverName = | ||
| options.includeServerName === false | ||
| ? undefined | ||
| : options.serverName || global.process.env.SENTRY_NAME || os.hostname(); | ||
| constructor(options) { | ||
| const serverName = options.includeServerName === false ? void 0 : options.serverName || global.process.env.SENTRY_NAME || os.hostname(); | ||
| const clientOptions = { | ||
| ...options, | ||
| platform: 'node', | ||
| runtime: { name: 'node', version: global.process.version }, | ||
| serverName, | ||
| platform: "node", | ||
| runtime: { name: "node", version: global.process.version }, | ||
| serverName | ||
| }; | ||
| core.applySdkMetadata(clientOptions, 'node-light', ['node-core']); | ||
| core.debug.log(`Initializing Sentry: process: ${process.pid}, thread: ${worker_threads.isMainThread ? 'main' : `worker-${worker_threads.threadId}`}.`); | ||
| core.applySdkMetadata(clientOptions, "node-light", ["node-core"]); | ||
| core.debug.log(`Initializing Sentry: process: ${process.pid}, thread: ${worker_threads.isMainThread ? "main" : `worker-${worker_threads.threadId}`}.`); | ||
| super(clientOptions); | ||
| if (this.getOptions().enableLogs) { | ||
@@ -37,44 +26,35 @@ this._logOnExitFlushListener = () => { | ||
| }; | ||
| if (serverName) { | ||
| this.on('beforeCaptureLog', log => { | ||
| this.on("beforeCaptureLog", (log) => { | ||
| log.attributes = { | ||
| ...log.attributes, | ||
| 'server.address': serverName, | ||
| "server.address": serverName | ||
| }; | ||
| }); | ||
| } | ||
| process.on('beforeExit', this._logOnExitFlushListener); | ||
| process.on("beforeExit", this._logOnExitFlushListener); | ||
| } | ||
| } | ||
| /** @inheritDoc */ | ||
| // @ts-expect-error - PromiseLike is a subset of Promise | ||
| async flush(timeout) { | ||
| async flush(timeout) { | ||
| if (this.getOptions().sendClientReports) { | ||
| this._flushOutcomes(); | ||
| } | ||
| return super.flush(timeout); | ||
| } | ||
| /** @inheritDoc */ | ||
| // @ts-expect-error - PromiseLike is a subset of Promise | ||
| async close(timeout) { | ||
| async close(timeout) { | ||
| if (this._clientReportInterval) { | ||
| clearInterval(this._clientReportInterval); | ||
| } | ||
| if (this._clientReportOnExitFlushListener) { | ||
| process.off('beforeExit', this._clientReportOnExitFlushListener); | ||
| process.off("beforeExit", this._clientReportOnExitFlushListener); | ||
| } | ||
| if (this._logOnExitFlushListener) { | ||
| process.off('beforeExit', this._logOnExitFlushListener); | ||
| process.off("beforeExit", this._logOnExitFlushListener); | ||
| } | ||
| return super.close(timeout); | ||
| } | ||
| /** | ||
@@ -95,3 +75,3 @@ * Will start tracking client reports for this client. | ||
| // collected, but it did not work, because the cleanup function never got called. | ||
| startClientReportTracking() { | ||
| startClientReportTracking() { | ||
| const clientOptions = this.getOptions(); | ||
@@ -102,11 +82,7 @@ if (clientOptions.sendClientReports) { | ||
| }; | ||
| this._clientReportInterval = setInterval(() => { | ||
| debugBuild.DEBUG_BUILD && core.debug.log('Flushing client reports based on interval.'); | ||
| debugBuild.DEBUG_BUILD && core.debug.log("Flushing client reports based on interval."); | ||
| this._flushOutcomes(); | ||
| }, clientOptions.clientReportFlushInterval ?? DEFAULT_CLIENT_REPORT_FLUSH_INTERVAL_MS) | ||
| // Unref is critical for not preventing the process from exiting because the interval is active. | ||
| .unref(); | ||
| process.on('beforeExit', this._clientReportOnExitFlushListener); | ||
| }, clientOptions.clientReportFlushInterval ?? DEFAULT_CLIENT_REPORT_FLUSH_INTERVAL_MS).unref(); | ||
| process.on("beforeExit", this._clientReportOnExitFlushListener); | ||
| } | ||
@@ -113,0 +89,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"client.js","sources":["../../../src/light/client.ts"],"sourcesContent":["import * as os from 'node:os';\nimport type { ServerRuntimeClientOptions } from '@sentry/core';\nimport { _INTERNAL_flushLogsBuffer, applySdkMetadata, debug, ServerRuntimeClient } from '@sentry/core';\nimport { isMainThread, threadId } from 'worker_threads';\nimport { DEBUG_BUILD } from '../debug-build';\nimport type { NodeClientOptions } from '../types';\n\nconst DEFAULT_CLIENT_REPORT_FLUSH_INTERVAL_MS = 60_000; // 60s was chosen arbitrarily\n\n/** A lightweight client for using Sentry with Node without OpenTelemetry. */\nexport class LightNodeClient extends ServerRuntimeClient<NodeClientOptions> {\n private _clientReportInterval: NodeJS.Timeout | undefined;\n private _clientReportOnExitFlushListener: (() => void) | undefined;\n private _logOnExitFlushListener: (() => void) | undefined;\n\n public constructor(options: NodeClientOptions) {\n const serverName =\n options.includeServerName === false\n ? undefined\n : options.serverName || global.process.env.SENTRY_NAME || os.hostname();\n\n const clientOptions: ServerRuntimeClientOptions = {\n ...options,\n platform: 'node',\n runtime: { name: 'node', version: global.process.version },\n serverName,\n };\n\n applySdkMetadata(clientOptions, 'node-light', ['node-core']);\n\n debug.log(`Initializing Sentry: process: ${process.pid}, thread: ${isMainThread ? 'main' : `worker-${threadId}`}.`);\n\n super(clientOptions);\n\n if (this.getOptions().enableLogs) {\n this._logOnExitFlushListener = () => {\n _INTERNAL_flushLogsBuffer(this);\n };\n\n if (serverName) {\n this.on('beforeCaptureLog', log => {\n log.attributes = {\n ...log.attributes,\n 'server.address': serverName,\n };\n });\n }\n\n process.on('beforeExit', this._logOnExitFlushListener);\n }\n }\n\n /** @inheritDoc */\n // @ts-expect-error - PromiseLike is a subset of Promise\n public async flush(timeout?: number): PromiseLike<boolean> {\n if (this.getOptions().sendClientReports) {\n this._flushOutcomes();\n }\n\n return super.flush(timeout);\n }\n\n /** @inheritDoc */\n // @ts-expect-error - PromiseLike is a subset of Promise\n public async close(timeout?: number | undefined): PromiseLike<boolean> {\n if (this._clientReportInterval) {\n clearInterval(this._clientReportInterval);\n }\n\n if (this._clientReportOnExitFlushListener) {\n process.off('beforeExit', this._clientReportOnExitFlushListener);\n }\n\n if (this._logOnExitFlushListener) {\n process.off('beforeExit', this._logOnExitFlushListener);\n }\n\n return super.close(timeout);\n }\n\n /**\n * Will start tracking client reports for this client.\n *\n * NOTICE: This method will create an interval that is periodically called and attach a `process.on('beforeExit')`\n * hook. To clean up these resources, call `.close()` when you no longer intend to use the client. Not doing so will\n * result in a memory leak.\n */\n // The reason client reports need to be manually activated with this method instead of just enabling them in a\n // constructor, is that if users periodically and unboundedly create new clients, we will create more and more\n // intervals and beforeExit listeners, thus leaking memory. In these situations, users are required to call\n // `client.close()` in order to dispose of the acquired resources.\n // We assume that calling this method in Sentry.init() is a sensible default, because calling Sentry.init() over and\n // over again would also result in memory leaks.\n // Note: We have experimented with using `FinalizationRegisty` to clear the interval when the client is garbage\n // collected, but it did not work, because the cleanup function never got called.\n public startClientReportTracking(): void {\n const clientOptions = this.getOptions();\n if (clientOptions.sendClientReports) {\n this._clientReportOnExitFlushListener = () => {\n this._flushOutcomes();\n };\n\n this._clientReportInterval = setInterval(() => {\n DEBUG_BUILD && debug.log('Flushing client reports based on interval.');\n this._flushOutcomes();\n }, clientOptions.clientReportFlushInterval ?? DEFAULT_CLIENT_REPORT_FLUSH_INTERVAL_MS)\n // Unref is critical for not preventing the process from exiting because the interval is active.\n .unref();\n\n process.on('beforeExit', this._clientReportOnExitFlushListener);\n }\n }\n}\n"],"names":["ServerRuntimeClient","applySdkMetadata","debug","isMainThread","threadId","_INTERNAL_flushLogsBuffer","DEBUG_BUILD"],"mappings":"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"} | ||
| {"version":3,"file":"client.js","sources":["../../../src/light/client.ts"],"sourcesContent":["import * as os from 'node:os';\nimport type { ServerRuntimeClientOptions } from '@sentry/core';\nimport { _INTERNAL_flushLogsBuffer, applySdkMetadata, debug, ServerRuntimeClient } from '@sentry/core';\nimport { isMainThread, threadId } from 'worker_threads';\nimport { DEBUG_BUILD } from '../debug-build';\nimport type { NodeClientOptions } from '../types';\n\nconst DEFAULT_CLIENT_REPORT_FLUSH_INTERVAL_MS = 60_000; // 60s was chosen arbitrarily\n\n/** A lightweight client for using Sentry with Node without OpenTelemetry. */\nexport class LightNodeClient extends ServerRuntimeClient<NodeClientOptions> {\n private _clientReportInterval: NodeJS.Timeout | undefined;\n private _clientReportOnExitFlushListener: (() => void) | undefined;\n private _logOnExitFlushListener: (() => void) | undefined;\n\n public constructor(options: NodeClientOptions) {\n const serverName =\n options.includeServerName === false\n ? undefined\n : options.serverName || global.process.env.SENTRY_NAME || os.hostname();\n\n const clientOptions: ServerRuntimeClientOptions = {\n ...options,\n platform: 'node',\n runtime: { name: 'node', version: global.process.version },\n serverName,\n };\n\n applySdkMetadata(clientOptions, 'node-light', ['node-core']);\n\n debug.log(`Initializing Sentry: process: ${process.pid}, thread: ${isMainThread ? 'main' : `worker-${threadId}`}.`);\n\n super(clientOptions);\n\n if (this.getOptions().enableLogs) {\n this._logOnExitFlushListener = () => {\n _INTERNAL_flushLogsBuffer(this);\n };\n\n if (serverName) {\n this.on('beforeCaptureLog', log => {\n log.attributes = {\n ...log.attributes,\n 'server.address': serverName,\n };\n });\n }\n\n process.on('beforeExit', this._logOnExitFlushListener);\n }\n }\n\n /** @inheritDoc */\n // @ts-expect-error - PromiseLike is a subset of Promise\n public async flush(timeout?: number): PromiseLike<boolean> {\n if (this.getOptions().sendClientReports) {\n this._flushOutcomes();\n }\n\n return super.flush(timeout);\n }\n\n /** @inheritDoc */\n // @ts-expect-error - PromiseLike is a subset of Promise\n public async close(timeout?: number | undefined): PromiseLike<boolean> {\n if (this._clientReportInterval) {\n clearInterval(this._clientReportInterval);\n }\n\n if (this._clientReportOnExitFlushListener) {\n process.off('beforeExit', this._clientReportOnExitFlushListener);\n }\n\n if (this._logOnExitFlushListener) {\n process.off('beforeExit', this._logOnExitFlushListener);\n }\n\n return super.close(timeout);\n }\n\n /**\n * Will start tracking client reports for this client.\n *\n * NOTICE: This method will create an interval that is periodically called and attach a `process.on('beforeExit')`\n * hook. To clean up these resources, call `.close()` when you no longer intend to use the client. Not doing so will\n * result in a memory leak.\n */\n // The reason client reports need to be manually activated with this method instead of just enabling them in a\n // constructor, is that if users periodically and unboundedly create new clients, we will create more and more\n // intervals and beforeExit listeners, thus leaking memory. In these situations, users are required to call\n // `client.close()` in order to dispose of the acquired resources.\n // We assume that calling this method in Sentry.init() is a sensible default, because calling Sentry.init() over and\n // over again would also result in memory leaks.\n // Note: We have experimented with using `FinalizationRegisty` to clear the interval when the client is garbage\n // collected, but it did not work, because the cleanup function never got called.\n public startClientReportTracking(): void {\n const clientOptions = this.getOptions();\n if (clientOptions.sendClientReports) {\n this._clientReportOnExitFlushListener = () => {\n this._flushOutcomes();\n };\n\n this._clientReportInterval = setInterval(() => {\n DEBUG_BUILD && debug.log('Flushing client reports based on interval.');\n this._flushOutcomes();\n }, clientOptions.clientReportFlushInterval ?? DEFAULT_CLIENT_REPORT_FLUSH_INTERVAL_MS)\n // Unref is critical for not preventing the process from exiting because the interval is active.\n .unref();\n\n process.on('beforeExit', this._clientReportOnExitFlushListener);\n }\n }\n}\n"],"names":["ServerRuntimeClient","applySdkMetadata","debug","isMainThread","threadId","_INTERNAL_flushLogsBuffer","DEBUG_BUILD"],"mappings":"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"} |
@@ -5,21 +5,12 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const core = require('@sentry/core'); | ||
| const debugBuild = require('../../debug-build.js'); | ||
| const captureRequestBody = require('../../utils/captureRequestBody.js'); | ||
| const node_events = require('node:events'); | ||
| const nodeVersion = require('../../nodeVersion.js'); | ||
| const INTEGRATION_NAME = 'Http'; | ||
| const FULLY_SUPPORTS_HTTP_DIAGNOSTICS_CHANNEL = | ||
| (nodeVersion.NODE_VERSION.major === 22 && nodeVersion.NODE_VERSION.minor >= 12) || | ||
| (nodeVersion.NODE_VERSION.major === 23 && nodeVersion.NODE_VERSION.minor >= 2) || | ||
| nodeVersion.NODE_VERSION.major >= 24; | ||
| // We keep track of emit functions we wrapped, to avoid double wrapping | ||
| const wrappedEmitFns = new WeakSet(); | ||
| const INTEGRATION_NAME = "Http"; | ||
| const FULLY_SUPPORTS_HTTP_DIAGNOSTICS_CHANNEL = nodeVersion.NODE_VERSION.major === 22 && nodeVersion.NODE_VERSION.minor >= 12 || nodeVersion.NODE_VERSION.major === 23 && nodeVersion.NODE_VERSION.minor >= 2 || nodeVersion.NODE_VERSION.major >= 24; | ||
| const _httpIntegration = ((options = {}) => { | ||
| const _options = { | ||
| ...options, | ||
| maxRequestBodySize: options.maxRequestBodySize ?? 'medium', | ||
| sessions: false, | ||
| maxRequestBodySize: options.maxRequestBodySize ?? "medium", | ||
| ignoreRequestBody: options.ignoreRequestBody, | ||
@@ -29,41 +20,28 @@ breadcrumbs: options.breadcrumbs ?? true, | ||
| ignoreOutgoingRequests: options.ignoreOutgoingRequests, | ||
| // no spans created in light mode | ||
| spans: false, | ||
| errorMonitor: node_events.errorMonitor | ||
| }; | ||
| return { | ||
| name: INTEGRATION_NAME, | ||
| setupOnce() { | ||
| const onHttpServerRequestStart = (_data) => { | ||
| const data = _data ; | ||
| instrumentServer(data.server, _options); | ||
| }; | ||
| const { [core.HTTP_ON_SERVER_REQUEST]: onHttpServerRequestStart } = core.getHttpServerSubscriptions(_options); | ||
| const { ignoreOutgoingRequests } = _options; | ||
| const { [core.HTTP_ON_CLIENT_REQUEST]: onHttpClientRequestCreated } = core.getHttpClientSubscriptions({ | ||
| breadcrumbs: _options.breadcrumbs, | ||
| propagateTrace: _options.tracePropagation, | ||
| ignoreOutgoingRequests: ignoreOutgoingRequests | ||
| ? (url, request) => ignoreOutgoingRequests(url, core.getRequestOptions(request )) | ||
| : undefined, | ||
| ignoreOutgoingRequests: ignoreOutgoingRequests ? (url, request) => ignoreOutgoingRequests(url, core.getRequestOptions(request)) : void 0, | ||
| // No spans in light mode | ||
| // means we don't have pass modules to detect OTel double-wrap | ||
| spans: false, | ||
| errorMonitor: node_events.errorMonitor, | ||
| errorMonitor: node_events.errorMonitor | ||
| }); | ||
| diagnosticsChannel.subscribe('http.server.request.start', onHttpServerRequestStart); | ||
| // Subscribe on the request creation in node versions that support it | ||
| diagnosticsChannel.subscribe(core.HTTP_ON_SERVER_REQUEST, onHttpServerRequestStart); | ||
| diagnosticsChannel.subscribe(core.HTTP_ON_CLIENT_REQUEST, onHttpClientRequestCreated); | ||
| // fall back to just doing breadcrumbs on the request.end() channel | ||
| // if we do not have earlier access to the request object at creation | ||
| // time. The http.client.request.error channel is only available on | ||
| // the same node versions as client.request.created, so no help. | ||
| if (_options.breadcrumbs && !FULLY_SUPPORTS_HTTP_DIAGNOSTICS_CHANNEL) { | ||
| diagnosticsChannel.subscribe('http.client.request.start', (data) => { | ||
| const { request } = data ; | ||
| request.on(node_events.errorMonitor, () => onOutgoingResponseFinish(request, undefined, _options)); | ||
| request.prependListener('response', response => { | ||
| if (request.listenerCount('response') <= 1) { | ||
| diagnosticsChannel.subscribe("http.client.request.start", (data) => { | ||
| const { request } = data; | ||
| request.on(node_events.errorMonitor, () => onOutgoingResponseFinish(request, void 0, _options)); | ||
| request.prependListener("response", (response) => { | ||
| if (request.listenerCount("response") <= 1) { | ||
| response.resume(); | ||
@@ -75,17 +53,9 @@ } | ||
| } | ||
| }, | ||
| } | ||
| }; | ||
| }) ; | ||
| function onOutgoingResponseFinish( | ||
| request, | ||
| response, | ||
| options | ||
| , | ||
| ) { | ||
| }); | ||
| function onOutgoingResponseFinish(request, response, options) { | ||
| if (!options.breadcrumbs) { | ||
| return; | ||
| } | ||
| // Check if tracing is suppressed (e.g. for Sentry's own transport requests) | ||
| if (core.getCurrentScope().getScopeData().sdkProcessingMetadata[core.SUPPRESS_TRACING_KEY]) { | ||
@@ -96,4 +66,4 @@ return; | ||
| if (ignoreOutgoingRequests) { | ||
| const url = core.getRequestUrlFromClientRequest(request ); | ||
| if (ignoreOutgoingRequests(url, core.getRequestOptions(request ))) { | ||
| const url = core.getRequestUrlFromClientRequest(request); | ||
| if (ignoreOutgoingRequests(url, core.getRequestOptions(request))) { | ||
| return; | ||
@@ -104,100 +74,5 @@ } | ||
| } | ||
| const httpIntegration = _httpIntegration; | ||
| /** | ||
| * This integration handles incoming and outgoing HTTP requests in light mode (without OpenTelemetry). | ||
| * | ||
| * It uses Node's native diagnostics channels (Node.js 22+) for request isolation, | ||
| * trace propagation, and breadcrumb creation. | ||
| */ | ||
| const httpIntegration = _httpIntegration | ||
| ; | ||
| /** | ||
| * Instrument a server to capture incoming requests. | ||
| */ | ||
| function instrumentServer( | ||
| server, | ||
| { | ||
| ignoreRequestBody, | ||
| maxRequestBodySize, | ||
| } | ||
| , | ||
| ) { | ||
| // eslint-disable-next-line @typescript-eslint/unbound-method | ||
| const originalEmit = server.emit; | ||
| if (wrappedEmitFns.has(originalEmit)) { | ||
| return; | ||
| } | ||
| const newEmit = new Proxy(originalEmit, { | ||
| apply(target, thisArg, args) { | ||
| // Only handle request events | ||
| if (args[0] !== 'request') { | ||
| return target.apply(thisArg, args); | ||
| } | ||
| const client = core.getCurrentScope().getClient(); | ||
| if (!client) { | ||
| return target.apply(thisArg, args); | ||
| } | ||
| debugBuild.DEBUG_BUILD && core.debug.log(INTEGRATION_NAME, 'Handling incoming request'); | ||
| const isolationScope = core.getIsolationScope().clone(); | ||
| const request = args[1] ; | ||
| const normalizedRequest = core.httpRequestToRequestData(request); | ||
| // request.ip is non-standard but some frameworks set this | ||
| const ipAddress = (request ).ip || request.socket?.remoteAddress; | ||
| const url = request.url || '/'; | ||
| if (maxRequestBodySize !== 'none' && !ignoreRequestBody?.(url, request)) { | ||
| captureRequestBody.patchRequestToCaptureBody(request, isolationScope, maxRequestBodySize, INTEGRATION_NAME); | ||
| } | ||
| // Update the isolation scope, isolate this request | ||
| isolationScope.setSDKProcessingMetadata({ normalizedRequest, ipAddress }); | ||
| // attempt to update the scope's `transactionName` based on the request URL | ||
| // Ideally, framework instrumentations coming after the HttpInstrumentation | ||
| // update the transactionName once we get a parameterized route. | ||
| const httpMethod = (request.method || 'GET').toUpperCase(); | ||
| const httpTargetWithoutQueryFragment = core.stripUrlQueryAndFragment(url); | ||
| const bestEffortTransactionName = `${httpMethod} ${httpTargetWithoutQueryFragment}`; | ||
| isolationScope.setTransactionName(bestEffortTransactionName); | ||
| return core.withIsolationScope(isolationScope, () => { | ||
| // Handle trace propagation using Sentry's continueTrace | ||
| // This replaces OpenTelemetry's propagation.extract() + context.with() | ||
| const sentryTrace = normalizedRequest.headers?.['sentry-trace']; | ||
| const baggage = normalizedRequest.headers?.['baggage']; | ||
| return core.continueTrace( | ||
| { | ||
| sentryTrace: Array.isArray(sentryTrace) ? sentryTrace[0] : sentryTrace, | ||
| baggage: Array.isArray(baggage) ? baggage[0] : baggage, | ||
| }, | ||
| () => { | ||
| // Set propagationSpanId after continueTrace because it calls withScope + | ||
| // setPropagationContext internally, which would overwrite any previously set value. | ||
| core.getCurrentScope().getPropagationContext().propagationSpanId = core.generateSpanId(); | ||
| return target.apply(thisArg, args); | ||
| }, | ||
| ); | ||
| }); | ||
| }, | ||
| }); | ||
| wrappedEmitFns.add(newEmit); | ||
| server.emit = newEmit; | ||
| } | ||
| exports.httpIntegration = httpIntegration; | ||
| //# sourceMappingURL=httpIntegration.js.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"httpIntegration.js","sources":["../../../../src/light/integrations/httpIntegration.ts"],"sourcesContent":["import { subscribe } from 'node:diagnostics_channel';\nimport type { RequestOptions } from 'node:http';\nimport type { HttpClientRequest, HttpIncomingMessage, Integration, IntegrationFn } from '@sentry/core';\nimport {\n addOutgoingRequestBreadcrumb,\n continueTrace,\n debug,\n generateSpanId,\n getCurrentScope,\n getHttpClientSubscriptions,\n getIsolationScope,\n HTTP_ON_CLIENT_REQUEST,\n httpRequestToRequestData,\n stripUrlQueryAndFragment,\n SUPPRESS_TRACING_KEY,\n withIsolationScope,\n getRequestOptions,\n getRequestUrlFromClientRequest,\n} from '@sentry/core';\nimport type { ClientRequest, IncomingMessage, Server } from 'node:http';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport { patchRequestToCaptureBody } from '../../utils/captureRequestBody';\nimport type { LightNodeClient } from '../client';\nimport { errorMonitor } from 'node:events';\nimport { NODE_VERSION } from '../../nodeVersion';\n\nconst INTEGRATION_NAME = 'Http';\n\nconst FULLY_SUPPORTS_HTTP_DIAGNOSTICS_CHANNEL =\n (NODE_VERSION.major === 22 && NODE_VERSION.minor >= 12) ||\n (NODE_VERSION.major === 23 && NODE_VERSION.minor >= 2) ||\n NODE_VERSION.major >= 24;\n\n// We keep track of emit functions we wrapped, to avoid double wrapping\nconst wrappedEmitFns = new WeakSet<typeof Server.prototype.emit>();\n\nexport interface HttpIntegrationOptions {\n /**\n * Do not capture the request body for incoming HTTP requests to URLs where the given callback returns `true`.\n * This can be useful for long running requests where the body is not needed and we want to avoid capturing it.\n *\n * @param url Contains the entire URL, including query string (if any), protocol, host, etc. of the incoming request.\n * @param request Contains the {@type RequestOptions} object used to make the incoming request.\n */\n ignoreRequestBody?: (url: string, request: RequestOptions) => boolean;\n\n /**\n * Controls the maximum size of incoming HTTP request bodies attached to events.\n *\n * Available options:\n * - 'none': No request bodies will be attached\n * - 'small': Request bodies up to 1,000 bytes will be attached\n * - 'medium': Request bodies up to 10,000 bytes will be attached (default)\n * - 'always': Request bodies will always be attached\n *\n * Note that even with 'always' setting, bodies exceeding 1MB will never be attached\n * for performance and security reasons.\n *\n * @default 'medium'\n */\n maxRequestBodySize?: 'none' | 'small' | 'medium' | 'always';\n\n /**\n * Whether breadcrumbs should be recorded for outgoing requests.\n *\n * @default `true`\n */\n breadcrumbs?: boolean;\n\n /**\n * Whether to inject trace propagation headers (sentry-trace, baggage, traceparent) into outgoing HTTP requests.\n *\n * When set to `false`, Sentry will not inject any trace propagation headers, but will still create breadcrumbs\n * (if `breadcrumbs` is enabled).\n *\n * @default `true`\n */\n tracePropagation?: boolean;\n\n /**\n * Do not capture breadcrumbs or propagate trace headers for outgoing HTTP requests to URLs\n * where the given callback returns `true`.\n *\n * @param url Contains the entire URL, including query string (if any), protocol, host, etc. of the outgoing request.\n * @param request Contains the {@type RequestOptions} object used to make the outgoing request.\n */\n ignoreOutgoingRequests?: (url: string, request: RequestOptions) => boolean;\n}\n\nconst _httpIntegration = ((options: HttpIntegrationOptions = {}) => {\n const _options = {\n ...options,\n sessions: false,\n maxRequestBodySize: options.maxRequestBodySize ?? 'medium',\n ignoreRequestBody: options.ignoreRequestBody,\n breadcrumbs: options.breadcrumbs ?? true,\n tracePropagation: options.tracePropagation ?? true,\n ignoreOutgoingRequests: options.ignoreOutgoingRequests,\n };\n\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n const onHttpServerRequestStart = (_data: unknown) => {\n const data = _data as { server: Server };\n instrumentServer(data.server, _options);\n };\n\n const { ignoreOutgoingRequests } = _options;\n\n const { [HTTP_ON_CLIENT_REQUEST]: onHttpClientRequestCreated } = getHttpClientSubscriptions({\n breadcrumbs: _options.breadcrumbs,\n propagateTrace: _options.tracePropagation,\n ignoreOutgoingRequests: ignoreOutgoingRequests\n ? (url, request) => ignoreOutgoingRequests(url, getRequestOptions(request as ClientRequest))\n : undefined,\n // No spans in light mode\n // means we don't have pass modules to detect OTel double-wrap\n spans: false,\n errorMonitor,\n });\n\n subscribe('http.server.request.start', onHttpServerRequestStart);\n\n // Subscribe on the request creation in node versions that support it\n subscribe(HTTP_ON_CLIENT_REQUEST, onHttpClientRequestCreated);\n\n // fall back to just doing breadcrumbs on the request.end() channel\n // if we do not have earlier access to the request object at creation\n // time. The http.client.request.error channel is only available on\n // the same node versions as client.request.created, so no help.\n if (_options.breadcrumbs && !FULLY_SUPPORTS_HTTP_DIAGNOSTICS_CHANNEL) {\n subscribe('http.client.request.start', (data: unknown) => {\n const { request } = data as { request: HttpClientRequest };\n request.on(errorMonitor, () => onOutgoingResponseFinish(request, undefined, _options));\n request.prependListener('response', response => {\n if (request.listenerCount('response') <= 1) {\n response.resume();\n }\n onOutgoingResponseFinish(request, response, _options);\n });\n });\n }\n },\n };\n}) satisfies IntegrationFn;\n\nfunction onOutgoingResponseFinish(\n request: HttpClientRequest,\n response: HttpIncomingMessage | undefined,\n options: {\n breadcrumbs: boolean;\n ignoreOutgoingRequests?: (url: string, request: RequestOptions) => boolean;\n },\n): void {\n if (!options.breadcrumbs) {\n return;\n }\n // Check if tracing is suppressed (e.g. for Sentry's own transport requests)\n if (getCurrentScope().getScopeData().sdkProcessingMetadata[SUPPRESS_TRACING_KEY]) {\n return;\n }\n const { ignoreOutgoingRequests } = options;\n if (ignoreOutgoingRequests) {\n const url = getRequestUrlFromClientRequest(request as ClientRequest);\n if (ignoreOutgoingRequests(url, getRequestOptions(request as ClientRequest))) {\n return;\n }\n }\n addOutgoingRequestBreadcrumb(request, response);\n}\n\n/**\n * This integration handles incoming and outgoing HTTP requests in light mode (without OpenTelemetry).\n *\n * It uses Node's native diagnostics channels (Node.js 22+) for request isolation,\n * trace propagation, and breadcrumb creation.\n */\nexport const httpIntegration = _httpIntegration as (options?: HttpIntegrationOptions) => Integration & {\n name: 'Http';\n setupOnce: () => void;\n};\n\n/**\n * Instrument a server to capture incoming requests.\n */\nfunction instrumentServer(\n server: Server,\n {\n ignoreRequestBody,\n maxRequestBodySize,\n }: {\n ignoreRequestBody?: (url: string, request: IncomingMessage) => boolean;\n maxRequestBodySize: 'small' | 'medium' | 'always' | 'none';\n },\n): void {\n // eslint-disable-next-line @typescript-eslint/unbound-method\n const originalEmit: typeof Server.prototype.emit = server.emit;\n\n if (wrappedEmitFns.has(originalEmit)) {\n return;\n }\n\n const newEmit = new Proxy(originalEmit, {\n apply(target, thisArg, args: [event: string, ...args: unknown[]]) {\n // Only handle request events\n if (args[0] !== 'request') {\n return target.apply(thisArg, args);\n }\n\n const client = getCurrentScope().getClient<LightNodeClient>();\n\n if (!client) {\n return target.apply(thisArg, args);\n }\n\n DEBUG_BUILD && debug.log(INTEGRATION_NAME, 'Handling incoming request');\n\n const isolationScope = getIsolationScope().clone();\n const request = args[1] as IncomingMessage;\n\n const normalizedRequest = httpRequestToRequestData(request);\n\n // request.ip is non-standard but some frameworks set this\n const ipAddress = (request as { ip?: string }).ip || request.socket?.remoteAddress;\n\n const url = request.url || '/';\n if (maxRequestBodySize !== 'none' && !ignoreRequestBody?.(url, request)) {\n patchRequestToCaptureBody(request, isolationScope, maxRequestBodySize, INTEGRATION_NAME);\n }\n\n // Update the isolation scope, isolate this request\n isolationScope.setSDKProcessingMetadata({ normalizedRequest, ipAddress });\n\n // attempt to update the scope's `transactionName` based on the request URL\n // Ideally, framework instrumentations coming after the HttpInstrumentation\n // update the transactionName once we get a parameterized route.\n const httpMethod = (request.method || 'GET').toUpperCase();\n const httpTargetWithoutQueryFragment = stripUrlQueryAndFragment(url);\n\n const bestEffortTransactionName = `${httpMethod} ${httpTargetWithoutQueryFragment}`;\n\n isolationScope.setTransactionName(bestEffortTransactionName);\n\n return withIsolationScope(isolationScope, () => {\n // Handle trace propagation using Sentry's continueTrace\n // This replaces OpenTelemetry's propagation.extract() + context.with()\n const sentryTrace = normalizedRequest.headers?.['sentry-trace'];\n const baggage = normalizedRequest.headers?.['baggage'];\n\n return continueTrace(\n {\n sentryTrace: Array.isArray(sentryTrace) ? sentryTrace[0] : sentryTrace,\n baggage: Array.isArray(baggage) ? baggage[0] : baggage,\n },\n () => {\n // Set propagationSpanId after continueTrace because it calls withScope +\n // setPropagationContext internally, which would overwrite any previously set value.\n getCurrentScope().getPropagationContext().propagationSpanId = generateSpanId();\n return target.apply(thisArg, args);\n },\n );\n });\n },\n });\n\n wrappedEmitFns.add(newEmit);\n server.emit = newEmit;\n}\n"],"names":["NODE_VERSION","HTTP_ON_CLIENT_REQUEST","getHttpClientSubscriptions","getRequestOptions","errorMonitor","subscribe","getCurrentScope","SUPPRESS_TRACING_KEY","getRequestUrlFromClientRequest","addOutgoingRequestBreadcrumb","DEBUG_BUILD","debug","getIsolationScope","httpRequestToRequestData","patchRequestToCaptureBody","stripUrlQueryAndFragment","withIsolationScope","continueTrace","generateSpanId"],"mappings":"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"} | ||
| {"version":3,"file":"httpIntegration.js","sources":["../../../../src/light/integrations/httpIntegration.ts"],"sourcesContent":["import { subscribe } from 'node:diagnostics_channel';\nimport type { RequestOptions } from 'node:http';\nimport type { HttpClientRequest, HttpIncomingMessage, Integration, IntegrationFn } from '@sentry/core';\nimport {\n addOutgoingRequestBreadcrumb,\n getCurrentScope,\n getHttpClientSubscriptions,\n getHttpServerSubscriptions,\n getRequestOptions,\n getRequestUrlFromClientRequest,\n HTTP_ON_CLIENT_REQUEST,\n HTTP_ON_SERVER_REQUEST,\n SUPPRESS_TRACING_KEY,\n} from '@sentry/core';\nimport type { ClientRequest } from 'node:http';\nimport { errorMonitor } from 'node:events';\nimport { NODE_VERSION } from '../../nodeVersion';\n\nconst INTEGRATION_NAME = 'Http';\n\nconst FULLY_SUPPORTS_HTTP_DIAGNOSTICS_CHANNEL =\n (NODE_VERSION.major === 22 && NODE_VERSION.minor >= 12) ||\n (NODE_VERSION.major === 23 && NODE_VERSION.minor >= 2) ||\n NODE_VERSION.major >= 24;\n\nexport interface HttpIntegrationOptions {\n /**\n * Do not capture the request body for incoming HTTP requests to URLs where the given callback returns `true`.\n * This can be useful for long running requests where the body is not needed and we want to avoid capturing it.\n *\n * @param url Contains the entire URL, including query string (if any), protocol, host, etc. of the incoming request.\n * @param request Contains the {@type RequestOptions} object used to make the incoming request.\n */\n ignoreRequestBody?: (url: string, request: RequestOptions) => boolean;\n\n /**\n * Controls the maximum size of incoming HTTP request bodies attached to events.\n *\n * Available options:\n * - 'none': No request bodies will be attached\n * - 'small': Request bodies up to 1,000 bytes will be attached\n * - 'medium': Request bodies up to 10,000 bytes will be attached (default)\n * - 'always': Request bodies will always be attached\n *\n * Note that even with 'always' setting, bodies exceeding 1MB will never be attached\n * for performance and security reasons.\n *\n * @default 'medium'\n */\n maxRequestBodySize?: 'none' | 'small' | 'medium' | 'always';\n\n /**\n * Whether breadcrumbs should be recorded for outgoing requests.\n *\n * @default `true`\n */\n breadcrumbs?: boolean;\n\n /**\n * Whether to inject trace propagation headers (sentry-trace, baggage, traceparent) into outgoing HTTP requests.\n *\n * When set to `false`, Sentry will not inject any trace propagation headers, but will still create breadcrumbs\n * (if `breadcrumbs` is enabled).\n *\n * @default `true`\n */\n tracePropagation?: boolean;\n\n /**\n * Do not capture breadcrumbs or propagate trace headers for outgoing HTTP requests to URLs\n * where the given callback returns `true`.\n *\n * @param url Contains the entire URL, including query string (if any), protocol, host, etc. of the outgoing request.\n * @param request Contains the {@type RequestOptions} object used to make the outgoing request.\n */\n ignoreOutgoingRequests?: (url: string, request: RequestOptions) => boolean;\n}\n\nconst _httpIntegration = ((options: HttpIntegrationOptions = {}) => {\n const _options = {\n ...options,\n sessions: false,\n maxRequestBodySize: options.maxRequestBodySize ?? 'medium',\n ignoreRequestBody: options.ignoreRequestBody,\n breadcrumbs: options.breadcrumbs ?? true,\n tracePropagation: options.tracePropagation ?? true,\n ignoreOutgoingRequests: options.ignoreOutgoingRequests,\n // no spans created in light mode\n spans: false,\n errorMonitor,\n };\n\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n const { [HTTP_ON_SERVER_REQUEST]: onHttpServerRequestStart } = getHttpServerSubscriptions(_options);\n\n const { ignoreOutgoingRequests } = _options;\n\n const { [HTTP_ON_CLIENT_REQUEST]: onHttpClientRequestCreated } = getHttpClientSubscriptions({\n breadcrumbs: _options.breadcrumbs,\n propagateTrace: _options.tracePropagation,\n ignoreOutgoingRequests: ignoreOutgoingRequests\n ? (url, request) => ignoreOutgoingRequests(url, getRequestOptions(request as ClientRequest))\n : undefined,\n // No spans in light mode\n // means we don't have pass modules to detect OTel double-wrap\n spans: false,\n errorMonitor,\n });\n\n subscribe(HTTP_ON_SERVER_REQUEST, onHttpServerRequestStart);\n\n // Subscribe on the request creation in node versions that support it\n subscribe(HTTP_ON_CLIENT_REQUEST, onHttpClientRequestCreated);\n\n // fall back to just doing breadcrumbs on the request.end() channel\n // if we do not have earlier access to the request object at creation\n // time. The http.client.request.error channel is only available on\n // the same node versions as client.request.created, so no help.\n if (_options.breadcrumbs && !FULLY_SUPPORTS_HTTP_DIAGNOSTICS_CHANNEL) {\n subscribe('http.client.request.start', (data: unknown) => {\n const { request } = data as { request: HttpClientRequest };\n request.on(errorMonitor, () => onOutgoingResponseFinish(request, undefined, _options));\n request.prependListener('response', response => {\n if (request.listenerCount('response') <= 1) {\n response.resume();\n }\n onOutgoingResponseFinish(request, response, _options);\n });\n });\n }\n },\n };\n}) satisfies IntegrationFn;\n\nfunction onOutgoingResponseFinish(\n request: HttpClientRequest,\n response: HttpIncomingMessage | undefined,\n options: {\n breadcrumbs: boolean;\n ignoreOutgoingRequests?: (url: string, request: RequestOptions) => boolean;\n },\n): void {\n if (!options.breadcrumbs) {\n return;\n }\n // Check if tracing is suppressed (e.g. for Sentry's own transport requests)\n if (getCurrentScope().getScopeData().sdkProcessingMetadata[SUPPRESS_TRACING_KEY]) {\n return;\n }\n const { ignoreOutgoingRequests } = options;\n if (ignoreOutgoingRequests) {\n const url = getRequestUrlFromClientRequest(request as ClientRequest);\n if (ignoreOutgoingRequests(url, getRequestOptions(request as ClientRequest))) {\n return;\n }\n }\n addOutgoingRequestBreadcrumb(request, response);\n}\n\n/**\n * This integration handles incoming and outgoing HTTP requests in light mode (without OpenTelemetry).\n *\n * It uses Node's native diagnostics channels (Node.js 22+) for request isolation,\n * trace propagation, and breadcrumb creation.\n */\nexport const httpIntegration = _httpIntegration as (options?: HttpIntegrationOptions) => Integration & {\n name: 'Http';\n setupOnce: () => void;\n};\n"],"names":["NODE_VERSION","errorMonitor","HTTP_ON_SERVER_REQUEST","getHttpServerSubscriptions","HTTP_ON_CLIENT_REQUEST","getHttpClientSubscriptions","getRequestOptions","subscribe","getCurrentScope","SUPPRESS_TRACING_KEY","getRequestUrlFromClientRequest","addOutgoingRequestBreadcrumb"],"mappings":"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"} |
@@ -7,4 +7,3 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const INTEGRATION_NAME = 'NodeFetch'; | ||
| const INTEGRATION_NAME = "NodeFetch"; | ||
| const _nativeNodeFetchIntegration = ((options = {}) => { | ||
@@ -14,8 +13,6 @@ const _options = { | ||
| tracePropagation: options.tracePropagation ?? true, | ||
| ignoreOutgoingRequests: options.ignoreOutgoingRequests, | ||
| ignoreOutgoingRequests: options.ignoreOutgoingRequests | ||
| }; | ||
| const propagationDecisionMap = new core.LRUMap(100); | ||
| const ignoreOutgoingRequestsMap = new WeakMap(); | ||
| const ignoreOutgoingRequestsMap = /* @__PURE__ */ new WeakMap(); | ||
| return { | ||
@@ -25,38 +22,21 @@ name: INTEGRATION_NAME, | ||
| const onRequestCreated = ((_data) => { | ||
| const data = _data ; | ||
| const data = _data; | ||
| onUndiciRequestCreated(data.request, _options, propagationDecisionMap, ignoreOutgoingRequestsMap); | ||
| }) ; | ||
| }); | ||
| const onResponseHeaders = ((_data) => { | ||
| const data = _data ; | ||
| const data = _data; | ||
| onUndiciResponseHeaders(data.request, data.response, _options, ignoreOutgoingRequestsMap); | ||
| }) ; | ||
| diagnosticsChannel.subscribe('undici:request:create', onRequestCreated); | ||
| diagnosticsChannel.subscribe('undici:request:headers', onResponseHeaders); | ||
| }, | ||
| }); | ||
| diagnosticsChannel.subscribe("undici:request:create", onRequestCreated); | ||
| diagnosticsChannel.subscribe("undici:request:headers", onResponseHeaders); | ||
| } | ||
| }; | ||
| }) ; | ||
| /** | ||
| * This integration handles outgoing fetch (undici) requests in light mode (without OpenTelemetry). | ||
| * It propagates trace headers and creates breadcrumbs for responses. | ||
| */ | ||
| const nativeNodeFetchIntegration = _nativeNodeFetchIntegration | ||
| ; | ||
| function onUndiciRequestCreated( | ||
| request, | ||
| options, | ||
| propagationDecisionMap, | ||
| ignoreOutgoingRequestsMap, | ||
| ) { | ||
| }); | ||
| const nativeNodeFetchIntegration = _nativeNodeFetchIntegration; | ||
| function onUndiciRequestCreated(request, options, propagationDecisionMap, ignoreOutgoingRequestsMap) { | ||
| const shouldIgnore = shouldIgnoreRequest(request, options); | ||
| ignoreOutgoingRequestsMap.set(request, shouldIgnore); | ||
| if (shouldIgnore) { | ||
| return; | ||
| } | ||
| if (options.tracePropagation) { | ||
@@ -66,13 +46,6 @@ outgoingFetchRequest.addTracePropagationHeadersToFetchRequest(request, propagationDecisionMap); | ||
| } | ||
| function onUndiciResponseHeaders( | ||
| request, | ||
| response, | ||
| options, | ||
| ignoreOutgoingRequestsMap, | ||
| ) { | ||
| function onUndiciResponseHeaders(request, response, options, ignoreOutgoingRequestsMap) { | ||
| if (!options.breadcrumbs) { | ||
| return; | ||
| } | ||
| const shouldIgnore = ignoreOutgoingRequestsMap.get(request); | ||
@@ -82,22 +55,12 @@ if (shouldIgnore) { | ||
| } | ||
| outgoingFetchRequest.addFetchRequestBreadcrumb(request, response); | ||
| } | ||
| /** Check if the given outgoing request should be ignored. */ | ||
| function shouldIgnoreRequest( | ||
| request, | ||
| options, | ||
| ) { | ||
| // Check if tracing is suppressed (e.g. for Sentry's own transport requests) | ||
| function shouldIgnoreRequest(request, options) { | ||
| if (core.getCurrentScope().getScopeData().sdkProcessingMetadata.__SENTRY_SUPPRESS_TRACING__) { | ||
| return true; | ||
| } | ||
| const { ignoreOutgoingRequests } = options; | ||
| if (!ignoreOutgoingRequests) { | ||
| return false; | ||
| } | ||
| const url = outgoingFetchRequest.getAbsoluteUrl(request.origin, request.path); | ||
@@ -104,0 +67,0 @@ return ignoreOutgoingRequests(url); |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"nativeNodeFetchIntegration.js","sources":["../../../../src/light/integrations/nativeNodeFetchIntegration.ts"],"sourcesContent":["import type { ChannelListener } from 'node:diagnostics_channel';\nimport { subscribe } from 'node:diagnostics_channel';\nimport type { Integration, IntegrationFn } from '@sentry/core';\nimport { getCurrentScope, LRUMap } from '@sentry/core';\nimport type { UndiciRequest, UndiciResponse } from '../../integrations/node-fetch/types';\nimport {\n addFetchRequestBreadcrumb,\n addTracePropagationHeadersToFetchRequest,\n getAbsoluteUrl,\n} from '../../utils/outgoingFetchRequest';\n\nconst INTEGRATION_NAME = 'NodeFetch';\n\nexport interface NativeNodeFetchIntegrationOptions {\n /**\n * Whether breadcrumbs should be recorded for requests.\n *\n * @default `true`\n */\n breadcrumbs?: boolean;\n\n /**\n * Whether to inject trace propagation headers (sentry-trace, baggage, traceparent) into outgoing fetch requests.\n *\n * When set to `false`, Sentry will not inject any trace propagation headers, but will still create breadcrumbs\n * (if `breadcrumbs` is enabled).\n *\n * @default `true`\n */\n tracePropagation?: boolean;\n\n /**\n * Do not capture breadcrumbs or inject headers for outgoing fetch requests to URLs\n * where the given callback returns `true`.\n *\n * @param url Contains the entire URL, including query string (if any), protocol, host, etc. of the outgoing request.\n */\n ignoreOutgoingRequests?: (url: string) => boolean;\n}\n\nconst _nativeNodeFetchIntegration = ((options: NativeNodeFetchIntegrationOptions = {}) => {\n const _options = {\n breadcrumbs: options.breadcrumbs ?? true,\n tracePropagation: options.tracePropagation ?? true,\n ignoreOutgoingRequests: options.ignoreOutgoingRequests,\n };\n\n const propagationDecisionMap = new LRUMap<string, boolean>(100);\n const ignoreOutgoingRequestsMap = new WeakMap<UndiciRequest, boolean>();\n\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n const onRequestCreated = ((_data: unknown) => {\n const data = _data as { request: UndiciRequest };\n onUndiciRequestCreated(data.request, _options, propagationDecisionMap, ignoreOutgoingRequestsMap);\n }) satisfies ChannelListener;\n\n const onResponseHeaders = ((_data: unknown) => {\n const data = _data as { request: UndiciRequest; response: UndiciResponse };\n onUndiciResponseHeaders(data.request, data.response, _options, ignoreOutgoingRequestsMap);\n }) satisfies ChannelListener;\n\n subscribe('undici:request:create', onRequestCreated);\n subscribe('undici:request:headers', onResponseHeaders);\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * This integration handles outgoing fetch (undici) requests in light mode (without OpenTelemetry).\n * It propagates trace headers and creates breadcrumbs for responses.\n */\nexport const nativeNodeFetchIntegration = _nativeNodeFetchIntegration as (\n options?: NativeNodeFetchIntegrationOptions,\n) => Integration & {\n name: 'NodeFetch';\n setupOnce: () => void;\n};\n\nfunction onUndiciRequestCreated(\n request: UndiciRequest,\n options: { tracePropagation: boolean; ignoreOutgoingRequests?: (url: string) => boolean },\n propagationDecisionMap: LRUMap<string, boolean>,\n ignoreOutgoingRequestsMap: WeakMap<UndiciRequest, boolean>,\n): void {\n const shouldIgnore = shouldIgnoreRequest(request, options);\n ignoreOutgoingRequestsMap.set(request, shouldIgnore);\n\n if (shouldIgnore) {\n return;\n }\n\n if (options.tracePropagation) {\n addTracePropagationHeadersToFetchRequest(request, propagationDecisionMap);\n }\n}\n\nfunction onUndiciResponseHeaders(\n request: UndiciRequest,\n response: UndiciResponse,\n options: { breadcrumbs: boolean },\n ignoreOutgoingRequestsMap: WeakMap<UndiciRequest, boolean>,\n): void {\n if (!options.breadcrumbs) {\n return;\n }\n\n const shouldIgnore = ignoreOutgoingRequestsMap.get(request);\n if (shouldIgnore) {\n return;\n }\n\n addFetchRequestBreadcrumb(request, response);\n}\n\n/** Check if the given outgoing request should be ignored. */\nfunction shouldIgnoreRequest(\n request: UndiciRequest,\n options: { ignoreOutgoingRequests?: (url: string) => boolean },\n): boolean {\n // Check if tracing is suppressed (e.g. for Sentry's own transport requests)\n if (getCurrentScope().getScopeData().sdkProcessingMetadata.__SENTRY_SUPPRESS_TRACING__) {\n return true;\n }\n\n const { ignoreOutgoingRequests } = options;\n\n if (!ignoreOutgoingRequests) {\n return false;\n }\n\n const url = getAbsoluteUrl(request.origin, request.path);\n return ignoreOutgoingRequests(url);\n}\n"],"names":["LRUMap","subscribe","addTracePropagationHeadersToFetchRequest","addFetchRequestBreadcrumb","getCurrentScope","getAbsoluteUrl"],"mappings":"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"} | ||
| {"version":3,"file":"nativeNodeFetchIntegration.js","sources":["../../../../src/light/integrations/nativeNodeFetchIntegration.ts"],"sourcesContent":["import type { ChannelListener } from 'node:diagnostics_channel';\nimport { subscribe } from 'node:diagnostics_channel';\nimport type { Integration, IntegrationFn } from '@sentry/core';\nimport { getCurrentScope, LRUMap } from '@sentry/core';\nimport type { UndiciRequest, UndiciResponse } from '../../integrations/node-fetch/types';\nimport {\n addFetchRequestBreadcrumb,\n addTracePropagationHeadersToFetchRequest,\n getAbsoluteUrl,\n} from '../../utils/outgoingFetchRequest';\n\nconst INTEGRATION_NAME = 'NodeFetch';\n\nexport interface NativeNodeFetchIntegrationOptions {\n /**\n * Whether breadcrumbs should be recorded for requests.\n *\n * @default `true`\n */\n breadcrumbs?: boolean;\n\n /**\n * Whether to inject trace propagation headers (sentry-trace, baggage, traceparent) into outgoing fetch requests.\n *\n * When set to `false`, Sentry will not inject any trace propagation headers, but will still create breadcrumbs\n * (if `breadcrumbs` is enabled).\n *\n * @default `true`\n */\n tracePropagation?: boolean;\n\n /**\n * Do not capture breadcrumbs or inject headers for outgoing fetch requests to URLs\n * where the given callback returns `true`.\n *\n * @param url Contains the entire URL, including query string (if any), protocol, host, etc. of the outgoing request.\n */\n ignoreOutgoingRequests?: (url: string) => boolean;\n}\n\nconst _nativeNodeFetchIntegration = ((options: NativeNodeFetchIntegrationOptions = {}) => {\n const _options = {\n breadcrumbs: options.breadcrumbs ?? true,\n tracePropagation: options.tracePropagation ?? true,\n ignoreOutgoingRequests: options.ignoreOutgoingRequests,\n };\n\n const propagationDecisionMap = new LRUMap<string, boolean>(100);\n const ignoreOutgoingRequestsMap = new WeakMap<UndiciRequest, boolean>();\n\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n const onRequestCreated = ((_data: unknown) => {\n const data = _data as { request: UndiciRequest };\n onUndiciRequestCreated(data.request, _options, propagationDecisionMap, ignoreOutgoingRequestsMap);\n }) satisfies ChannelListener;\n\n const onResponseHeaders = ((_data: unknown) => {\n const data = _data as { request: UndiciRequest; response: UndiciResponse };\n onUndiciResponseHeaders(data.request, data.response, _options, ignoreOutgoingRequestsMap);\n }) satisfies ChannelListener;\n\n subscribe('undici:request:create', onRequestCreated);\n subscribe('undici:request:headers', onResponseHeaders);\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * This integration handles outgoing fetch (undici) requests in light mode (without OpenTelemetry).\n * It propagates trace headers and creates breadcrumbs for responses.\n */\nexport const nativeNodeFetchIntegration = _nativeNodeFetchIntegration as (\n options?: NativeNodeFetchIntegrationOptions,\n) => Integration & {\n name: 'NodeFetch';\n setupOnce: () => void;\n};\n\nfunction onUndiciRequestCreated(\n request: UndiciRequest,\n options: { tracePropagation: boolean; ignoreOutgoingRequests?: (url: string) => boolean },\n propagationDecisionMap: LRUMap<string, boolean>,\n ignoreOutgoingRequestsMap: WeakMap<UndiciRequest, boolean>,\n): void {\n const shouldIgnore = shouldIgnoreRequest(request, options);\n ignoreOutgoingRequestsMap.set(request, shouldIgnore);\n\n if (shouldIgnore) {\n return;\n }\n\n if (options.tracePropagation) {\n addTracePropagationHeadersToFetchRequest(request, propagationDecisionMap);\n }\n}\n\nfunction onUndiciResponseHeaders(\n request: UndiciRequest,\n response: UndiciResponse,\n options: { breadcrumbs: boolean },\n ignoreOutgoingRequestsMap: WeakMap<UndiciRequest, boolean>,\n): void {\n if (!options.breadcrumbs) {\n return;\n }\n\n const shouldIgnore = ignoreOutgoingRequestsMap.get(request);\n if (shouldIgnore) {\n return;\n }\n\n addFetchRequestBreadcrumb(request, response);\n}\n\n/** Check if the given outgoing request should be ignored. */\nfunction shouldIgnoreRequest(\n request: UndiciRequest,\n options: { ignoreOutgoingRequests?: (url: string) => boolean },\n): boolean {\n // Check if tracing is suppressed (e.g. for Sentry's own transport requests)\n if (getCurrentScope().getScopeData().sdkProcessingMetadata.__SENTRY_SUPPRESS_TRACING__) {\n return true;\n }\n\n const { ignoreOutgoingRequests } = options;\n\n if (!ignoreOutgoingRequests) {\n return false;\n }\n\n const url = getAbsoluteUrl(request.origin, request.path);\n return ignoreOutgoingRequests(url);\n}\n"],"names":["LRUMap","subscribe","addTracePropagationHeadersToFetchRequest","addFetchRequestBreadcrumb","getCurrentScope","getAbsoluteUrl"],"mappings":";;;;;;AAWA,MAAM,gBAAA,GAAmB,WAAA;AA6BzB,MAAM,2BAAA,IAA+B,CAAC,OAAA,GAA6C,EAAC,KAAM;AACxF,EAAA,MAAM,QAAA,GAAW;AAAA,IACf,WAAA,EAAa,QAAQ,WAAA,IAAe,IAAA;AAAA,IACpC,gBAAA,EAAkB,QAAQ,gBAAA,IAAoB,IAAA;AAAA,IAC9C,wBAAwB,OAAA,CAAQ;AAAA,GAClC;AAEA,EAAA,MAAM,sBAAA,GAAyB,IAAIA,WAAA,CAAwB,GAAG,CAAA;AAC9D,EAAA,MAAM,yBAAA,uBAAgC,OAAA,EAAgC;AAEtE,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,SAAA,GAAY;AACV,MAAA,MAAM,gBAAA,IAAoB,CAAC,KAAA,KAAmB;AAC5C,QAAA,MAAM,IAAA,GAAO,KAAA;AACb,QAAA,sBAAA,CAAuB,IAAA,CAAK,OAAA,EAAS,QAAA,EAAU,sBAAA,EAAwB,yBAAyB,CAAA;AAAA,MAClG,CAAA,CAAA;AAEA,MAAA,MAAM,iBAAA,IAAqB,CAAC,KAAA,KAAmB;AAC7C,QAAA,MAAM,IAAA,GAAO,KAAA;AACb,QAAA,uBAAA,CAAwB,IAAA,CAAK,OAAA,EAAS,IAAA,CAAK,QAAA,EAAU,UAAU,yBAAyB,CAAA;AAAA,MAC1F,CAAA,CAAA;AAEA,MAAAC,4BAAA,CAAU,yBAAyB,gBAAgB,CAAA;AACnD,MAAAA,4BAAA,CAAU,0BAA0B,iBAAiB,CAAA;AAAA,IACvD;AAAA,GACF;AACF,CAAA,CAAA;AAMO,MAAM,0BAAA,GAA6B;AAO1C,SAAS,sBAAA,CACP,OAAA,EACA,OAAA,EACA,sBAAA,EACA,yBAAA,EACM;AACN,EAAA,MAAM,YAAA,GAAe,mBAAA,CAAoB,OAAA,EAAS,OAAO,CAAA;AACzD,EAAA,yBAAA,CAA0B,GAAA,CAAI,SAAS,YAAY,CAAA;AAEnD,EAAA,IAAI,YAAA,EAAc;AAChB,IAAA;AAAA,EACF;AAEA,EAAA,IAAI,QAAQ,gBAAA,EAAkB;AAC5B,IAAAC,6DAAA,CAAyC,SAAS,sBAAsB,CAAA;AAAA,EAC1E;AACF;AAEA,SAAS,uBAAA,CACP,OAAA,EACA,QAAA,EACA,OAAA,EACA,yBAAA,EACM;AACN,EAAA,IAAI,CAAC,QAAQ,WAAA,EAAa;AACxB,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,YAAA,GAAe,yBAAA,CAA0B,GAAA,CAAI,OAAO,CAAA;AAC1D,EAAA,IAAI,YAAA,EAAc;AAChB,IAAA;AAAA,EACF;AAEA,EAAAC,8CAAA,CAA0B,SAAS,QAAQ,CAAA;AAC7C;AAGA,SAAS,mBAAA,CACP,SACA,OAAA,EACS;AAET,EAAA,IAAIC,oBAAA,EAAgB,CAAE,YAAA,EAAa,CAAE,sBAAsB,2BAAA,EAA6B;AACtF,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,MAAM,EAAE,wBAAuB,GAAI,OAAA;AAEnC,EAAA,IAAI,CAAC,sBAAA,EAAwB;AAC3B,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,MAAM,GAAA,GAAMC,mCAAA,CAAe,OAAA,CAAQ,MAAA,EAAQ,QAAQ,IAAI,CAAA;AACvD,EAAA,OAAO,uBAAuB,GAAG,CAAA;AACnC;;;;"} |
@@ -8,23 +8,17 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const INTEGRATION_NAME = 'OtlpIntegration'; | ||
| const INTEGRATION_NAME = "OtlpIntegration"; | ||
| const _otlpIntegration = ((userOptions = {}) => { | ||
| const options = { | ||
| setupOtlpTracesExporter: userOptions.setupOtlpTracesExporter ?? true, | ||
| collectorUrl: userOptions.collectorUrl, | ||
| collectorUrl: userOptions.collectorUrl | ||
| }; | ||
| let _spanProcessor; | ||
| let _tracerProvider; | ||
| return { | ||
| name: INTEGRATION_NAME, | ||
| setup(_client) { | ||
| // Always register external propagation context so that Sentry error/log events | ||
| // are linked to the active OTel trace context. | ||
| core.registerExternalPropagationContext(() => { | ||
| const activeSpan = api.trace.getActiveSpan(); | ||
| if (!activeSpan) { | ||
| return undefined; | ||
| return void 0; | ||
| } | ||
@@ -34,6 +28,4 @@ const spanContext = activeSpan.spanContext(); | ||
| }); | ||
| core.debug.log(`[${INTEGRATION_NAME}] External propagation context registered.`); | ||
| }, | ||
| afterAllSetup(client) { | ||
@@ -43,9 +35,7 @@ if (options.setupOtlpTracesExporter) { | ||
| } | ||
| }, | ||
| } | ||
| }; | ||
| function setupTracesExporter(client) { | ||
| let endpoint; | ||
| let headers; | ||
| if (options.collectorUrl) { | ||
@@ -60,16 +50,12 @@ endpoint = options.collectorUrl; | ||
| } | ||
| const { protocol, host, port, path, projectId, publicKey } = dsn; | ||
| const basePath = path ? `/${path}` : ''; | ||
| const portStr = port ? `:${port}` : ''; | ||
| const basePath = path ? `/${path}` : ""; | ||
| const portStr = port ? `:${port}` : ""; | ||
| endpoint = `${protocol}://${host}${portStr}${basePath}/api/${projectId}/integration/otlp/v1/traces/`; | ||
| const sdkInfo = client.getSdkMetadata()?.sdk; | ||
| const sentryClient = sdkInfo ? `, sentry_client=${sdkInfo.name}/${sdkInfo.version}` : ''; | ||
| const sentryClient = sdkInfo ? `, sentry_client=${sdkInfo.name}/${sdkInfo.version}` : ""; | ||
| headers = { | ||
| 'X-Sentry-Auth': `Sentry sentry_version=${core.SENTRY_API_VERSION}, sentry_key=${publicKey}${sentryClient}`, | ||
| "X-Sentry-Auth": `Sentry sentry_version=${core.SENTRY_API_VERSION}, sentry_key=${publicKey}${sentryClient}` | ||
| }; | ||
| } | ||
| let exporter; | ||
@@ -79,3 +65,3 @@ try { | ||
| url: endpoint, | ||
| headers, | ||
| headers | ||
| }); | ||
@@ -86,18 +72,6 @@ } catch (e) { | ||
| } | ||
| _spanProcessor = new sdkTraceBase.BatchSpanProcessor(exporter); | ||
| // Add span processor to existing global tracer provider. | ||
| // trace.getTracerProvider() returns a ProxyTracerProvider; unwrap it to get the real provider. | ||
| const globalProvider = api.trace.getTracerProvider(); | ||
| const delegate = | ||
| 'getDelegate' in globalProvider | ||
| ? (globalProvider ).getDelegate() | ||
| : globalProvider; | ||
| // In OTel v2, addSpanProcessor was removed. We push into the internal _spanProcessors | ||
| // array on the MultiSpanProcessor, which is how OTel's own forceFlush() accesses it. | ||
| const activeProcessor = (delegate )?._activeSpanProcessor | ||
| ; | ||
| const delegate = "getDelegate" in globalProvider ? globalProvider.getDelegate() : globalProvider; | ||
| const activeProcessor = delegate?._activeSpanProcessor; | ||
| if (activeProcessor?._spanProcessors) { | ||
@@ -107,5 +81,4 @@ activeProcessor._spanProcessors.push(_spanProcessor); | ||
| } else { | ||
| // No user-configured provider; create a minimal one and set it as global | ||
| _tracerProvider = new sdkTraceBase.BasicTracerProvider({ | ||
| spanProcessors: [_spanProcessor], | ||
| spanProcessors: [_spanProcessor] | ||
| }); | ||
@@ -115,8 +88,6 @@ api.trace.setGlobalTracerProvider(_tracerProvider); | ||
| } | ||
| client.on('flush', () => { | ||
| client.on("flush", () => { | ||
| void _spanProcessor?.forceFlush(); | ||
| }); | ||
| client.on('close', () => { | ||
| client.on("close", () => { | ||
| void _spanProcessor?.shutdown(); | ||
@@ -126,11 +97,3 @@ void _tracerProvider?.shutdown(); | ||
| } | ||
| }) ; | ||
| /** | ||
| * OTLP integration for the Sentry light SDK. | ||
| * | ||
| * Bridges an existing OpenTelemetry setup with Sentry by: | ||
| * 1. Linking Sentry error/log events to the active OTel trace context | ||
| * 2. Exporting OTel spans to Sentry via OTLP (or to a custom collector) | ||
| */ | ||
| }); | ||
| const otlpIntegration = core.defineIntegration(_otlpIntegration); | ||
@@ -137,0 +100,0 @@ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"otlpIntegration.js","sources":["../../../../src/light/integrations/otlpIntegration.ts"],"sourcesContent":["import { trace } from '@opentelemetry/api';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';\nimport type { SpanExporter } from '@opentelemetry/sdk-trace-base';\nimport { BasicTracerProvider, BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';\nimport type { Client, IntegrationFn } from '@sentry/core';\nimport { debug, defineIntegration, registerExternalPropagationContext, SENTRY_API_VERSION } from '@sentry/core';\n\ninterface OtlpIntegrationOptions {\n /**\n * Whether to set up the OTLP traces exporter that sends spans to Sentry.\n * Default: true\n */\n setupOtlpTracesExporter?: boolean;\n\n /**\n * URL of your own OpenTelemetry collector.\n * When set, the exporter will send traces to this URL instead of the Sentry OTLP endpoint derived from the DSN.\n * Default: undefined (uses DSN-derived endpoint)\n */\n collectorUrl?: string;\n}\n\nconst INTEGRATION_NAME = 'OtlpIntegration';\n\nconst _otlpIntegration = ((userOptions: OtlpIntegrationOptions = {}) => {\n const options = {\n setupOtlpTracesExporter: userOptions.setupOtlpTracesExporter ?? true,\n collectorUrl: userOptions.collectorUrl,\n };\n\n let _spanProcessor: BatchSpanProcessor | undefined;\n let _tracerProvider: BasicTracerProvider | undefined;\n\n return {\n name: INTEGRATION_NAME,\n\n setup(_client: Client): void {\n // Always register external propagation context so that Sentry error/log events\n // are linked to the active OTel trace context.\n registerExternalPropagationContext(() => {\n const activeSpan = trace.getActiveSpan();\n if (!activeSpan) {\n return undefined;\n }\n const spanContext = activeSpan.spanContext();\n return { traceId: spanContext.traceId, spanId: spanContext.spanId };\n });\n\n debug.log(`[${INTEGRATION_NAME}] External propagation context registered.`);\n },\n\n afterAllSetup(client: Client): void {\n if (options.setupOtlpTracesExporter) {\n setupTracesExporter(client);\n }\n },\n };\n\n function setupTracesExporter(client: Client): void {\n let endpoint: string;\n let headers: Record<string, string> | undefined;\n\n if (options.collectorUrl) {\n endpoint = options.collectorUrl;\n debug.log(`[${INTEGRATION_NAME}] Sending traces to collector at ${endpoint}`);\n } else {\n const dsn = client.getDsn();\n if (!dsn) {\n debug.warn(`[${INTEGRATION_NAME}] No DSN found. OTLP exporter not set up.`);\n return;\n }\n\n const { protocol, host, port, path, projectId, publicKey } = dsn;\n\n const basePath = path ? `/${path}` : '';\n const portStr = port ? `:${port}` : '';\n endpoint = `${protocol}://${host}${portStr}${basePath}/api/${projectId}/integration/otlp/v1/traces/`;\n\n const sdkInfo = client.getSdkMetadata()?.sdk;\n const sentryClient = sdkInfo ? `, sentry_client=${sdkInfo.name}/${sdkInfo.version}` : '';\n headers = {\n 'X-Sentry-Auth': `Sentry sentry_version=${SENTRY_API_VERSION}, sentry_key=${publicKey}${sentryClient}`,\n };\n }\n\n let exporter: SpanExporter;\n try {\n exporter = new OTLPTraceExporter({\n url: endpoint,\n headers,\n });\n } catch (e) {\n debug.warn(`[${INTEGRATION_NAME}] Failed to create OTLPTraceExporter:`, e);\n return;\n }\n\n _spanProcessor = new BatchSpanProcessor(exporter);\n\n // Add span processor to existing global tracer provider.\n // trace.getTracerProvider() returns a ProxyTracerProvider; unwrap it to get the real provider.\n const globalProvider = trace.getTracerProvider();\n const delegate =\n 'getDelegate' in globalProvider\n ? (globalProvider as unknown as { getDelegate(): unknown }).getDelegate()\n : globalProvider;\n\n // In OTel v2, addSpanProcessor was removed. We push into the internal _spanProcessors\n // array on the MultiSpanProcessor, which is how OTel's own forceFlush() accesses it.\n const activeProcessor = (delegate as Record<string, unknown>)?._activeSpanProcessor as\n | { _spanProcessors?: unknown[] }\n | undefined;\n if (activeProcessor?._spanProcessors) {\n activeProcessor._spanProcessors.push(_spanProcessor);\n debug.log(`[${INTEGRATION_NAME}] Added span processor to existing TracerProvider.`);\n } else {\n // No user-configured provider; create a minimal one and set it as global\n _tracerProvider = new BasicTracerProvider({\n spanProcessors: [_spanProcessor],\n });\n trace.setGlobalTracerProvider(_tracerProvider);\n debug.log(`[${INTEGRATION_NAME}] Created new TracerProvider with OTLP span processor.`);\n }\n\n client.on('flush', () => {\n void _spanProcessor?.forceFlush();\n });\n\n client.on('close', () => {\n void _spanProcessor?.shutdown();\n void _tracerProvider?.shutdown();\n });\n }\n}) satisfies IntegrationFn;\n\n/**\n * OTLP integration for the Sentry light SDK.\n *\n * Bridges an existing OpenTelemetry setup with Sentry by:\n * 1. Linking Sentry error/log events to the active OTel trace context\n * 2. Exporting OTel spans to Sentry via OTLP (or to a custom collector)\n */\nexport const otlpIntegration = defineIntegration(_otlpIntegration);\n"],"names":["registerExternalPropagationContext","trace","debug","SENTRY_API_VERSION","OTLPTraceExporter","BatchSpanProcessor","BasicTracerProvider","defineIntegration"],"mappings":"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"} | ||
| {"version":3,"file":"otlpIntegration.js","sources":["../../../../src/light/integrations/otlpIntegration.ts"],"sourcesContent":["import { trace } from '@opentelemetry/api';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';\nimport type { SpanExporter } from '@opentelemetry/sdk-trace-base';\nimport { BasicTracerProvider, BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';\nimport type { Client, IntegrationFn } from '@sentry/core';\nimport { debug, defineIntegration, registerExternalPropagationContext, SENTRY_API_VERSION } from '@sentry/core';\n\ninterface OtlpIntegrationOptions {\n /**\n * Whether to set up the OTLP traces exporter that sends spans to Sentry.\n * Default: true\n */\n setupOtlpTracesExporter?: boolean;\n\n /**\n * URL of your own OpenTelemetry collector.\n * When set, the exporter will send traces to this URL instead of the Sentry OTLP endpoint derived from the DSN.\n * Default: undefined (uses DSN-derived endpoint)\n */\n collectorUrl?: string;\n}\n\nconst INTEGRATION_NAME = 'OtlpIntegration';\n\nconst _otlpIntegration = ((userOptions: OtlpIntegrationOptions = {}) => {\n const options = {\n setupOtlpTracesExporter: userOptions.setupOtlpTracesExporter ?? true,\n collectorUrl: userOptions.collectorUrl,\n };\n\n let _spanProcessor: BatchSpanProcessor | undefined;\n let _tracerProvider: BasicTracerProvider | undefined;\n\n return {\n name: INTEGRATION_NAME,\n\n setup(_client: Client): void {\n // Always register external propagation context so that Sentry error/log events\n // are linked to the active OTel trace context.\n registerExternalPropagationContext(() => {\n const activeSpan = trace.getActiveSpan();\n if (!activeSpan) {\n return undefined;\n }\n const spanContext = activeSpan.spanContext();\n return { traceId: spanContext.traceId, spanId: spanContext.spanId };\n });\n\n debug.log(`[${INTEGRATION_NAME}] External propagation context registered.`);\n },\n\n afterAllSetup(client: Client): void {\n if (options.setupOtlpTracesExporter) {\n setupTracesExporter(client);\n }\n },\n };\n\n function setupTracesExporter(client: Client): void {\n let endpoint: string;\n let headers: Record<string, string> | undefined;\n\n if (options.collectorUrl) {\n endpoint = options.collectorUrl;\n debug.log(`[${INTEGRATION_NAME}] Sending traces to collector at ${endpoint}`);\n } else {\n const dsn = client.getDsn();\n if (!dsn) {\n debug.warn(`[${INTEGRATION_NAME}] No DSN found. OTLP exporter not set up.`);\n return;\n }\n\n const { protocol, host, port, path, projectId, publicKey } = dsn;\n\n const basePath = path ? `/${path}` : '';\n const portStr = port ? `:${port}` : '';\n endpoint = `${protocol}://${host}${portStr}${basePath}/api/${projectId}/integration/otlp/v1/traces/`;\n\n const sdkInfo = client.getSdkMetadata()?.sdk;\n const sentryClient = sdkInfo ? `, sentry_client=${sdkInfo.name}/${sdkInfo.version}` : '';\n headers = {\n 'X-Sentry-Auth': `Sentry sentry_version=${SENTRY_API_VERSION}, sentry_key=${publicKey}${sentryClient}`,\n };\n }\n\n let exporter: SpanExporter;\n try {\n exporter = new OTLPTraceExporter({\n url: endpoint,\n headers,\n });\n } catch (e) {\n debug.warn(`[${INTEGRATION_NAME}] Failed to create OTLPTraceExporter:`, e);\n return;\n }\n\n _spanProcessor = new BatchSpanProcessor(exporter);\n\n // Add span processor to existing global tracer provider.\n // trace.getTracerProvider() returns a ProxyTracerProvider; unwrap it to get the real provider.\n const globalProvider = trace.getTracerProvider();\n const delegate =\n 'getDelegate' in globalProvider\n ? (globalProvider as unknown as { getDelegate(): unknown }).getDelegate()\n : globalProvider;\n\n // In OTel v2, addSpanProcessor was removed. We push into the internal _spanProcessors\n // array on the MultiSpanProcessor, which is how OTel's own forceFlush() accesses it.\n const activeProcessor = (delegate as Record<string, unknown>)?._activeSpanProcessor as\n | { _spanProcessors?: unknown[] }\n | undefined;\n if (activeProcessor?._spanProcessors) {\n activeProcessor._spanProcessors.push(_spanProcessor);\n debug.log(`[${INTEGRATION_NAME}] Added span processor to existing TracerProvider.`);\n } else {\n // No user-configured provider; create a minimal one and set it as global\n _tracerProvider = new BasicTracerProvider({\n spanProcessors: [_spanProcessor],\n });\n trace.setGlobalTracerProvider(_tracerProvider);\n debug.log(`[${INTEGRATION_NAME}] Created new TracerProvider with OTLP span processor.`);\n }\n\n client.on('flush', () => {\n void _spanProcessor?.forceFlush();\n });\n\n client.on('close', () => {\n void _spanProcessor?.shutdown();\n void _tracerProvider?.shutdown();\n });\n }\n}) satisfies IntegrationFn;\n\n/**\n * OTLP integration for the Sentry light SDK.\n *\n * Bridges an existing OpenTelemetry setup with Sentry by:\n * 1. Linking Sentry error/log events to the active OTel trace context\n * 2. Exporting OTel spans to Sentry via OTLP (or to a custom collector)\n */\nexport const otlpIntegration = defineIntegration(_otlpIntegration);\n"],"names":["registerExternalPropagationContext","trace","debug","SENTRY_API_VERSION","OTLPTraceExporter","BatchSpanProcessor","BasicTracerProvider","defineIntegration"],"mappings":"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"} |
+19
-78
@@ -25,5 +25,2 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| /** | ||
| * Get default integrations for the Light Node-Core SDK. | ||
| */ | ||
| function getDefaultIntegrations() { | ||
@@ -50,29 +47,13 @@ return [ | ||
| processSession.processSessionIntegration(), | ||
| modules.modulesIntegration(), | ||
| modules.modulesIntegration() | ||
| ]; | ||
| } | ||
| /** | ||
| * Initialize Sentry for Node in light mode (without OpenTelemetry). | ||
| */ | ||
| function init(options = {}) { | ||
| return _init(options, getDefaultIntegrations); | ||
| } | ||
| /** | ||
| * Initialize Sentry for Node in light mode, without any integrations added by default. | ||
| */ | ||
| function initWithoutDefaultIntegrations(options = {}) { | ||
| return _init(options, () => []); | ||
| } | ||
| /** | ||
| * Initialize Sentry for Node in light mode. | ||
| */ | ||
| function _init( | ||
| _options = {}, | ||
| getDefaultIntegrationsImpl, | ||
| ) { | ||
| function _init(_options = {}, getDefaultIntegrationsImpl) { | ||
| const options = getClientOptions(_options, getDefaultIntegrationsImpl); | ||
| if (options.debug === true) { | ||
@@ -82,58 +63,35 @@ if (debugBuild.DEBUG_BUILD) { | ||
| } else { | ||
| // use `console.warn` rather than `debug.warn` since by non-debug bundles have all `debug.x` statements stripped | ||
| core.consoleSandbox(() => { | ||
| // eslint-disable-next-line no-console | ||
| console.warn('[Sentry] Cannot initialize SDK with `debug` option using a non-debug bundle.'); | ||
| console.warn("[Sentry] Cannot initialize SDK with `debug` option using a non-debug bundle."); | ||
| }); | ||
| } | ||
| } | ||
| // Use AsyncLocalStorage-based context strategy instead of OpenTelemetry | ||
| asyncLocalStorageStrategy.setAsyncLocalStorageAsyncContextStrategy(); | ||
| const scope = core.getCurrentScope(); | ||
| scope.update(options.initialScope); | ||
| if (options.spotlight && !options.integrations.some(({ name }) => name === spotlight.INTEGRATION_NAME)) { | ||
| options.integrations.push( | ||
| spotlight.spotlightIntegration({ | ||
| sidecarUrl: typeof options.spotlight === 'string' ? options.spotlight : undefined, | ||
| }), | ||
| sidecarUrl: typeof options.spotlight === "string" ? options.spotlight : void 0 | ||
| }) | ||
| ); | ||
| } | ||
| core.applySdkMetadata(options, 'node-light', ['node-core']); | ||
| core.applySdkMetadata(options, "node-light", ["node-core"]); | ||
| const client$1 = new client.LightNodeClient(options); | ||
| // The client is on the current scope, from where it generally is inherited | ||
| core.getCurrentScope().setClient(client$1); | ||
| client$1.init(); | ||
| core.debug.log(`SDK initialized from ${detection.isCjs() ? 'CommonJS' : 'ESM'} (light mode)`); | ||
| core.debug.log(`SDK initialized from ${detection.isCjs() ? "CommonJS" : "ESM"} (light mode)`); | ||
| client$1.startClientReportTracking(); | ||
| updateScopeFromEnvVariables(); | ||
| // Ensure we flush events when vercel functions are ended | ||
| // See: https://vercel.com/docs/functions/functions-api-reference#sigterm-signal | ||
| if (process.env.VERCEL) { | ||
| process.on('SIGTERM', async () => { | ||
| // We have 500ms for processing here, so we try to make sure to have enough time to send the events | ||
| process.on("SIGTERM", async () => { | ||
| await client$1.flush(200); | ||
| }); | ||
| } | ||
| return client$1; | ||
| } | ||
| function getClientOptions( | ||
| options, | ||
| getDefaultIntegrationsImpl, | ||
| ) { | ||
| function getClientOptions(options, getDefaultIntegrationsImpl) { | ||
| const release = getRelease(options.release); | ||
| const spotlight = spotlight$1.getSpotlightConfig(options.spotlight); | ||
| const tracesSampleRate = getTracesSampleRate(options.tracesSampleRate); | ||
| const mergedOptions = { | ||
@@ -149,56 +107,39 @@ ...options, | ||
| spotlight, | ||
| debug: core.envToBool(options.debug ?? process.env.SENTRY_DEBUG), | ||
| debug: core.envToBool(options.debug ?? process.env.SENTRY_DEBUG) | ||
| }; | ||
| const integrations = options.integrations; | ||
| const defaultIntegrations = options.defaultIntegrations ?? getDefaultIntegrationsImpl(mergedOptions); | ||
| const resolvedIntegrations = core.getIntegrationsToSetup({ | ||
| defaultIntegrations, | ||
| integrations, | ||
| integrations | ||
| }); | ||
| if (mergedOptions.traceLifecycle === 'stream' && !resolvedIntegrations.some(i => i.name === 'SpanStreaming')) { | ||
| if (mergedOptions.traceLifecycle === "stream" && !resolvedIntegrations.some((i) => i.name === "SpanStreaming")) { | ||
| resolvedIntegrations.push(core.spanStreamingIntegration()); | ||
| } | ||
| return { | ||
| ...mergedOptions, | ||
| integrations: resolvedIntegrations, | ||
| integrations: resolvedIntegrations | ||
| }; | ||
| } | ||
| function getRelease(release) { | ||
| if (release !== undefined) { | ||
| if (release !== void 0) { | ||
| return release; | ||
| } | ||
| const detectedRelease = api.getSentryRelease(); | ||
| if (detectedRelease !== undefined) { | ||
| if (detectedRelease !== void 0) { | ||
| return detectedRelease; | ||
| } | ||
| return undefined; | ||
| return void 0; | ||
| } | ||
| function getTracesSampleRate(tracesSampleRate) { | ||
| if (tracesSampleRate !== undefined) { | ||
| if (tracesSampleRate !== void 0) { | ||
| return tracesSampleRate; | ||
| } | ||
| const sampleRateFromEnv = process.env.SENTRY_TRACES_SAMPLE_RATE; | ||
| if (!sampleRateFromEnv) { | ||
| return undefined; | ||
| return void 0; | ||
| } | ||
| const parsed = parseFloat(sampleRateFromEnv); | ||
| return isFinite(parsed) ? parsed : undefined; | ||
| return isFinite(parsed) ? parsed : void 0; | ||
| } | ||
| /** | ||
| * Update scope and propagation context based on environmental variables. | ||
| * | ||
| * See https://github.com/getsentry/rfcs/blob/main/text/0071-continue-trace-over-process-boundaries.md | ||
| * for more details. | ||
| */ | ||
| function updateScopeFromEnvVariables() { | ||
@@ -205,0 +146,0 @@ if (core.envToBool(process.env.SENTRY_USE_ENVIRONMENT) !== false) { |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"sdk.js","sources":["../../../src/light/sdk.ts"],"sourcesContent":["import type { Integration, Options } from '@sentry/core';\nimport {\n applySdkMetadata,\n consoleSandbox,\n debug,\n envToBool,\n eventFiltersIntegration,\n functionToStringIntegration,\n getCurrentScope,\n getIntegrationsToSetup,\n linkedErrorsIntegration,\n propagationContextFromHeaders,\n requestDataIntegration,\n spanStreamingIntegration,\n stackParserFromStackParserOptions,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { childProcessIntegration } from '../integrations/childProcess';\nimport { nodeContextIntegration } from '../integrations/context';\nimport { contextLinesIntegration } from '../integrations/contextlines';\nimport { localVariablesIntegration } from '../integrations/local-variables';\nimport { modulesIntegration } from '../integrations/modules';\nimport { onUncaughtExceptionIntegration } from '../integrations/onuncaughtexception';\nimport { onUnhandledRejectionIntegration } from '../integrations/onunhandledrejection';\nimport { processSessionIntegration } from '../integrations/processSession';\nimport { INTEGRATION_NAME as SPOTLIGHT_INTEGRATION_NAME, spotlightIntegration } from '../integrations/spotlight';\nimport { consoleIntegration } from '../integrations/console';\nimport { systemErrorIntegration } from '../integrations/systemError';\nimport { defaultStackParser, getSentryRelease } from '../sdk/api';\nimport { makeNodeTransport } from '../transports';\nimport type { NodeClientOptions, NodeOptions } from '../types';\nimport { isCjs } from '../utils/detection';\nimport { getSpotlightConfig } from '../utils/spotlight';\nimport { setAsyncLocalStorageAsyncContextStrategy } from './asyncLocalStorageStrategy';\nimport { LightNodeClient } from './client';\nimport { httpIntegration } from './integrations/httpIntegration';\nimport { nativeNodeFetchIntegration } from './integrations/nativeNodeFetchIntegration';\n\n/**\n * Get default integrations for the Light Node-Core SDK.\n */\nexport function getDefaultIntegrations(): Integration[] {\n return [\n // Common\n eventFiltersIntegration(),\n functionToStringIntegration(),\n linkedErrorsIntegration(),\n requestDataIntegration(),\n systemErrorIntegration(),\n // Native Wrappers\n consoleIntegration(),\n httpIntegration(),\n nativeNodeFetchIntegration(),\n // Global Handlers\n onUncaughtExceptionIntegration(),\n onUnhandledRejectionIntegration(),\n // Event Info\n contextLinesIntegration(),\n localVariablesIntegration(),\n nodeContextIntegration(),\n childProcessIntegration(),\n processSessionIntegration(),\n modulesIntegration(),\n ];\n}\n\n/**\n * Initialize Sentry for Node in light mode (without OpenTelemetry).\n */\nexport function init(options: NodeOptions | undefined = {}): LightNodeClient | undefined {\n return _init(options, getDefaultIntegrations);\n}\n\n/**\n * Initialize Sentry for Node in light mode, without any integrations added by default.\n */\nexport function initWithoutDefaultIntegrations(options: NodeOptions | undefined = {}): LightNodeClient {\n return _init(options, () => []);\n}\n\n/**\n * Initialize Sentry for Node in light mode.\n */\nfunction _init(\n _options: NodeOptions | undefined = {},\n getDefaultIntegrationsImpl: (options: Options) => Integration[],\n): LightNodeClient {\n const options = getClientOptions(_options, getDefaultIntegrationsImpl);\n\n if (options.debug === true) {\n if (DEBUG_BUILD) {\n debug.enable();\n } else {\n // use `console.warn` rather than `debug.warn` since by non-debug bundles have all `debug.x` statements stripped\n consoleSandbox(() => {\n // eslint-disable-next-line no-console\n console.warn('[Sentry] Cannot initialize SDK with `debug` option using a non-debug bundle.');\n });\n }\n }\n\n // Use AsyncLocalStorage-based context strategy instead of OpenTelemetry\n setAsyncLocalStorageAsyncContextStrategy();\n\n const scope = getCurrentScope();\n scope.update(options.initialScope);\n\n if (options.spotlight && !options.integrations.some(({ name }) => name === SPOTLIGHT_INTEGRATION_NAME)) {\n options.integrations.push(\n spotlightIntegration({\n sidecarUrl: typeof options.spotlight === 'string' ? options.spotlight : undefined,\n }),\n );\n }\n\n applySdkMetadata(options, 'node-light', ['node-core']);\n\n const client = new LightNodeClient(options);\n // The client is on the current scope, from where it generally is inherited\n getCurrentScope().setClient(client);\n\n client.init();\n\n debug.log(`SDK initialized from ${isCjs() ? 'CommonJS' : 'ESM'} (light mode)`);\n\n client.startClientReportTracking();\n\n updateScopeFromEnvVariables();\n\n // Ensure we flush events when vercel functions are ended\n // See: https://vercel.com/docs/functions/functions-api-reference#sigterm-signal\n if (process.env.VERCEL) {\n process.on('SIGTERM', async () => {\n // We have 500ms for processing here, so we try to make sure to have enough time to send the events\n await client.flush(200);\n });\n }\n\n return client;\n}\n\nfunction getClientOptions(\n options: NodeOptions,\n getDefaultIntegrationsImpl: (options: Options) => Integration[],\n): NodeClientOptions {\n const release = getRelease(options.release);\n const spotlight = getSpotlightConfig(options.spotlight);\n const tracesSampleRate = getTracesSampleRate(options.tracesSampleRate);\n\n const mergedOptions = {\n ...options,\n dsn: options.dsn ?? process.env.SENTRY_DSN,\n environment: options.environment ?? process.env.SENTRY_ENVIRONMENT,\n sendClientReports: options.sendClientReports ?? true,\n transport: options.transport ?? makeNodeTransport,\n stackParser: stackParserFromStackParserOptions(options.stackParser || defaultStackParser),\n release,\n tracesSampleRate,\n spotlight,\n debug: envToBool(options.debug ?? process.env.SENTRY_DEBUG),\n };\n\n const integrations = options.integrations;\n const defaultIntegrations = options.defaultIntegrations ?? getDefaultIntegrationsImpl(mergedOptions);\n\n const resolvedIntegrations = getIntegrationsToSetup({\n defaultIntegrations,\n integrations,\n });\n\n if (mergedOptions.traceLifecycle === 'stream' && !resolvedIntegrations.some(i => i.name === 'SpanStreaming')) {\n resolvedIntegrations.push(spanStreamingIntegration());\n }\n\n return {\n ...mergedOptions,\n integrations: resolvedIntegrations,\n };\n}\n\nfunction getRelease(release: NodeOptions['release']): string | undefined {\n if (release !== undefined) {\n return release;\n }\n\n const detectedRelease = getSentryRelease();\n if (detectedRelease !== undefined) {\n return detectedRelease;\n }\n\n return undefined;\n}\n\nfunction getTracesSampleRate(tracesSampleRate: NodeOptions['tracesSampleRate']): number | undefined {\n if (tracesSampleRate !== undefined) {\n return tracesSampleRate;\n }\n\n const sampleRateFromEnv = process.env.SENTRY_TRACES_SAMPLE_RATE;\n if (!sampleRateFromEnv) {\n return undefined;\n }\n\n const parsed = parseFloat(sampleRateFromEnv);\n return isFinite(parsed) ? parsed : undefined;\n}\n\n/**\n * Update scope and propagation context based on environmental variables.\n *\n * See https://github.com/getsentry/rfcs/blob/main/text/0071-continue-trace-over-process-boundaries.md\n * for more details.\n */\nfunction updateScopeFromEnvVariables(): void {\n if (envToBool(process.env.SENTRY_USE_ENVIRONMENT) !== false) {\n const sentryTraceEnv = process.env.SENTRY_TRACE;\n const baggageEnv = process.env.SENTRY_BAGGAGE;\n const propagationContext = propagationContextFromHeaders(sentryTraceEnv, baggageEnv);\n getCurrentScope().setPropagationContext(propagationContext);\n }\n}\n"],"names":["eventFiltersIntegration","functionToStringIntegration","linkedErrorsIntegration","requestDataIntegration","systemErrorIntegration","consoleIntegration","httpIntegration","nativeNodeFetchIntegration","onUncaughtExceptionIntegration","onUnhandledRejectionIntegration","contextLinesIntegration","localVariablesIntegration","nodeContextIntegration","childProcessIntegration","processSessionIntegration","modulesIntegration","DEBUG_BUILD","debug","consoleSandbox","setAsyncLocalStorageAsyncContextStrategy","getCurrentScope","SPOTLIGHT_INTEGRATION_NAME","spotlightIntegration","applySdkMetadata","client","LightNodeClient","isCjs","getSpotlightConfig","makeNodeTransport","stackParserFromStackParserOptions","defaultStackParser","envToBool","getIntegrationsToSetup","spanStreamingIntegration","getSentryRelease","propagationContextFromHeaders"],"mappings":"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"} | ||
| {"version":3,"file":"sdk.js","sources":["../../../src/light/sdk.ts"],"sourcesContent":["import type { Integration, Options } from '@sentry/core';\nimport {\n applySdkMetadata,\n consoleSandbox,\n debug,\n envToBool,\n eventFiltersIntegration,\n functionToStringIntegration,\n getCurrentScope,\n getIntegrationsToSetup,\n linkedErrorsIntegration,\n propagationContextFromHeaders,\n requestDataIntegration,\n spanStreamingIntegration,\n stackParserFromStackParserOptions,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { childProcessIntegration } from '../integrations/childProcess';\nimport { nodeContextIntegration } from '../integrations/context';\nimport { contextLinesIntegration } from '../integrations/contextlines';\nimport { localVariablesIntegration } from '../integrations/local-variables';\nimport { modulesIntegration } from '../integrations/modules';\nimport { onUncaughtExceptionIntegration } from '../integrations/onuncaughtexception';\nimport { onUnhandledRejectionIntegration } from '../integrations/onunhandledrejection';\nimport { processSessionIntegration } from '../integrations/processSession';\nimport { INTEGRATION_NAME as SPOTLIGHT_INTEGRATION_NAME, spotlightIntegration } from '../integrations/spotlight';\nimport { consoleIntegration } from '../integrations/console';\nimport { systemErrorIntegration } from '../integrations/systemError';\nimport { defaultStackParser, getSentryRelease } from '../sdk/api';\nimport { makeNodeTransport } from '../transports';\nimport type { NodeClientOptions, NodeOptions } from '../types';\nimport { isCjs } from '../utils/detection';\nimport { getSpotlightConfig } from '../utils/spotlight';\nimport { setAsyncLocalStorageAsyncContextStrategy } from './asyncLocalStorageStrategy';\nimport { LightNodeClient } from './client';\nimport { httpIntegration } from './integrations/httpIntegration';\nimport { nativeNodeFetchIntegration } from './integrations/nativeNodeFetchIntegration';\n\n/**\n * Get default integrations for the Light Node-Core SDK.\n */\nexport function getDefaultIntegrations(): Integration[] {\n return [\n // Common\n eventFiltersIntegration(),\n functionToStringIntegration(),\n linkedErrorsIntegration(),\n requestDataIntegration(),\n systemErrorIntegration(),\n // Native Wrappers\n consoleIntegration(),\n httpIntegration(),\n nativeNodeFetchIntegration(),\n // Global Handlers\n onUncaughtExceptionIntegration(),\n onUnhandledRejectionIntegration(),\n // Event Info\n contextLinesIntegration(),\n localVariablesIntegration(),\n nodeContextIntegration(),\n childProcessIntegration(),\n processSessionIntegration(),\n modulesIntegration(),\n ];\n}\n\n/**\n * Initialize Sentry for Node in light mode (without OpenTelemetry).\n */\nexport function init(options: NodeOptions | undefined = {}): LightNodeClient | undefined {\n return _init(options, getDefaultIntegrations);\n}\n\n/**\n * Initialize Sentry for Node in light mode, without any integrations added by default.\n */\nexport function initWithoutDefaultIntegrations(options: NodeOptions | undefined = {}): LightNodeClient {\n return _init(options, () => []);\n}\n\n/**\n * Initialize Sentry for Node in light mode.\n */\nfunction _init(\n _options: NodeOptions | undefined = {},\n getDefaultIntegrationsImpl: (options: Options) => Integration[],\n): LightNodeClient {\n const options = getClientOptions(_options, getDefaultIntegrationsImpl);\n\n if (options.debug === true) {\n if (DEBUG_BUILD) {\n debug.enable();\n } else {\n // use `console.warn` rather than `debug.warn` since by non-debug bundles have all `debug.x` statements stripped\n consoleSandbox(() => {\n // eslint-disable-next-line no-console\n console.warn('[Sentry] Cannot initialize SDK with `debug` option using a non-debug bundle.');\n });\n }\n }\n\n // Use AsyncLocalStorage-based context strategy instead of OpenTelemetry\n setAsyncLocalStorageAsyncContextStrategy();\n\n const scope = getCurrentScope();\n scope.update(options.initialScope);\n\n if (options.spotlight && !options.integrations.some(({ name }) => name === SPOTLIGHT_INTEGRATION_NAME)) {\n options.integrations.push(\n spotlightIntegration({\n sidecarUrl: typeof options.spotlight === 'string' ? options.spotlight : undefined,\n }),\n );\n }\n\n applySdkMetadata(options, 'node-light', ['node-core']);\n\n const client = new LightNodeClient(options);\n // The client is on the current scope, from where it generally is inherited\n getCurrentScope().setClient(client);\n\n client.init();\n\n debug.log(`SDK initialized from ${isCjs() ? 'CommonJS' : 'ESM'} (light mode)`);\n\n client.startClientReportTracking();\n\n updateScopeFromEnvVariables();\n\n // Ensure we flush events when vercel functions are ended\n // See: https://vercel.com/docs/functions/functions-api-reference#sigterm-signal\n if (process.env.VERCEL) {\n process.on('SIGTERM', async () => {\n // We have 500ms for processing here, so we try to make sure to have enough time to send the events\n await client.flush(200);\n });\n }\n\n return client;\n}\n\nfunction getClientOptions(\n options: NodeOptions,\n getDefaultIntegrationsImpl: (options: Options) => Integration[],\n): NodeClientOptions {\n const release = getRelease(options.release);\n const spotlight = getSpotlightConfig(options.spotlight);\n const tracesSampleRate = getTracesSampleRate(options.tracesSampleRate);\n\n const mergedOptions = {\n ...options,\n dsn: options.dsn ?? process.env.SENTRY_DSN,\n environment: options.environment ?? process.env.SENTRY_ENVIRONMENT,\n sendClientReports: options.sendClientReports ?? true,\n transport: options.transport ?? makeNodeTransport,\n stackParser: stackParserFromStackParserOptions(options.stackParser || defaultStackParser),\n release,\n tracesSampleRate,\n spotlight,\n debug: envToBool(options.debug ?? process.env.SENTRY_DEBUG),\n };\n\n const integrations = options.integrations;\n const defaultIntegrations = options.defaultIntegrations ?? getDefaultIntegrationsImpl(mergedOptions);\n\n const resolvedIntegrations = getIntegrationsToSetup({\n defaultIntegrations,\n integrations,\n });\n\n if (mergedOptions.traceLifecycle === 'stream' && !resolvedIntegrations.some(i => i.name === 'SpanStreaming')) {\n resolvedIntegrations.push(spanStreamingIntegration());\n }\n\n return {\n ...mergedOptions,\n integrations: resolvedIntegrations,\n };\n}\n\nfunction getRelease(release: NodeOptions['release']): string | undefined {\n if (release !== undefined) {\n return release;\n }\n\n const detectedRelease = getSentryRelease();\n if (detectedRelease !== undefined) {\n return detectedRelease;\n }\n\n return undefined;\n}\n\nfunction getTracesSampleRate(tracesSampleRate: NodeOptions['tracesSampleRate']): number | undefined {\n if (tracesSampleRate !== undefined) {\n return tracesSampleRate;\n }\n\n const sampleRateFromEnv = process.env.SENTRY_TRACES_SAMPLE_RATE;\n if (!sampleRateFromEnv) {\n return undefined;\n }\n\n const parsed = parseFloat(sampleRateFromEnv);\n return isFinite(parsed) ? parsed : undefined;\n}\n\n/**\n * Update scope and propagation context based on environmental variables.\n *\n * See https://github.com/getsentry/rfcs/blob/main/text/0071-continue-trace-over-process-boundaries.md\n * for more details.\n */\nfunction updateScopeFromEnvVariables(): void {\n if (envToBool(process.env.SENTRY_USE_ENVIRONMENT) !== false) {\n const sentryTraceEnv = process.env.SENTRY_TRACE;\n const baggageEnv = process.env.SENTRY_BAGGAGE;\n const propagationContext = propagationContextFromHeaders(sentryTraceEnv, baggageEnv);\n getCurrentScope().setPropagationContext(propagationContext);\n }\n}\n"],"names":["eventFiltersIntegration","functionToStringIntegration","linkedErrorsIntegration","requestDataIntegration","systemErrorIntegration","consoleIntegration","httpIntegration","nativeNodeFetchIntegration","onUncaughtExceptionIntegration","onUnhandledRejectionIntegration","contextLinesIntegration","localVariablesIntegration","nodeContextIntegration","childProcessIntegration","processSessionIntegration","modulesIntegration","DEBUG_BUILD","debug","consoleSandbox","setAsyncLocalStorageAsyncContextStrategy","getCurrentScope","SPOTLIGHT_INTEGRATION_NAME","spotlightIntegration","applySdkMetadata","client","LightNodeClient","isCjs","getSpotlightConfig","makeNodeTransport","stackParserFromStackParserOptions","defaultStackParser","envToBool","getIntegrationsToSetup","spanStreamingIntegration","getSentryRelease","propagationContextFromHeaders"],"mappings":"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"} |
@@ -6,19 +6,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| /** | ||
| * Additional metadata to capture the log with. | ||
| */ | ||
| /** | ||
| * Capture a log with the given level. | ||
| * | ||
| * @param level - The level of the log. | ||
| * @param message - The message to log. | ||
| * @param attributes - Arbitrary structured data that stores information about the log - e.g., userId: 100. | ||
| */ | ||
| function captureLog(level, ...args) { | ||
| const [messageOrMessageTemplate, paramsOrAttributes, maybeAttributesOrMetadata, maybeMetadata] = args; | ||
| if (Array.isArray(paramsOrAttributes)) { | ||
| // type-casting here because from the type definitions we know that `maybeAttributesOrMetadata` is an attributes object (or undefined) | ||
| const attributes = { ...(maybeAttributesOrMetadata ) }; | ||
| attributes['sentry.message.template'] = messageOrMessageTemplate; | ||
| const attributes = { ...maybeAttributesOrMetadata }; | ||
| attributes["sentry.message.template"] = messageOrMessageTemplate; | ||
| paramsOrAttributes.forEach((param, index) => { | ||
@@ -33,3 +21,3 @@ attributes[`sentry.message.parameter.${index}`] = param; | ||
| // type-casting here because from the type definitions we know that `maybeAttributesOrMetadata` is a metadata object (or undefined) | ||
| (maybeAttributesOrMetadata )?.scope ?? maybeMetadata?.scope, | ||
| maybeAttributesOrMetadata?.scope ?? maybeMetadata?.scope | ||
| ); | ||
@@ -36,0 +24,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"capture.js","sources":["../../../src/logs/capture.ts"],"sourcesContent":["import { format } from 'node:util';\nimport type { Log, LogSeverityLevel, ParameterizedString, Scope } from '@sentry/core';\nimport { _INTERNAL_captureLog } from '@sentry/core';\n\n/**\n * Additional metadata to capture the log with.\n */\ninterface CaptureLogMetadata {\n scope?: Scope;\n}\n\ntype CaptureLogArgWithTemplate = [\n messageTemplate: string,\n messageParams: Array<unknown>,\n attributes?: Log['attributes'],\n metadata?: CaptureLogMetadata,\n];\n\ntype CaptureLogArgWithoutTemplate = [\n message: ParameterizedString,\n attributes?: Log['attributes'],\n metadata?: CaptureLogMetadata,\n];\n\nexport type CaptureLogArgs = CaptureLogArgWithTemplate | CaptureLogArgWithoutTemplate;\n\n/**\n * Capture a log with the given level.\n *\n * @param level - The level of the log.\n * @param message - The message to log.\n * @param attributes - Arbitrary structured data that stores information about the log - e.g., userId: 100.\n */\nexport function captureLog(level: LogSeverityLevel, ...args: CaptureLogArgs): void {\n const [messageOrMessageTemplate, paramsOrAttributes, maybeAttributesOrMetadata, maybeMetadata] = args;\n if (Array.isArray(paramsOrAttributes)) {\n // type-casting here because from the type definitions we know that `maybeAttributesOrMetadata` is an attributes object (or undefined)\n const attributes = { ...(maybeAttributesOrMetadata as Log['attributes'] | undefined) };\n attributes['sentry.message.template'] = messageOrMessageTemplate;\n paramsOrAttributes.forEach((param, index) => {\n attributes[`sentry.message.parameter.${index}`] = param;\n });\n const message = format(messageOrMessageTemplate, ...paramsOrAttributes);\n _INTERNAL_captureLog({ level, message, attributes }, maybeMetadata?.scope);\n } else {\n _INTERNAL_captureLog(\n { level, message: messageOrMessageTemplate, attributes: paramsOrAttributes },\n // type-casting here because from the type definitions we know that `maybeAttributesOrMetadata` is a metadata object (or undefined)\n (maybeAttributesOrMetadata as CaptureLogMetadata | undefined)?.scope ?? maybeMetadata?.scope,\n );\n }\n}\n"],"names":["format","_INTERNAL_captureLog"],"mappings":";;;;;AAIA;AACA;AACA;;AAoBA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,UAAU,CAAC,KAAK,EAAoB,GAAG,IAAI,EAAwB;AACnF,EAAE,MAAM,CAAC,wBAAwB,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,aAAa,CAAA,GAAI,IAAI;AACvG,EAAE,IAAI,KAAK,CAAC,OAAO,CAAC,kBAAkB,CAAC,EAAE;AACzC;AACA,IAAI,MAAM,aAAa,EAAE,IAAI,yBAAA,IAA6D;AAC1F,IAAI,UAAU,CAAC,yBAAyB,CAAA,GAAI,wBAAwB;AACpE,IAAI,kBAAkB,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK;AACjD,MAAM,UAAU,CAAC,CAAC,yBAAyB,EAAE,KAAK,CAAC,CAAA,CAAA,GAAA,KAAA;AACA,IAAA,CAAA,CAAA;AACA,IAAA,MAAA,OAAA,GAAAA,WAAA,CAAA,wBAAA,EAAA,GAAA,kBAAA,CAAA;AACA,IAAAC,yBAAA,CAAA,EAAA,KAAA,EAAA,OAAA,EAAA,UAAA,EAAA,EAAA,aAAA,EAAA,KAAA,CAAA;AACA,EAAA,CAAA,MAAA;AACA,IAAAA,yBAAA;AACA,MAAA,EAAA,KAAA,EAAA,OAAA,EAAA,wBAAA,EAAA,UAAA,EAAA,kBAAA,EAAA;AACA;AACA,MAAA,CAAA,yBAAA,IAAA,KAAA,IAAA,aAAA,EAAA,KAAA;AACA,KAAA;AACA,EAAA;AACA;;;;"} | ||
| {"version":3,"file":"capture.js","sources":["../../../src/logs/capture.ts"],"sourcesContent":["import { format } from 'node:util';\nimport type { Log, LogSeverityLevel, ParameterizedString, Scope } from '@sentry/core';\nimport { _INTERNAL_captureLog } from '@sentry/core';\n\n/**\n * Additional metadata to capture the log with.\n */\ninterface CaptureLogMetadata {\n scope?: Scope;\n}\n\ntype CaptureLogArgWithTemplate = [\n messageTemplate: string,\n messageParams: Array<unknown>,\n attributes?: Log['attributes'],\n metadata?: CaptureLogMetadata,\n];\n\ntype CaptureLogArgWithoutTemplate = [\n message: ParameterizedString,\n attributes?: Log['attributes'],\n metadata?: CaptureLogMetadata,\n];\n\nexport type CaptureLogArgs = CaptureLogArgWithTemplate | CaptureLogArgWithoutTemplate;\n\n/**\n * Capture a log with the given level.\n *\n * @param level - The level of the log.\n * @param message - The message to log.\n * @param attributes - Arbitrary structured data that stores information about the log - e.g., userId: 100.\n */\nexport function captureLog(level: LogSeverityLevel, ...args: CaptureLogArgs): void {\n const [messageOrMessageTemplate, paramsOrAttributes, maybeAttributesOrMetadata, maybeMetadata] = args;\n if (Array.isArray(paramsOrAttributes)) {\n // type-casting here because from the type definitions we know that `maybeAttributesOrMetadata` is an attributes object (or undefined)\n const attributes = { ...(maybeAttributesOrMetadata as Log['attributes'] | undefined) };\n attributes['sentry.message.template'] = messageOrMessageTemplate;\n paramsOrAttributes.forEach((param, index) => {\n attributes[`sentry.message.parameter.${index}`] = param;\n });\n const message = format(messageOrMessageTemplate, ...paramsOrAttributes);\n _INTERNAL_captureLog({ level, message, attributes }, maybeMetadata?.scope);\n } else {\n _INTERNAL_captureLog(\n { level, message: messageOrMessageTemplate, attributes: paramsOrAttributes },\n // type-casting here because from the type definitions we know that `maybeAttributesOrMetadata` is a metadata object (or undefined)\n (maybeAttributesOrMetadata as CaptureLogMetadata | undefined)?.scope ?? maybeMetadata?.scope,\n );\n }\n}\n"],"names":["format","_INTERNAL_captureLog"],"mappings":";;;;;AAiCO,SAAS,UAAA,CAAW,UAA4B,IAAA,EAA4B;AACjF,EAAA,MAAM,CAAC,wBAAA,EAA0B,kBAAA,EAAoB,yBAAA,EAA2B,aAAa,CAAA,GAAI,IAAA;AACjG,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,kBAAkB,CAAA,EAAG;AAErC,IAAA,MAAM,UAAA,GAAa,EAAE,GAAI,yBAAA,EAA4D;AACrF,IAAA,UAAA,CAAW,yBAAyB,CAAA,GAAI,wBAAA;AACxC,IAAA,kBAAA,CAAmB,OAAA,CAAQ,CAAC,KAAA,EAAO,KAAA,KAAU;AAC3C,MAAA,UAAA,CAAW,CAAA,yBAAA,EAA4B,KAAK,CAAA,CAAE,CAAA,GAAI,KAAA;AAAA,IACpD,CAAC,CAAA;AACD,IAAA,MAAM,OAAA,GAAUA,WAAA,CAAO,wBAAA,EAA0B,GAAG,kBAAkB,CAAA;AACtE,IAAAC,yBAAA,CAAqB,EAAE,KAAA,EAAO,OAAA,EAAS,UAAA,EAAW,EAAG,eAAe,KAAK,CAAA;AAAA,EAC3E,CAAA,MAAO;AACL,IAAAA,yBAAA;AAAA,MACE,EAAE,KAAA,EAAO,OAAA,EAAS,wBAAA,EAA0B,YAAY,kBAAA,EAAmB;AAAA;AAAA,MAE1E,yBAAA,EAA8D,SAAS,aAAA,EAAe;AAAA,KACzF;AAAA,EACF;AACF;;;;"} |
@@ -6,165 +6,19 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| /** | ||
| * @summary Capture a log with the `trace` level. Requires the `enableLogs` option to be enabled. | ||
| * | ||
| * You can either pass a message and attributes or a message template, params and attributes. | ||
| * | ||
| * @example | ||
| * | ||
| * ``` | ||
| * Sentry.logger.trace('Starting database connection', { | ||
| * database: 'users', | ||
| * connectionId: 'conn_123' | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @example With template strings | ||
| * | ||
| * ``` | ||
| * Sentry.logger.trace('Database connection %s established for %s', | ||
| * ['successful', 'users'], | ||
| * { connectionId: 'conn_123' } | ||
| * ); | ||
| * ``` | ||
| */ | ||
| function trace(...args) { | ||
| capture.captureLog('trace', ...args); | ||
| capture.captureLog("trace", ...args); | ||
| } | ||
| /** | ||
| * @summary Capture a log with the `debug` level. Requires the `enableLogs` option to be enabled. | ||
| * | ||
| * You can either pass a message and attributes or a message template, params and attributes. | ||
| * | ||
| * @example | ||
| * | ||
| * ``` | ||
| * Sentry.logger.debug('Cache miss for user profile', { | ||
| * userId: 'user_123', | ||
| * cacheKey: 'profile:user_123' | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @example With template strings | ||
| * | ||
| * ``` | ||
| * Sentry.logger.debug('Cache %s for %s: %s', | ||
| * ['miss', 'user profile', 'key not found'], | ||
| * { userId: 'user_123' } | ||
| * ); | ||
| * ``` | ||
| */ | ||
| function debug(...args) { | ||
| capture.captureLog('debug', ...args); | ||
| capture.captureLog("debug", ...args); | ||
| } | ||
| /** | ||
| * @summary Capture a log with the `info` level. Requires the `enableLogs` option to be enabled. | ||
| * | ||
| * You can either pass a message and attributes or a message template, params and attributes. | ||
| * | ||
| * @example | ||
| * | ||
| * ``` | ||
| * Sentry.logger.info('User profile updated', { | ||
| * userId: 'user_123', | ||
| * updatedFields: ['email', 'preferences'] | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @example With template strings | ||
| * | ||
| * ``` | ||
| * Sentry.logger.info('User %s updated their %s', | ||
| * ['John Doe', 'profile settings'], | ||
| * { userId: 'user_123' } | ||
| * ); | ||
| * ``` | ||
| */ | ||
| function info(...args) { | ||
| capture.captureLog('info', ...args); | ||
| capture.captureLog("info", ...args); | ||
| } | ||
| /** | ||
| * @summary Capture a log with the `warn` level. Requires the `enableLogs` option to be enabled. | ||
| * | ||
| * You can either pass a message and attributes or a message template, params and attributes. | ||
| * | ||
| * @example | ||
| * | ||
| * ``` | ||
| * Sentry.logger.warn('Rate limit approaching', { | ||
| * endpoint: '/api/users', | ||
| * currentRate: '95/100', | ||
| * resetTime: '2024-03-20T10:00:00Z' | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @example With template strings | ||
| * | ||
| * ``` | ||
| * Sentry.logger.warn('Rate limit %s for %s: %s', | ||
| * ['approaching', '/api/users', '95/100 requests'], | ||
| * { resetTime: '2024-03-20T10:00:00Z' } | ||
| * ); | ||
| * ``` | ||
| */ | ||
| function warn(...args) { | ||
| capture.captureLog('warn', ...args); | ||
| capture.captureLog("warn", ...args); | ||
| } | ||
| /** | ||
| * @summary Capture a log with the `error` level. Requires the `enableLogs` option to be enabled. | ||
| * | ||
| * You can either pass a message and attributes or a message template, params and attributes. | ||
| * | ||
| * @example | ||
| * | ||
| * ``` | ||
| * Sentry.logger.error('Failed to process payment', { | ||
| * orderId: 'order_123', | ||
| * errorCode: 'PAYMENT_FAILED', | ||
| * amount: 99.99 | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @example With template strings | ||
| * | ||
| * ``` | ||
| * Sentry.logger.error('Payment processing failed for order %s: %s', | ||
| * ['order_123', 'insufficient funds'], | ||
| * { amount: 99.99 } | ||
| * ); | ||
| * ``` | ||
| */ | ||
| function error(...args) { | ||
| capture.captureLog('error', ...args); | ||
| capture.captureLog("error", ...args); | ||
| } | ||
| /** | ||
| * @summary Capture a log with the `fatal` level. Requires the `enableLogs` option to be enabled. | ||
| * | ||
| * You can either pass a message and attributes or a message template, params and attributes. | ||
| * | ||
| * @example | ||
| * | ||
| * ``` | ||
| * Sentry.logger.fatal('Database connection pool exhausted', { | ||
| * database: 'users', | ||
| * activeConnections: 100, | ||
| * maxConnections: 100 | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @example With template strings | ||
| * | ||
| * ``` | ||
| * Sentry.logger.fatal('Database %s: %s connections active', | ||
| * ['connection pool exhausted', '100/100'], | ||
| * { database: 'users' } | ||
| * ); | ||
| * ``` | ||
| */ | ||
| function fatal(...args) { | ||
| capture.captureLog('fatal', ...args); | ||
| capture.captureLog("fatal", ...args); | ||
| } | ||
@@ -171,0 +25,0 @@ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"exports.js","sources":["../../../src/logs/exports.ts"],"sourcesContent":["import { captureLog, type CaptureLogArgs } from './capture';\n\n/**\n * @summary Capture a log with the `trace` level. Requires the `enableLogs` option to be enabled.\n *\n * You can either pass a message and attributes or a message template, params and attributes.\n *\n * @example\n *\n * ```\n * Sentry.logger.trace('Starting database connection', {\n * database: 'users',\n * connectionId: 'conn_123'\n * });\n * ```\n *\n * @example With template strings\n *\n * ```\n * Sentry.logger.trace('Database connection %s established for %s',\n * ['successful', 'users'],\n * { connectionId: 'conn_123' }\n * );\n * ```\n */\nexport function trace(...args: CaptureLogArgs): void {\n captureLog('trace', ...args);\n}\n\n/**\n * @summary Capture a log with the `debug` level. Requires the `enableLogs` option to be enabled.\n *\n * You can either pass a message and attributes or a message template, params and attributes.\n *\n * @example\n *\n * ```\n * Sentry.logger.debug('Cache miss for user profile', {\n * userId: 'user_123',\n * cacheKey: 'profile:user_123'\n * });\n * ```\n *\n * @example With template strings\n *\n * ```\n * Sentry.logger.debug('Cache %s for %s: %s',\n * ['miss', 'user profile', 'key not found'],\n * { userId: 'user_123' }\n * );\n * ```\n */\nexport function debug(...args: CaptureLogArgs): void {\n captureLog('debug', ...args);\n}\n\n/**\n * @summary Capture a log with the `info` level. Requires the `enableLogs` option to be enabled.\n *\n * You can either pass a message and attributes or a message template, params and attributes.\n *\n * @example\n *\n * ```\n * Sentry.logger.info('User profile updated', {\n * userId: 'user_123',\n * updatedFields: ['email', 'preferences']\n * });\n * ```\n *\n * @example With template strings\n *\n * ```\n * Sentry.logger.info('User %s updated their %s',\n * ['John Doe', 'profile settings'],\n * { userId: 'user_123' }\n * );\n * ```\n */\nexport function info(...args: CaptureLogArgs): void {\n captureLog('info', ...args);\n}\n\n/**\n * @summary Capture a log with the `warn` level. Requires the `enableLogs` option to be enabled.\n *\n * You can either pass a message and attributes or a message template, params and attributes.\n *\n * @example\n *\n * ```\n * Sentry.logger.warn('Rate limit approaching', {\n * endpoint: '/api/users',\n * currentRate: '95/100',\n * resetTime: '2024-03-20T10:00:00Z'\n * });\n * ```\n *\n * @example With template strings\n *\n * ```\n * Sentry.logger.warn('Rate limit %s for %s: %s',\n * ['approaching', '/api/users', '95/100 requests'],\n * { resetTime: '2024-03-20T10:00:00Z' }\n * );\n * ```\n */\nexport function warn(...args: CaptureLogArgs): void {\n captureLog('warn', ...args);\n}\n\n/**\n * @summary Capture a log with the `error` level. Requires the `enableLogs` option to be enabled.\n *\n * You can either pass a message and attributes or a message template, params and attributes.\n *\n * @example\n *\n * ```\n * Sentry.logger.error('Failed to process payment', {\n * orderId: 'order_123',\n * errorCode: 'PAYMENT_FAILED',\n * amount: 99.99\n * });\n * ```\n *\n * @example With template strings\n *\n * ```\n * Sentry.logger.error('Payment processing failed for order %s: %s',\n * ['order_123', 'insufficient funds'],\n * { amount: 99.99 }\n * );\n * ```\n */\nexport function error(...args: CaptureLogArgs): void {\n captureLog('error', ...args);\n}\n\n/**\n * @summary Capture a log with the `fatal` level. Requires the `enableLogs` option to be enabled.\n *\n * You can either pass a message and attributes or a message template, params and attributes.\n *\n * @example\n *\n * ```\n * Sentry.logger.fatal('Database connection pool exhausted', {\n * database: 'users',\n * activeConnections: 100,\n * maxConnections: 100\n * });\n * ```\n *\n * @example With template strings\n *\n * ```\n * Sentry.logger.fatal('Database %s: %s connections active',\n * ['connection pool exhausted', '100/100'],\n * { database: 'users' }\n * );\n * ```\n */\nexport function fatal(...args: CaptureLogArgs): void {\n captureLog('fatal', ...args);\n}\n\nexport { fmt } from '@sentry/core';\n"],"names":["captureLog"],"mappings":";;;;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,KAAK,CAAC,GAAG,IAAI,EAAwB;AACrD,EAAEA,kBAAU,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC;AAC9B;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,KAAK,CAAC,GAAG,IAAI,EAAwB;AACrD,EAAEA,kBAAU,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC;AAC9B;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,IAAI,CAAC,GAAG,IAAI,EAAwB;AACpD,EAAEA,kBAAU,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC;AAC7B;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,IAAI,CAAC,GAAG,IAAI,EAAwB;AACpD,EAAEA,kBAAU,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC;AAC7B;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,KAAK,CAAC,GAAG,IAAI,EAAwB;AACrD,EAAEA,kBAAU,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC;AAC9B;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,KAAK,CAAC,GAAG,IAAI,EAAwB;AACrD,EAAEA,kBAAU,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC;AAC9B;;;;;;;;;;"} | ||
| {"version":3,"file":"exports.js","sources":["../../../src/logs/exports.ts"],"sourcesContent":["import { captureLog, type CaptureLogArgs } from './capture';\n\n/**\n * @summary Capture a log with the `trace` level. Requires the `enableLogs` option to be enabled.\n *\n * You can either pass a message and attributes or a message template, params and attributes.\n *\n * @example\n *\n * ```\n * Sentry.logger.trace('Starting database connection', {\n * database: 'users',\n * connectionId: 'conn_123'\n * });\n * ```\n *\n * @example With template strings\n *\n * ```\n * Sentry.logger.trace('Database connection %s established for %s',\n * ['successful', 'users'],\n * { connectionId: 'conn_123' }\n * );\n * ```\n */\nexport function trace(...args: CaptureLogArgs): void {\n captureLog('trace', ...args);\n}\n\n/**\n * @summary Capture a log with the `debug` level. Requires the `enableLogs` option to be enabled.\n *\n * You can either pass a message and attributes or a message template, params and attributes.\n *\n * @example\n *\n * ```\n * Sentry.logger.debug('Cache miss for user profile', {\n * userId: 'user_123',\n * cacheKey: 'profile:user_123'\n * });\n * ```\n *\n * @example With template strings\n *\n * ```\n * Sentry.logger.debug('Cache %s for %s: %s',\n * ['miss', 'user profile', 'key not found'],\n * { userId: 'user_123' }\n * );\n * ```\n */\nexport function debug(...args: CaptureLogArgs): void {\n captureLog('debug', ...args);\n}\n\n/**\n * @summary Capture a log with the `info` level. Requires the `enableLogs` option to be enabled.\n *\n * You can either pass a message and attributes or a message template, params and attributes.\n *\n * @example\n *\n * ```\n * Sentry.logger.info('User profile updated', {\n * userId: 'user_123',\n * updatedFields: ['email', 'preferences']\n * });\n * ```\n *\n * @example With template strings\n *\n * ```\n * Sentry.logger.info('User %s updated their %s',\n * ['John Doe', 'profile settings'],\n * { userId: 'user_123' }\n * );\n * ```\n */\nexport function info(...args: CaptureLogArgs): void {\n captureLog('info', ...args);\n}\n\n/**\n * @summary Capture a log with the `warn` level. Requires the `enableLogs` option to be enabled.\n *\n * You can either pass a message and attributes or a message template, params and attributes.\n *\n * @example\n *\n * ```\n * Sentry.logger.warn('Rate limit approaching', {\n * endpoint: '/api/users',\n * currentRate: '95/100',\n * resetTime: '2024-03-20T10:00:00Z'\n * });\n * ```\n *\n * @example With template strings\n *\n * ```\n * Sentry.logger.warn('Rate limit %s for %s: %s',\n * ['approaching', '/api/users', '95/100 requests'],\n * { resetTime: '2024-03-20T10:00:00Z' }\n * );\n * ```\n */\nexport function warn(...args: CaptureLogArgs): void {\n captureLog('warn', ...args);\n}\n\n/**\n * @summary Capture a log with the `error` level. Requires the `enableLogs` option to be enabled.\n *\n * You can either pass a message and attributes or a message template, params and attributes.\n *\n * @example\n *\n * ```\n * Sentry.logger.error('Failed to process payment', {\n * orderId: 'order_123',\n * errorCode: 'PAYMENT_FAILED',\n * amount: 99.99\n * });\n * ```\n *\n * @example With template strings\n *\n * ```\n * Sentry.logger.error('Payment processing failed for order %s: %s',\n * ['order_123', 'insufficient funds'],\n * { amount: 99.99 }\n * );\n * ```\n */\nexport function error(...args: CaptureLogArgs): void {\n captureLog('error', ...args);\n}\n\n/**\n * @summary Capture a log with the `fatal` level. Requires the `enableLogs` option to be enabled.\n *\n * You can either pass a message and attributes or a message template, params and attributes.\n *\n * @example\n *\n * ```\n * Sentry.logger.fatal('Database connection pool exhausted', {\n * database: 'users',\n * activeConnections: 100,\n * maxConnections: 100\n * });\n * ```\n *\n * @example With template strings\n *\n * ```\n * Sentry.logger.fatal('Database %s: %s connections active',\n * ['connection pool exhausted', '100/100'],\n * { database: 'users' }\n * );\n * ```\n */\nexport function fatal(...args: CaptureLogArgs): void {\n captureLog('fatal', ...args);\n}\n\nexport { fmt } from '@sentry/core';\n"],"names":["captureLog"],"mappings":";;;;;AAyBO,SAAS,SAAS,IAAA,EAA4B;AACnD,EAAAA,kBAAA,CAAW,OAAA,EAAS,GAAG,IAAI,CAAA;AAC7B;AAyBO,SAAS,SAAS,IAAA,EAA4B;AACnD,EAAAA,kBAAA,CAAW,OAAA,EAAS,GAAG,IAAI,CAAA;AAC7B;AAyBO,SAAS,QAAQ,IAAA,EAA4B;AAClD,EAAAA,kBAAA,CAAW,MAAA,EAAQ,GAAG,IAAI,CAAA;AAC5B;AA0BO,SAAS,QAAQ,IAAA,EAA4B;AAClD,EAAAA,kBAAA,CAAW,MAAA,EAAQ,GAAG,IAAI,CAAA;AAC5B;AA0BO,SAAS,SAAS,IAAA,EAA4B;AACnD,EAAAA,kBAAA,CAAW,OAAA,EAAS,GAAG,IAAI,CAAA;AAC7B;AA0BO,SAAS,SAAS,IAAA,EAA4B;AACnD,EAAAA,kBAAA,CAAW,OAAA,EAAS,GAAG,IAAI,CAAA;AAC7B;;;;;;;;;;"} |
@@ -5,3 +5,3 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const NODE_VERSION = core.parseSemver(process.versions.node) ; | ||
| const NODE_VERSION = core.parseSemver(process.versions.node); | ||
| const NODE_MAJOR = NODE_VERSION.major; | ||
@@ -8,0 +8,0 @@ const NODE_MINOR = NODE_VERSION.minor; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"nodeVersion.js","sources":["../../src/nodeVersion.ts"],"sourcesContent":["import { parseSemver } from '@sentry/core';\n\nexport const NODE_VERSION = parseSemver(process.versions.node) as { major: number; minor: number; patch: number };\nexport const NODE_MAJOR = NODE_VERSION.major;\nexport const NODE_MINOR = NODE_VERSION.minor;\n"],"names":["parseSemver"],"mappings":";;;;AAEO,MAAM,YAAA,GAAeA,gBAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAA;AACtD,MAAM,UAAA,GAAa,YAAY,CAAC;AAChC,MAAM,UAAA,GAAa,YAAY,CAAC;;;;;;"} | ||
| {"version":3,"file":"nodeVersion.js","sources":["../../src/nodeVersion.ts"],"sourcesContent":["import { parseSemver } from '@sentry/core';\n\nexport const NODE_VERSION = parseSemver(process.versions.node) as { major: number; minor: number; patch: number };\nexport const NODE_MAJOR = NODE_VERSION.major;\nexport const NODE_MINOR = NODE_VERSION.minor;\n"],"names":["parseSemver"],"mappings":";;;;AAEO,MAAM,YAAA,GAAeA,gBAAA,CAAY,OAAA,CAAQ,QAAA,CAAS,IAAI;AACtD,MAAM,aAAa,YAAA,CAAa;AAChC,MAAM,aAAa,YAAA,CAAa;;;;;;"} |
@@ -5,6 +5,2 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| /** | ||
| * This is a custom ContextManager for OpenTelemetry & Sentry. | ||
| * It ensures that we create a new hub per context, so that the OTEL Context & the Sentry Scopes are always in sync. | ||
| */ | ||
| const SentryContextManager = opentelemetry.SentryAsyncLocalStorageContextManager; | ||
@@ -11,0 +7,0 @@ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"contextManager.js","sources":["../../../src/otel/contextManager.ts"],"sourcesContent":["import { SentryAsyncLocalStorageContextManager } from '@sentry/opentelemetry';\n\n/**\n * This is a custom ContextManager for OpenTelemetry & Sentry.\n * It ensures that we create a new hub per context, so that the OTEL Context & the Sentry Scopes are always in sync.\n */\nexport const SentryContextManager = SentryAsyncLocalStorageContextManager;\n"],"names":["SentryAsyncLocalStorageContextManager"],"mappings":";;;;AAEA;AACA;AACA;AACA;AACO,MAAM,oBAAA,GAAuBA;;;;"} | ||
| {"version":3,"file":"contextManager.js","sources":["../../../src/otel/contextManager.ts"],"sourcesContent":["import { SentryAsyncLocalStorageContextManager } from '@sentry/opentelemetry';\n\n/**\n * This is a custom ContextManager for OpenTelemetry & Sentry.\n * It ensures that we create a new hub per context, so that the OTEL Context & the Sentry Scopes are always in sync.\n */\nexport const SentryContextManager = SentryAsyncLocalStorageContextManager;\n"],"names":["SentryAsyncLocalStorageContextManager"],"mappings":";;;;AAMO,MAAM,oBAAA,GAAuBA;;;;"} |
@@ -5,37 +5,18 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| /** Exported only for tests. */ | ||
| const INSTRUMENTED = {}; | ||
| /** | ||
| * Instrument an OpenTelemetry instrumentation once. | ||
| * This will skip running instrumentation again if it was already instrumented. | ||
| */ | ||
| function generateInstrumentOnce( | ||
| name, | ||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| creatorOrClass, | ||
| optionsCallback, | ||
| ) { | ||
| function generateInstrumentOnce(name, creatorOrClass, optionsCallback) { | ||
| if (optionsCallback) { | ||
| return _generateInstrumentOnceWithOptions( | ||
| name, | ||
| creatorOrClass , | ||
| optionsCallback, | ||
| creatorOrClass, | ||
| optionsCallback | ||
| ); | ||
| } | ||
| return _generateInstrumentOnce(name, creatorOrClass ); | ||
| return _generateInstrumentOnce(name, creatorOrClass); | ||
| } | ||
| // The plain version without handling of options | ||
| // Should not be used with custom options that are mutated in the creator! | ||
| function _generateInstrumentOnce( | ||
| name, | ||
| creator, | ||
| ) { | ||
| function _generateInstrumentOnce(name, creator) { | ||
| return Object.assign( | ||
| (options) => { | ||
| const instrumented = INSTRUMENTED[name] ; | ||
| const instrumented = INSTRUMENTED[name]; | ||
| if (instrumented) { | ||
| // If options are provided, ensure we update them | ||
| if (options) { | ||
@@ -46,72 +27,41 @@ instrumented.setConfig(options); | ||
| } | ||
| const instrumentation$1 = creator(options); | ||
| INSTRUMENTED[name] = instrumentation$1; | ||
| instrumentation.registerInstrumentations({ | ||
| instrumentations: [instrumentation$1], | ||
| instrumentations: [instrumentation$1] | ||
| }); | ||
| return instrumentation$1; | ||
| }, | ||
| { id: name }, | ||
| { id: name } | ||
| ); | ||
| } | ||
| // This version handles options properly | ||
| function _generateInstrumentOnceWithOptions | ||
| ( | ||
| name, | ||
| instrumentationClass, | ||
| optionsCallback, | ||
| ) { | ||
| function _generateInstrumentOnceWithOptions(name, instrumentationClass, optionsCallback) { | ||
| return Object.assign( | ||
| (_options) => { | ||
| const options = optionsCallback(_options); | ||
| const instrumented = INSTRUMENTED[name] ; | ||
| const instrumented = INSTRUMENTED[name]; | ||
| if (instrumented) { | ||
| // Ensure we update options | ||
| instrumented.setConfig(options); | ||
| return instrumented; | ||
| } | ||
| const instrumentation$1 = new instrumentationClass(options) ; | ||
| const instrumentation$1 = new instrumentationClass(options); | ||
| INSTRUMENTED[name] = instrumentation$1; | ||
| instrumentation.registerInstrumentations({ | ||
| instrumentations: [instrumentation$1], | ||
| instrumentations: [instrumentation$1] | ||
| }); | ||
| return instrumentation$1; | ||
| }, | ||
| { id: name }, | ||
| { id: name } | ||
| ); | ||
| } | ||
| /** | ||
| * Ensure a given callback is called when the instrumentation is actually wrapping something. | ||
| * This can be used to ensure some logic is only called when the instrumentation is actually active. | ||
| * | ||
| * This function returns a function that can be invoked with a callback. | ||
| * This callback will either be invoked immediately | ||
| * (e.g. if the instrumentation was already wrapped, or if _wrap could not be patched), | ||
| * or once the instrumentation is actually wrapping something. | ||
| * | ||
| * Make sure to call this function right after adding the instrumentation, otherwise it may be too late! | ||
| * The returned callback can be used any time, and also multiple times. | ||
| */ | ||
| function instrumentWhenWrapped(instrumentation) { | ||
| let isWrapped = false; | ||
| let callbacks = []; | ||
| if (!hasWrap(instrumentation)) { | ||
| isWrapped = true; | ||
| } else { | ||
| const originalWrap = instrumentation['_wrap']; | ||
| instrumentation['_wrap'] = (...args) => { | ||
| const originalWrap = instrumentation["_wrap"]; | ||
| instrumentation["_wrap"] = (...args) => { | ||
| isWrapped = true; | ||
| callbacks.forEach(callback => callback()); | ||
| callbacks.forEach((callback) => callback()); | ||
| callbacks = []; | ||
@@ -121,3 +71,2 @@ return originalWrap(...args); | ||
| } | ||
| const registerCallback = (callback) => { | ||
@@ -130,10 +79,6 @@ if (isWrapped) { | ||
| }; | ||
| return registerCallback; | ||
| } | ||
| function hasWrap( | ||
| instrumentation, | ||
| ) { | ||
| return typeof (instrumentation )['_wrap'] === 'function'; | ||
| function hasWrap(instrumentation) { | ||
| return typeof instrumentation["_wrap"] === "function"; | ||
| } | ||
@@ -140,0 +85,0 @@ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"instrument.js","sources":["../../../src/otel/instrument.ts"],"sourcesContent":["import { type Instrumentation, registerInstrumentations } from '@opentelemetry/instrumentation';\n\n/** Exported only for tests. */\nexport const INSTRUMENTED: Record<string, Instrumentation> = {};\n\nexport function generateInstrumentOnce<\n Options,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n InstrumentationClass extends new (...args: any[]) => Instrumentation,\n>(\n name: string,\n instrumentationClass: InstrumentationClass,\n optionsCallback: (options: Options) => ConstructorParameters<InstrumentationClass>[0],\n): ((options: Options) => InstanceType<InstrumentationClass>) & { id: string };\nexport function generateInstrumentOnce<\n Options = unknown,\n InstrumentationInstance extends Instrumentation = Instrumentation,\n>(\n name: string,\n creator: (options?: Options) => InstrumentationInstance,\n): ((options?: Options) => InstrumentationInstance) & { id: string };\n/**\n * Instrument an OpenTelemetry instrumentation once.\n * This will skip running instrumentation again if it was already instrumented.\n */\nexport function generateInstrumentOnce<Options>(\n name: string,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n creatorOrClass: (new (...args: any[]) => Instrumentation) | ((options?: Options) => Instrumentation),\n optionsCallback?: (options: Options) => unknown,\n): ((options: Options) => Instrumentation) & { id: string } {\n if (optionsCallback) {\n return _generateInstrumentOnceWithOptions(\n name,\n creatorOrClass as new (...args: unknown[]) => Instrumentation,\n optionsCallback,\n );\n }\n\n return _generateInstrumentOnce(name, creatorOrClass as (options?: Options) => Instrumentation);\n}\n\n// The plain version without handling of options\n// Should not be used with custom options that are mutated in the creator!\nfunction _generateInstrumentOnce<Options = unknown, InstrumentationInstance extends Instrumentation = Instrumentation>(\n name: string,\n creator: (options?: Options) => InstrumentationInstance,\n): ((options?: Options) => InstrumentationInstance) & { id: string } {\n return Object.assign(\n (options?: Options) => {\n const instrumented = INSTRUMENTED[name] as InstrumentationInstance | undefined;\n if (instrumented) {\n // If options are provided, ensure we update them\n if (options) {\n instrumented.setConfig(options);\n }\n return instrumented;\n }\n\n const instrumentation = creator(options);\n INSTRUMENTED[name] = instrumentation;\n\n registerInstrumentations({\n instrumentations: [instrumentation],\n });\n\n return instrumentation;\n },\n { id: name },\n );\n}\n\n// This version handles options properly\nfunction _generateInstrumentOnceWithOptions<\n Options,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n InstrumentationClass extends new (...args: any[]) => Instrumentation,\n>(\n name: string,\n instrumentationClass: InstrumentationClass,\n optionsCallback: (options: Options) => ConstructorParameters<InstrumentationClass>[0],\n): ((options: Options) => InstanceType<InstrumentationClass>) & { id: string } {\n return Object.assign(\n (_options: Options) => {\n const options = optionsCallback(_options);\n\n const instrumented = INSTRUMENTED[name] as InstanceType<InstrumentationClass> | undefined;\n if (instrumented) {\n // Ensure we update options\n instrumented.setConfig(options);\n return instrumented;\n }\n\n const instrumentation = new instrumentationClass(options) as InstanceType<InstrumentationClass>;\n INSTRUMENTED[name] = instrumentation;\n\n registerInstrumentations({\n instrumentations: [instrumentation],\n });\n\n return instrumentation;\n },\n { id: name },\n );\n}\n\n/**\n * Ensure a given callback is called when the instrumentation is actually wrapping something.\n * This can be used to ensure some logic is only called when the instrumentation is actually active.\n *\n * This function returns a function that can be invoked with a callback.\n * This callback will either be invoked immediately\n * (e.g. if the instrumentation was already wrapped, or if _wrap could not be patched),\n * or once the instrumentation is actually wrapping something.\n *\n * Make sure to call this function right after adding the instrumentation, otherwise it may be too late!\n * The returned callback can be used any time, and also multiple times.\n */\nexport function instrumentWhenWrapped<T extends Instrumentation>(instrumentation: T): (callback: () => void) => void {\n let isWrapped = false;\n let callbacks: (() => void)[] = [];\n\n if (!hasWrap(instrumentation)) {\n isWrapped = true;\n } else {\n const originalWrap = instrumentation['_wrap'];\n\n instrumentation['_wrap'] = (...args: Parameters<typeof originalWrap>) => {\n isWrapped = true;\n callbacks.forEach(callback => callback());\n callbacks = [];\n return originalWrap(...args);\n };\n }\n\n const registerCallback = (callback: () => void): void => {\n if (isWrapped) {\n callback();\n } else {\n callbacks.push(callback);\n }\n };\n\n return registerCallback;\n}\n\nfunction hasWrap<T extends Instrumentation>(\n instrumentation: T,\n): instrumentation is T & { _wrap: (...args: unknown[]) => unknown } {\n return typeof (instrumentation as T & { _wrap?: (...args: unknown[]) => unknown })['_wrap'] === 'function';\n}\n"],"names":["instrumentation","registerInstrumentations"],"mappings":"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"} | ||
| {"version":3,"file":"instrument.js","sources":["../../../src/otel/instrument.ts"],"sourcesContent":["import { type Instrumentation, registerInstrumentations } from '@opentelemetry/instrumentation';\n\n/** Exported only for tests. */\nexport const INSTRUMENTED: Record<string, Instrumentation> = {};\n\nexport function generateInstrumentOnce<\n Options,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n InstrumentationClass extends new (...args: any[]) => Instrumentation,\n>(\n name: string,\n instrumentationClass: InstrumentationClass,\n optionsCallback: (options: Options) => ConstructorParameters<InstrumentationClass>[0],\n): ((options: Options) => InstanceType<InstrumentationClass>) & { id: string };\nexport function generateInstrumentOnce<\n Options = unknown,\n InstrumentationInstance extends Instrumentation = Instrumentation,\n>(\n name: string,\n creator: (options?: Options) => InstrumentationInstance,\n): ((options?: Options) => InstrumentationInstance) & { id: string };\n/**\n * Instrument an OpenTelemetry instrumentation once.\n * This will skip running instrumentation again if it was already instrumented.\n */\nexport function generateInstrumentOnce<Options>(\n name: string,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n creatorOrClass: (new (...args: any[]) => Instrumentation) | ((options?: Options) => Instrumentation),\n optionsCallback?: (options: Options) => unknown,\n): ((options: Options) => Instrumentation) & { id: string } {\n if (optionsCallback) {\n return _generateInstrumentOnceWithOptions(\n name,\n creatorOrClass as new (...args: unknown[]) => Instrumentation,\n optionsCallback,\n );\n }\n\n return _generateInstrumentOnce(name, creatorOrClass as (options?: Options) => Instrumentation);\n}\n\n// The plain version without handling of options\n// Should not be used with custom options that are mutated in the creator!\nfunction _generateInstrumentOnce<Options = unknown, InstrumentationInstance extends Instrumentation = Instrumentation>(\n name: string,\n creator: (options?: Options) => InstrumentationInstance,\n): ((options?: Options) => InstrumentationInstance) & { id: string } {\n return Object.assign(\n (options?: Options) => {\n const instrumented = INSTRUMENTED[name] as InstrumentationInstance | undefined;\n if (instrumented) {\n // If options are provided, ensure we update them\n if (options) {\n instrumented.setConfig(options);\n }\n return instrumented;\n }\n\n const instrumentation = creator(options);\n INSTRUMENTED[name] = instrumentation;\n\n registerInstrumentations({\n instrumentations: [instrumentation],\n });\n\n return instrumentation;\n },\n { id: name },\n );\n}\n\n// This version handles options properly\nfunction _generateInstrumentOnceWithOptions<\n Options,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n InstrumentationClass extends new (...args: any[]) => Instrumentation,\n>(\n name: string,\n instrumentationClass: InstrumentationClass,\n optionsCallback: (options: Options) => ConstructorParameters<InstrumentationClass>[0],\n): ((options: Options) => InstanceType<InstrumentationClass>) & { id: string } {\n return Object.assign(\n (_options: Options) => {\n const options = optionsCallback(_options);\n\n const instrumented = INSTRUMENTED[name] as InstanceType<InstrumentationClass> | undefined;\n if (instrumented) {\n // Ensure we update options\n instrumented.setConfig(options);\n return instrumented;\n }\n\n const instrumentation = new instrumentationClass(options) as InstanceType<InstrumentationClass>;\n INSTRUMENTED[name] = instrumentation;\n\n registerInstrumentations({\n instrumentations: [instrumentation],\n });\n\n return instrumentation;\n },\n { id: name },\n );\n}\n\n/**\n * Ensure a given callback is called when the instrumentation is actually wrapping something.\n * This can be used to ensure some logic is only called when the instrumentation is actually active.\n *\n * This function returns a function that can be invoked with a callback.\n * This callback will either be invoked immediately\n * (e.g. if the instrumentation was already wrapped, or if _wrap could not be patched),\n * or once the instrumentation is actually wrapping something.\n *\n * Make sure to call this function right after adding the instrumentation, otherwise it may be too late!\n * The returned callback can be used any time, and also multiple times.\n */\nexport function instrumentWhenWrapped<T extends Instrumentation>(instrumentation: T): (callback: () => void) => void {\n let isWrapped = false;\n let callbacks: (() => void)[] = [];\n\n if (!hasWrap(instrumentation)) {\n isWrapped = true;\n } else {\n const originalWrap = instrumentation['_wrap'];\n\n instrumentation['_wrap'] = (...args: Parameters<typeof originalWrap>) => {\n isWrapped = true;\n callbacks.forEach(callback => callback());\n callbacks = [];\n return originalWrap(...args);\n };\n }\n\n const registerCallback = (callback: () => void): void => {\n if (isWrapped) {\n callback();\n } else {\n callbacks.push(callback);\n }\n };\n\n return registerCallback;\n}\n\nfunction hasWrap<T extends Instrumentation>(\n instrumentation: T,\n): instrumentation is T & { _wrap: (...args: unknown[]) => unknown } {\n return typeof (instrumentation as T & { _wrap?: (...args: unknown[]) => unknown })['_wrap'] === 'function';\n}\n"],"names":["instrumentation","registerInstrumentations"],"mappings":"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"} |
@@ -6,7 +6,3 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| /** | ||
| * Setup the OTEL logger to use our own debug logger. | ||
| */ | ||
| function setupOpenTelemetryLogger() { | ||
| // Disable diag, to ensure this works even if called multiple times | ||
| api.diag.disable(); | ||
@@ -19,5 +15,5 @@ api.diag.setLogger( | ||
| debug: core.debug.log, | ||
| verbose: core.debug.log, | ||
| verbose: core.debug.log | ||
| }, | ||
| api.DiagLogLevel.DEBUG, | ||
| api.DiagLogLevel.DEBUG | ||
| ); | ||
@@ -24,0 +20,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"logger.js","sources":["../../../src/otel/logger.ts"],"sourcesContent":["import { diag, DiagLogLevel } from '@opentelemetry/api';\nimport { debug } from '@sentry/core';\n\n/**\n * Setup the OTEL logger to use our own debug logger.\n */\nexport function setupOpenTelemetryLogger(): void {\n // Disable diag, to ensure this works even if called multiple times\n diag.disable();\n diag.setLogger(\n {\n error: debug.error,\n warn: debug.warn,\n info: debug.log,\n debug: debug.log,\n verbose: debug.log,\n },\n DiagLogLevel.DEBUG,\n );\n}\n"],"names":["diag","debug","DiagLogLevel"],"mappings":";;;;;AAGA;AACA;AACA;AACO,SAAS,wBAAwB,GAAS;AACjD;AACA,EAAEA,QAAI,CAAC,OAAO,EAAE;AAChB,EAAEA,QAAI,CAAC,SAAS;AAChB,IAAI;AACJ,MAAM,KAAK,EAAEC,UAAK,CAAC,KAAK;AACxB,MAAM,IAAI,EAAEA,UAAK,CAAC,IAAI;AACtB,MAAM,IAAI,EAAEA,UAAK,CAAC,GAAG;AACrB,MAAM,KAAK,EAAEA,UAAK,CAAC,GAAG;AACtB,MAAM,OAAO,EAAEA,UAAK,CAAC,GAAG;AACxB,KAAK;AACL,IAAIC,gBAAY,CAAC,KAAK;AACtB,GAAG;AACH;;;;"} | ||
| {"version":3,"file":"logger.js","sources":["../../../src/otel/logger.ts"],"sourcesContent":["import { diag, DiagLogLevel } from '@opentelemetry/api';\nimport { debug } from '@sentry/core';\n\n/**\n * Setup the OTEL logger to use our own debug logger.\n */\nexport function setupOpenTelemetryLogger(): void {\n // Disable diag, to ensure this works even if called multiple times\n diag.disable();\n diag.setLogger(\n {\n error: debug.error,\n warn: debug.warn,\n info: debug.log,\n debug: debug.log,\n verbose: debug.log,\n },\n DiagLogLevel.DEBUG,\n );\n}\n"],"names":["diag","debug","DiagLogLevel"],"mappings":";;;;;AAMO,SAAS,wBAAA,GAAiC;AAE/C,EAAAA,QAAA,CAAK,OAAA,EAAQ;AACb,EAAAA,QAAA,CAAK,SAAA;AAAA,IACH;AAAA,MACE,OAAOC,UAAA,CAAM,KAAA;AAAA,MACb,MAAMA,UAAA,CAAM,IAAA;AAAA,MACZ,MAAMA,UAAA,CAAM,GAAA;AAAA,MACZ,OAAOA,UAAA,CAAM,GAAA;AAAA,MACb,SAASA,UAAA,CAAM;AAAA,KACjB;AAAA,IACAC,gBAAA,CAAa;AAAA,GACf;AACF;;;;"} |
+20
-73
@@ -6,37 +6,5 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| /** | ||
| * This code was originally forked from https://github.com/TooTallNate/proxy-agents/tree/b133295fd16f6475578b6b15bd9b4e33ecb0d0b7 | ||
| * With the following LICENSE: | ||
| * | ||
| * (The MIT License) | ||
| * | ||
| * Copyright (c) 2013 Nathan Rajlich <nathan@tootallnate.net>* | ||
| * | ||
| * Permission is hereby granted, free of charge, to any person obtaining | ||
| * a copy of this software and associated documentation files (the | ||
| * 'Software'), to deal in the Software without restriction, including | ||
| * without limitation the rights to use, copy, modify, merge, publish, | ||
| * distribute, sublicense, and/or sell copies of the Software, and to | ||
| * permit persons to whom the Software is furnished to do so, subject to | ||
| * the following conditions:* | ||
| * | ||
| * The above copyright notice and this permission notice shall be | ||
| * included in all copies or substantial portions of the Software.* | ||
| * | ||
| * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, | ||
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
| * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||
| * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | ||
| * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | ||
| * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| */ | ||
| const INTERNAL = Symbol('AgentBaseInternalState'); | ||
| class Agent extends http.Agent { | ||
| // Set by `http.Agent` - missing from `@types/node` | ||
| var _a; | ||
| const INTERNAL = /* @__PURE__ */ Symbol("AgentBaseInternalState"); | ||
| class Agent extends (_a = http.Agent, _a) { | ||
| constructor(opts) { | ||
@@ -46,3 +14,2 @@ super(opts); | ||
| } | ||
| /** | ||
@@ -53,55 +20,37 @@ * Determine whether this is an `http` or `https` request. | ||
| if (options) { | ||
| // First check the `secureEndpoint` property explicitly, since this | ||
| // means that a parent `Agent` is "passing through" to this instance. | ||
| if (typeof (options ).secureEndpoint === 'boolean') { | ||
| if (typeof options.secureEndpoint === "boolean") { | ||
| return options.secureEndpoint; | ||
| } | ||
| // If no explicit `secure` endpoint, check if `protocol` property is | ||
| // set. This will usually be the case since using a full string URL | ||
| // or `URL` instance should be the most common usage. | ||
| if (typeof options.protocol === 'string') { | ||
| return options.protocol === 'https:'; | ||
| if (typeof options.protocol === "string") { | ||
| return options.protocol === "https:"; | ||
| } | ||
| } | ||
| // Finally, if no `protocol` property was set, then fall back to | ||
| // checking the stack trace of the current call stack, and try to | ||
| // detect the "https" module. | ||
| const { stack } = new Error(); | ||
| if (typeof stack !== 'string') return false; | ||
| return stack.split('\n').some(l => l.indexOf('(https.js:') !== -1 || l.indexOf('node:https:') !== -1); | ||
| if (typeof stack !== "string") return false; | ||
| return stack.split("\n").some((l) => l.indexOf("(https.js:") !== -1 || l.indexOf("node:https:") !== -1); | ||
| } | ||
| createSocket(req, options, cb) { | ||
| const connectOpts = { | ||
| ...options, | ||
| secureEndpoint: this.isSecureEndpoint(options), | ||
| secureEndpoint: this.isSecureEndpoint(options) | ||
| }; | ||
| Promise.resolve() | ||
| .then(() => this.connect(req, connectOpts)) | ||
| .then(socket => { | ||
| if (socket instanceof http.Agent) { | ||
| // @ts-expect-error `addRequest()` isn't defined in `@types/node` | ||
| return socket.addRequest(req, connectOpts); | ||
| } | ||
| this[INTERNAL].currentSocket = socket; | ||
| // @ts-expect-error `createSocket()` isn't defined in `@types/node` | ||
| super.createSocket(req, options, cb); | ||
| }, cb); | ||
| Promise.resolve().then(() => this.connect(req, connectOpts)).then((socket) => { | ||
| if (socket instanceof http.Agent) { | ||
| return socket.addRequest(req, connectOpts); | ||
| } | ||
| this[INTERNAL].currentSocket = socket; | ||
| super.createSocket(req, options, cb); | ||
| }, cb); | ||
| } | ||
| createConnection() { | ||
| const socket = this[INTERNAL].currentSocket; | ||
| this[INTERNAL].currentSocket = undefined; | ||
| this[INTERNAL].currentSocket = void 0; | ||
| if (!socket) { | ||
| throw new Error('No socket was returned in the `connect()` function'); | ||
| throw new Error("No socket was returned in the `connect()` function"); | ||
| } | ||
| return socket; | ||
| } | ||
| get defaultPort() { | ||
| return this[INTERNAL].defaultPort ?? (this.protocol === 'https:' ? 443 : 80); | ||
| return this[INTERNAL].defaultPort ?? (this.protocol === "https:" ? 443 : 80); | ||
| } | ||
| set defaultPort(v) { | ||
@@ -112,7 +61,5 @@ if (this[INTERNAL]) { | ||
| } | ||
| get protocol() { | ||
| return this[INTERNAL].protocol ?? (this.isSecureEndpoint() ? 'https:' : 'http:'); | ||
| return this[INTERNAL].protocol ?? (this.isSecureEndpoint() ? "https:" : "http:"); | ||
| } | ||
| set protocol(v) { | ||
@@ -119,0 +66,0 @@ if (this[INTERNAL]) { |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"base.js","sources":["../../../src/proxy/base.ts"],"sourcesContent":["/**\n * This code was originally forked from https://github.com/TooTallNate/proxy-agents/tree/b133295fd16f6475578b6b15bd9b4e33ecb0d0b7\n * With the following LICENSE:\n *\n * (The MIT License)\n *\n * Copyright (c) 2013 Nathan Rajlich <nathan@tootallnate.net>*\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * 'Software'), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:*\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.*\n *\n * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/* eslint-disable @typescript-eslint/explicit-member-accessibility */\n/* eslint-disable @typescript-eslint/member-ordering */\n/* eslint-disable jsdoc/require-jsdoc */\nimport * as http from 'node:http';\nimport type * as net from 'node:net';\nimport type { Duplex } from 'node:stream';\nimport type * as tls from 'node:tls';\n\nexport * from './helpers';\n\ninterface HttpConnectOpts extends net.TcpNetConnectOpts {\n secureEndpoint: false;\n protocol?: string;\n}\n\ninterface HttpsConnectOpts extends tls.ConnectionOptions {\n secureEndpoint: true;\n protocol?: string;\n port: number;\n}\n\nexport type AgentConnectOpts = HttpConnectOpts | HttpsConnectOpts;\n\nconst INTERNAL = Symbol('AgentBaseInternalState');\n\ninterface InternalState {\n defaultPort?: number;\n protocol?: string;\n currentSocket?: Duplex;\n}\n\nexport abstract class Agent extends http.Agent {\n private [INTERNAL]: InternalState;\n\n // Set by `http.Agent` - missing from `@types/node`\n options!: Partial<net.TcpNetConnectOpts & tls.ConnectionOptions>;\n keepAlive!: boolean;\n\n constructor(opts?: http.AgentOptions) {\n super(opts);\n this[INTERNAL] = {};\n }\n\n abstract connect(\n req: http.ClientRequest,\n options: AgentConnectOpts,\n ): Promise<Duplex | http.Agent> | Duplex | http.Agent;\n\n /**\n * Determine whether this is an `http` or `https` request.\n */\n isSecureEndpoint(options?: AgentConnectOpts): boolean {\n if (options) {\n // First check the `secureEndpoint` property explicitly, since this\n // means that a parent `Agent` is \"passing through\" to this instance.\n if (typeof (options as Partial<typeof options>).secureEndpoint === 'boolean') {\n return options.secureEndpoint;\n }\n\n // If no explicit `secure` endpoint, check if `protocol` property is\n // set. This will usually be the case since using a full string URL\n // or `URL` instance should be the most common usage.\n if (typeof options.protocol === 'string') {\n return options.protocol === 'https:';\n }\n }\n\n // Finally, if no `protocol` property was set, then fall back to\n // checking the stack trace of the current call stack, and try to\n // detect the \"https\" module.\n const { stack } = new Error();\n if (typeof stack !== 'string') return false;\n return stack.split('\\n').some(l => l.indexOf('(https.js:') !== -1 || l.indexOf('node:https:') !== -1);\n }\n\n createSocket(req: http.ClientRequest, options: AgentConnectOpts, cb: (err: Error | null, s?: Duplex) => void): void {\n const connectOpts = {\n ...options,\n secureEndpoint: this.isSecureEndpoint(options),\n };\n Promise.resolve()\n .then(() => this.connect(req, connectOpts))\n .then(socket => {\n if (socket instanceof http.Agent) {\n // @ts-expect-error `addRequest()` isn't defined in `@types/node`\n return socket.addRequest(req, connectOpts);\n }\n this[INTERNAL].currentSocket = socket;\n // @ts-expect-error `createSocket()` isn't defined in `@types/node`\n super.createSocket(req, options, cb);\n }, cb);\n }\n\n createConnection(): Duplex {\n const socket = this[INTERNAL].currentSocket;\n this[INTERNAL].currentSocket = undefined;\n if (!socket) {\n throw new Error('No socket was returned in the `connect()` function');\n }\n return socket;\n }\n\n get defaultPort(): number {\n return this[INTERNAL].defaultPort ?? (this.protocol === 'https:' ? 443 : 80);\n }\n\n set defaultPort(v: number) {\n if (this[INTERNAL]) {\n this[INTERNAL].defaultPort = v;\n }\n }\n\n get protocol(): string {\n return this[INTERNAL].protocol ?? (this.isSecureEndpoint() ? 'https:' : 'http:');\n }\n\n set protocol(v: string) {\n if (this[INTERNAL]) {\n this[INTERNAL].protocol = v;\n }\n }\n}\n"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"base.js","sources":["../../../src/proxy/base.ts"],"sourcesContent":["/**\n * This code was originally forked from https://github.com/TooTallNate/proxy-agents/tree/b133295fd16f6475578b6b15bd9b4e33ecb0d0b7\n * With the following LICENSE:\n *\n * (The MIT License)\n *\n * Copyright (c) 2013 Nathan Rajlich <nathan@tootallnate.net>*\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * 'Software'), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:*\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.*\n *\n * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/* eslint-disable @typescript-eslint/explicit-member-accessibility */\n/* eslint-disable @typescript-eslint/member-ordering */\n/* eslint-disable jsdoc/require-jsdoc */\nimport * as http from 'node:http';\nimport type * as net from 'node:net';\nimport type { Duplex } from 'node:stream';\nimport type * as tls from 'node:tls';\n\nexport * from './helpers';\n\ninterface HttpConnectOpts extends net.TcpNetConnectOpts {\n secureEndpoint: false;\n protocol?: string;\n}\n\ninterface HttpsConnectOpts extends tls.ConnectionOptions {\n secureEndpoint: true;\n protocol?: string;\n port: number;\n}\n\nexport type AgentConnectOpts = HttpConnectOpts | HttpsConnectOpts;\n\nconst INTERNAL = Symbol('AgentBaseInternalState');\n\ninterface InternalState {\n defaultPort?: number;\n protocol?: string;\n currentSocket?: Duplex;\n}\n\nexport abstract class Agent extends http.Agent {\n private [INTERNAL]: InternalState;\n\n // Set by `http.Agent` - missing from `@types/node`\n options!: Partial<net.TcpNetConnectOpts & tls.ConnectionOptions>;\n keepAlive!: boolean;\n\n constructor(opts?: http.AgentOptions) {\n super(opts);\n this[INTERNAL] = {};\n }\n\n abstract connect(\n req: http.ClientRequest,\n options: AgentConnectOpts,\n ): Promise<Duplex | http.Agent> | Duplex | http.Agent;\n\n /**\n * Determine whether this is an `http` or `https` request.\n */\n isSecureEndpoint(options?: AgentConnectOpts): boolean {\n if (options) {\n // First check the `secureEndpoint` property explicitly, since this\n // means that a parent `Agent` is \"passing through\" to this instance.\n if (typeof (options as Partial<typeof options>).secureEndpoint === 'boolean') {\n return options.secureEndpoint;\n }\n\n // If no explicit `secure` endpoint, check if `protocol` property is\n // set. This will usually be the case since using a full string URL\n // or `URL` instance should be the most common usage.\n if (typeof options.protocol === 'string') {\n return options.protocol === 'https:';\n }\n }\n\n // Finally, if no `protocol` property was set, then fall back to\n // checking the stack trace of the current call stack, and try to\n // detect the \"https\" module.\n const { stack } = new Error();\n if (typeof stack !== 'string') return false;\n return stack.split('\\n').some(l => l.indexOf('(https.js:') !== -1 || l.indexOf('node:https:') !== -1);\n }\n\n createSocket(req: http.ClientRequest, options: AgentConnectOpts, cb: (err: Error | null, s?: Duplex) => void): void {\n const connectOpts = {\n ...options,\n secureEndpoint: this.isSecureEndpoint(options),\n };\n Promise.resolve()\n .then(() => this.connect(req, connectOpts))\n .then(socket => {\n if (socket instanceof http.Agent) {\n // @ts-expect-error `addRequest()` isn't defined in `@types/node`\n return socket.addRequest(req, connectOpts);\n }\n this[INTERNAL].currentSocket = socket;\n // @ts-expect-error `createSocket()` isn't defined in `@types/node`\n super.createSocket(req, options, cb);\n }, cb);\n }\n\n createConnection(): Duplex {\n const socket = this[INTERNAL].currentSocket;\n this[INTERNAL].currentSocket = undefined;\n if (!socket) {\n throw new Error('No socket was returned in the `connect()` function');\n }\n return socket;\n }\n\n get defaultPort(): number {\n return this[INTERNAL].defaultPort ?? (this.protocol === 'https:' ? 443 : 80);\n }\n\n set defaultPort(v: number) {\n if (this[INTERNAL]) {\n this[INTERNAL].defaultPort = v;\n }\n }\n\n get protocol(): string {\n return this[INTERNAL].protocol ?? (this.isSecureEndpoint() ? 'https:' : 'http:');\n }\n\n set protocol(v: string) {\n if (this[INTERNAL]) {\n this[INTERNAL].protocol = v;\n }\n }\n}\n"],"names":[],"mappings":"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"} |
+34
-95
@@ -10,39 +10,21 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| function debugLog(...args) { | ||
| core.debug.log('[https-proxy-agent]', ...args); | ||
| core.debug.log("[https-proxy-agent]", ...args); | ||
| } | ||
| /** | ||
| * The `HttpsProxyAgent` implements an HTTP Agent subclass that connects to | ||
| * the specified "HTTP(s) proxy server" in order to proxy HTTPS requests. | ||
| * | ||
| * Outgoing HTTP requests are first tunneled through the proxy server using the | ||
| * `CONNECT` HTTP request method to establish a connection to the proxy server, | ||
| * and then the proxy server connects to the destination target and issues the | ||
| * HTTP request from the proxy server. | ||
| * | ||
| * `https:` requests have their socket connection upgraded to TLS once | ||
| * the connection to the proxy server has been established. | ||
| */ | ||
| class HttpsProxyAgent extends base.Agent { | ||
| static __initStatic() {this.protocols = ['http', 'https']; } | ||
| constructor(proxy, opts) { | ||
| super(opts); | ||
| this.options = {}; | ||
| this.proxy = typeof proxy === 'string' ? new URL(proxy) : proxy; | ||
| this.proxy = typeof proxy === "string" ? new URL(proxy) : proxy; | ||
| this.proxyHeaders = opts?.headers ?? {}; | ||
| debugLog('Creating new HttpsProxyAgent instance: %o', this.proxy.href); | ||
| // Trim off the brackets from IPv6 addresses | ||
| const host = (this.proxy.hostname || this.proxy.host).replace(/^\[|\]$/g, ''); | ||
| const port = this.proxy.port ? parseInt(this.proxy.port, 10) : this.proxy.protocol === 'https:' ? 443 : 80; | ||
| debugLog("Creating new HttpsProxyAgent instance: %o", this.proxy.href); | ||
| const host = (this.proxy.hostname || this.proxy.host).replace(/^\[|\]$/g, ""); | ||
| const port = this.proxy.port ? parseInt(this.proxy.port, 10) : this.proxy.protocol === "https:" ? 443 : 80; | ||
| this.connectOpts = { | ||
| // Attempt to negotiate http/1.1 for proxy servers that support http/2 | ||
| ALPNProtocols: ['http/1.1'], | ||
| ...(opts ? omit(opts, 'headers') : null), | ||
| ALPNProtocols: ["http/1.1"], | ||
| ...opts ? omit(opts, "headers") : null, | ||
| host, | ||
| port, | ||
| port | ||
| }; | ||
| } | ||
| /** | ||
@@ -54,112 +36,69 @@ * Called when the node-core HTTP client library is creating a | ||
| const { proxy } = this; | ||
| if (!opts.host) { | ||
| throw new TypeError('No "host" provided'); | ||
| } | ||
| // Create a socket connection to the proxy server. | ||
| let socket; | ||
| if (proxy.protocol === 'https:') { | ||
| debugLog('Creating `tls.Socket`: %o', this.connectOpts); | ||
| if (proxy.protocol === "https:") { | ||
| debugLog("Creating `tls.Socket`: %o", this.connectOpts); | ||
| const servername = this.connectOpts.servername || this.connectOpts.host; | ||
| socket = tls.connect({ | ||
| ...this.connectOpts, | ||
| servername: servername && net.isIP(servername) ? undefined : servername, | ||
| servername: servername && net.isIP(servername) ? void 0 : servername | ||
| }); | ||
| } else { | ||
| debugLog('Creating `net.Socket`: %o', this.connectOpts); | ||
| debugLog("Creating `net.Socket`: %o", this.connectOpts); | ||
| socket = net.connect(this.connectOpts); | ||
| } | ||
| const headers = | ||
| typeof this.proxyHeaders === 'function' ? this.proxyHeaders() : { ...this.proxyHeaders }; | ||
| const headers = typeof this.proxyHeaders === "function" ? this.proxyHeaders() : { ...this.proxyHeaders }; | ||
| const host = net.isIPv6(opts.host) ? `[${opts.host}]` : opts.host; | ||
| let payload = `CONNECT ${host}:${opts.port} HTTP/1.1\r\n`; | ||
| // Inject the `Proxy-Authorization` header if necessary. | ||
| let payload = `CONNECT ${host}:${opts.port} HTTP/1.1\r | ||
| `; | ||
| if (proxy.username || proxy.password) { | ||
| const auth = `${decodeURIComponent(proxy.username)}:${decodeURIComponent(proxy.password)}`; | ||
| headers['Proxy-Authorization'] = `Basic ${Buffer.from(auth).toString('base64')}`; | ||
| headers["Proxy-Authorization"] = `Basic ${Buffer.from(auth).toString("base64")}`; | ||
| } | ||
| headers.Host = `${host}:${opts.port}`; | ||
| if (!headers['Proxy-Connection']) { | ||
| headers['Proxy-Connection'] = this.keepAlive ? 'Keep-Alive' : 'close'; | ||
| if (!headers["Proxy-Connection"]) { | ||
| headers["Proxy-Connection"] = this.keepAlive ? "Keep-Alive" : "close"; | ||
| } | ||
| for (const name of Object.keys(headers)) { | ||
| payload += `${name}: ${headers[name]}\r\n`; | ||
| payload += `${name}: ${headers[name]}\r | ||
| `; | ||
| } | ||
| const proxyResponsePromise = parseProxyResponse.parseProxyResponse(socket); | ||
| socket.write(`${payload}\r\n`); | ||
| socket.write(`${payload}\r | ||
| `); | ||
| const { connect, buffered } = await proxyResponsePromise; | ||
| req.emit('proxyConnect', connect); | ||
| // eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
| // @ts-ignore Not EventEmitter in Node types | ||
| this.emit('proxyConnect', connect, req); | ||
| req.emit("proxyConnect", connect); | ||
| this.emit("proxyConnect", connect, req); | ||
| if (connect.statusCode === 200) { | ||
| req.once('socket', resume); | ||
| req.once("socket", resume); | ||
| if (opts.secureEndpoint) { | ||
| // The proxy is connecting to a TLS server, so upgrade | ||
| // this socket connection to a TLS connection. | ||
| debugLog('Upgrading socket connection to TLS'); | ||
| debugLog("Upgrading socket connection to TLS"); | ||
| const servername = opts.servername || opts.host; | ||
| return tls.connect({ | ||
| ...omit(opts, 'host', 'path', 'port'), | ||
| ...omit(opts, "host", "path", "port"), | ||
| socket, | ||
| servername: net.isIP(servername) ? undefined : servername, | ||
| servername: net.isIP(servername) ? void 0 : servername | ||
| }); | ||
| } | ||
| return socket; | ||
| } | ||
| // Some other status code that's not 200... need to re-play the HTTP | ||
| // header "data" events onto the socket once the HTTP machinery is | ||
| // attached so that the node core `http` can parse and handle the | ||
| // error status code. | ||
| // Close the original socket, and a new "fake" socket is returned | ||
| // instead, so that the proxy doesn't get the HTTP request | ||
| // written to it (which may contain `Authorization` headers or other | ||
| // sensitive data). | ||
| // | ||
| // See: https://hackerone.com/reports/541502 | ||
| socket.destroy(); | ||
| const fakeSocket = new net.Socket({ writable: false }); | ||
| fakeSocket.readable = true; | ||
| // Need to wait for the "socket" event to re-play the "data" events. | ||
| req.once('socket', (s) => { | ||
| debugLog('Replaying proxy buffer for failed request'); | ||
| // Replay the "buffered" Buffer onto the fake `socket`, since at | ||
| // this point the HTTP module machinery has been hooked up for | ||
| // the user. | ||
| req.once("socket", (s) => { | ||
| debugLog("Replaying proxy buffer for failed request"); | ||
| s.push(buffered); | ||
| s.push(null); | ||
| }); | ||
| return fakeSocket; | ||
| } | ||
| } HttpsProxyAgent.__initStatic(); | ||
| } | ||
| HttpsProxyAgent.protocols = ["http", "https"]; | ||
| function resume(socket) { | ||
| socket.resume(); | ||
| } | ||
| function omit( | ||
| obj, | ||
| ...keys | ||
| ) | ||
| { | ||
| const ret = {} | ||
| ; | ||
| function omit(obj, ...keys) { | ||
| const ret = {}; | ||
| let key; | ||
@@ -166,0 +105,0 @@ for (key in obj) { |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.js","sources":["../../../src/proxy/index.ts"],"sourcesContent":["/**\n * This code was originally forked from https://github.com/TooTallNate/proxy-agents/tree/b133295fd16f6475578b6b15bd9b4e33ecb0d0b7\n * With the following LICENSE:\n *\n * (The MIT License)\n *\n * Copyright (c) 2013 Nathan Rajlich <nathan@tootallnate.net>*\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * 'Software'), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:*\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.*\n *\n * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/* eslint-disable @typescript-eslint/explicit-member-accessibility */\n/* eslint-disable @typescript-eslint/no-unused-vars */\n// eslint-disable-next-line import/no-duplicates\nimport type * as http from 'node:http';\n// eslint-disable-next-line import/no-duplicates\nimport type { OutgoingHttpHeaders } from 'node:http';\nimport * as net from 'node:net';\nimport * as tls from 'node:tls';\nimport { debug } from '@sentry/core';\nimport type { AgentConnectOpts } from './base';\nimport { Agent } from './base';\nimport { parseProxyResponse } from './parse-proxy-response';\n\nfunction debugLog(...args: unknown[]): void {\n debug.log('[https-proxy-agent]', ...args);\n}\n\ntype Protocol<T> = T extends `${infer Protocol}:${infer _}` ? Protocol : never;\n\ntype ConnectOptsMap = {\n http: Omit<net.TcpNetConnectOpts, 'host' | 'port'>;\n https: Omit<tls.ConnectionOptions, 'host' | 'port'>;\n};\n\ntype ConnectOpts<T> = {\n [P in keyof ConnectOptsMap]: Protocol<T> extends P ? ConnectOptsMap[P] : never;\n}[keyof ConnectOptsMap];\n\nexport type HttpsProxyAgentOptions<T> = ConnectOpts<T> &\n http.AgentOptions & {\n headers?: OutgoingHttpHeaders | (() => OutgoingHttpHeaders);\n };\n\n/**\n * The `HttpsProxyAgent` implements an HTTP Agent subclass that connects to\n * the specified \"HTTP(s) proxy server\" in order to proxy HTTPS requests.\n *\n * Outgoing HTTP requests are first tunneled through the proxy server using the\n * `CONNECT` HTTP request method to establish a connection to the proxy server,\n * and then the proxy server connects to the destination target and issues the\n * HTTP request from the proxy server.\n *\n * `https:` requests have their socket connection upgraded to TLS once\n * the connection to the proxy server has been established.\n */\nexport class HttpsProxyAgent<Uri extends string> extends Agent {\n static protocols = ['http', 'https'] as const;\n\n readonly proxy: URL;\n proxyHeaders: OutgoingHttpHeaders | (() => OutgoingHttpHeaders);\n connectOpts: net.TcpNetConnectOpts & tls.ConnectionOptions;\n\n constructor(proxy: Uri | URL, opts?: HttpsProxyAgentOptions<Uri>) {\n super(opts);\n this.options = {};\n this.proxy = typeof proxy === 'string' ? new URL(proxy) : proxy;\n this.proxyHeaders = opts?.headers ?? {};\n debugLog('Creating new HttpsProxyAgent instance: %o', this.proxy.href);\n\n // Trim off the brackets from IPv6 addresses\n const host = (this.proxy.hostname || this.proxy.host).replace(/^\\[|\\]$/g, '');\n const port = this.proxy.port ? parseInt(this.proxy.port, 10) : this.proxy.protocol === 'https:' ? 443 : 80;\n this.connectOpts = {\n // Attempt to negotiate http/1.1 for proxy servers that support http/2\n ALPNProtocols: ['http/1.1'],\n ...(opts ? omit(opts, 'headers') : null),\n host,\n port,\n };\n }\n\n /**\n * Called when the node-core HTTP client library is creating a\n * new HTTP request.\n */\n async connect(req: http.ClientRequest, opts: AgentConnectOpts): Promise<net.Socket> {\n const { proxy } = this;\n\n if (!opts.host) {\n throw new TypeError('No \"host\" provided');\n }\n\n // Create a socket connection to the proxy server.\n let socket: net.Socket;\n if (proxy.protocol === 'https:') {\n debugLog('Creating `tls.Socket`: %o', this.connectOpts);\n const servername = this.connectOpts.servername || this.connectOpts.host;\n socket = tls.connect({\n ...this.connectOpts,\n servername: servername && net.isIP(servername) ? undefined : servername,\n });\n } else {\n debugLog('Creating `net.Socket`: %o', this.connectOpts);\n socket = net.connect(this.connectOpts);\n }\n\n const headers: OutgoingHttpHeaders =\n typeof this.proxyHeaders === 'function' ? this.proxyHeaders() : { ...this.proxyHeaders };\n const host = net.isIPv6(opts.host) ? `[${opts.host}]` : opts.host;\n let payload = `CONNECT ${host}:${opts.port} HTTP/1.1\\r\\n`;\n\n // Inject the `Proxy-Authorization` header if necessary.\n if (proxy.username || proxy.password) {\n const auth = `${decodeURIComponent(proxy.username)}:${decodeURIComponent(proxy.password)}`;\n headers['Proxy-Authorization'] = `Basic ${Buffer.from(auth).toString('base64')}`;\n }\n\n headers.Host = `${host}:${opts.port}`;\n\n if (!headers['Proxy-Connection']) {\n headers['Proxy-Connection'] = this.keepAlive ? 'Keep-Alive' : 'close';\n }\n for (const name of Object.keys(headers)) {\n payload += `${name}: ${headers[name]}\\r\\n`;\n }\n\n const proxyResponsePromise = parseProxyResponse(socket);\n\n socket.write(`${payload}\\r\\n`);\n\n const { connect, buffered } = await proxyResponsePromise;\n req.emit('proxyConnect', connect);\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore Not EventEmitter in Node types\n this.emit('proxyConnect', connect, req);\n\n if (connect.statusCode === 200) {\n req.once('socket', resume);\n\n if (opts.secureEndpoint) {\n // The proxy is connecting to a TLS server, so upgrade\n // this socket connection to a TLS connection.\n debugLog('Upgrading socket connection to TLS');\n const servername = opts.servername || opts.host;\n return tls.connect({\n ...omit(opts, 'host', 'path', 'port'),\n socket,\n servername: net.isIP(servername) ? undefined : servername,\n });\n }\n\n return socket;\n }\n\n // Some other status code that's not 200... need to re-play the HTTP\n // header \"data\" events onto the socket once the HTTP machinery is\n // attached so that the node core `http` can parse and handle the\n // error status code.\n\n // Close the original socket, and a new \"fake\" socket is returned\n // instead, so that the proxy doesn't get the HTTP request\n // written to it (which may contain `Authorization` headers or other\n // sensitive data).\n //\n // See: https://hackerone.com/reports/541502\n socket.destroy();\n\n const fakeSocket = new net.Socket({ writable: false });\n fakeSocket.readable = true;\n\n // Need to wait for the \"socket\" event to re-play the \"data\" events.\n req.once('socket', (s: net.Socket) => {\n debugLog('Replaying proxy buffer for failed request');\n // Replay the \"buffered\" Buffer onto the fake `socket`, since at\n // this point the HTTP module machinery has been hooked up for\n // the user.\n s.push(buffered);\n s.push(null);\n });\n\n return fakeSocket;\n }\n}\n\nfunction resume(socket: net.Socket | tls.TLSSocket): void {\n socket.resume();\n}\n\nfunction omit<T extends object, K extends [...(keyof T)[]]>(\n obj: T,\n ...keys: K\n): {\n [K2 in Exclude<keyof T, K[number]>]: T[K2];\n} {\n const ret = {} as {\n [K in keyof typeof obj]: (typeof obj)[K];\n };\n let key: keyof typeof obj;\n for (key in obj) {\n if (!keys.includes(key)) {\n ret[key] = obj[key];\n }\n }\n return ret;\n}\n"],"names":["debug","Agent","parseProxyResponse"],"mappings":"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"} | ||
| {"version":3,"file":"index.js","sources":["../../../src/proxy/index.ts"],"sourcesContent":["/**\n * This code was originally forked from https://github.com/TooTallNate/proxy-agents/tree/b133295fd16f6475578b6b15bd9b4e33ecb0d0b7\n * With the following LICENSE:\n *\n * (The MIT License)\n *\n * Copyright (c) 2013 Nathan Rajlich <nathan@tootallnate.net>*\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * 'Software'), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:*\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.*\n *\n * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/* eslint-disable @typescript-eslint/explicit-member-accessibility */\n/* eslint-disable @typescript-eslint/no-unused-vars */\n// eslint-disable-next-line import/no-duplicates\nimport type * as http from 'node:http';\n// eslint-disable-next-line import/no-duplicates\nimport type { OutgoingHttpHeaders } from 'node:http';\nimport * as net from 'node:net';\nimport * as tls from 'node:tls';\nimport { debug } from '@sentry/core';\nimport type { AgentConnectOpts } from './base';\nimport { Agent } from './base';\nimport { parseProxyResponse } from './parse-proxy-response';\n\nfunction debugLog(...args: unknown[]): void {\n debug.log('[https-proxy-agent]', ...args);\n}\n\ntype Protocol<T> = T extends `${infer Protocol}:${infer _}` ? Protocol : never;\n\ntype ConnectOptsMap = {\n http: Omit<net.TcpNetConnectOpts, 'host' | 'port'>;\n https: Omit<tls.ConnectionOptions, 'host' | 'port'>;\n};\n\ntype ConnectOpts<T> = {\n [P in keyof ConnectOptsMap]: Protocol<T> extends P ? ConnectOptsMap[P] : never;\n}[keyof ConnectOptsMap];\n\nexport type HttpsProxyAgentOptions<T> = ConnectOpts<T> &\n http.AgentOptions & {\n headers?: OutgoingHttpHeaders | (() => OutgoingHttpHeaders);\n };\n\n/**\n * The `HttpsProxyAgent` implements an HTTP Agent subclass that connects to\n * the specified \"HTTP(s) proxy server\" in order to proxy HTTPS requests.\n *\n * Outgoing HTTP requests are first tunneled through the proxy server using the\n * `CONNECT` HTTP request method to establish a connection to the proxy server,\n * and then the proxy server connects to the destination target and issues the\n * HTTP request from the proxy server.\n *\n * `https:` requests have their socket connection upgraded to TLS once\n * the connection to the proxy server has been established.\n */\nexport class HttpsProxyAgent<Uri extends string> extends Agent {\n static protocols = ['http', 'https'] as const;\n\n readonly proxy: URL;\n proxyHeaders: OutgoingHttpHeaders | (() => OutgoingHttpHeaders);\n connectOpts: net.TcpNetConnectOpts & tls.ConnectionOptions;\n\n constructor(proxy: Uri | URL, opts?: HttpsProxyAgentOptions<Uri>) {\n super(opts);\n this.options = {};\n this.proxy = typeof proxy === 'string' ? new URL(proxy) : proxy;\n this.proxyHeaders = opts?.headers ?? {};\n debugLog('Creating new HttpsProxyAgent instance: %o', this.proxy.href);\n\n // Trim off the brackets from IPv6 addresses\n const host = (this.proxy.hostname || this.proxy.host).replace(/^\\[|\\]$/g, '');\n const port = this.proxy.port ? parseInt(this.proxy.port, 10) : this.proxy.protocol === 'https:' ? 443 : 80;\n this.connectOpts = {\n // Attempt to negotiate http/1.1 for proxy servers that support http/2\n ALPNProtocols: ['http/1.1'],\n ...(opts ? omit(opts, 'headers') : null),\n host,\n port,\n };\n }\n\n /**\n * Called when the node-core HTTP client library is creating a\n * new HTTP request.\n */\n async connect(req: http.ClientRequest, opts: AgentConnectOpts): Promise<net.Socket> {\n const { proxy } = this;\n\n if (!opts.host) {\n throw new TypeError('No \"host\" provided');\n }\n\n // Create a socket connection to the proxy server.\n let socket: net.Socket;\n if (proxy.protocol === 'https:') {\n debugLog('Creating `tls.Socket`: %o', this.connectOpts);\n const servername = this.connectOpts.servername || this.connectOpts.host;\n socket = tls.connect({\n ...this.connectOpts,\n servername: servername && net.isIP(servername) ? undefined : servername,\n });\n } else {\n debugLog('Creating `net.Socket`: %o', this.connectOpts);\n socket = net.connect(this.connectOpts);\n }\n\n const headers: OutgoingHttpHeaders =\n typeof this.proxyHeaders === 'function' ? this.proxyHeaders() : { ...this.proxyHeaders };\n const host = net.isIPv6(opts.host) ? `[${opts.host}]` : opts.host;\n let payload = `CONNECT ${host}:${opts.port} HTTP/1.1\\r\\n`;\n\n // Inject the `Proxy-Authorization` header if necessary.\n if (proxy.username || proxy.password) {\n const auth = `${decodeURIComponent(proxy.username)}:${decodeURIComponent(proxy.password)}`;\n headers['Proxy-Authorization'] = `Basic ${Buffer.from(auth).toString('base64')}`;\n }\n\n headers.Host = `${host}:${opts.port}`;\n\n if (!headers['Proxy-Connection']) {\n headers['Proxy-Connection'] = this.keepAlive ? 'Keep-Alive' : 'close';\n }\n for (const name of Object.keys(headers)) {\n payload += `${name}: ${headers[name]}\\r\\n`;\n }\n\n const proxyResponsePromise = parseProxyResponse(socket);\n\n socket.write(`${payload}\\r\\n`);\n\n const { connect, buffered } = await proxyResponsePromise;\n req.emit('proxyConnect', connect);\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore Not EventEmitter in Node types\n this.emit('proxyConnect', connect, req);\n\n if (connect.statusCode === 200) {\n req.once('socket', resume);\n\n if (opts.secureEndpoint) {\n // The proxy is connecting to a TLS server, so upgrade\n // this socket connection to a TLS connection.\n debugLog('Upgrading socket connection to TLS');\n const servername = opts.servername || opts.host;\n return tls.connect({\n ...omit(opts, 'host', 'path', 'port'),\n socket,\n servername: net.isIP(servername) ? undefined : servername,\n });\n }\n\n return socket;\n }\n\n // Some other status code that's not 200... need to re-play the HTTP\n // header \"data\" events onto the socket once the HTTP machinery is\n // attached so that the node core `http` can parse and handle the\n // error status code.\n\n // Close the original socket, and a new \"fake\" socket is returned\n // instead, so that the proxy doesn't get the HTTP request\n // written to it (which may contain `Authorization` headers or other\n // sensitive data).\n //\n // See: https://hackerone.com/reports/541502\n socket.destroy();\n\n const fakeSocket = new net.Socket({ writable: false });\n fakeSocket.readable = true;\n\n // Need to wait for the \"socket\" event to re-play the \"data\" events.\n req.once('socket', (s: net.Socket) => {\n debugLog('Replaying proxy buffer for failed request');\n // Replay the \"buffered\" Buffer onto the fake `socket`, since at\n // this point the HTTP module machinery has been hooked up for\n // the user.\n s.push(buffered);\n s.push(null);\n });\n\n return fakeSocket;\n }\n}\n\nfunction resume(socket: net.Socket | tls.TLSSocket): void {\n socket.resume();\n}\n\nfunction omit<T extends object, K extends [...(keyof T)[]]>(\n obj: T,\n ...keys: K\n): {\n [K2 in Exclude<keyof T, K[number]>]: T[K2];\n} {\n const ret = {} as {\n [K in keyof typeof obj]: (typeof obj)[K];\n };\n let key: keyof typeof obj;\n for (key in obj) {\n if (!keys.includes(key)) {\n ret[key] = obj[key];\n }\n }\n return ret;\n}\n"],"names":["debug","Agent","parseProxyResponse"],"mappings":"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"} |
@@ -6,65 +6,51 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| function debugLog(...args) { | ||
| core.debug.log('[https-proxy-agent:parse-proxy-response]', ...args); | ||
| core.debug.log("[https-proxy-agent:parse-proxy-response]", ...args); | ||
| } | ||
| function parseProxyResponse(socket) { | ||
| return new Promise((resolve, reject) => { | ||
| // we need to buffer any HTTP traffic that happens with the proxy before we get | ||
| // the CONNECT response, so that if the response is anything other than an "200" | ||
| // response code, then we can re-play the "data" events on the socket once the | ||
| // HTTP parser is hooked up... | ||
| let buffersLength = 0; | ||
| const buffers = []; | ||
| function read() { | ||
| const b = socket.read(); | ||
| if (b) ondata(b); | ||
| else socket.once('readable', read); | ||
| else socket.once("readable", read); | ||
| } | ||
| function cleanup() { | ||
| socket.removeListener('end', onend); | ||
| socket.removeListener('error', onerror); | ||
| socket.removeListener('readable', read); | ||
| socket.removeListener("end", onend); | ||
| socket.removeListener("error", onerror); | ||
| socket.removeListener("readable", read); | ||
| } | ||
| function onend() { | ||
| cleanup(); | ||
| debugLog('onend'); | ||
| reject(new Error('Proxy connection ended before receiving CONNECT response')); | ||
| debugLog("onend"); | ||
| reject(new Error("Proxy connection ended before receiving CONNECT response")); | ||
| } | ||
| function onerror(err) { | ||
| cleanup(); | ||
| debugLog('onerror %o', err); | ||
| debugLog("onerror %o", err); | ||
| reject(err); | ||
| } | ||
| function ondata(b) { | ||
| buffers.push(b); | ||
| buffersLength += b.length; | ||
| const buffered = Buffer.concat(buffers, buffersLength); | ||
| const endOfHeaders = buffered.indexOf('\r\n\r\n'); | ||
| const endOfHeaders = buffered.indexOf("\r\n\r\n"); | ||
| if (endOfHeaders === -1) { | ||
| // keep buffering | ||
| debugLog('have not received end of HTTP headers yet...'); | ||
| debugLog("have not received end of HTTP headers yet..."); | ||
| read(); | ||
| return; | ||
| } | ||
| const headerParts = buffered.subarray(0, endOfHeaders).toString('ascii').split('\r\n'); | ||
| const headerParts = buffered.subarray(0, endOfHeaders).toString("ascii").split("\r\n"); | ||
| const firstLine = headerParts.shift(); | ||
| if (!firstLine) { | ||
| socket.destroy(); | ||
| return reject(new Error('No header received from proxy CONNECT response')); | ||
| return reject(new Error("No header received from proxy CONNECT response")); | ||
| } | ||
| const firstLineParts = firstLine.split(' '); | ||
| const firstLineParts = firstLine.split(" "); | ||
| const statusCode = +(firstLineParts[1] || 0); | ||
| const statusText = firstLineParts.slice(2).join(' '); | ||
| const statusText = firstLineParts.slice(2).join(" "); | ||
| const headers = {}; | ||
| for (const header of headerParts) { | ||
| if (!header) continue; | ||
| const firstColon = header.indexOf(':'); | ||
| const firstColon = header.indexOf(":"); | ||
| if (firstColon === -1) { | ||
@@ -77,3 +63,3 @@ socket.destroy(); | ||
| const current = headers[key]; | ||
| if (typeof current === 'string') { | ||
| if (typeof current === "string") { | ||
| headers[key] = [current, value]; | ||
@@ -86,3 +72,3 @@ } else if (Array.isArray(current)) { | ||
| } | ||
| debugLog('got proxy server response: %o %o', firstLine, headers); | ||
| debugLog("got proxy server response: %o %o", firstLine, headers); | ||
| cleanup(); | ||
@@ -93,11 +79,9 @@ resolve({ | ||
| statusText, | ||
| headers, | ||
| headers | ||
| }, | ||
| buffered, | ||
| buffered | ||
| }); | ||
| } | ||
| socket.on('error', onerror); | ||
| socket.on('end', onend); | ||
| socket.on("error", onerror); | ||
| socket.on("end", onend); | ||
| read(); | ||
@@ -104,0 +88,0 @@ }); |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"parse-proxy-response.js","sources":["../../../src/proxy/parse-proxy-response.ts"],"sourcesContent":["/**\n * This code was originally forked from https://github.com/TooTallNate/proxy-agents/tree/b133295fd16f6475578b6b15bd9b4e33ecb0d0b7\n * With the following LICENSE:\n *\n * (The MIT License)\n *\n * Copyright (c) 2013 Nathan Rajlich <nathan@tootallnate.net>*\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * 'Software'), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:*\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.*\n *\n * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/* eslint-disable @typescript-eslint/explicit-function-return-type */\n/* eslint-disable jsdoc/require-jsdoc */\nimport type { IncomingHttpHeaders } from 'node:http';\nimport type { Readable } from 'node:stream';\nimport { debug } from '@sentry/core';\n\nfunction debugLog(...args: unknown[]): void {\n debug.log('[https-proxy-agent:parse-proxy-response]', ...args);\n}\n\nexport interface ConnectResponse {\n statusCode: number;\n statusText: string;\n headers: IncomingHttpHeaders;\n}\n\nexport function parseProxyResponse(socket: Readable): Promise<{ connect: ConnectResponse; buffered: Buffer }> {\n return new Promise((resolve, reject) => {\n // we need to buffer any HTTP traffic that happens with the proxy before we get\n // the CONNECT response, so that if the response is anything other than an \"200\"\n // response code, then we can re-play the \"data\" events on the socket once the\n // HTTP parser is hooked up...\n let buffersLength = 0;\n const buffers: Buffer[] = [];\n\n function read() {\n const b = socket.read();\n if (b) ondata(b);\n else socket.once('readable', read);\n }\n\n function cleanup() {\n socket.removeListener('end', onend);\n socket.removeListener('error', onerror);\n socket.removeListener('readable', read);\n }\n\n function onend() {\n cleanup();\n debugLog('onend');\n reject(new Error('Proxy connection ended before receiving CONNECT response'));\n }\n\n function onerror(err: Error) {\n cleanup();\n debugLog('onerror %o', err);\n reject(err);\n }\n\n function ondata(b: Buffer) {\n buffers.push(b);\n buffersLength += b.length;\n\n const buffered = Buffer.concat(buffers, buffersLength);\n const endOfHeaders = buffered.indexOf('\\r\\n\\r\\n');\n\n if (endOfHeaders === -1) {\n // keep buffering\n debugLog('have not received end of HTTP headers yet...');\n read();\n return;\n }\n\n const headerParts = buffered.subarray(0, endOfHeaders).toString('ascii').split('\\r\\n');\n const firstLine = headerParts.shift();\n if (!firstLine) {\n socket.destroy();\n return reject(new Error('No header received from proxy CONNECT response'));\n }\n const firstLineParts = firstLine.split(' ');\n const statusCode = +(firstLineParts[1] || 0);\n const statusText = firstLineParts.slice(2).join(' ');\n const headers: IncomingHttpHeaders = {};\n for (const header of headerParts) {\n if (!header) continue;\n const firstColon = header.indexOf(':');\n if (firstColon === -1) {\n socket.destroy();\n return reject(new Error(`Invalid header from proxy CONNECT response: \"${header}\"`));\n }\n const key = header.slice(0, firstColon).toLowerCase();\n const value = header.slice(firstColon + 1).trimStart();\n const current = headers[key];\n if (typeof current === 'string') {\n headers[key] = [current, value];\n } else if (Array.isArray(current)) {\n current.push(value);\n } else {\n headers[key] = value;\n }\n }\n debugLog('got proxy server response: %o %o', firstLine, headers);\n cleanup();\n resolve({\n connect: {\n statusCode,\n statusText,\n headers,\n },\n buffered,\n });\n }\n\n socket.on('error', onerror);\n socket.on('end', onend);\n\n read();\n });\n}\n"],"names":["debug"],"mappings":"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"} | ||
| {"version":3,"file":"parse-proxy-response.js","sources":["../../../src/proxy/parse-proxy-response.ts"],"sourcesContent":["/**\n * This code was originally forked from https://github.com/TooTallNate/proxy-agents/tree/b133295fd16f6475578b6b15bd9b4e33ecb0d0b7\n * With the following LICENSE:\n *\n * (The MIT License)\n *\n * Copyright (c) 2013 Nathan Rajlich <nathan@tootallnate.net>*\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * 'Software'), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:*\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.*\n *\n * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/* eslint-disable @typescript-eslint/explicit-function-return-type */\n/* eslint-disable jsdoc/require-jsdoc */\nimport type { IncomingHttpHeaders } from 'node:http';\nimport type { Readable } from 'node:stream';\nimport { debug } from '@sentry/core';\n\nfunction debugLog(...args: unknown[]): void {\n debug.log('[https-proxy-agent:parse-proxy-response]', ...args);\n}\n\nexport interface ConnectResponse {\n statusCode: number;\n statusText: string;\n headers: IncomingHttpHeaders;\n}\n\nexport function parseProxyResponse(socket: Readable): Promise<{ connect: ConnectResponse; buffered: Buffer }> {\n return new Promise((resolve, reject) => {\n // we need to buffer any HTTP traffic that happens with the proxy before we get\n // the CONNECT response, so that if the response is anything other than an \"200\"\n // response code, then we can re-play the \"data\" events on the socket once the\n // HTTP parser is hooked up...\n let buffersLength = 0;\n const buffers: Buffer[] = [];\n\n function read() {\n const b = socket.read();\n if (b) ondata(b);\n else socket.once('readable', read);\n }\n\n function cleanup() {\n socket.removeListener('end', onend);\n socket.removeListener('error', onerror);\n socket.removeListener('readable', read);\n }\n\n function onend() {\n cleanup();\n debugLog('onend');\n reject(new Error('Proxy connection ended before receiving CONNECT response'));\n }\n\n function onerror(err: Error) {\n cleanup();\n debugLog('onerror %o', err);\n reject(err);\n }\n\n function ondata(b: Buffer) {\n buffers.push(b);\n buffersLength += b.length;\n\n const buffered = Buffer.concat(buffers, buffersLength);\n const endOfHeaders = buffered.indexOf('\\r\\n\\r\\n');\n\n if (endOfHeaders === -1) {\n // keep buffering\n debugLog('have not received end of HTTP headers yet...');\n read();\n return;\n }\n\n const headerParts = buffered.subarray(0, endOfHeaders).toString('ascii').split('\\r\\n');\n const firstLine = headerParts.shift();\n if (!firstLine) {\n socket.destroy();\n return reject(new Error('No header received from proxy CONNECT response'));\n }\n const firstLineParts = firstLine.split(' ');\n const statusCode = +(firstLineParts[1] || 0);\n const statusText = firstLineParts.slice(2).join(' ');\n const headers: IncomingHttpHeaders = {};\n for (const header of headerParts) {\n if (!header) continue;\n const firstColon = header.indexOf(':');\n if (firstColon === -1) {\n socket.destroy();\n return reject(new Error(`Invalid header from proxy CONNECT response: \"${header}\"`));\n }\n const key = header.slice(0, firstColon).toLowerCase();\n const value = header.slice(firstColon + 1).trimStart();\n const current = headers[key];\n if (typeof current === 'string') {\n headers[key] = [current, value];\n } else if (Array.isArray(current)) {\n current.push(value);\n } else {\n headers[key] = value;\n }\n }\n debugLog('got proxy server response: %o %o', firstLine, headers);\n cleanup();\n resolve({\n connect: {\n statusCode,\n statusText,\n headers,\n },\n buffered,\n });\n }\n\n socket.on('error', onerror);\n socket.on('end', onend);\n\n read();\n });\n}\n"],"names":["debug"],"mappings":"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"} |
+39
-97
@@ -6,111 +6,53 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| /** | ||
| * Returns a release dynamically from environment variables. | ||
| */ | ||
| // eslint-disable-next-line complexity | ||
| function getSentryRelease(fallback) { | ||
| // Always read first as Sentry takes this as precedence | ||
| if (process.env.SENTRY_RELEASE) { | ||
| return process.env.SENTRY_RELEASE; | ||
| } | ||
| // This supports the variable that sentry-webpack-plugin injects | ||
| if (core.GLOBAL_OBJ.SENTRY_RELEASE?.id) { | ||
| return core.GLOBAL_OBJ.SENTRY_RELEASE.id; | ||
| } | ||
| // This list is in approximate alpha order, separated into 3 categories: | ||
| // 1. Git providers | ||
| // 2. CI providers with specific environment variables (has the provider name in the variable name) | ||
| // 3. CI providers with generic environment variables (checked for last to prevent possible false positives) | ||
| const possibleReleaseNameOfGitProvider = | ||
| const possibleReleaseNameOfGitProvider = ( | ||
| // GitHub Actions - https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables#default-environment-variables | ||
| process.env['GITHUB_SHA'] || | ||
| // GitLab CI - https://docs.gitlab.com/ee/ci/variables/predefined_variables.html | ||
| process.env['CI_MERGE_REQUEST_SOURCE_BRANCH_SHA'] || | ||
| process.env['CI_BUILD_REF'] || | ||
| process.env['CI_COMMIT_SHA'] || | ||
| // Bitbucket - https://support.atlassian.com/bitbucket-cloud/docs/variables-and-secrets/ | ||
| process.env['BITBUCKET_COMMIT']; | ||
| const possibleReleaseNameOfCiProvidersWithSpecificEnvVar = | ||
| process.env["GITHUB_SHA"] || // GitLab CI - https://docs.gitlab.com/ee/ci/variables/predefined_variables.html | ||
| process.env["CI_MERGE_REQUEST_SOURCE_BRANCH_SHA"] || process.env["CI_BUILD_REF"] || process.env["CI_COMMIT_SHA"] || // Bitbucket - https://support.atlassian.com/bitbucket-cloud/docs/variables-and-secrets/ | ||
| process.env["BITBUCKET_COMMIT"] | ||
| ); | ||
| const possibleReleaseNameOfCiProvidersWithSpecificEnvVar = ( | ||
| // AppVeyor - https://www.appveyor.com/docs/environment-variables/ | ||
| process.env['APPVEYOR_PULL_REQUEST_HEAD_COMMIT'] || | ||
| process.env['APPVEYOR_REPO_COMMIT'] || | ||
| // AWS CodeBuild - https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-env-vars.html | ||
| process.env['CODEBUILD_RESOLVED_SOURCE_VERSION'] || | ||
| // AWS Amplify - https://docs.aws.amazon.com/amplify/latest/userguide/environment-variables.html | ||
| process.env['AWS_COMMIT_ID'] || | ||
| // Azure Pipelines - https://docs.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml | ||
| process.env['BUILD_SOURCEVERSION'] || | ||
| // Bitrise - https://devcenter.bitrise.io/builds/available-environment-variables/ | ||
| process.env['GIT_CLONE_COMMIT_HASH'] || | ||
| // Buddy CI - https://buddy.works/docs/pipelines/environment-variables#default-environment-variables | ||
| process.env['BUDDY_EXECUTION_REVISION'] || | ||
| // Builtkite - https://buildkite.com/docs/pipelines/environment-variables | ||
| process.env['BUILDKITE_COMMIT'] || | ||
| // CircleCI - https://circleci.com/docs/variables/ | ||
| process.env['CIRCLE_SHA1'] || | ||
| // Cirrus CI - https://cirrus-ci.org/guide/writing-tasks/#environment-variables | ||
| process.env['CIRRUS_CHANGE_IN_REPO'] || | ||
| // Codefresh - https://codefresh.io/docs/docs/codefresh-yaml/variables/ | ||
| process.env['CF_REVISION'] || | ||
| // Codemagic - https://docs.codemagic.io/yaml-basic-configuration/environment-variables/ | ||
| process.env['CM_COMMIT'] || | ||
| // Cloudflare Pages - https://developers.cloudflare.com/pages/platform/build-configuration/#environment-variables | ||
| process.env['CF_PAGES_COMMIT_SHA'] || | ||
| // Drone - https://docs.drone.io/pipeline/environment/reference/ | ||
| process.env['DRONE_COMMIT_SHA'] || | ||
| // Flightcontrol - https://www.flightcontrol.dev/docs/guides/flightcontrol/environment-variables#built-in-environment-variables | ||
| process.env['FC_GIT_COMMIT_SHA'] || | ||
| // Heroku #1 https://devcenter.heroku.com/articles/heroku-ci | ||
| process.env['HEROKU_TEST_RUN_COMMIT_VERSION'] || | ||
| // Heroku #2 https://devcenter.heroku.com/articles/dyno-metadata#dyno-metadata | ||
| process.env['HEROKU_BUILD_COMMIT'] || | ||
| // Heroku #3 (deprecated by Heroku, kept for backward compatibility) | ||
| process.env['HEROKU_SLUG_COMMIT'] || | ||
| // Railway - https://docs.railway.app/reference/variables#git-variables | ||
| process.env['RAILWAY_GIT_COMMIT_SHA'] || | ||
| // Render - https://render.com/docs/environment-variables | ||
| process.env['RENDER_GIT_COMMIT'] || | ||
| // Semaphore CI - https://docs.semaphoreci.com/ci-cd-environment/environment-variables | ||
| process.env['SEMAPHORE_GIT_SHA'] || | ||
| // TravisCI - https://docs.travis-ci.com/user/environment-variables/#default-environment-variables | ||
| process.env['TRAVIS_PULL_REQUEST_SHA'] || | ||
| // Vercel - https://vercel.com/docs/v2/build-step#system-environment-variables | ||
| process.env['VERCEL_GIT_COMMIT_SHA'] || | ||
| process.env['VERCEL_GITHUB_COMMIT_SHA'] || | ||
| process.env['VERCEL_GITLAB_COMMIT_SHA'] || | ||
| process.env['VERCEL_BITBUCKET_COMMIT_SHA'] || | ||
| // Zeit (now known as Vercel) | ||
| process.env['ZEIT_GITHUB_COMMIT_SHA'] || | ||
| process.env['ZEIT_GITLAB_COMMIT_SHA'] || | ||
| process.env['ZEIT_BITBUCKET_COMMIT_SHA']; | ||
| const possibleReleaseNameOfCiProvidersWithGenericEnvVar = | ||
| process.env["APPVEYOR_PULL_REQUEST_HEAD_COMMIT"] || process.env["APPVEYOR_REPO_COMMIT"] || // AWS CodeBuild - https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-env-vars.html | ||
| process.env["CODEBUILD_RESOLVED_SOURCE_VERSION"] || // AWS Amplify - https://docs.aws.amazon.com/amplify/latest/userguide/environment-variables.html | ||
| process.env["AWS_COMMIT_ID"] || // Azure Pipelines - https://docs.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml | ||
| process.env["BUILD_SOURCEVERSION"] || // Bitrise - https://devcenter.bitrise.io/builds/available-environment-variables/ | ||
| process.env["GIT_CLONE_COMMIT_HASH"] || // Buddy CI - https://buddy.works/docs/pipelines/environment-variables#default-environment-variables | ||
| process.env["BUDDY_EXECUTION_REVISION"] || // Builtkite - https://buildkite.com/docs/pipelines/environment-variables | ||
| process.env["BUILDKITE_COMMIT"] || // CircleCI - https://circleci.com/docs/variables/ | ||
| process.env["CIRCLE_SHA1"] || // Cirrus CI - https://cirrus-ci.org/guide/writing-tasks/#environment-variables | ||
| process.env["CIRRUS_CHANGE_IN_REPO"] || // Codefresh - https://codefresh.io/docs/docs/codefresh-yaml/variables/ | ||
| process.env["CF_REVISION"] || // Codemagic - https://docs.codemagic.io/yaml-basic-configuration/environment-variables/ | ||
| process.env["CM_COMMIT"] || // Cloudflare Pages - https://developers.cloudflare.com/pages/platform/build-configuration/#environment-variables | ||
| process.env["CF_PAGES_COMMIT_SHA"] || // Drone - https://docs.drone.io/pipeline/environment/reference/ | ||
| process.env["DRONE_COMMIT_SHA"] || // Flightcontrol - https://www.flightcontrol.dev/docs/guides/flightcontrol/environment-variables#built-in-environment-variables | ||
| process.env["FC_GIT_COMMIT_SHA"] || // Heroku #1 https://devcenter.heroku.com/articles/heroku-ci | ||
| process.env["HEROKU_TEST_RUN_COMMIT_VERSION"] || // Heroku #2 https://devcenter.heroku.com/articles/dyno-metadata#dyno-metadata | ||
| process.env["HEROKU_BUILD_COMMIT"] || // Heroku #3 (deprecated by Heroku, kept for backward compatibility) | ||
| process.env["HEROKU_SLUG_COMMIT"] || // Railway - https://docs.railway.app/reference/variables#git-variables | ||
| process.env["RAILWAY_GIT_COMMIT_SHA"] || // Render - https://render.com/docs/environment-variables | ||
| process.env["RENDER_GIT_COMMIT"] || // Semaphore CI - https://docs.semaphoreci.com/ci-cd-environment/environment-variables | ||
| process.env["SEMAPHORE_GIT_SHA"] || // TravisCI - https://docs.travis-ci.com/user/environment-variables/#default-environment-variables | ||
| process.env["TRAVIS_PULL_REQUEST_SHA"] || // Vercel - https://vercel.com/docs/v2/build-step#system-environment-variables | ||
| process.env["VERCEL_GIT_COMMIT_SHA"] || process.env["VERCEL_GITHUB_COMMIT_SHA"] || process.env["VERCEL_GITLAB_COMMIT_SHA"] || process.env["VERCEL_BITBUCKET_COMMIT_SHA"] || // Zeit (now known as Vercel) | ||
| process.env["ZEIT_GITHUB_COMMIT_SHA"] || process.env["ZEIT_GITLAB_COMMIT_SHA"] || process.env["ZEIT_BITBUCKET_COMMIT_SHA"] | ||
| ); | ||
| const possibleReleaseNameOfCiProvidersWithGenericEnvVar = ( | ||
| // CloudBees CodeShip - https://docs.cloudbees.com/docs/cloudbees-codeship/latest/pro-builds-and-configuration/environment-variables | ||
| process.env['CI_COMMIT_ID'] || | ||
| // Coolify - https://coolify.io/docs/knowledge-base/environment-variables | ||
| process.env['SOURCE_COMMIT'] || | ||
| // Heroku #3 https://devcenter.heroku.com/changelog-items/630 | ||
| process.env['SOURCE_VERSION'] || | ||
| // Jenkins - https://plugins.jenkins.io/git/#environment-variables | ||
| process.env['GIT_COMMIT'] || | ||
| // Netlify - https://docs.netlify.com/configure-builds/environment-variables/#build-metadata | ||
| process.env['COMMIT_REF'] || | ||
| // TeamCity - https://www.jetbrains.com/help/teamcity/predefined-build-parameters.html | ||
| process.env['BUILD_VCS_NUMBER'] || | ||
| // Woodpecker CI - https://woodpecker-ci.org/docs/usage/environment | ||
| process.env['CI_COMMIT_SHA']; | ||
| return ( | ||
| possibleReleaseNameOfGitProvider || | ||
| possibleReleaseNameOfCiProvidersWithSpecificEnvVar || | ||
| possibleReleaseNameOfCiProvidersWithGenericEnvVar || | ||
| fallback | ||
| process.env["CI_COMMIT_ID"] || // Coolify - https://coolify.io/docs/knowledge-base/environment-variables | ||
| process.env["SOURCE_COMMIT"] || // Heroku #3 https://devcenter.heroku.com/changelog-items/630 | ||
| process.env["SOURCE_VERSION"] || // Jenkins - https://plugins.jenkins.io/git/#environment-variables | ||
| process.env["GIT_COMMIT"] || // Netlify - https://docs.netlify.com/configure-builds/environment-variables/#build-metadata | ||
| process.env["COMMIT_REF"] || // TeamCity - https://www.jetbrains.com/help/teamcity/predefined-build-parameters.html | ||
| process.env["BUILD_VCS_NUMBER"] || // Woodpecker CI - https://woodpecker-ci.org/docs/usage/environment | ||
| process.env["CI_COMMIT_SHA"] | ||
| ); | ||
| return possibleReleaseNameOfGitProvider || possibleReleaseNameOfCiProvidersWithSpecificEnvVar || possibleReleaseNameOfCiProvidersWithGenericEnvVar || fallback; | ||
| } | ||
| /** Node.js stack parser */ | ||
| const defaultStackParser = core.createStackParser(core.nodeStackLineParser(module$1.createGetModuleFromFilename())); | ||
@@ -117,0 +59,0 @@ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"api.js","sources":["../../../src/sdk/api.ts"],"sourcesContent":["// PUBLIC APIS\n\nimport type { StackParser } from '@sentry/core';\nimport { createStackParser, GLOBAL_OBJ, nodeStackLineParser } from '@sentry/core';\nimport { createGetModuleFromFilename } from '../utils/module';\n\n/**\n * Returns a release dynamically from environment variables.\n */\n// eslint-disable-next-line complexity\nexport function getSentryRelease(fallback?: string): string | undefined {\n // Always read first as Sentry takes this as precedence\n if (process.env.SENTRY_RELEASE) {\n return process.env.SENTRY_RELEASE;\n }\n\n // This supports the variable that sentry-webpack-plugin injects\n if (GLOBAL_OBJ.SENTRY_RELEASE?.id) {\n return GLOBAL_OBJ.SENTRY_RELEASE.id;\n }\n\n // This list is in approximate alpha order, separated into 3 categories:\n // 1. Git providers\n // 2. CI providers with specific environment variables (has the provider name in the variable name)\n // 3. CI providers with generic environment variables (checked for last to prevent possible false positives)\n\n const possibleReleaseNameOfGitProvider =\n // GitHub Actions - https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables#default-environment-variables\n process.env['GITHUB_SHA'] ||\n // GitLab CI - https://docs.gitlab.com/ee/ci/variables/predefined_variables.html\n process.env['CI_MERGE_REQUEST_SOURCE_BRANCH_SHA'] ||\n process.env['CI_BUILD_REF'] ||\n process.env['CI_COMMIT_SHA'] ||\n // Bitbucket - https://support.atlassian.com/bitbucket-cloud/docs/variables-and-secrets/\n process.env['BITBUCKET_COMMIT'];\n\n const possibleReleaseNameOfCiProvidersWithSpecificEnvVar =\n // AppVeyor - https://www.appveyor.com/docs/environment-variables/\n process.env['APPVEYOR_PULL_REQUEST_HEAD_COMMIT'] ||\n process.env['APPVEYOR_REPO_COMMIT'] ||\n // AWS CodeBuild - https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-env-vars.html\n process.env['CODEBUILD_RESOLVED_SOURCE_VERSION'] ||\n // AWS Amplify - https://docs.aws.amazon.com/amplify/latest/userguide/environment-variables.html\n process.env['AWS_COMMIT_ID'] ||\n // Azure Pipelines - https://docs.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml\n process.env['BUILD_SOURCEVERSION'] ||\n // Bitrise - https://devcenter.bitrise.io/builds/available-environment-variables/\n process.env['GIT_CLONE_COMMIT_HASH'] ||\n // Buddy CI - https://buddy.works/docs/pipelines/environment-variables#default-environment-variables\n process.env['BUDDY_EXECUTION_REVISION'] ||\n // Builtkite - https://buildkite.com/docs/pipelines/environment-variables\n process.env['BUILDKITE_COMMIT'] ||\n // CircleCI - https://circleci.com/docs/variables/\n process.env['CIRCLE_SHA1'] ||\n // Cirrus CI - https://cirrus-ci.org/guide/writing-tasks/#environment-variables\n process.env['CIRRUS_CHANGE_IN_REPO'] ||\n // Codefresh - https://codefresh.io/docs/docs/codefresh-yaml/variables/\n process.env['CF_REVISION'] ||\n // Codemagic - https://docs.codemagic.io/yaml-basic-configuration/environment-variables/\n process.env['CM_COMMIT'] ||\n // Cloudflare Pages - https://developers.cloudflare.com/pages/platform/build-configuration/#environment-variables\n process.env['CF_PAGES_COMMIT_SHA'] ||\n // Drone - https://docs.drone.io/pipeline/environment/reference/\n process.env['DRONE_COMMIT_SHA'] ||\n // Flightcontrol - https://www.flightcontrol.dev/docs/guides/flightcontrol/environment-variables#built-in-environment-variables\n process.env['FC_GIT_COMMIT_SHA'] ||\n // Heroku #1 https://devcenter.heroku.com/articles/heroku-ci\n process.env['HEROKU_TEST_RUN_COMMIT_VERSION'] ||\n // Heroku #2 https://devcenter.heroku.com/articles/dyno-metadata#dyno-metadata\n process.env['HEROKU_BUILD_COMMIT'] ||\n // Heroku #3 (deprecated by Heroku, kept for backward compatibility)\n process.env['HEROKU_SLUG_COMMIT'] ||\n // Railway - https://docs.railway.app/reference/variables#git-variables\n process.env['RAILWAY_GIT_COMMIT_SHA'] ||\n // Render - https://render.com/docs/environment-variables\n process.env['RENDER_GIT_COMMIT'] ||\n // Semaphore CI - https://docs.semaphoreci.com/ci-cd-environment/environment-variables\n process.env['SEMAPHORE_GIT_SHA'] ||\n // TravisCI - https://docs.travis-ci.com/user/environment-variables/#default-environment-variables\n process.env['TRAVIS_PULL_REQUEST_SHA'] ||\n // Vercel - https://vercel.com/docs/v2/build-step#system-environment-variables\n process.env['VERCEL_GIT_COMMIT_SHA'] ||\n process.env['VERCEL_GITHUB_COMMIT_SHA'] ||\n process.env['VERCEL_GITLAB_COMMIT_SHA'] ||\n process.env['VERCEL_BITBUCKET_COMMIT_SHA'] ||\n // Zeit (now known as Vercel)\n process.env['ZEIT_GITHUB_COMMIT_SHA'] ||\n process.env['ZEIT_GITLAB_COMMIT_SHA'] ||\n process.env['ZEIT_BITBUCKET_COMMIT_SHA'];\n\n const possibleReleaseNameOfCiProvidersWithGenericEnvVar =\n // CloudBees CodeShip - https://docs.cloudbees.com/docs/cloudbees-codeship/latest/pro-builds-and-configuration/environment-variables\n process.env['CI_COMMIT_ID'] ||\n // Coolify - https://coolify.io/docs/knowledge-base/environment-variables\n process.env['SOURCE_COMMIT'] ||\n // Heroku #3 https://devcenter.heroku.com/changelog-items/630\n process.env['SOURCE_VERSION'] ||\n // Jenkins - https://plugins.jenkins.io/git/#environment-variables\n process.env['GIT_COMMIT'] ||\n // Netlify - https://docs.netlify.com/configure-builds/environment-variables/#build-metadata\n process.env['COMMIT_REF'] ||\n // TeamCity - https://www.jetbrains.com/help/teamcity/predefined-build-parameters.html\n process.env['BUILD_VCS_NUMBER'] ||\n // Woodpecker CI - https://woodpecker-ci.org/docs/usage/environment\n process.env['CI_COMMIT_SHA'];\n\n return (\n possibleReleaseNameOfGitProvider ||\n possibleReleaseNameOfCiProvidersWithSpecificEnvVar ||\n possibleReleaseNameOfCiProvidersWithGenericEnvVar ||\n fallback\n );\n}\n\n/** Node.js stack parser */\nexport const defaultStackParser: StackParser = createStackParser(nodeStackLineParser(createGetModuleFromFilename()));\n"],"names":["GLOBAL_OBJ","createStackParser","nodeStackLineParser","createGetModuleFromFilename"],"mappings":"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"} | ||
| {"version":3,"file":"api.js","sources":["../../../src/sdk/api.ts"],"sourcesContent":["// PUBLIC APIS\n\nimport type { StackParser } from '@sentry/core';\nimport { createStackParser, GLOBAL_OBJ, nodeStackLineParser } from '@sentry/core';\nimport { createGetModuleFromFilename } from '../utils/module';\n\n/**\n * Returns a release dynamically from environment variables.\n */\n// eslint-disable-next-line complexity\nexport function getSentryRelease(fallback?: string): string | undefined {\n // Always read first as Sentry takes this as precedence\n if (process.env.SENTRY_RELEASE) {\n return process.env.SENTRY_RELEASE;\n }\n\n // This supports the variable that sentry-webpack-plugin injects\n if (GLOBAL_OBJ.SENTRY_RELEASE?.id) {\n return GLOBAL_OBJ.SENTRY_RELEASE.id;\n }\n\n // This list is in approximate alpha order, separated into 3 categories:\n // 1. Git providers\n // 2. CI providers with specific environment variables (has the provider name in the variable name)\n // 3. CI providers with generic environment variables (checked for last to prevent possible false positives)\n\n const possibleReleaseNameOfGitProvider =\n // GitHub Actions - https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables#default-environment-variables\n process.env['GITHUB_SHA'] ||\n // GitLab CI - https://docs.gitlab.com/ee/ci/variables/predefined_variables.html\n process.env['CI_MERGE_REQUEST_SOURCE_BRANCH_SHA'] ||\n process.env['CI_BUILD_REF'] ||\n process.env['CI_COMMIT_SHA'] ||\n // Bitbucket - https://support.atlassian.com/bitbucket-cloud/docs/variables-and-secrets/\n process.env['BITBUCKET_COMMIT'];\n\n const possibleReleaseNameOfCiProvidersWithSpecificEnvVar =\n // AppVeyor - https://www.appveyor.com/docs/environment-variables/\n process.env['APPVEYOR_PULL_REQUEST_HEAD_COMMIT'] ||\n process.env['APPVEYOR_REPO_COMMIT'] ||\n // AWS CodeBuild - https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-env-vars.html\n process.env['CODEBUILD_RESOLVED_SOURCE_VERSION'] ||\n // AWS Amplify - https://docs.aws.amazon.com/amplify/latest/userguide/environment-variables.html\n process.env['AWS_COMMIT_ID'] ||\n // Azure Pipelines - https://docs.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml\n process.env['BUILD_SOURCEVERSION'] ||\n // Bitrise - https://devcenter.bitrise.io/builds/available-environment-variables/\n process.env['GIT_CLONE_COMMIT_HASH'] ||\n // Buddy CI - https://buddy.works/docs/pipelines/environment-variables#default-environment-variables\n process.env['BUDDY_EXECUTION_REVISION'] ||\n // Builtkite - https://buildkite.com/docs/pipelines/environment-variables\n process.env['BUILDKITE_COMMIT'] ||\n // CircleCI - https://circleci.com/docs/variables/\n process.env['CIRCLE_SHA1'] ||\n // Cirrus CI - https://cirrus-ci.org/guide/writing-tasks/#environment-variables\n process.env['CIRRUS_CHANGE_IN_REPO'] ||\n // Codefresh - https://codefresh.io/docs/docs/codefresh-yaml/variables/\n process.env['CF_REVISION'] ||\n // Codemagic - https://docs.codemagic.io/yaml-basic-configuration/environment-variables/\n process.env['CM_COMMIT'] ||\n // Cloudflare Pages - https://developers.cloudflare.com/pages/platform/build-configuration/#environment-variables\n process.env['CF_PAGES_COMMIT_SHA'] ||\n // Drone - https://docs.drone.io/pipeline/environment/reference/\n process.env['DRONE_COMMIT_SHA'] ||\n // Flightcontrol - https://www.flightcontrol.dev/docs/guides/flightcontrol/environment-variables#built-in-environment-variables\n process.env['FC_GIT_COMMIT_SHA'] ||\n // Heroku #1 https://devcenter.heroku.com/articles/heroku-ci\n process.env['HEROKU_TEST_RUN_COMMIT_VERSION'] ||\n // Heroku #2 https://devcenter.heroku.com/articles/dyno-metadata#dyno-metadata\n process.env['HEROKU_BUILD_COMMIT'] ||\n // Heroku #3 (deprecated by Heroku, kept for backward compatibility)\n process.env['HEROKU_SLUG_COMMIT'] ||\n // Railway - https://docs.railway.app/reference/variables#git-variables\n process.env['RAILWAY_GIT_COMMIT_SHA'] ||\n // Render - https://render.com/docs/environment-variables\n process.env['RENDER_GIT_COMMIT'] ||\n // Semaphore CI - https://docs.semaphoreci.com/ci-cd-environment/environment-variables\n process.env['SEMAPHORE_GIT_SHA'] ||\n // TravisCI - https://docs.travis-ci.com/user/environment-variables/#default-environment-variables\n process.env['TRAVIS_PULL_REQUEST_SHA'] ||\n // Vercel - https://vercel.com/docs/v2/build-step#system-environment-variables\n process.env['VERCEL_GIT_COMMIT_SHA'] ||\n process.env['VERCEL_GITHUB_COMMIT_SHA'] ||\n process.env['VERCEL_GITLAB_COMMIT_SHA'] ||\n process.env['VERCEL_BITBUCKET_COMMIT_SHA'] ||\n // Zeit (now known as Vercel)\n process.env['ZEIT_GITHUB_COMMIT_SHA'] ||\n process.env['ZEIT_GITLAB_COMMIT_SHA'] ||\n process.env['ZEIT_BITBUCKET_COMMIT_SHA'];\n\n const possibleReleaseNameOfCiProvidersWithGenericEnvVar =\n // CloudBees CodeShip - https://docs.cloudbees.com/docs/cloudbees-codeship/latest/pro-builds-and-configuration/environment-variables\n process.env['CI_COMMIT_ID'] ||\n // Coolify - https://coolify.io/docs/knowledge-base/environment-variables\n process.env['SOURCE_COMMIT'] ||\n // Heroku #3 https://devcenter.heroku.com/changelog-items/630\n process.env['SOURCE_VERSION'] ||\n // Jenkins - https://plugins.jenkins.io/git/#environment-variables\n process.env['GIT_COMMIT'] ||\n // Netlify - https://docs.netlify.com/configure-builds/environment-variables/#build-metadata\n process.env['COMMIT_REF'] ||\n // TeamCity - https://www.jetbrains.com/help/teamcity/predefined-build-parameters.html\n process.env['BUILD_VCS_NUMBER'] ||\n // Woodpecker CI - https://woodpecker-ci.org/docs/usage/environment\n process.env['CI_COMMIT_SHA'];\n\n return (\n possibleReleaseNameOfGitProvider ||\n possibleReleaseNameOfCiProvidersWithSpecificEnvVar ||\n possibleReleaseNameOfCiProvidersWithGenericEnvVar ||\n fallback\n );\n}\n\n/** Node.js stack parser */\nexport const defaultStackParser: StackParser = createStackParser(nodeStackLineParser(createGetModuleFromFilename()));\n"],"names":["GLOBAL_OBJ","createStackParser","nodeStackLineParser","createGetModuleFromFilename"],"mappings":";;;;;AAUO,SAAS,iBAAiB,QAAA,EAAuC;AAEtE,EAAA,IAAI,OAAA,CAAQ,IAAI,cAAA,EAAgB;AAC9B,IAAA,OAAO,QAAQ,GAAA,CAAI,cAAA;AAAA,EACrB;AAGA,EAAA,IAAIA,eAAA,CAAW,gBAAgB,EAAA,EAAI;AACjC,IAAA,OAAOA,gBAAW,cAAA,CAAe,EAAA;AAAA,EACnC;AAOA,EAAA,MAAM,gCAAA;AAAA;AAAA,IAEJ,OAAA,CAAQ,IAAI,YAAY,CAAA;AAAA,IAExB,OAAA,CAAQ,GAAA,CAAI,oCAAoC,CAAA,IAChD,OAAA,CAAQ,IAAI,cAAc,CAAA,IAC1B,OAAA,CAAQ,GAAA,CAAI,eAAe,CAAA;AAAA,IAE3B,OAAA,CAAQ,IAAI,kBAAkB;AAAA,GAAA;AAEhC,EAAA,MAAM,kDAAA;AAAA;AAAA,IAEJ,QAAQ,GAAA,CAAI,mCAAmC,CAAA,IAC/C,OAAA,CAAQ,IAAI,sBAAsB,CAAA;AAAA,IAElC,OAAA,CAAQ,IAAI,mCAAmC,CAAA;AAAA,IAE/C,OAAA,CAAQ,IAAI,eAAe,CAAA;AAAA,IAE3B,OAAA,CAAQ,IAAI,qBAAqB,CAAA;AAAA,IAEjC,OAAA,CAAQ,IAAI,uBAAuB,CAAA;AAAA,IAEnC,OAAA,CAAQ,IAAI,0BAA0B,CAAA;AAAA,IAEtC,OAAA,CAAQ,IAAI,kBAAkB,CAAA;AAAA,IAE9B,OAAA,CAAQ,IAAI,aAAa,CAAA;AAAA,IAEzB,OAAA,CAAQ,IAAI,uBAAuB,CAAA;AAAA,IAEnC,OAAA,CAAQ,IAAI,aAAa,CAAA;AAAA,IAEzB,OAAA,CAAQ,IAAI,WAAW,CAAA;AAAA,IAEvB,OAAA,CAAQ,IAAI,qBAAqB,CAAA;AAAA,IAEjC,OAAA,CAAQ,IAAI,kBAAkB,CAAA;AAAA,IAE9B,OAAA,CAAQ,IAAI,mBAAmB,CAAA;AAAA,IAE/B,OAAA,CAAQ,IAAI,gCAAgC,CAAA;AAAA,IAE5C,OAAA,CAAQ,IAAI,qBAAqB,CAAA;AAAA,IAEjC,OAAA,CAAQ,IAAI,oBAAoB,CAAA;AAAA,IAEhC,OAAA,CAAQ,IAAI,wBAAwB,CAAA;AAAA,IAEpC,OAAA,CAAQ,IAAI,mBAAmB,CAAA;AAAA,IAE/B,OAAA,CAAQ,IAAI,mBAAmB,CAAA;AAAA,IAE/B,OAAA,CAAQ,IAAI,yBAAyB,CAAA;AAAA,IAErC,OAAA,CAAQ,GAAA,CAAI,uBAAuB,CAAA,IACnC,QAAQ,GAAA,CAAI,0BAA0B,CAAA,IACtC,OAAA,CAAQ,GAAA,CAAI,0BAA0B,CAAA,IACtC,OAAA,CAAQ,IAAI,6BAA6B,CAAA;AAAA,IAEzC,OAAA,CAAQ,GAAA,CAAI,wBAAwB,CAAA,IACpC,OAAA,CAAQ,IAAI,wBAAwB,CAAA,IACpC,OAAA,CAAQ,GAAA,CAAI,2BAA2B;AAAA,GAAA;AAEzC,EAAA,MAAM,iDAAA;AAAA;AAAA,IAEJ,OAAA,CAAQ,IAAI,cAAc,CAAA;AAAA,IAE1B,OAAA,CAAQ,IAAI,eAAe,CAAA;AAAA,IAE3B,OAAA,CAAQ,IAAI,gBAAgB,CAAA;AAAA,IAE5B,OAAA,CAAQ,IAAI,YAAY,CAAA;AAAA,IAExB,OAAA,CAAQ,IAAI,YAAY,CAAA;AAAA,IAExB,OAAA,CAAQ,IAAI,kBAAkB,CAAA;AAAA,IAE9B,OAAA,CAAQ,IAAI,eAAe;AAAA,GAAA;AAE7B,EAAA,OACE,gCAAA,IACA,sDACA,iDAAA,IACA,QAAA;AAEJ;AAGO,MAAM,kBAAA,GAAkCC,sBAAA,CAAkBC,wBAAA,CAAoBC,oCAAA,EAA6B,CAAC;;;;;"} |
+25
-63
@@ -11,33 +11,21 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const DEFAULT_CLIENT_REPORT_FLUSH_INTERVAL_MS = 60000; // 60s was chosen arbitrarily | ||
| /** A client for using Sentry with Node & OpenTelemetry. */ | ||
| const DEFAULT_CLIENT_REPORT_FLUSH_INTERVAL_MS = 6e4; | ||
| class NodeClient extends core.ServerRuntimeClient { | ||
| constructor(options) { | ||
| const serverName = | ||
| options.includeServerName === false | ||
| ? undefined | ||
| : options.serverName || global.process.env.SENTRY_NAME || os.hostname(); | ||
| constructor(options) { | ||
| const serverName = options.includeServerName === false ? void 0 : options.serverName || global.process.env.SENTRY_NAME || os.hostname(); | ||
| const clientOptions = { | ||
| ...options, | ||
| platform: 'node', | ||
| platform: "node", | ||
| // Use provided runtime or default to 'node' with current process version | ||
| runtime: options.runtime || { name: 'node', version: global.process.version }, | ||
| serverName, | ||
| runtime: options.runtime || { name: "node", version: global.process.version }, | ||
| serverName | ||
| }; | ||
| if (options.openTelemetryInstrumentations) { | ||
| instrumentation.registerInstrumentations({ | ||
| instrumentations: options.openTelemetryInstrumentations, | ||
| instrumentations: options.openTelemetryInstrumentations | ||
| }); | ||
| } | ||
| core.applySdkMetadata(clientOptions, 'node'); | ||
| core.debug.log(`Initializing Sentry: process: ${process.pid}, thread: ${worker_threads.isMainThread ? 'main' : `worker-${worker_threads.threadId}`}.`); | ||
| core.applySdkMetadata(clientOptions, "node"); | ||
| core.debug.log(`Initializing Sentry: process: ${process.pid}, thread: ${worker_threads.isMainThread ? "main" : `worker-${worker_threads.threadId}`}.`); | ||
| super(clientOptions); | ||
| if (this.getOptions().enableLogs) { | ||
@@ -47,57 +35,45 @@ this._logOnExitFlushListener = () => { | ||
| }; | ||
| if (serverName) { | ||
| this.on('beforeCaptureLog', log => { | ||
| this.on("beforeCaptureLog", (log) => { | ||
| log.attributes = { | ||
| ...log.attributes, | ||
| 'server.address': serverName, | ||
| "server.address": serverName | ||
| }; | ||
| }); | ||
| } | ||
| process.on('beforeExit', this._logOnExitFlushListener); | ||
| process.on("beforeExit", this._logOnExitFlushListener); | ||
| } | ||
| } | ||
| /** Get the OTEL tracer. */ | ||
| get tracer() { | ||
| get tracer() { | ||
| if (this._tracer) { | ||
| return this._tracer; | ||
| } | ||
| const name = '@sentry/node'; | ||
| const name = "@sentry/node"; | ||
| const version = core.SDK_VERSION; | ||
| const tracer = api.trace.getTracer(name, version); | ||
| this._tracer = tracer; | ||
| return tracer; | ||
| } | ||
| /** @inheritDoc */ | ||
| // @ts-expect-error - PromiseLike is a subset of Promise | ||
| async flush(timeout) { | ||
| async flush(timeout) { | ||
| await this.traceProvider?.forceFlush(); | ||
| if (this.getOptions().sendClientReports) { | ||
| this._flushOutcomes(); | ||
| } | ||
| return super.flush(timeout); | ||
| } | ||
| /** @inheritDoc */ | ||
| // @ts-expect-error - PromiseLike is a subset of Promise | ||
| async close(timeout) { | ||
| async close(timeout) { | ||
| if (this._clientReportInterval) { | ||
| clearInterval(this._clientReportInterval); | ||
| } | ||
| if (this._clientReportOnExitFlushListener) { | ||
| process.off('beforeExit', this._clientReportOnExitFlushListener); | ||
| process.off("beforeExit", this._clientReportOnExitFlushListener); | ||
| } | ||
| if (this._logOnExitFlushListener) { | ||
| process.off('beforeExit', this._logOnExitFlushListener); | ||
| process.off("beforeExit", this._logOnExitFlushListener); | ||
| } | ||
| const allEventsSent = await super.close(timeout); | ||
@@ -107,6 +83,4 @@ if (this.traceProvider) { | ||
| } | ||
| return allEventsSent; | ||
| } | ||
| /** | ||
@@ -127,3 +101,3 @@ * Will start tracking client reports for this client. | ||
| // collected, but it did not work, because the cleanup function never got called. | ||
| startClientReportTracking() { | ||
| startClientReportTracking() { | ||
| const clientOptions = this.getOptions(); | ||
@@ -134,31 +108,19 @@ if (clientOptions.sendClientReports) { | ||
| }; | ||
| this._clientReportInterval = setInterval(() => { | ||
| debugBuild.DEBUG_BUILD && core.debug.log('Flushing client reports based on interval.'); | ||
| debugBuild.DEBUG_BUILD && core.debug.log("Flushing client reports based on interval."); | ||
| this._flushOutcomes(); | ||
| }, clientOptions.clientReportFlushInterval ?? DEFAULT_CLIENT_REPORT_FLUSH_INTERVAL_MS) | ||
| // Unref is critical for not preventing the process from exiting because the interval is active. | ||
| .unref(); | ||
| process.on('beforeExit', this._clientReportOnExitFlushListener); | ||
| }, clientOptions.clientReportFlushInterval ?? DEFAULT_CLIENT_REPORT_FLUSH_INTERVAL_MS).unref(); | ||
| process.on("beforeExit", this._clientReportOnExitFlushListener); | ||
| } | ||
| } | ||
| /** @inheritDoc */ | ||
| _setupIntegrations() { | ||
| // Clear AI provider skip registrations before setting up integrations | ||
| // This ensures a clean state between different client initializations | ||
| // (e.g., when LangChain skips OpenAI in one client, but a subsequent client uses OpenAI standalone) | ||
| _setupIntegrations() { | ||
| core._INTERNAL_clearAiProviderSkips(); | ||
| super._setupIntegrations(); | ||
| } | ||
| /** Custom implementation for OTEL, so we can handle scope-span linking. */ | ||
| _getTraceInfoFromScope( | ||
| scope, | ||
| ) { | ||
| _getTraceInfoFromScope(scope) { | ||
| if (!scope) { | ||
| return [undefined, undefined]; | ||
| return [void 0, void 0]; | ||
| } | ||
| return opentelemetry.getTraceContextForScope(this, scope); | ||
@@ -165,0 +127,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"client.js","sources":["../../../src/sdk/client.ts"],"sourcesContent":["import * as os from 'node:os';\nimport type { Tracer } from '@opentelemetry/api';\nimport { trace } from '@opentelemetry/api';\nimport { registerInstrumentations } from '@opentelemetry/instrumentation';\nimport type { BasicTracerProvider } from '@opentelemetry/sdk-trace-base';\nimport type { DynamicSamplingContext, Scope, ServerRuntimeClientOptions, TraceContext } from '@sentry/core';\nimport {\n _INTERNAL_clearAiProviderSkips,\n _INTERNAL_flushLogsBuffer,\n applySdkMetadata,\n debug,\n SDK_VERSION,\n ServerRuntimeClient,\n} from '@sentry/core';\nimport { type AsyncLocalStorageLookup, getTraceContextForScope } from '@sentry/opentelemetry';\nimport { isMainThread, threadId } from 'worker_threads';\nimport { DEBUG_BUILD } from '../debug-build';\nimport type { NodeClientOptions } from '../types';\n\nconst DEFAULT_CLIENT_REPORT_FLUSH_INTERVAL_MS = 60_000; // 60s was chosen arbitrarily\n\n/** A client for using Sentry with Node & OpenTelemetry. */\nexport class NodeClient extends ServerRuntimeClient<NodeClientOptions> {\n public traceProvider: BasicTracerProvider | undefined;\n public asyncLocalStorageLookup: AsyncLocalStorageLookup | undefined;\n\n private _tracer: Tracer | undefined;\n private _clientReportInterval: NodeJS.Timeout | undefined;\n private _clientReportOnExitFlushListener: (() => void) | undefined;\n private _logOnExitFlushListener: (() => void) | undefined;\n\n public constructor(options: NodeClientOptions) {\n const serverName =\n options.includeServerName === false\n ? undefined\n : options.serverName || global.process.env.SENTRY_NAME || os.hostname();\n\n const clientOptions: ServerRuntimeClientOptions = {\n ...options,\n platform: 'node',\n // Use provided runtime or default to 'node' with current process version\n runtime: options.runtime || { name: 'node', version: global.process.version },\n serverName,\n };\n\n if (options.openTelemetryInstrumentations) {\n registerInstrumentations({\n instrumentations: options.openTelemetryInstrumentations,\n });\n }\n\n applySdkMetadata(clientOptions, 'node');\n\n debug.log(`Initializing Sentry: process: ${process.pid}, thread: ${isMainThread ? 'main' : `worker-${threadId}`}.`);\n\n super(clientOptions);\n\n if (this.getOptions().enableLogs) {\n this._logOnExitFlushListener = () => {\n _INTERNAL_flushLogsBuffer(this);\n };\n\n if (serverName) {\n this.on('beforeCaptureLog', log => {\n log.attributes = {\n ...log.attributes,\n 'server.address': serverName,\n };\n });\n }\n\n process.on('beforeExit', this._logOnExitFlushListener);\n }\n }\n\n /** Get the OTEL tracer. */\n public get tracer(): Tracer {\n if (this._tracer) {\n return this._tracer;\n }\n\n const name = '@sentry/node';\n const version = SDK_VERSION;\n const tracer = trace.getTracer(name, version);\n this._tracer = tracer;\n\n return tracer;\n }\n\n /** @inheritDoc */\n // @ts-expect-error - PromiseLike is a subset of Promise\n public async flush(timeout?: number): PromiseLike<boolean> {\n await this.traceProvider?.forceFlush();\n\n if (this.getOptions().sendClientReports) {\n this._flushOutcomes();\n }\n\n return super.flush(timeout);\n }\n\n /** @inheritDoc */\n // @ts-expect-error - PromiseLike is a subset of Promise\n public async close(timeout?: number | undefined): PromiseLike<boolean> {\n if (this._clientReportInterval) {\n clearInterval(this._clientReportInterval);\n }\n\n if (this._clientReportOnExitFlushListener) {\n process.off('beforeExit', this._clientReportOnExitFlushListener);\n }\n\n if (this._logOnExitFlushListener) {\n process.off('beforeExit', this._logOnExitFlushListener);\n }\n\n const allEventsSent = await super.close(timeout);\n if (this.traceProvider) {\n await this.traceProvider.shutdown();\n }\n\n return allEventsSent;\n }\n\n /**\n * Will start tracking client reports for this client.\n *\n * NOTICE: This method will create an interval that is periodically called and attach a `process.on('beforeExit')`\n * hook. To clean up these resources, call `.close()` when you no longer intend to use the client. Not doing so will\n * result in a memory leak.\n */\n // The reason client reports need to be manually activated with this method instead of just enabling them in a\n // constructor, is that if users periodically and unboundedly create new clients, we will create more and more\n // intervals and beforeExit listeners, thus leaking memory. In these situations, users are required to call\n // `client.close()` in order to dispose of the acquired resources.\n // We assume that calling this method in Sentry.init() is a sensible default, because calling Sentry.init() over and\n // over again would also result in memory leaks.\n // Note: We have experimented with using `FinalizationRegisty` to clear the interval when the client is garbage\n // collected, but it did not work, because the cleanup function never got called.\n public startClientReportTracking(): void {\n const clientOptions = this.getOptions();\n if (clientOptions.sendClientReports) {\n this._clientReportOnExitFlushListener = () => {\n this._flushOutcomes();\n };\n\n this._clientReportInterval = setInterval(() => {\n DEBUG_BUILD && debug.log('Flushing client reports based on interval.');\n this._flushOutcomes();\n }, clientOptions.clientReportFlushInterval ?? DEFAULT_CLIENT_REPORT_FLUSH_INTERVAL_MS)\n // Unref is critical for not preventing the process from exiting because the interval is active.\n .unref();\n\n process.on('beforeExit', this._clientReportOnExitFlushListener);\n }\n }\n\n /** @inheritDoc */\n protected _setupIntegrations(): void {\n // Clear AI provider skip registrations before setting up integrations\n // This ensures a clean state between different client initializations\n // (e.g., when LangChain skips OpenAI in one client, but a subsequent client uses OpenAI standalone)\n _INTERNAL_clearAiProviderSkips();\n super._setupIntegrations();\n }\n\n /** Custom implementation for OTEL, so we can handle scope-span linking. */\n protected _getTraceInfoFromScope(\n scope: Scope | undefined,\n ): [dynamicSamplingContext: Partial<DynamicSamplingContext> | undefined, traceContext: TraceContext | undefined] {\n if (!scope) {\n return [undefined, undefined];\n }\n\n return getTraceContextForScope(this, scope);\n }\n}\n"],"names":["ServerRuntimeClient","registerInstrumentations","applySdkMetadata","debug","isMainThread","threadId","_INTERNAL_flushLogsBuffer","SDK_VERSION","trace","DEBUG_BUILD","_INTERNAL_clearAiProviderSkips","getTraceContextForScope"],"mappings":"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"} | ||
| {"version":3,"file":"client.js","sources":["../../../src/sdk/client.ts"],"sourcesContent":["import * as os from 'node:os';\nimport type { Tracer } from '@opentelemetry/api';\nimport { trace } from '@opentelemetry/api';\nimport { registerInstrumentations } from '@opentelemetry/instrumentation';\nimport type { BasicTracerProvider } from '@opentelemetry/sdk-trace-base';\nimport type { DynamicSamplingContext, Scope, ServerRuntimeClientOptions, TraceContext } from '@sentry/core';\nimport {\n _INTERNAL_clearAiProviderSkips,\n _INTERNAL_flushLogsBuffer,\n applySdkMetadata,\n debug,\n SDK_VERSION,\n ServerRuntimeClient,\n} from '@sentry/core';\nimport { type AsyncLocalStorageLookup, getTraceContextForScope } from '@sentry/opentelemetry';\nimport { isMainThread, threadId } from 'worker_threads';\nimport { DEBUG_BUILD } from '../debug-build';\nimport type { NodeClientOptions } from '../types';\n\nconst DEFAULT_CLIENT_REPORT_FLUSH_INTERVAL_MS = 60_000; // 60s was chosen arbitrarily\n\n/** A client for using Sentry with Node & OpenTelemetry. */\nexport class NodeClient extends ServerRuntimeClient<NodeClientOptions> {\n public traceProvider: BasicTracerProvider | undefined;\n public asyncLocalStorageLookup: AsyncLocalStorageLookup | undefined;\n\n private _tracer: Tracer | undefined;\n private _clientReportInterval: NodeJS.Timeout | undefined;\n private _clientReportOnExitFlushListener: (() => void) | undefined;\n private _logOnExitFlushListener: (() => void) | undefined;\n\n public constructor(options: NodeClientOptions) {\n const serverName =\n options.includeServerName === false\n ? undefined\n : options.serverName || global.process.env.SENTRY_NAME || os.hostname();\n\n const clientOptions: ServerRuntimeClientOptions = {\n ...options,\n platform: 'node',\n // Use provided runtime or default to 'node' with current process version\n runtime: options.runtime || { name: 'node', version: global.process.version },\n serverName,\n };\n\n if (options.openTelemetryInstrumentations) {\n registerInstrumentations({\n instrumentations: options.openTelemetryInstrumentations,\n });\n }\n\n applySdkMetadata(clientOptions, 'node');\n\n debug.log(`Initializing Sentry: process: ${process.pid}, thread: ${isMainThread ? 'main' : `worker-${threadId}`}.`);\n\n super(clientOptions);\n\n if (this.getOptions().enableLogs) {\n this._logOnExitFlushListener = () => {\n _INTERNAL_flushLogsBuffer(this);\n };\n\n if (serverName) {\n this.on('beforeCaptureLog', log => {\n log.attributes = {\n ...log.attributes,\n 'server.address': serverName,\n };\n });\n }\n\n process.on('beforeExit', this._logOnExitFlushListener);\n }\n }\n\n /** Get the OTEL tracer. */\n public get tracer(): Tracer {\n if (this._tracer) {\n return this._tracer;\n }\n\n const name = '@sentry/node';\n const version = SDK_VERSION;\n const tracer = trace.getTracer(name, version);\n this._tracer = tracer;\n\n return tracer;\n }\n\n /** @inheritDoc */\n // @ts-expect-error - PromiseLike is a subset of Promise\n public async flush(timeout?: number): PromiseLike<boolean> {\n await this.traceProvider?.forceFlush();\n\n if (this.getOptions().sendClientReports) {\n this._flushOutcomes();\n }\n\n return super.flush(timeout);\n }\n\n /** @inheritDoc */\n // @ts-expect-error - PromiseLike is a subset of Promise\n public async close(timeout?: number | undefined): PromiseLike<boolean> {\n if (this._clientReportInterval) {\n clearInterval(this._clientReportInterval);\n }\n\n if (this._clientReportOnExitFlushListener) {\n process.off('beforeExit', this._clientReportOnExitFlushListener);\n }\n\n if (this._logOnExitFlushListener) {\n process.off('beforeExit', this._logOnExitFlushListener);\n }\n\n const allEventsSent = await super.close(timeout);\n if (this.traceProvider) {\n await this.traceProvider.shutdown();\n }\n\n return allEventsSent;\n }\n\n /**\n * Will start tracking client reports for this client.\n *\n * NOTICE: This method will create an interval that is periodically called and attach a `process.on('beforeExit')`\n * hook. To clean up these resources, call `.close()` when you no longer intend to use the client. Not doing so will\n * result in a memory leak.\n */\n // The reason client reports need to be manually activated with this method instead of just enabling them in a\n // constructor, is that if users periodically and unboundedly create new clients, we will create more and more\n // intervals and beforeExit listeners, thus leaking memory. In these situations, users are required to call\n // `client.close()` in order to dispose of the acquired resources.\n // We assume that calling this method in Sentry.init() is a sensible default, because calling Sentry.init() over and\n // over again would also result in memory leaks.\n // Note: We have experimented with using `FinalizationRegisty` to clear the interval when the client is garbage\n // collected, but it did not work, because the cleanup function never got called.\n public startClientReportTracking(): void {\n const clientOptions = this.getOptions();\n if (clientOptions.sendClientReports) {\n this._clientReportOnExitFlushListener = () => {\n this._flushOutcomes();\n };\n\n this._clientReportInterval = setInterval(() => {\n DEBUG_BUILD && debug.log('Flushing client reports based on interval.');\n this._flushOutcomes();\n }, clientOptions.clientReportFlushInterval ?? DEFAULT_CLIENT_REPORT_FLUSH_INTERVAL_MS)\n // Unref is critical for not preventing the process from exiting because the interval is active.\n .unref();\n\n process.on('beforeExit', this._clientReportOnExitFlushListener);\n }\n }\n\n /** @inheritDoc */\n protected _setupIntegrations(): void {\n // Clear AI provider skip registrations before setting up integrations\n // This ensures a clean state between different client initializations\n // (e.g., when LangChain skips OpenAI in one client, but a subsequent client uses OpenAI standalone)\n _INTERNAL_clearAiProviderSkips();\n super._setupIntegrations();\n }\n\n /** Custom implementation for OTEL, so we can handle scope-span linking. */\n protected _getTraceInfoFromScope(\n scope: Scope | undefined,\n ): [dynamicSamplingContext: Partial<DynamicSamplingContext> | undefined, traceContext: TraceContext | undefined] {\n if (!scope) {\n return [undefined, undefined];\n }\n\n return getTraceContextForScope(this, scope);\n }\n}\n"],"names":["ServerRuntimeClient","registerInstrumentations","applySdkMetadata","debug","isMainThread","threadId","_INTERNAL_flushLogsBuffer","SDK_VERSION","trace","DEBUG_BUILD","_INTERNAL_clearAiProviderSkips","getTraceContextForScope"],"mappings":"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"} |
@@ -9,8 +9,2 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null; | ||
| /** | ||
| * Initialize the ESM loader - This method is private and not part of the public | ||
| * API. | ||
| * | ||
| * @ignore | ||
| */ | ||
| function initializeEsmLoader() { | ||
@@ -20,12 +14,9 @@ if (!detection.supportsEsmLoaderHooks()) { | ||
| } | ||
| if (!core.GLOBAL_OBJ._sentryEsmLoaderHookRegistered) { | ||
| core.GLOBAL_OBJ._sentryEsmLoaderHookRegistered = true; | ||
| try { | ||
| const { addHookMessagePort } = importInTheMiddle.createAddHookMessageChannel(); | ||
| // @ts-expect-error register is available in these versions | ||
| moduleModule.register('import-in-the-middle/hook.mjs', (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('sdk/esmLoader.js', document.baseURI).href)), { | ||
| moduleModule.register("import-in-the-middle/hook.mjs", (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('sdk/esmLoader.js', document.baseURI).href)), { | ||
| data: { addHookMessagePort, include: [] }, | ||
| transferList: [addHookMessagePort], | ||
| transferList: [addHookMessagePort] | ||
| }); | ||
@@ -32,0 +23,0 @@ } catch (error) { |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"esmLoader.js","sources":["../../../src/sdk/esmLoader.ts"],"sourcesContent":["import { debug, GLOBAL_OBJ } from '@sentry/core';\nimport { createAddHookMessageChannel } from 'import-in-the-middle';\nimport * as moduleModule from 'module';\nimport { supportsEsmLoaderHooks } from '../utils/detection';\n\n/**\n * Initialize the ESM loader - This method is private and not part of the public\n * API.\n *\n * @ignore\n */\nexport function initializeEsmLoader(): void {\n if (!supportsEsmLoaderHooks()) {\n return;\n }\n\n if (!GLOBAL_OBJ._sentryEsmLoaderHookRegistered) {\n GLOBAL_OBJ._sentryEsmLoaderHookRegistered = true;\n\n try {\n const { addHookMessagePort } = createAddHookMessageChannel();\n // @ts-expect-error register is available in these versions\n moduleModule.register('import-in-the-middle/hook.mjs', import.meta.url, {\n data: { addHookMessagePort, include: [] },\n transferList: [addHookMessagePort],\n });\n } catch (error) {\n debug.warn(\"Failed to register 'import-in-the-middle' hook\", error);\n }\n }\n}\n"],"names":["supportsEsmLoaderHooks","GLOBAL_OBJ","createAddHookMessageChannel","debug"],"mappings":";;;;;;;;AAKA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,mBAAmB,GAAS;AAC5C,EAAE,IAAI,CAACA,gCAAsB,EAAE,EAAE;AACjC,IAAI;AACJ,EAAE;;AAEF,EAAE,IAAI,CAACC,eAAU,CAAC,8BAA8B,EAAE;AAClD,IAAIA,eAAU,CAAC,8BAAA,GAAiC,IAAI;;AAEpD,IAAI,IAAI;AACR,MAAM,MAAM,EAAE,kBAAA,KAAuBC,6CAA2B,EAAE;AAClE;AACA,MAAM,YAAY,CAAC,QAAQ,CAAC,+BAA+B,EAAE,kQAAe,EAAE;AAC9E,QAAQ,IAAI,EAAE,EAAE,kBAAkB,EAAE,OAAO,EAAE,EAAC,EAAG;AACjD,QAAQ,YAAY,EAAE,CAAC,kBAAkB,CAAC;AAC1C,OAAO,CAAC;AACR,IAAI,CAAA,CAAE,OAAO,KAAK,EAAE;AACpB,MAAMC,UAAK,CAAC,IAAI,CAAC,gDAAgD,EAAE,KAAK,CAAC;AACzE,IAAI;AACJ,EAAE;AACF;;;;"} | ||
| {"version":3,"file":"esmLoader.js","sources":["../../../src/sdk/esmLoader.ts"],"sourcesContent":["import { debug, GLOBAL_OBJ } from '@sentry/core';\nimport { createAddHookMessageChannel } from 'import-in-the-middle';\nimport * as moduleModule from 'module';\nimport { supportsEsmLoaderHooks } from '../utils/detection';\n\n/**\n * Initialize the ESM loader - This method is private and not part of the public\n * API.\n *\n * @ignore\n */\nexport function initializeEsmLoader(): void {\n if (!supportsEsmLoaderHooks()) {\n return;\n }\n\n if (!GLOBAL_OBJ._sentryEsmLoaderHookRegistered) {\n GLOBAL_OBJ._sentryEsmLoaderHookRegistered = true;\n\n try {\n const { addHookMessagePort } = createAddHookMessageChannel();\n // @ts-expect-error register is available in these versions\n moduleModule.register('import-in-the-middle/hook.mjs', import.meta.url, {\n data: { addHookMessagePort, include: [] },\n transferList: [addHookMessagePort],\n });\n } catch (error) {\n debug.warn(\"Failed to register 'import-in-the-middle' hook\", error);\n }\n }\n}\n"],"names":["supportsEsmLoaderHooks","GLOBAL_OBJ","createAddHookMessageChannel","debug"],"mappings":";;;;;;;;AAWO,SAAS,mBAAA,GAA4B;AAC1C,EAAA,IAAI,CAACA,kCAAuB,EAAG;AAC7B,IAAA;AAAA,EACF;AAEA,EAAA,IAAI,CAACC,gBAAW,8BAAA,EAAgC;AAC9C,IAAAA,eAAA,CAAW,8BAAA,GAAiC,IAAA;AAE5C,IAAA,IAAI;AACF,MAAA,MAAM,EAAE,kBAAA,EAAmB,GAAIC,6CAAA,EAA4B;AAE3D,MAAA,YAAA,CAAa,QAAA,CAAS,+BAAA,EAAiC,kQAAY,EAAK;AAAA,QACtE,IAAA,EAAM,EAAE,kBAAA,EAAoB,OAAA,EAAS,EAAC,EAAE;AAAA,QACxC,YAAA,EAAc,CAAC,kBAAkB;AAAA,OAClC,CAAA;AAAA,IACH,SAAS,KAAA,EAAO;AACd,MAAAC,UAAA,CAAM,IAAA,CAAK,kDAAkD,KAAK,CAAA;AAAA,IACpE;AAAA,EACF;AACF;;;;"} |
+24
-95
@@ -26,5 +26,2 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| /** | ||
| * Get default integrations for the Node-Core SDK. | ||
| */ | ||
| function getDefaultIntegrations() { | ||
@@ -54,29 +51,13 @@ return [ | ||
| processSession.processSessionIntegration(), | ||
| modules.modulesIntegration(), | ||
| modules.modulesIntegration() | ||
| ]; | ||
| } | ||
| /** | ||
| * Initialize Sentry for Node. | ||
| */ | ||
| function init(options = {}) { | ||
| return _init(options, getDefaultIntegrations); | ||
| } | ||
| /** | ||
| * Initialize Sentry for Node, without any integrations added by default. | ||
| */ | ||
| function initWithoutDefaultIntegrations(options = {}) { | ||
| return _init(options, () => []); | ||
| } | ||
| /** | ||
| * Initialize Sentry for Node, without performance instrumentation. | ||
| */ | ||
| function _init( | ||
| _options = {}, | ||
| getDefaultIntegrationsImpl, | ||
| ) { | ||
| function _init(_options = {}, getDefaultIntegrationsImpl) { | ||
| const options = getClientOptions(_options, getDefaultIntegrationsImpl); | ||
| if (options.debug === true) { | ||
@@ -86,59 +67,36 @@ if (debugBuild.DEBUG_BUILD) { | ||
| } else { | ||
| // use `console.warn` rather than `debug.warn` since by non-debug bundles have all `debug.x` statements stripped | ||
| core.consoleSandbox(() => { | ||
| // eslint-disable-next-line no-console | ||
| console.warn('[Sentry] Cannot initialize SDK with `debug` option using a non-debug bundle.'); | ||
| console.warn("[Sentry] Cannot initialize SDK with `debug` option using a non-debug bundle."); | ||
| }); | ||
| } | ||
| } | ||
| if (options.registerEsmLoaderHooks !== false) { | ||
| esmLoader.initializeEsmLoader(); | ||
| } | ||
| opentelemetry.setOpenTelemetryContextAsyncContextStrategy(); | ||
| const scope = core.getCurrentScope(); | ||
| scope.update(options.initialScope); | ||
| if (options.spotlight && !options.integrations.some(({ name }) => name === spotlight.INTEGRATION_NAME)) { | ||
| options.integrations.push( | ||
| spotlight.spotlightIntegration({ | ||
| sidecarUrl: typeof options.spotlight === 'string' ? options.spotlight : undefined, | ||
| }), | ||
| sidecarUrl: typeof options.spotlight === "string" ? options.spotlight : void 0 | ||
| }) | ||
| ); | ||
| } | ||
| core.applySdkMetadata(options, 'node-core'); | ||
| core.applySdkMetadata(options, "node-core"); | ||
| const client$1 = new client.NodeClient(options); | ||
| // The client is on the current scope, from where it generally is inherited | ||
| core.getCurrentScope().setClient(client$1); | ||
| client$1.init(); | ||
| core.debug.log(`SDK initialized from ${detection.isCjs() ? 'CommonJS' : 'ESM'}`); | ||
| core.debug.log(`SDK initialized from ${detection.isCjs() ? "CommonJS" : "ESM"}`); | ||
| client$1.startClientReportTracking(); | ||
| updateScopeFromEnvVariables(); | ||
| opentelemetry.enhanceDscWithOpenTelemetryRootSpanName(client$1); | ||
| opentelemetry.setupEventContextTrace(client$1); | ||
| // Ensure we flush events when vercel functions are ended | ||
| // See: https://vercel.com/docs/functions/functions-api-reference#sigterm-signal | ||
| if (process.env.VERCEL) { | ||
| process.on('SIGTERM', async () => { | ||
| // We have 500ms for processing here, so we try to make sure to have enough time to send the events | ||
| process.on("SIGTERM", async () => { | ||
| await client$1.flush(200); | ||
| }); | ||
| } | ||
| return client$1; | ||
| } | ||
| /** | ||
| * Validate that your OpenTelemetry setup is correct. | ||
| */ | ||
| function validateOpenTelemetrySetup() { | ||
@@ -148,36 +106,24 @@ if (!debugBuild.DEBUG_BUILD) { | ||
| } | ||
| const setup = opentelemetry.openTelemetrySetupCheck(); | ||
| const required = ['SentryContextManager', 'SentryPropagator']; | ||
| const required = ["SentryContextManager", "SentryPropagator"]; | ||
| if (core.hasSpansEnabled()) { | ||
| required.push('SentrySpanProcessor'); | ||
| required.push("SentrySpanProcessor"); | ||
| } | ||
| for (const k of required) { | ||
| if (!setup.includes(k)) { | ||
| core.debug.error( | ||
| `You have to set up the ${k}. Without this, the OpenTelemetry & Sentry integration will not work properly.`, | ||
| `You have to set up the ${k}. Without this, the OpenTelemetry & Sentry integration will not work properly.` | ||
| ); | ||
| } | ||
| } | ||
| if (!setup.includes('SentrySampler')) { | ||
| if (!setup.includes("SentrySampler")) { | ||
| core.debug.warn( | ||
| 'You have to set up the SentrySampler. Without this, the OpenTelemetry & Sentry integration may still work, but sample rates set for the Sentry SDK will not be respected. If you use a custom sampler, make sure to use `wrapSamplingDecision`.', | ||
| "You have to set up the SentrySampler. Without this, the OpenTelemetry & Sentry integration may still work, but sample rates set for the Sentry SDK will not be respected. If you use a custom sampler, make sure to use `wrapSamplingDecision`." | ||
| ); | ||
| } | ||
| } | ||
| function getClientOptions( | ||
| options, | ||
| getDefaultIntegrationsImpl, | ||
| ) { | ||
| function getClientOptions(options, getDefaultIntegrationsImpl) { | ||
| const release = getRelease(options.release); | ||
| const spotlight = spotlight$1.getSpotlightConfig(options.spotlight); | ||
| const tracesSampleRate = getTracesSampleRate(options.tracesSampleRate); | ||
| const mergedOptions = { | ||
@@ -193,56 +139,39 @@ ...options, | ||
| spotlight, | ||
| debug: core.envToBool(options.debug ?? process.env.SENTRY_DEBUG), | ||
| debug: core.envToBool(options.debug ?? process.env.SENTRY_DEBUG) | ||
| }; | ||
| const integrations = options.integrations; | ||
| const defaultIntegrations = options.defaultIntegrations ?? getDefaultIntegrationsImpl(mergedOptions); | ||
| const resolvedIntegrations = core.getIntegrationsToSetup({ | ||
| defaultIntegrations, | ||
| integrations, | ||
| integrations | ||
| }); | ||
| if (mergedOptions.traceLifecycle === 'stream' && !resolvedIntegrations.some(i => i.name === 'SpanStreaming')) { | ||
| if (mergedOptions.traceLifecycle === "stream" && !resolvedIntegrations.some((i) => i.name === "SpanStreaming")) { | ||
| resolvedIntegrations.push(core.spanStreamingIntegration()); | ||
| } | ||
| return { | ||
| ...mergedOptions, | ||
| integrations: resolvedIntegrations, | ||
| integrations: resolvedIntegrations | ||
| }; | ||
| } | ||
| function getRelease(release) { | ||
| if (release !== undefined) { | ||
| if (release !== void 0) { | ||
| return release; | ||
| } | ||
| const detectedRelease = api.getSentryRelease(); | ||
| if (detectedRelease !== undefined) { | ||
| if (detectedRelease !== void 0) { | ||
| return detectedRelease; | ||
| } | ||
| return undefined; | ||
| return void 0; | ||
| } | ||
| function getTracesSampleRate(tracesSampleRate) { | ||
| if (tracesSampleRate !== undefined) { | ||
| if (tracesSampleRate !== void 0) { | ||
| return tracesSampleRate; | ||
| } | ||
| const sampleRateFromEnv = process.env.SENTRY_TRACES_SAMPLE_RATE; | ||
| if (!sampleRateFromEnv) { | ||
| return undefined; | ||
| return void 0; | ||
| } | ||
| const parsed = parseFloat(sampleRateFromEnv); | ||
| return isFinite(parsed) ? parsed : undefined; | ||
| return isFinite(parsed) ? parsed : void 0; | ||
| } | ||
| /** | ||
| * Update scope and propagation context based on environmental variables. | ||
| * | ||
| * See https://github.com/getsentry/rfcs/blob/main/text/0071-continue-trace-over-process-boundaries.md | ||
| * for more details. | ||
| */ | ||
| function updateScopeFromEnvVariables() { | ||
@@ -249,0 +178,0 @@ if (core.envToBool(process.env.SENTRY_USE_ENVIRONMENT) !== false) { |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.js","sources":["../../../src/sdk/index.ts"],"sourcesContent":["import type { Integration, Options } from '@sentry/core';\nimport {\n applySdkMetadata,\n consoleSandbox,\n conversationIdIntegration,\n debug,\n envToBool,\n functionToStringIntegration,\n getCurrentScope,\n getIntegrationsToSetup,\n hasSpansEnabled,\n inboundFiltersIntegration,\n linkedErrorsIntegration,\n propagationContextFromHeaders,\n requestDataIntegration,\n spanStreamingIntegration,\n stackParserFromStackParserOptions,\n} from '@sentry/core';\nimport {\n enhanceDscWithOpenTelemetryRootSpanName,\n openTelemetrySetupCheck,\n setOpenTelemetryContextAsyncContextStrategy,\n setupEventContextTrace,\n} from '@sentry/opentelemetry';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { childProcessIntegration } from '../integrations/childProcess';\nimport { nodeContextIntegration } from '../integrations/context';\nimport { contextLinesIntegration } from '../integrations/contextlines';\nimport { httpIntegration } from '../integrations/http';\nimport { localVariablesIntegration } from '../integrations/local-variables';\nimport { modulesIntegration } from '../integrations/modules';\nimport { nativeNodeFetchIntegration } from '../integrations/node-fetch';\nimport { onUncaughtExceptionIntegration } from '../integrations/onuncaughtexception';\nimport { onUnhandledRejectionIntegration } from '../integrations/onunhandledrejection';\nimport { processSessionIntegration } from '../integrations/processSession';\nimport { INTEGRATION_NAME as SPOTLIGHT_INTEGRATION_NAME, spotlightIntegration } from '../integrations/spotlight';\nimport { consoleIntegration } from '../integrations/console';\nimport { systemErrorIntegration } from '../integrations/systemError';\nimport { makeNodeTransport } from '../transports';\nimport type { NodeClientOptions, NodeOptions } from '../types';\nimport { isCjs } from '../utils/detection';\nimport { getSpotlightConfig } from '../utils/spotlight';\nimport { defaultStackParser, getSentryRelease } from './api';\nimport { NodeClient } from './client';\nimport { initializeEsmLoader } from './esmLoader';\n\n/**\n * Get default integrations for the Node-Core SDK.\n */\nexport function getDefaultIntegrations(): Integration[] {\n return [\n // Common\n // TODO(v11): Replace with `eventFiltersIntegration` once we remove the deprecated `inboundFiltersIntegration`\n // eslint-disable-next-line deprecation/deprecation\n inboundFiltersIntegration(),\n functionToStringIntegration(),\n linkedErrorsIntegration(),\n requestDataIntegration(),\n systemErrorIntegration(),\n conversationIdIntegration(),\n // Native Wrappers\n consoleIntegration(),\n httpIntegration(),\n nativeNodeFetchIntegration(),\n // Global Handlers\n onUncaughtExceptionIntegration(),\n onUnhandledRejectionIntegration(),\n // Event Info\n contextLinesIntegration(),\n localVariablesIntegration(),\n nodeContextIntegration(),\n childProcessIntegration(),\n processSessionIntegration(),\n modulesIntegration(),\n ];\n}\n\n/**\n * Initialize Sentry for Node.\n */\nexport function init(options: NodeOptions | undefined = {}): NodeClient | undefined {\n return _init(options, getDefaultIntegrations);\n}\n\n/**\n * Initialize Sentry for Node, without any integrations added by default.\n */\nexport function initWithoutDefaultIntegrations(options: NodeOptions | undefined = {}): NodeClient {\n return _init(options, () => []);\n}\n\n/**\n * Initialize Sentry for Node, without performance instrumentation.\n */\nfunction _init(\n _options: NodeOptions | undefined = {},\n getDefaultIntegrationsImpl: (options: Options) => Integration[],\n): NodeClient {\n const options = getClientOptions(_options, getDefaultIntegrationsImpl);\n\n if (options.debug === true) {\n if (DEBUG_BUILD) {\n debug.enable();\n } else {\n // use `console.warn` rather than `debug.warn` since by non-debug bundles have all `debug.x` statements stripped\n consoleSandbox(() => {\n // eslint-disable-next-line no-console\n console.warn('[Sentry] Cannot initialize SDK with `debug` option using a non-debug bundle.');\n });\n }\n }\n\n if (options.registerEsmLoaderHooks !== false) {\n initializeEsmLoader();\n }\n\n setOpenTelemetryContextAsyncContextStrategy();\n\n const scope = getCurrentScope();\n scope.update(options.initialScope);\n\n if (options.spotlight && !options.integrations.some(({ name }) => name === SPOTLIGHT_INTEGRATION_NAME)) {\n options.integrations.push(\n spotlightIntegration({\n sidecarUrl: typeof options.spotlight === 'string' ? options.spotlight : undefined,\n }),\n );\n }\n\n applySdkMetadata(options, 'node-core');\n\n const client = new NodeClient(options);\n // The client is on the current scope, from where it generally is inherited\n getCurrentScope().setClient(client);\n\n client.init();\n\n debug.log(`SDK initialized from ${isCjs() ? 'CommonJS' : 'ESM'}`);\n\n client.startClientReportTracking();\n\n updateScopeFromEnvVariables();\n\n enhanceDscWithOpenTelemetryRootSpanName(client);\n setupEventContextTrace(client);\n\n // Ensure we flush events when vercel functions are ended\n // See: https://vercel.com/docs/functions/functions-api-reference#sigterm-signal\n if (process.env.VERCEL) {\n process.on('SIGTERM', async () => {\n // We have 500ms for processing here, so we try to make sure to have enough time to send the events\n await client.flush(200);\n });\n }\n\n return client;\n}\n\n/**\n * Validate that your OpenTelemetry setup is correct.\n */\nexport function validateOpenTelemetrySetup(): void {\n if (!DEBUG_BUILD) {\n return;\n }\n\n const setup = openTelemetrySetupCheck();\n\n const required: ReturnType<typeof openTelemetrySetupCheck> = ['SentryContextManager', 'SentryPropagator'];\n\n if (hasSpansEnabled()) {\n required.push('SentrySpanProcessor');\n }\n\n for (const k of required) {\n if (!setup.includes(k)) {\n debug.error(\n `You have to set up the ${k}. Without this, the OpenTelemetry & Sentry integration will not work properly.`,\n );\n }\n }\n\n if (!setup.includes('SentrySampler')) {\n debug.warn(\n 'You have to set up the SentrySampler. Without this, the OpenTelemetry & Sentry integration may still work, but sample rates set for the Sentry SDK will not be respected. If you use a custom sampler, make sure to use `wrapSamplingDecision`.',\n );\n }\n}\n\nfunction getClientOptions(\n options: NodeOptions,\n getDefaultIntegrationsImpl: (options: Options) => Integration[],\n): NodeClientOptions {\n const release = getRelease(options.release);\n\n const spotlight = getSpotlightConfig(options.spotlight);\n\n const tracesSampleRate = getTracesSampleRate(options.tracesSampleRate);\n\n const mergedOptions = {\n ...options,\n dsn: options.dsn ?? process.env.SENTRY_DSN,\n environment: options.environment ?? process.env.SENTRY_ENVIRONMENT,\n sendClientReports: options.sendClientReports ?? true,\n transport: options.transport ?? makeNodeTransport,\n stackParser: stackParserFromStackParserOptions(options.stackParser || defaultStackParser),\n release,\n tracesSampleRate,\n spotlight,\n debug: envToBool(options.debug ?? process.env.SENTRY_DEBUG),\n };\n\n const integrations = options.integrations;\n const defaultIntegrations = options.defaultIntegrations ?? getDefaultIntegrationsImpl(mergedOptions);\n\n const resolvedIntegrations = getIntegrationsToSetup({\n defaultIntegrations,\n integrations,\n });\n\n if (mergedOptions.traceLifecycle === 'stream' && !resolvedIntegrations.some(i => i.name === 'SpanStreaming')) {\n resolvedIntegrations.push(spanStreamingIntegration());\n }\n\n return {\n ...mergedOptions,\n integrations: resolvedIntegrations,\n };\n}\n\nfunction getRelease(release: NodeOptions['release']): string | undefined {\n if (release !== undefined) {\n return release;\n }\n\n const detectedRelease = getSentryRelease();\n if (detectedRelease !== undefined) {\n return detectedRelease;\n }\n\n return undefined;\n}\n\nfunction getTracesSampleRate(tracesSampleRate: NodeOptions['tracesSampleRate']): number | undefined {\n if (tracesSampleRate !== undefined) {\n return tracesSampleRate;\n }\n\n const sampleRateFromEnv = process.env.SENTRY_TRACES_SAMPLE_RATE;\n if (!sampleRateFromEnv) {\n return undefined;\n }\n\n const parsed = parseFloat(sampleRateFromEnv);\n return isFinite(parsed) ? parsed : undefined;\n}\n\n/**\n * Update scope and propagation context based on environmental variables.\n *\n * See https://github.com/getsentry/rfcs/blob/main/text/0071-continue-trace-over-process-boundaries.md\n * for more details.\n */\nfunction updateScopeFromEnvVariables(): void {\n if (envToBool(process.env.SENTRY_USE_ENVIRONMENT) !== false) {\n const sentryTraceEnv = process.env.SENTRY_TRACE;\n const baggageEnv = process.env.SENTRY_BAGGAGE;\n const propagationContext = propagationContextFromHeaders(sentryTraceEnv, baggageEnv);\n getCurrentScope().setPropagationContext(propagationContext);\n }\n}\n"],"names":["inboundFiltersIntegration","functionToStringIntegration","linkedErrorsIntegration","requestDataIntegration","systemErrorIntegration","conversationIdIntegration","consoleIntegration","httpIntegration","nativeNodeFetchIntegration","onUncaughtExceptionIntegration","onUnhandledRejectionIntegration","contextLinesIntegration","localVariablesIntegration","nodeContextIntegration","childProcessIntegration","processSessionIntegration","modulesIntegration","DEBUG_BUILD","debug","consoleSandbox","initializeEsmLoader","setOpenTelemetryContextAsyncContextStrategy","getCurrentScope","SPOTLIGHT_INTEGRATION_NAME","spotlightIntegration","applySdkMetadata","client","NodeClient","isCjs","enhanceDscWithOpenTelemetryRootSpanName","setupEventContextTrace","openTelemetrySetupCheck","hasSpansEnabled","getSpotlightConfig","makeNodeTransport","stackParserFromStackParserOptions","defaultStackParser","envToBool","getIntegrationsToSetup","spanStreamingIntegration","getSentryRelease","propagationContextFromHeaders"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA8CA;AACA;AACA;AACO,SAAS,sBAAsB,GAAkB;AACxD,EAAE,OAAO;AACT;AACA;AACA;AACA,IAAIA,8BAAyB,EAAE;AAC/B,IAAIC,gCAA2B,EAAE;AACjC,IAAIC,4BAAuB,EAAE;AAC7B,IAAIC,2BAAsB,EAAE;AAC5B,IAAIC,kCAAsB,EAAE;AAC5B,IAAIC,8BAAyB,EAAE;AAC/B;AACA,IAAIC,4BAAkB,EAAE;AACxB,IAAIC,qBAAe,EAAE;AACrB,IAAIC,kCAA0B,EAAE;AAChC;AACA,IAAIC,kDAA8B,EAAE;AACpC,IAAIC,oDAA+B,EAAE;AACrC;AACA,IAAIC,oCAAuB,EAAE;AAC7B,IAAIC,iCAAyB,EAAE;AAC/B,IAAIC,8BAAsB,EAAE;AAC5B,IAAIC,oCAAuB,EAAE;AAC7B,IAAIC,wCAAyB,EAAE;AAC/B,IAAIC,0BAAkB,EAAE;AACxB,GAAG;AACH;;AAEA;AACA;AACA;AACO,SAAS,IAAI,CAAC,OAAO,GAA4B,EAAE,EAA0B;AACpF,EAAE,OAAO,KAAK,CAAC,OAAO,EAAE,sBAAsB,CAAC;AAC/C;;AAEA;AACA;AACA;AACO,SAAS,8BAA8B,CAAC,OAAO,GAA4B,EAAE,EAAc;AAClG,EAAE,OAAO,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;AACjC;;AAEA;AACA;AACA;AACA,SAAS,KAAK;AACd,EAAE,QAAQ,GAA4B,EAAE;AACxC,EAAE,0BAA0B;AAC5B,EAAc;AACd,EAAE,MAAM,UAAU,gBAAgB,CAAC,QAAQ,EAAE,0BAA0B,CAAC;;AAExE,EAAE,IAAI,OAAO,CAAC,KAAA,KAAU,IAAI,EAAE;AAC9B,IAAI,IAAIC,sBAAW,EAAE;AACrB,MAAMC,UAAK,CAAC,MAAM,EAAE;AACpB,IAAI,OAAO;AACX;AACA,MAAMC,mBAAc,CAAC,MAAM;AAC3B;AACA,QAAQ,OAAO,CAAC,IAAI,CAAC,8EAA8E,CAAC;AACpG,MAAM,CAAC,CAAC;AACR,IAAI;AACJ,EAAE;;AAEF,EAAE,IAAI,OAAO,CAAC,sBAAA,KAA2B,KAAK,EAAE;AAChD,IAAIC,6BAAmB,EAAE;AACzB,EAAE;;AAEF,EAAEC,yDAA2C,EAAE;;AAE/C,EAAE,MAAM,KAAA,GAAQC,oBAAe,EAAE;AACjC,EAAE,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC;;AAEpC,EAAE,IAAI,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,EAAE,IAAA,EAAM,KAAK,IAAA,KAASC,0BAA0B,CAAC,EAAE;AAC1G,IAAI,OAAO,CAAC,YAAY,CAAC,IAAI;AAC7B,MAAMC,8BAAoB,CAAC;AAC3B,QAAQ,UAAU,EAAE,OAAO,OAAO,CAAC,SAAA,KAAc,QAAA,GAAW,OAAO,CAAC,SAAA,GAAY,SAAS;AACzF,OAAO,CAAC;AACR,KAAK;AACL,EAAE;;AAEF,EAAEC,qBAAgB,CAAC,OAAO,EAAE,WAAW,CAAC;;AAExC,EAAE,MAAMC,QAAA,GAAS,IAAIC,iBAAU,CAAC,OAAO,CAAC;AACxC;AACA,EAAEL,oBAAe,EAAE,CAAC,SAAS,CAACI,QAAM,CAAC;;AAErC,EAAEA,QAAM,CAAC,IAAI,EAAE;;AAEf,EAAER,UAAK,CAAC,GAAG,CAAC,CAAC,qBAAqB,EAAEU,eAAK,KAAK,UAAA,GAAa,KAAK,CAAC,CAAA,CAAA;;AAEA,EAAAF,QAAA,CAAA,yBAAA,EAAA;;AAEA,EAAA,2BAAA,EAAA;;AAEA,EAAAG,qDAAA,CAAAH,QAAA,CAAA;AACA,EAAAI,oCAAA,CAAAJ,QAAA,CAAA;;AAEA;AACA;AACA,EAAA,IAAA,OAAA,CAAA,GAAA,CAAA,MAAA,EAAA;AACA,IAAA,OAAA,CAAA,EAAA,CAAA,SAAA,EAAA,YAAA;AACA;AACA,MAAA,MAAAA,QAAA,CAAA,KAAA,CAAA,GAAA,CAAA;AACA,IAAA,CAAA,CAAA;AACA,EAAA;;AAEA,EAAA,OAAAA,QAAA;AACA;;AAEA;AACA;AACA;AACA,SAAA,0BAAA,GAAA;AACA,EAAA,IAAA,CAAAT,sBAAA,EAAA;AACA,IAAA;AACA,EAAA;;AAEA,EAAA,MAAA,KAAA,GAAAc,qCAAA,EAAA;;AAEA,EAAA,MAAA,QAAA,GAAA,CAAA,sBAAA,EAAA,kBAAA,CAAA;;AAEA,EAAA,IAAAC,oBAAA,EAAA,EAAA;AACA,IAAA,QAAA,CAAA,IAAA,CAAA,qBAAA,CAAA;AACA,EAAA;;AAEA,EAAA,KAAA,MAAA,CAAA,IAAA,QAAA,EAAA;AACA,IAAA,IAAA,CAAA,KAAA,CAAA,QAAA,CAAA,CAAA,CAAA,EAAA;AACA,MAAAd,UAAA,CAAA,KAAA;AACA,QAAA,CAAA,uBAAA,EAAA,CAAA,CAAA,8EAAA,CAAA;AACA,OAAA;AACA,IAAA;AACA,EAAA;;AAEA,EAAA,IAAA,CAAA,KAAA,CAAA,QAAA,CAAA,eAAA,CAAA,EAAA;AACA,IAAAA,UAAA,CAAA,IAAA;AACA,MAAA,iPAAA;AACA,KAAA;AACA,EAAA;AACA;;AAEA,SAAA,gBAAA;AACA,EAAA,OAAA;AACA,EAAA,0BAAA;AACA,EAAA;AACA,EAAA,MAAA,OAAA,GAAA,UAAA,CAAA,OAAA,CAAA,OAAA,CAAA;;AAEA,EAAA,MAAA,SAAA,GAAAe,8BAAA,CAAA,OAAA,CAAA,SAAA,CAAA;;AAEA,EAAA,MAAA,gBAAA,GAAA,mBAAA,CAAA,OAAA,CAAA,gBAAA,CAAA;;AAEA,EAAA,MAAA,aAAA,GAAA;AACA,IAAA,GAAA,OAAA;AACA,IAAA,GAAA,EAAA,OAAA,CAAA,GAAA,IAAA,OAAA,CAAA,GAAA,CAAA,UAAA;AACA,IAAA,WAAA,EAAA,OAAA,CAAA,WAAA,IAAA,OAAA,CAAA,GAAA,CAAA,kBAAA;AACA,IAAA,iBAAA,EAAA,OAAA,CAAA,iBAAA,IAAA,IAAA;AACA,IAAA,SAAA,EAAA,OAAA,CAAA,SAAA,IAAAC,sBAAA;AACA,IAAA,WAAA,EAAAC,sCAAA,CAAA,OAAA,CAAA,WAAA,IAAAC,sBAAA,CAAA;AACA,IAAA,OAAA;AACA,IAAA,gBAAA;AACA,IAAA,SAAA;AACA,IAAA,KAAA,EAAAC,cAAA,CAAA,OAAA,CAAA,KAAA,IAAA,OAAA,CAAA,GAAA,CAAA,YAAA,CAAA;AACA,GAAA;;AAEA,EAAA,MAAA,YAAA,GAAA,OAAA,CAAA,YAAA;AACA,EAAA,MAAA,mBAAA,GAAA,OAAA,CAAA,mBAAA,IAAA,0BAAA,CAAA,aAAA,CAAA;;AAEA,EAAA,MAAA,oBAAA,GAAAC,2BAAA,CAAA;AACA,IAAA,mBAAA;AACA,IAAA,YAAA;AACA,GAAA,CAAA;;AAEA,EAAA,IAAA,aAAA,CAAA,cAAA,KAAA,QAAA,IAAA,CAAA,oBAAA,CAAA,IAAA,CAAA,CAAA,IAAA,CAAA,CAAA,IAAA,KAAA,eAAA,CAAA,EAAA;AACA,IAAA,oBAAA,CAAA,IAAA,CAAAC,6BAAA,EAAA,CAAA;AACA,EAAA;;AAEA,EAAA,OAAA;AACA,IAAA,GAAA,aAAA;AACA,IAAA,YAAA,EAAA,oBAAA;AACA,GAAA;AACA;;AAEA,SAAA,UAAA,CAAA,OAAA,EAAA;AACA,EAAA,IAAA,OAAA,KAAA,SAAA,EAAA;AACA,IAAA,OAAA,OAAA;AACA,EAAA;;AAEA,EAAA,MAAA,eAAA,GAAAC,oBAAA,EAAA;AACA,EAAA,IAAA,eAAA,KAAA,SAAA,EAAA;AACA,IAAA,OAAA,eAAA;AACA,EAAA;;AAEA,EAAA,OAAA,SAAA;AACA;;AAEA,SAAA,mBAAA,CAAA,gBAAA,EAAA;AACA,EAAA,IAAA,gBAAA,KAAA,SAAA,EAAA;AACA,IAAA,OAAA,gBAAA;AACA,EAAA;;AAEA,EAAA,MAAA,iBAAA,GAAA,OAAA,CAAA,GAAA,CAAA,yBAAA;AACA,EAAA,IAAA,CAAA,iBAAA,EAAA;AACA,IAAA,OAAA,SAAA;AACA,EAAA;;AAEA,EAAA,MAAA,MAAA,GAAA,UAAA,CAAA,iBAAA,CAAA;AACA,EAAA,OAAA,QAAA,CAAA,MAAA,CAAA,GAAA,MAAA,GAAA,SAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,SAAA,2BAAA,GAAA;AACA,EAAA,IAAAH,cAAA,CAAA,OAAA,CAAA,GAAA,CAAA,sBAAA,CAAA,KAAA,KAAA,EAAA;AACA,IAAA,MAAA,cAAA,GAAA,OAAA,CAAA,GAAA,CAAA,YAAA;AACA,IAAA,MAAA,UAAA,GAAA,OAAA,CAAA,GAAA,CAAA,cAAA;AACA,IAAA,MAAA,kBAAA,GAAAI,kCAAA,CAAA,cAAA,EAAA,UAAA,CAAA;AACA,IAAAnB,oBAAA,EAAA,CAAA,qBAAA,CAAA,kBAAA,CAAA;AACA,EAAA;AACA;;;;;;;"} | ||
| {"version":3,"file":"index.js","sources":["../../../src/sdk/index.ts"],"sourcesContent":["import type { Integration, Options } from '@sentry/core';\nimport {\n applySdkMetadata,\n consoleSandbox,\n conversationIdIntegration,\n debug,\n envToBool,\n functionToStringIntegration,\n getCurrentScope,\n getIntegrationsToSetup,\n hasSpansEnabled,\n inboundFiltersIntegration,\n linkedErrorsIntegration,\n propagationContextFromHeaders,\n requestDataIntegration,\n spanStreamingIntegration,\n stackParserFromStackParserOptions,\n} from '@sentry/core';\nimport {\n enhanceDscWithOpenTelemetryRootSpanName,\n openTelemetrySetupCheck,\n setOpenTelemetryContextAsyncContextStrategy,\n setupEventContextTrace,\n} from '@sentry/opentelemetry';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { childProcessIntegration } from '../integrations/childProcess';\nimport { nodeContextIntegration } from '../integrations/context';\nimport { contextLinesIntegration } from '../integrations/contextlines';\nimport { httpIntegration } from '../integrations/http';\nimport { localVariablesIntegration } from '../integrations/local-variables';\nimport { modulesIntegration } from '../integrations/modules';\nimport { nativeNodeFetchIntegration } from '../integrations/node-fetch';\nimport { onUncaughtExceptionIntegration } from '../integrations/onuncaughtexception';\nimport { onUnhandledRejectionIntegration } from '../integrations/onunhandledrejection';\nimport { processSessionIntegration } from '../integrations/processSession';\nimport { INTEGRATION_NAME as SPOTLIGHT_INTEGRATION_NAME, spotlightIntegration } from '../integrations/spotlight';\nimport { consoleIntegration } from '../integrations/console';\nimport { systemErrorIntegration } from '../integrations/systemError';\nimport { makeNodeTransport } from '../transports';\nimport type { NodeClientOptions, NodeOptions } from '../types';\nimport { isCjs } from '../utils/detection';\nimport { getSpotlightConfig } from '../utils/spotlight';\nimport { defaultStackParser, getSentryRelease } from './api';\nimport { NodeClient } from './client';\nimport { initializeEsmLoader } from './esmLoader';\n\n/**\n * Get default integrations for the Node-Core SDK.\n */\nexport function getDefaultIntegrations(): Integration[] {\n return [\n // Common\n // TODO(v11): Replace with `eventFiltersIntegration` once we remove the deprecated `inboundFiltersIntegration`\n // eslint-disable-next-line deprecation/deprecation\n inboundFiltersIntegration(),\n functionToStringIntegration(),\n linkedErrorsIntegration(),\n requestDataIntegration(),\n systemErrorIntegration(),\n conversationIdIntegration(),\n // Native Wrappers\n consoleIntegration(),\n httpIntegration(),\n nativeNodeFetchIntegration(),\n // Global Handlers\n onUncaughtExceptionIntegration(),\n onUnhandledRejectionIntegration(),\n // Event Info\n contextLinesIntegration(),\n localVariablesIntegration(),\n nodeContextIntegration(),\n childProcessIntegration(),\n processSessionIntegration(),\n modulesIntegration(),\n ];\n}\n\n/**\n * Initialize Sentry for Node.\n */\nexport function init(options: NodeOptions | undefined = {}): NodeClient | undefined {\n return _init(options, getDefaultIntegrations);\n}\n\n/**\n * Initialize Sentry for Node, without any integrations added by default.\n */\nexport function initWithoutDefaultIntegrations(options: NodeOptions | undefined = {}): NodeClient {\n return _init(options, () => []);\n}\n\n/**\n * Initialize Sentry for Node, without performance instrumentation.\n */\nfunction _init(\n _options: NodeOptions | undefined = {},\n getDefaultIntegrationsImpl: (options: Options) => Integration[],\n): NodeClient {\n const options = getClientOptions(_options, getDefaultIntegrationsImpl);\n\n if (options.debug === true) {\n if (DEBUG_BUILD) {\n debug.enable();\n } else {\n // use `console.warn` rather than `debug.warn` since by non-debug bundles have all `debug.x` statements stripped\n consoleSandbox(() => {\n // eslint-disable-next-line no-console\n console.warn('[Sentry] Cannot initialize SDK with `debug` option using a non-debug bundle.');\n });\n }\n }\n\n if (options.registerEsmLoaderHooks !== false) {\n initializeEsmLoader();\n }\n\n setOpenTelemetryContextAsyncContextStrategy();\n\n const scope = getCurrentScope();\n scope.update(options.initialScope);\n\n if (options.spotlight && !options.integrations.some(({ name }) => name === SPOTLIGHT_INTEGRATION_NAME)) {\n options.integrations.push(\n spotlightIntegration({\n sidecarUrl: typeof options.spotlight === 'string' ? options.spotlight : undefined,\n }),\n );\n }\n\n applySdkMetadata(options, 'node-core');\n\n const client = new NodeClient(options);\n // The client is on the current scope, from where it generally is inherited\n getCurrentScope().setClient(client);\n\n client.init();\n\n debug.log(`SDK initialized from ${isCjs() ? 'CommonJS' : 'ESM'}`);\n\n client.startClientReportTracking();\n\n updateScopeFromEnvVariables();\n\n enhanceDscWithOpenTelemetryRootSpanName(client);\n setupEventContextTrace(client);\n\n // Ensure we flush events when vercel functions are ended\n // See: https://vercel.com/docs/functions/functions-api-reference#sigterm-signal\n if (process.env.VERCEL) {\n process.on('SIGTERM', async () => {\n // We have 500ms for processing here, so we try to make sure to have enough time to send the events\n await client.flush(200);\n });\n }\n\n return client;\n}\n\n/**\n * Validate that your OpenTelemetry setup is correct.\n */\nexport function validateOpenTelemetrySetup(): void {\n if (!DEBUG_BUILD) {\n return;\n }\n\n const setup = openTelemetrySetupCheck();\n\n const required: ReturnType<typeof openTelemetrySetupCheck> = ['SentryContextManager', 'SentryPropagator'];\n\n if (hasSpansEnabled()) {\n required.push('SentrySpanProcessor');\n }\n\n for (const k of required) {\n if (!setup.includes(k)) {\n debug.error(\n `You have to set up the ${k}. Without this, the OpenTelemetry & Sentry integration will not work properly.`,\n );\n }\n }\n\n if (!setup.includes('SentrySampler')) {\n debug.warn(\n 'You have to set up the SentrySampler. Without this, the OpenTelemetry & Sentry integration may still work, but sample rates set for the Sentry SDK will not be respected. If you use a custom sampler, make sure to use `wrapSamplingDecision`.',\n );\n }\n}\n\nfunction getClientOptions(\n options: NodeOptions,\n getDefaultIntegrationsImpl: (options: Options) => Integration[],\n): NodeClientOptions {\n const release = getRelease(options.release);\n\n const spotlight = getSpotlightConfig(options.spotlight);\n\n const tracesSampleRate = getTracesSampleRate(options.tracesSampleRate);\n\n const mergedOptions = {\n ...options,\n dsn: options.dsn ?? process.env.SENTRY_DSN,\n environment: options.environment ?? process.env.SENTRY_ENVIRONMENT,\n sendClientReports: options.sendClientReports ?? true,\n transport: options.transport ?? makeNodeTransport,\n stackParser: stackParserFromStackParserOptions(options.stackParser || defaultStackParser),\n release,\n tracesSampleRate,\n spotlight,\n debug: envToBool(options.debug ?? process.env.SENTRY_DEBUG),\n };\n\n const integrations = options.integrations;\n const defaultIntegrations = options.defaultIntegrations ?? getDefaultIntegrationsImpl(mergedOptions);\n\n const resolvedIntegrations = getIntegrationsToSetup({\n defaultIntegrations,\n integrations,\n });\n\n if (mergedOptions.traceLifecycle === 'stream' && !resolvedIntegrations.some(i => i.name === 'SpanStreaming')) {\n resolvedIntegrations.push(spanStreamingIntegration());\n }\n\n return {\n ...mergedOptions,\n integrations: resolvedIntegrations,\n };\n}\n\nfunction getRelease(release: NodeOptions['release']): string | undefined {\n if (release !== undefined) {\n return release;\n }\n\n const detectedRelease = getSentryRelease();\n if (detectedRelease !== undefined) {\n return detectedRelease;\n }\n\n return undefined;\n}\n\nfunction getTracesSampleRate(tracesSampleRate: NodeOptions['tracesSampleRate']): number | undefined {\n if (tracesSampleRate !== undefined) {\n return tracesSampleRate;\n }\n\n const sampleRateFromEnv = process.env.SENTRY_TRACES_SAMPLE_RATE;\n if (!sampleRateFromEnv) {\n return undefined;\n }\n\n const parsed = parseFloat(sampleRateFromEnv);\n return isFinite(parsed) ? parsed : undefined;\n}\n\n/**\n * Update scope and propagation context based on environmental variables.\n *\n * See https://github.com/getsentry/rfcs/blob/main/text/0071-continue-trace-over-process-boundaries.md\n * for more details.\n */\nfunction updateScopeFromEnvVariables(): void {\n if (envToBool(process.env.SENTRY_USE_ENVIRONMENT) !== false) {\n const sentryTraceEnv = process.env.SENTRY_TRACE;\n const baggageEnv = process.env.SENTRY_BAGGAGE;\n const propagationContext = propagationContextFromHeaders(sentryTraceEnv, baggageEnv);\n getCurrentScope().setPropagationContext(propagationContext);\n }\n}\n"],"names":["inboundFiltersIntegration","functionToStringIntegration","linkedErrorsIntegration","requestDataIntegration","systemErrorIntegration","conversationIdIntegration","consoleIntegration","httpIntegration","nativeNodeFetchIntegration","onUncaughtExceptionIntegration","onUnhandledRejectionIntegration","contextLinesIntegration","localVariablesIntegration","nodeContextIntegration","childProcessIntegration","processSessionIntegration","modulesIntegration","DEBUG_BUILD","debug","consoleSandbox","initializeEsmLoader","setOpenTelemetryContextAsyncContextStrategy","getCurrentScope","SPOTLIGHT_INTEGRATION_NAME","spotlightIntegration","applySdkMetadata","client","NodeClient","isCjs","enhanceDscWithOpenTelemetryRootSpanName","setupEventContextTrace","openTelemetrySetupCheck","hasSpansEnabled","getSpotlightConfig","makeNodeTransport","stackParserFromStackParserOptions","defaultStackParser","envToBool","getIntegrationsToSetup","spanStreamingIntegration","getSentryRelease","propagationContextFromHeaders"],"mappings":"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"} |
@@ -6,6 +6,2 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| /** | ||
| * Update the active isolation scope. | ||
| * Should be used with caution! | ||
| */ | ||
| function setIsolationScope(isolationScope) { | ||
@@ -12,0 +8,0 @@ const scopes = opentelemetry.getScopesFromContext(api.context.active()); |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"scope.js","sources":["../../../src/sdk/scope.ts"],"sourcesContent":["import { context } from '@opentelemetry/api';\nimport type { Scope } from '@sentry/core';\nimport { getScopesFromContext } from '@sentry/opentelemetry';\n\n/**\n * Update the active isolation scope.\n * Should be used with caution!\n */\nexport function setIsolationScope(isolationScope: Scope): void {\n const scopes = getScopesFromContext(context.active());\n if (scopes) {\n scopes.isolationScope = isolationScope;\n }\n}\n"],"names":["getScopesFromContext","context"],"mappings":";;;;;AAIA;AACA;AACA;AACA;AACO,SAAS,iBAAiB,CAAC,cAAc,EAAe;AAC/D,EAAE,MAAM,MAAA,GAASA,kCAAoB,CAACC,WAAO,CAAC,MAAM,EAAE,CAAC;AACvD,EAAE,IAAI,MAAM,EAAE;AACd,IAAI,MAAM,CAAC,cAAA,GAAiB,cAAc;AAC1C,EAAE;AACF;;;;"} | ||
| {"version":3,"file":"scope.js","sources":["../../../src/sdk/scope.ts"],"sourcesContent":["import { context } from '@opentelemetry/api';\nimport type { Scope } from '@sentry/core';\nimport { getScopesFromContext } from '@sentry/opentelemetry';\n\n/**\n * Update the active isolation scope.\n * Should be used with caution!\n */\nexport function setIsolationScope(isolationScope: Scope): void {\n const scopes = getScopesFromContext(context.active());\n if (scopes) {\n scopes.isolationScope = isolationScope;\n }\n}\n"],"names":["getScopesFromContext","context"],"mappings":";;;;;AAQO,SAAS,kBAAkB,cAAA,EAA6B;AAC7D,EAAA,MAAM,MAAA,GAASA,kCAAA,CAAqBC,WAAA,CAAQ,MAAA,EAAQ,CAAA;AACpD,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,MAAA,CAAO,cAAA,GAAiB,cAAA;AAAA,EAC1B;AACF;;;;"} |
@@ -10,9 +10,3 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| // Estimated maximum size for reasonable standalone event | ||
| const GZIP_THRESHOLD = 1024 * 32; | ||
| /** | ||
| * Gets a stream from a Uint8Array or string | ||
| * Readable.from is ideal but was added in node.js v12.3.0 and v10.17.0 | ||
| */ | ||
| function streamFromBody(body) { | ||
@@ -23,12 +17,7 @@ return new node_stream.Readable({ | ||
| this.push(null); | ||
| }, | ||
| } | ||
| }); | ||
| } | ||
| /** | ||
| * Creates a Transport that uses native the native 'http' and 'https' modules to send events to Sentry. | ||
| */ | ||
| function makeNodeTransport(options) { | ||
| let urlSegments; | ||
| try { | ||
@@ -38,5 +27,4 @@ urlSegments = new URL(options.url); | ||
| core.consoleSandbox(() => { | ||
| // eslint-disable-next-line no-console | ||
| console.warn( | ||
| '[@sentry/node]: Invalid dsn or tunnel option, will not send any events. The tunnel option must be a full URL when used.', | ||
| "[@sentry/node]: Invalid dsn or tunnel option, will not send any events. The tunnel option must be a full URL when used." | ||
| ); | ||
@@ -46,43 +34,20 @@ }); | ||
| } | ||
| const isHttps = urlSegments.protocol === 'https:'; | ||
| // Proxy prioritization: http => `options.proxy` | `process.env.http_proxy` | ||
| // Proxy prioritization: https => `options.proxy` | `process.env.https_proxy` | `process.env.http_proxy` | ||
| const isHttps = urlSegments.protocol === "https:"; | ||
| const proxy = applyNoProxyOption( | ||
| urlSegments, | ||
| options.proxy || (isHttps ? process.env.https_proxy : undefined) || process.env.http_proxy, | ||
| options.proxy || (isHttps ? process.env.https_proxy : void 0) || process.env.http_proxy | ||
| ); | ||
| const nativeHttpModule = isHttps ? https : http; | ||
| const keepAlive = options.keepAlive === undefined ? false : options.keepAlive; | ||
| // TODO(v11): Evaluate if we can set keepAlive to true. This would involve testing for memory leaks in older node | ||
| // versions(>= 8) as they had memory leaks when using it: #2555 | ||
| const agent = proxy | ||
| ? (new index.HttpsProxyAgent(proxy) ) | ||
| : new nativeHttpModule.Agent({ keepAlive, maxSockets: 30, timeout: 2000 }); | ||
| const keepAlive = options.keepAlive === void 0 ? false : options.keepAlive; | ||
| const agent = proxy ? new index.HttpsProxyAgent(proxy) : new nativeHttpModule.Agent({ keepAlive, maxSockets: 30, timeout: 2e3 }); | ||
| const requestExecutor = createRequestExecutor(options, options.httpModule ?? nativeHttpModule, agent); | ||
| return core.createTransport(options, requestExecutor); | ||
| } | ||
| /** | ||
| * Honors the `no_proxy` env variable with the highest priority to allow for hosts exclusion. | ||
| * | ||
| * @param transportUrl The URL the transport intends to send events to. | ||
| * @param proxy The client configured proxy. | ||
| * @returns A proxy the transport should use. | ||
| */ | ||
| function applyNoProxyOption(transportUrlSegments, proxy) { | ||
| const { no_proxy } = process.env; | ||
| const urlIsExemptFromProxy = no_proxy | ||
| ?.split(',') | ||
| .some( | ||
| exemption => transportUrlSegments.host.endsWith(exemption) || transportUrlSegments.hostname.endsWith(exemption), | ||
| ); | ||
| const urlIsExemptFromProxy = no_proxy?.split(",").some( | ||
| (exemption) => transportUrlSegments.host.endsWith(exemption) || transportUrlSegments.hostname.endsWith(exemption) | ||
| ); | ||
| if (urlIsExemptFromProxy) { | ||
| return undefined; | ||
| return void 0; | ||
| } else { | ||
@@ -92,30 +57,17 @@ return proxy; | ||
| } | ||
| /** | ||
| * Creates a RequestExecutor to be used with `createTransport`. | ||
| */ | ||
| function createRequestExecutor( | ||
| options, | ||
| httpModule, | ||
| agent, | ||
| ) { | ||
| function createRequestExecutor(options, httpModule, agent) { | ||
| const { hostname, pathname, port, protocol, search } = new URL(options.url); | ||
| return function makeRequest(request) { | ||
| return new Promise((resolve, reject) => { | ||
| // This ensures we do not generate any spans in OpenTelemetry for the transport | ||
| core.suppressTracing(() => { | ||
| let body = streamFromBody(request.body); | ||
| const headers = { ...options.headers }; | ||
| if (request.body.length > GZIP_THRESHOLD) { | ||
| headers['content-encoding'] = 'gzip'; | ||
| headers["content-encoding"] = "gzip"; | ||
| body = body.pipe(node_zlib.createGzip()); | ||
| } | ||
| const hostnameIsIPv6 = hostname.startsWith('['); | ||
| const hostnameIsIPv6 = hostname.startsWith("["); | ||
| const req = httpModule.request( | ||
| { | ||
| method: 'POST', | ||
| method: "POST", | ||
| agent, | ||
@@ -128,33 +80,22 @@ headers, | ||
| protocol, | ||
| ca: options.caCerts, | ||
| ca: options.caCerts | ||
| }, | ||
| res => { | ||
| res.on('data', () => { | ||
| // Drain socket | ||
| (res) => { | ||
| res.on("data", () => { | ||
| }); | ||
| res.on('end', () => { | ||
| // Drain socket | ||
| res.on("end", () => { | ||
| }); | ||
| res.setEncoding('utf8'); | ||
| // "Key-value pairs of header names and values. Header names are lower-cased." | ||
| // https://nodejs.org/api/http.html#http_message_headers | ||
| const retryAfterHeader = res.headers['retry-after'] ?? null; | ||
| const rateLimitsHeader = res.headers['x-sentry-rate-limits'] ?? null; | ||
| res.setEncoding("utf8"); | ||
| const retryAfterHeader = res.headers["retry-after"] ?? null; | ||
| const rateLimitsHeader = res.headers["x-sentry-rate-limits"] ?? null; | ||
| resolve({ | ||
| statusCode: res.statusCode, | ||
| headers: { | ||
| 'retry-after': retryAfterHeader, | ||
| 'x-sentry-rate-limits': Array.isArray(rateLimitsHeader) | ||
| ? rateLimitsHeader[0] || null | ||
| : rateLimitsHeader, | ||
| }, | ||
| "retry-after": retryAfterHeader, | ||
| "x-sentry-rate-limits": Array.isArray(rateLimitsHeader) ? rateLimitsHeader[0] || null : rateLimitsHeader | ||
| } | ||
| }); | ||
| }, | ||
| } | ||
| ); | ||
| req.on('error', reject); | ||
| req.on("error", reject); | ||
| body.pipe(req); | ||
@@ -161,0 +102,0 @@ }); |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"http.js","sources":["../../../src/transports/http.ts"],"sourcesContent":["import * as http from 'node:http';\nimport * as https from 'node:https';\nimport { Readable } from 'node:stream';\nimport { createGzip } from 'node:zlib';\nimport type {\n BaseTransportOptions,\n Transport,\n TransportMakeRequestResponse,\n TransportRequest,\n TransportRequestExecutor,\n} from '@sentry/core';\nimport { consoleSandbox, createTransport, suppressTracing } from '@sentry/core';\nimport { HttpsProxyAgent } from '../proxy';\nimport type { HTTPModule } from './http-module';\n\nexport interface NodeTransportOptions extends BaseTransportOptions {\n /** Set a proxy that should be used for outbound requests. */\n proxy?: string;\n /** HTTPS proxy CA certificates */\n caCerts?: string | Buffer | Array<string | Buffer>;\n /** Custom HTTP module. Defaults to the native 'http' and 'https' modules. */\n httpModule?: HTTPModule;\n /** Allow overriding connection keepAlive, defaults to false */\n keepAlive?: boolean;\n}\n\n// Estimated maximum size for reasonable standalone event\nconst GZIP_THRESHOLD = 1024 * 32;\n\n/**\n * Gets a stream from a Uint8Array or string\n * Readable.from is ideal but was added in node.js v12.3.0 and v10.17.0\n */\nfunction streamFromBody(body: Uint8Array | string): Readable {\n return new Readable({\n read() {\n this.push(body);\n this.push(null);\n },\n });\n}\n\n/**\n * Creates a Transport that uses native the native 'http' and 'https' modules to send events to Sentry.\n */\nexport function makeNodeTransport(options: NodeTransportOptions): Transport {\n let urlSegments: URL;\n\n try {\n urlSegments = new URL(options.url);\n } catch (_e) {\n consoleSandbox(() => {\n // eslint-disable-next-line no-console\n console.warn(\n '[@sentry/node]: Invalid dsn or tunnel option, will not send any events. The tunnel option must be a full URL when used.',\n );\n });\n return createTransport(options, () => Promise.resolve({}));\n }\n\n const isHttps = urlSegments.protocol === 'https:';\n\n // Proxy prioritization: http => `options.proxy` | `process.env.http_proxy`\n // Proxy prioritization: https => `options.proxy` | `process.env.https_proxy` | `process.env.http_proxy`\n const proxy = applyNoProxyOption(\n urlSegments,\n options.proxy || (isHttps ? process.env.https_proxy : undefined) || process.env.http_proxy,\n );\n\n const nativeHttpModule = isHttps ? https : http;\n const keepAlive = options.keepAlive === undefined ? false : options.keepAlive;\n\n // TODO(v11): Evaluate if we can set keepAlive to true. This would involve testing for memory leaks in older node\n // versions(>= 8) as they had memory leaks when using it: #2555\n const agent = proxy\n ? (new HttpsProxyAgent(proxy) as http.Agent)\n : new nativeHttpModule.Agent({ keepAlive, maxSockets: 30, timeout: 2000 });\n\n const requestExecutor = createRequestExecutor(options, options.httpModule ?? nativeHttpModule, agent);\n return createTransport(options, requestExecutor);\n}\n\n/**\n * Honors the `no_proxy` env variable with the highest priority to allow for hosts exclusion.\n *\n * @param transportUrl The URL the transport intends to send events to.\n * @param proxy The client configured proxy.\n * @returns A proxy the transport should use.\n */\nfunction applyNoProxyOption(transportUrlSegments: URL, proxy: string | undefined): string | undefined {\n const { no_proxy } = process.env;\n\n const urlIsExemptFromProxy = no_proxy\n ?.split(',')\n .some(\n exemption => transportUrlSegments.host.endsWith(exemption) || transportUrlSegments.hostname.endsWith(exemption),\n );\n\n if (urlIsExemptFromProxy) {\n return undefined;\n } else {\n return proxy;\n }\n}\n\n/**\n * Creates a RequestExecutor to be used with `createTransport`.\n */\nfunction createRequestExecutor(\n options: NodeTransportOptions,\n httpModule: HTTPModule,\n agent: http.Agent,\n): TransportRequestExecutor {\n const { hostname, pathname, port, protocol, search } = new URL(options.url);\n return function makeRequest(request: TransportRequest): Promise<TransportMakeRequestResponse> {\n return new Promise((resolve, reject) => {\n // This ensures we do not generate any spans in OpenTelemetry for the transport\n suppressTracing(() => {\n let body = streamFromBody(request.body);\n\n const headers: Record<string, string> = { ...options.headers };\n\n if (request.body.length > GZIP_THRESHOLD) {\n headers['content-encoding'] = 'gzip';\n body = body.pipe(createGzip());\n }\n\n const hostnameIsIPv6 = hostname.startsWith('[');\n\n const req = httpModule.request(\n {\n method: 'POST',\n agent,\n headers,\n // Remove \"[\" and \"]\" from IPv6 hostnames\n hostname: hostnameIsIPv6 ? hostname.slice(1, -1) : hostname,\n path: `${pathname}${search}`,\n port,\n protocol,\n ca: options.caCerts,\n },\n res => {\n res.on('data', () => {\n // Drain socket\n });\n\n res.on('end', () => {\n // Drain socket\n });\n\n res.setEncoding('utf8');\n\n // \"Key-value pairs of header names and values. Header names are lower-cased.\"\n // https://nodejs.org/api/http.html#http_message_headers\n const retryAfterHeader = res.headers['retry-after'] ?? null;\n const rateLimitsHeader = res.headers['x-sentry-rate-limits'] ?? null;\n\n resolve({\n statusCode: res.statusCode,\n headers: {\n 'retry-after': retryAfterHeader,\n 'x-sentry-rate-limits': Array.isArray(rateLimitsHeader)\n ? rateLimitsHeader[0] || null\n : rateLimitsHeader,\n },\n });\n },\n );\n\n req.on('error', reject);\n body.pipe(req);\n });\n });\n };\n}\n"],"names":["Readable","consoleSandbox","createTransport","HttpsProxyAgent","suppressTracing","createGzip"],"mappings":"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"} | ||
| {"version":3,"file":"http.js","sources":["../../../src/transports/http.ts"],"sourcesContent":["import * as http from 'node:http';\nimport * as https from 'node:https';\nimport { Readable } from 'node:stream';\nimport { createGzip } from 'node:zlib';\nimport type {\n BaseTransportOptions,\n Transport,\n TransportMakeRequestResponse,\n TransportRequest,\n TransportRequestExecutor,\n} from '@sentry/core';\nimport { consoleSandbox, createTransport, suppressTracing } from '@sentry/core';\nimport { HttpsProxyAgent } from '../proxy';\nimport type { HTTPModule } from './http-module';\n\nexport interface NodeTransportOptions extends BaseTransportOptions {\n /** Set a proxy that should be used for outbound requests. */\n proxy?: string;\n /** HTTPS proxy CA certificates */\n caCerts?: string | Buffer | Array<string | Buffer>;\n /** Custom HTTP module. Defaults to the native 'http' and 'https' modules. */\n httpModule?: HTTPModule;\n /** Allow overriding connection keepAlive, defaults to false */\n keepAlive?: boolean;\n}\n\n// Estimated maximum size for reasonable standalone event\nconst GZIP_THRESHOLD = 1024 * 32;\n\n/**\n * Gets a stream from a Uint8Array or string\n * Readable.from is ideal but was added in node.js v12.3.0 and v10.17.0\n */\nfunction streamFromBody(body: Uint8Array | string): Readable {\n return new Readable({\n read() {\n this.push(body);\n this.push(null);\n },\n });\n}\n\n/**\n * Creates a Transport that uses native the native 'http' and 'https' modules to send events to Sentry.\n */\nexport function makeNodeTransport(options: NodeTransportOptions): Transport {\n let urlSegments: URL;\n\n try {\n urlSegments = new URL(options.url);\n } catch (_e) {\n consoleSandbox(() => {\n // eslint-disable-next-line no-console\n console.warn(\n '[@sentry/node]: Invalid dsn or tunnel option, will not send any events. The tunnel option must be a full URL when used.',\n );\n });\n return createTransport(options, () => Promise.resolve({}));\n }\n\n const isHttps = urlSegments.protocol === 'https:';\n\n // Proxy prioritization: http => `options.proxy` | `process.env.http_proxy`\n // Proxy prioritization: https => `options.proxy` | `process.env.https_proxy` | `process.env.http_proxy`\n const proxy = applyNoProxyOption(\n urlSegments,\n options.proxy || (isHttps ? process.env.https_proxy : undefined) || process.env.http_proxy,\n );\n\n const nativeHttpModule = isHttps ? https : http;\n const keepAlive = options.keepAlive === undefined ? false : options.keepAlive;\n\n // TODO(v11): Evaluate if we can set keepAlive to true. This would involve testing for memory leaks in older node\n // versions(>= 8) as they had memory leaks when using it: #2555\n const agent = proxy\n ? (new HttpsProxyAgent(proxy) as http.Agent)\n : new nativeHttpModule.Agent({ keepAlive, maxSockets: 30, timeout: 2000 });\n\n const requestExecutor = createRequestExecutor(options, options.httpModule ?? nativeHttpModule, agent);\n return createTransport(options, requestExecutor);\n}\n\n/**\n * Honors the `no_proxy` env variable with the highest priority to allow for hosts exclusion.\n *\n * @param transportUrl The URL the transport intends to send events to.\n * @param proxy The client configured proxy.\n * @returns A proxy the transport should use.\n */\nfunction applyNoProxyOption(transportUrlSegments: URL, proxy: string | undefined): string | undefined {\n const { no_proxy } = process.env;\n\n const urlIsExemptFromProxy = no_proxy\n ?.split(',')\n .some(\n exemption => transportUrlSegments.host.endsWith(exemption) || transportUrlSegments.hostname.endsWith(exemption),\n );\n\n if (urlIsExemptFromProxy) {\n return undefined;\n } else {\n return proxy;\n }\n}\n\n/**\n * Creates a RequestExecutor to be used with `createTransport`.\n */\nfunction createRequestExecutor(\n options: NodeTransportOptions,\n httpModule: HTTPModule,\n agent: http.Agent,\n): TransportRequestExecutor {\n const { hostname, pathname, port, protocol, search } = new URL(options.url);\n return function makeRequest(request: TransportRequest): Promise<TransportMakeRequestResponse> {\n return new Promise((resolve, reject) => {\n // This ensures we do not generate any spans in OpenTelemetry for the transport\n suppressTracing(() => {\n let body = streamFromBody(request.body);\n\n const headers: Record<string, string> = { ...options.headers };\n\n if (request.body.length > GZIP_THRESHOLD) {\n headers['content-encoding'] = 'gzip';\n body = body.pipe(createGzip());\n }\n\n const hostnameIsIPv6 = hostname.startsWith('[');\n\n const req = httpModule.request(\n {\n method: 'POST',\n agent,\n headers,\n // Remove \"[\" and \"]\" from IPv6 hostnames\n hostname: hostnameIsIPv6 ? hostname.slice(1, -1) : hostname,\n path: `${pathname}${search}`,\n port,\n protocol,\n ca: options.caCerts,\n },\n res => {\n res.on('data', () => {\n // Drain socket\n });\n\n res.on('end', () => {\n // Drain socket\n });\n\n res.setEncoding('utf8');\n\n // \"Key-value pairs of header names and values. Header names are lower-cased.\"\n // https://nodejs.org/api/http.html#http_message_headers\n const retryAfterHeader = res.headers['retry-after'] ?? null;\n const rateLimitsHeader = res.headers['x-sentry-rate-limits'] ?? null;\n\n resolve({\n statusCode: res.statusCode,\n headers: {\n 'retry-after': retryAfterHeader,\n 'x-sentry-rate-limits': Array.isArray(rateLimitsHeader)\n ? rateLimitsHeader[0] || null\n : rateLimitsHeader,\n },\n });\n },\n );\n\n req.on('error', reject);\n body.pipe(req);\n });\n });\n };\n}\n"],"names":["Readable","consoleSandbox","createTransport","HttpsProxyAgent","suppressTracing","createGzip"],"mappings":"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"} |
@@ -5,3 +5,2 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| /** Adds an origin to an OTEL Span. */ | ||
| function addOriginToSpan(span, origin) { | ||
@@ -8,0 +7,0 @@ span.setAttribute(core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, origin); |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"addOriginToSpan.js","sources":["../../../src/utils/addOriginToSpan.ts"],"sourcesContent":["import type { Span } from '@opentelemetry/api';\nimport type { SpanOrigin } from '@sentry/core';\nimport { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core';\n\n/** Adds an origin to an OTEL Span. */\nexport function addOriginToSpan(span: Span, origin: SpanOrigin): void {\n span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, origin);\n}\n"],"names":["SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN"],"mappings":";;;;AAIA;AACO,SAAS,eAAe,CAAC,IAAI,EAAQ,MAAM,EAAoB;AACtE,EAAE,IAAI,CAAC,YAAY,CAACA,qCAAgC,EAAE,MAAM,CAAC;AAC7D;;;;"} | ||
| {"version":3,"file":"addOriginToSpan.js","sources":["../../../src/utils/addOriginToSpan.ts"],"sourcesContent":["import type { Span } from '@opentelemetry/api';\nimport type { SpanOrigin } from '@sentry/core';\nimport { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core';\n\n/** Adds an origin to an OTEL Span. */\nexport function addOriginToSpan(span: Span, origin: SpanOrigin): void {\n span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, origin);\n}\n"],"names":["SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN"],"mappings":";;;;AAKO,SAAS,eAAA,CAAgB,MAAY,MAAA,EAA0B;AACpE,EAAA,IAAA,CAAK,YAAA,CAAaA,uCAAkC,MAAM,CAAA;AAC5D;;;;"} |
@@ -7,3 +7,3 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| package: pkg, | ||
| 'javascript.is_cjs': detection.isCjs(), | ||
| "javascript.is_cjs": detection.isCjs() | ||
| }); | ||
@@ -10,0 +10,0 @@ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"createMissingInstrumentationContext.js","sources":["../../../src/utils/createMissingInstrumentationContext.ts"],"sourcesContent":["import type { MissingInstrumentationContext } from '@sentry/core';\nimport { isCjs } from './detection';\n\nexport const createMissingInstrumentationContext = (pkg: string): MissingInstrumentationContext => ({\n package: pkg,\n 'javascript.is_cjs': isCjs(),\n});\n"],"names":["isCjs"],"mappings":";;;;MAGa,mCAAA,GAAsC,CAAC,GAAG,MAA6C;AACpG,EAAE,OAAO,EAAE,GAAG;AACd,EAAE,mBAAmB,EAAEA,eAAK,EAAE;AAC9B,CAAC;;;;"} | ||
| {"version":3,"file":"createMissingInstrumentationContext.js","sources":["../../../src/utils/createMissingInstrumentationContext.ts"],"sourcesContent":["import type { MissingInstrumentationContext } from '@sentry/core';\nimport { isCjs } from './detection';\n\nexport const createMissingInstrumentationContext = (pkg: string): MissingInstrumentationContext => ({\n package: pkg,\n 'javascript.is_cjs': isCjs(),\n});\n"],"names":["isCjs"],"mappings":";;;;AAGO,MAAM,mCAAA,GAAsC,CAAC,GAAA,MAAgD;AAAA,EAClG,OAAA,EAAS,GAAA;AAAA,EACT,qBAAqBA,eAAA;AACvB,CAAA;;;;"} |
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| let cachedDebuggerEnabled; | ||
| /** | ||
| * Was the debugger enabled when this function was first called? | ||
| */ | ||
| async function isDebuggerEnabled() { | ||
| if (cachedDebuggerEnabled === undefined) { | ||
| if (cachedDebuggerEnabled === void 0) { | ||
| try { | ||
| // Node can be built without inspector support | ||
| const inspector = await import('node:inspector'); | ||
@@ -18,3 +13,2 @@ cachedDebuggerEnabled = !!inspector.url(); | ||
| } | ||
| return cachedDebuggerEnabled; | ||
@@ -21,0 +15,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"debug.js","sources":["../../../src/utils/debug.ts"],"sourcesContent":["let cachedDebuggerEnabled: boolean | undefined;\n\n/**\n * Was the debugger enabled when this function was first called?\n */\nexport async function isDebuggerEnabled(): Promise<boolean> {\n if (cachedDebuggerEnabled === undefined) {\n try {\n // Node can be built without inspector support\n const inspector = await import('node:inspector');\n cachedDebuggerEnabled = !!inspector.url();\n } catch {\n cachedDebuggerEnabled = false;\n }\n }\n\n return cachedDebuggerEnabled;\n}\n"],"names":[],"mappings":";;AAAA,IAAI,qBAAqB;;AAEzB;AACA;AACA;AACO,eAAe,iBAAiB,GAAqB;AAC5D,EAAE,IAAI,qBAAA,KAA0B,SAAS,EAAE;AAC3C,IAAI,IAAI;AACR;AACA,MAAM,MAAM,SAAA,GAAY,MAAM,OAAO,gBAAgB,CAAC;AACtD,MAAM,qBAAA,GAAwB,CAAC,CAAC,SAAS,CAAC,GAAG,EAAE;AAC/C,IAAI,EAAE,MAAM;AACZ,MAAM,qBAAA,GAAwB,KAAK;AACnC,IAAI;AACJ,EAAE;;AAEF,EAAE,OAAO,qBAAqB;AAC9B;;;;"} | ||
| {"version":3,"file":"debug.js","sources":["../../../src/utils/debug.ts"],"sourcesContent":["let cachedDebuggerEnabled: boolean | undefined;\n\n/**\n * Was the debugger enabled when this function was first called?\n */\nexport async function isDebuggerEnabled(): Promise<boolean> {\n if (cachedDebuggerEnabled === undefined) {\n try {\n // Node can be built without inspector support\n const inspector = await import('node:inspector');\n cachedDebuggerEnabled = !!inspector.url();\n } catch {\n cachedDebuggerEnabled = false;\n }\n }\n\n return cachedDebuggerEnabled;\n}\n"],"names":[],"mappings":";;AAAA,IAAI,qBAAA;AAKJ,eAAsB,iBAAA,GAAsC;AAC1D,EAAA,IAAI,0BAA0B,MAAA,EAAW;AACvC,IAAA,IAAI;AAEF,MAAA,MAAM,SAAA,GAAY,MAAM,OAAO,gBAAgB,CAAA;AAC/C,MAAA,qBAAA,GAAwB,CAAC,CAAC,SAAA,CAAU,GAAA,EAAI;AAAA,IAC1C,CAAA,CAAA,MAAQ;AACN,MAAA,qBAAA,GAAwB,KAAA;AAAA,IAC1B;AAAA,EACF;AAEA,EAAA,OAAO,qBAAA;AACT;;;;"} |
@@ -6,7 +6,5 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| /** Detect CommonJS. */ | ||
| function isCjs() { | ||
| try { | ||
| // oxlint-disable-next-line typescript/prefer-optional-chain | ||
| return typeof module !== 'undefined' && typeof module.exports !== 'undefined'; | ||
| return typeof module !== "undefined" && typeof module.exports !== "undefined"; | ||
| } catch { | ||
@@ -16,8 +14,3 @@ return false; | ||
| } | ||
| let hasWarnedAboutNodeVersion; | ||
| /** | ||
| * Check if the current Node.js version supports module.register | ||
| */ | ||
| function supportsEsmLoaderHooks() { | ||
@@ -27,18 +20,13 @@ if (isCjs()) { | ||
| } | ||
| if (nodeVersion.NODE_MAJOR >= 21 || (nodeVersion.NODE_MAJOR === 20 && nodeVersion.NODE_MINOR >= 6) || (nodeVersion.NODE_MAJOR === 18 && nodeVersion.NODE_MINOR >= 19)) { | ||
| if (nodeVersion.NODE_MAJOR >= 21 || nodeVersion.NODE_MAJOR === 20 && nodeVersion.NODE_MINOR >= 6 || nodeVersion.NODE_MAJOR === 18 && nodeVersion.NODE_MINOR >= 19) { | ||
| return true; | ||
| } | ||
| if (!hasWarnedAboutNodeVersion) { | ||
| hasWarnedAboutNodeVersion = true; | ||
| core.consoleSandbox(() => { | ||
| // eslint-disable-next-line no-console | ||
| console.warn( | ||
| `[Sentry] You are using Node.js v${process.versions.node} in ESM mode ("import syntax"). The Sentry Node.js SDK is not compatible with ESM in Node.js versions before 18.19.0 or before 20.6.0. Please either build your application with CommonJS ("require() syntax"), or upgrade your Node.js version.`, | ||
| `[Sentry] You are using Node.js v${process.versions.node} in ESM mode ("import syntax"). The Sentry Node.js SDK is not compatible with ESM in Node.js versions before 18.19.0 or before 20.6.0. Please either build your application with CommonJS ("require() syntax"), or upgrade your Node.js version.` | ||
| ); | ||
| }); | ||
| } | ||
| return false; | ||
@@ -45,0 +33,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"detection.js","sources":["../../../src/utils/detection.ts"],"sourcesContent":["import { consoleSandbox } from '@sentry/core';\nimport { NODE_MAJOR, NODE_MINOR } from '../nodeVersion';\n\n/** Detect CommonJS. */\nexport function isCjs(): boolean {\n try {\n // oxlint-disable-next-line typescript/prefer-optional-chain\n return typeof module !== 'undefined' && typeof module.exports !== 'undefined';\n } catch {\n return false;\n }\n}\n\nlet hasWarnedAboutNodeVersion: boolean | undefined;\n\n/**\n * Check if the current Node.js version supports module.register\n */\nexport function supportsEsmLoaderHooks(): boolean {\n if (isCjs()) {\n return false;\n }\n\n if (NODE_MAJOR >= 21 || (NODE_MAJOR === 20 && NODE_MINOR >= 6) || (NODE_MAJOR === 18 && NODE_MINOR >= 19)) {\n return true;\n }\n\n if (!hasWarnedAboutNodeVersion) {\n hasWarnedAboutNodeVersion = true;\n\n consoleSandbox(() => {\n // eslint-disable-next-line no-console\n console.warn(\n `[Sentry] You are using Node.js v${process.versions.node} in ESM mode (\"import syntax\"). The Sentry Node.js SDK is not compatible with ESM in Node.js versions before 18.19.0 or before 20.6.0. Please either build your application with CommonJS (\"require() syntax\"), or upgrade your Node.js version.`,\n );\n });\n }\n\n return false;\n}\n"],"names":["NODE_MAJOR","NODE_MINOR","consoleSandbox"],"mappings":";;;;;AAGA;AACO,SAAS,KAAK,GAAY;AACjC,EAAE,IAAI;AACN;AACA,IAAI,OAAO,OAAO,MAAA,KAAW,WAAA,IAAe,OAAO,MAAM,CAAC,OAAA,KAAY,WAAW;AACjF,EAAE,EAAE,MAAM;AACV,IAAI,OAAO,KAAK;AAChB,EAAE;AACF;;AAEA,IAAI,yBAAyB;;AAE7B;AACA;AACA;AACO,SAAS,sBAAsB,GAAY;AAClD,EAAE,IAAI,KAAK,EAAE,EAAE;AACf,IAAI,OAAO,KAAK;AAChB,EAAE;;AAEF,EAAE,IAAIA,sBAAA,IAAc,EAAA,KAAOA,sBAAA,KAAe,EAAA,IAAMC,sBAAA,IAAc,CAAC,CAAA,KAAMD,sBAAA,KAAe,EAAA,IAAMC,sBAAA,IAAc,EAAE,CAAC,EAAE;AAC7G,IAAI,OAAO,IAAI;AACf,EAAE;;AAEF,EAAE,IAAI,CAAC,yBAAyB,EAAE;AAClC,IAAI,yBAAA,GAA4B,IAAI;;AAEpC,IAAIC,mBAAc,CAAC,MAAM;AACzB;AACA,MAAM,OAAO,CAAC,IAAI;AAClB,QAAQ,CAAC,gCAAgC,EAAE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,gPAAgP,CAAC;AAClT,OAAO;AACP,IAAI,CAAC,CAAC;AACN,EAAE;;AAEF,EAAE,OAAO,KAAK;AACd;;;;;"} | ||
| {"version":3,"file":"detection.js","sources":["../../../src/utils/detection.ts"],"sourcesContent":["import { consoleSandbox } from '@sentry/core';\nimport { NODE_MAJOR, NODE_MINOR } from '../nodeVersion';\n\n/** Detect CommonJS. */\nexport function isCjs(): boolean {\n try {\n // oxlint-disable-next-line typescript/prefer-optional-chain\n return typeof module !== 'undefined' && typeof module.exports !== 'undefined';\n } catch {\n return false;\n }\n}\n\nlet hasWarnedAboutNodeVersion: boolean | undefined;\n\n/**\n * Check if the current Node.js version supports module.register\n */\nexport function supportsEsmLoaderHooks(): boolean {\n if (isCjs()) {\n return false;\n }\n\n if (NODE_MAJOR >= 21 || (NODE_MAJOR === 20 && NODE_MINOR >= 6) || (NODE_MAJOR === 18 && NODE_MINOR >= 19)) {\n return true;\n }\n\n if (!hasWarnedAboutNodeVersion) {\n hasWarnedAboutNodeVersion = true;\n\n consoleSandbox(() => {\n // eslint-disable-next-line no-console\n console.warn(\n `[Sentry] You are using Node.js v${process.versions.node} in ESM mode (\"import syntax\"). The Sentry Node.js SDK is not compatible with ESM in Node.js versions before 18.19.0 or before 20.6.0. Please either build your application with CommonJS (\"require() syntax\"), or upgrade your Node.js version.`,\n );\n });\n }\n\n return false;\n}\n"],"names":["NODE_MAJOR","NODE_MINOR","consoleSandbox"],"mappings":";;;;;AAIO,SAAS,KAAA,GAAiB;AAC/B,EAAA,IAAI;AAEF,IAAA,OAAO,OAAO,MAAA,KAAW,WAAA,IAAe,OAAO,OAAO,OAAA,KAAY,WAAA;AAAA,EACpE,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,KAAA;AAAA,EACT;AACF;AAEA,IAAI,yBAAA;AAKG,SAAS,sBAAA,GAAkC;AAChD,EAAA,IAAI,OAAM,EAAG;AACX,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,IAAIA,sBAAA,IAAc,MAAOA,sBAAA,KAAe,EAAA,IAAMC,0BAAc,CAAA,IAAOD,sBAAA,KAAe,EAAA,IAAMC,sBAAA,IAAc,EAAA,EAAK;AACzG,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,IAAI,CAAC,yBAAA,EAA2B;AAC9B,IAAA,yBAAA,GAA4B,IAAA;AAE5B,IAAAC,mBAAA,CAAe,MAAM;AAEnB,MAAA,OAAA,CAAQ,IAAA;AAAA,QACN,CAAA,gCAAA,EAAmC,OAAA,CAAQ,QAAA,CAAS,IAAI,CAAA,gPAAA;AAAA,OAC1D;AAAA,IACF,CAAC,CAAA;AAAA,EACH;AAEA,EAAA,OAAO,KAAA;AACT;;;;;"} |
@@ -8,34 +8,17 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| /** | ||
| * Checks and warns if a framework isn't wrapped by opentelemetry. | ||
| */ | ||
| function ensureIsWrapped( | ||
| maybeWrappedFunction, | ||
| name, | ||
| ) { | ||
| function ensureIsWrapped(maybeWrappedFunction, name) { | ||
| const clientOptions = core.getClient()?.getOptions(); | ||
| if ( | ||
| !clientOptions?.disableInstrumentationWarnings && | ||
| !( | ||
| instrumentation.isWrapped(maybeWrappedFunction) || | ||
| typeof core.getOriginalFunction(maybeWrappedFunction ) === 'function' | ||
| ) && | ||
| core.isEnabled() && | ||
| core.hasSpansEnabled(clientOptions) | ||
| ) { | ||
| if (!clientOptions?.disableInstrumentationWarnings && !(instrumentation.isWrapped(maybeWrappedFunction) || typeof core.getOriginalFunction(maybeWrappedFunction) === "function") && core.isEnabled() && core.hasSpansEnabled(clientOptions)) { | ||
| core.consoleSandbox(() => { | ||
| if (detection.isCjs()) { | ||
| // eslint-disable-next-line no-console | ||
| console.warn( | ||
| `[Sentry] ${name} is not instrumented. This is likely because you required/imported ${name} before calling \`Sentry.init()\`.`, | ||
| `[Sentry] ${name} is not instrumented. This is likely because you required/imported ${name} before calling \`Sentry.init()\`.` | ||
| ); | ||
| } else { | ||
| // eslint-disable-next-line no-console | ||
| console.warn( | ||
| `[Sentry] ${name} is not instrumented. Please make sure to initialize Sentry in a separate file that you \`--import\` when running node, see: https://docs.sentry.io/platforms/javascript/guides/${name}/install/esm/.`, | ||
| `[Sentry] ${name} is not instrumented. Please make sure to initialize Sentry in a separate file that you \`--import\` when running node, see: https://docs.sentry.io/platforms/javascript/guides/${name}/install/esm/.` | ||
| ); | ||
| } | ||
| }); | ||
| core.getGlobalScope().setContext('missing_instrumentation', createMissingInstrumentationContext.createMissingInstrumentationContext(name)); | ||
| core.getGlobalScope().setContext("missing_instrumentation", createMissingInstrumentationContext.createMissingInstrumentationContext(name)); | ||
| } | ||
@@ -42,0 +25,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"ensureIsWrapped.js","sources":["../../../src/utils/ensureIsWrapped.ts"],"sourcesContent":["import { isWrapped } from '@opentelemetry/instrumentation';\nimport {\n consoleSandbox,\n getClient,\n getOriginalFunction,\n getGlobalScope,\n hasSpansEnabled,\n isEnabled,\n type WrappedFunction,\n} from '@sentry/core';\nimport type { NodeClient } from '../sdk/client';\nimport { createMissingInstrumentationContext } from './createMissingInstrumentationContext';\nimport { isCjs } from './detection';\n\n/**\n * Checks and warns if a framework isn't wrapped by opentelemetry.\n */\nexport function ensureIsWrapped(\n maybeWrappedFunction: unknown,\n name: 'express' | 'connect' | 'fastify' | 'hapi' | 'koa' | 'hono',\n): void {\n const clientOptions = getClient<NodeClient>()?.getOptions();\n if (\n !clientOptions?.disableInstrumentationWarnings &&\n !(\n isWrapped(maybeWrappedFunction) ||\n typeof getOriginalFunction(maybeWrappedFunction as WrappedFunction) === 'function'\n ) &&\n isEnabled() &&\n hasSpansEnabled(clientOptions)\n ) {\n consoleSandbox(() => {\n if (isCjs()) {\n // eslint-disable-next-line no-console\n console.warn(\n `[Sentry] ${name} is not instrumented. This is likely because you required/imported ${name} before calling \\`Sentry.init()\\`.`,\n );\n } else {\n // eslint-disable-next-line no-console\n console.warn(\n `[Sentry] ${name} is not instrumented. Please make sure to initialize Sentry in a separate file that you \\`--import\\` when running node, see: https://docs.sentry.io/platforms/javascript/guides/${name}/install/esm/.`,\n );\n }\n });\n\n getGlobalScope().setContext('missing_instrumentation', createMissingInstrumentationContext(name));\n }\n}\n"],"names":["getClient","isWrapped","getOriginalFunction","isEnabled","hasSpansEnabled","consoleSandbox","isCjs","getGlobalScope","createMissingInstrumentationContext"],"mappings":";;;;;;;AAcA;AACA;AACA;AACO,SAAS,eAAe;AAC/B,EAAE,oBAAoB;AACtB,EAAE,IAAI;AACN,EAAQ;AACR,EAAE,MAAM,gBAAgBA,cAAS,EAAc,EAAE,UAAU,EAAE;AAC7D,EAAE;AACF,IAAI,CAAC,aAAa,EAAE,8BAAA;AACpB,IAAI;AACJ,MAAMC,yBAAS,CAAC,oBAAoB,CAAA;AACpC,MAAM,OAAOC,wBAAmB,CAAC,oBAAA,OAA6C;AAC9E,KAAI;AACJ,IAAIC,cAAS,EAAC;AACd,IAAIC,oBAAe,CAAC,aAAa;AACjC,IAAI;AACJ,IAAIC,mBAAc,CAAC,MAAM;AACzB,MAAM,IAAIC,eAAK,EAAE,EAAE;AACnB;AACA,QAAQ,OAAO,CAAC,IAAI;AACpB,UAAU,CAAC,SAAS,EAAE,IAAI,CAAC,mEAAmE,EAAE,IAAI,CAAC,kCAAkC,CAAC;AACxI,SAAS;AACT,MAAM,OAAO;AACb;AACA,QAAQ,OAAO,CAAC,IAAI;AACpB,UAAU,CAAC,SAAS,EAAE,IAAI,CAAC,gLAAgL,EAAE,IAAI,CAAC,cAAc,CAAC;AACjO,SAAS;AACT,MAAM;AACN,IAAI,CAAC,CAAC;;AAEN,IAAIC,mBAAc,EAAE,CAAC,UAAU,CAAC,yBAAyB,EAAEC,uEAAmC,CAAC,IAAI,CAAC,CAAC;AACrG,EAAE;AACF;;;;"} | ||
| {"version":3,"file":"ensureIsWrapped.js","sources":["../../../src/utils/ensureIsWrapped.ts"],"sourcesContent":["import { isWrapped } from '@opentelemetry/instrumentation';\nimport {\n consoleSandbox,\n getClient,\n getOriginalFunction,\n getGlobalScope,\n hasSpansEnabled,\n isEnabled,\n type WrappedFunction,\n} from '@sentry/core';\nimport type { NodeClient } from '../sdk/client';\nimport { createMissingInstrumentationContext } from './createMissingInstrumentationContext';\nimport { isCjs } from './detection';\n\n/**\n * Checks and warns if a framework isn't wrapped by opentelemetry.\n */\nexport function ensureIsWrapped(\n maybeWrappedFunction: unknown,\n name: 'express' | 'connect' | 'fastify' | 'hapi' | 'koa' | 'hono',\n): void {\n const clientOptions = getClient<NodeClient>()?.getOptions();\n if (\n !clientOptions?.disableInstrumentationWarnings &&\n !(\n isWrapped(maybeWrappedFunction) ||\n typeof getOriginalFunction(maybeWrappedFunction as WrappedFunction) === 'function'\n ) &&\n isEnabled() &&\n hasSpansEnabled(clientOptions)\n ) {\n consoleSandbox(() => {\n if (isCjs()) {\n // eslint-disable-next-line no-console\n console.warn(\n `[Sentry] ${name} is not instrumented. This is likely because you required/imported ${name} before calling \\`Sentry.init()\\`.`,\n );\n } else {\n // eslint-disable-next-line no-console\n console.warn(\n `[Sentry] ${name} is not instrumented. Please make sure to initialize Sentry in a separate file that you \\`--import\\` when running node, see: https://docs.sentry.io/platforms/javascript/guides/${name}/install/esm/.`,\n );\n }\n });\n\n getGlobalScope().setContext('missing_instrumentation', createMissingInstrumentationContext(name));\n }\n}\n"],"names":["getClient","isWrapped","getOriginalFunction","isEnabled","hasSpansEnabled","consoleSandbox","isCjs","getGlobalScope","createMissingInstrumentationContext"],"mappings":";;;;;;;AAiBO,SAAS,eAAA,CACd,sBACA,IAAA,EACM;AACN,EAAA,MAAM,aAAA,GAAgBA,cAAA,EAAsB,EAAG,UAAA,EAAW;AAC1D,EAAA,IACE,CAAC,aAAA,EAAe,8BAAA,IAChB,EACEC,yBAAA,CAAU,oBAAoB,CAAA,IAC9B,OAAOC,wBAAA,CAAoB,oBAAuC,MAAM,UAAA,CAAA,IAE1EC,cAAA,EAAU,IACVC,oBAAA,CAAgB,aAAa,CAAA,EAC7B;AACA,IAAAC,mBAAA,CAAe,MAAM;AACnB,MAAA,IAAIC,iBAAM,EAAG;AAEX,QAAA,OAAA,CAAQ,IAAA;AAAA,UACN,CAAA,SAAA,EAAY,IAAI,CAAA,mEAAA,EAAsE,IAAI,CAAA,kCAAA;AAAA,SAC5F;AAAA,MACF,CAAA,MAAO;AAEL,QAAA,OAAA,CAAQ,IAAA;AAAA,UACN,CAAA,SAAA,EAAY,IAAI,CAAA,gLAAA,EAAmL,IAAI,CAAA,cAAA;AAAA,SACzM;AAAA,MACF;AAAA,IACF,CAAC,CAAA;AAED,IAAAC,mBAAA,EAAe,CAAE,UAAA,CAAW,yBAAA,EAA2BC,uEAAA,CAAoC,IAAI,CAAC,CAAA;AAAA,EAClG;AACF;;;;"} |
@@ -6,34 +6,25 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const DEFAULT_SHUTDOWN_TIMEOUT = 2000; | ||
| /** | ||
| * @hidden | ||
| */ | ||
| const DEFAULT_SHUTDOWN_TIMEOUT = 2e3; | ||
| function logAndExitProcess(error) { | ||
| core.consoleSandbox(() => { | ||
| // eslint-disable-next-line no-console | ||
| console.error(error); | ||
| }); | ||
| const client = core.getClient(); | ||
| if (client === undefined) { | ||
| debugBuild.DEBUG_BUILD && core.debug.warn('No NodeClient was defined, we are exiting the process now.'); | ||
| if (client === void 0) { | ||
| debugBuild.DEBUG_BUILD && core.debug.warn("No NodeClient was defined, we are exiting the process now."); | ||
| global.process.exit(1); | ||
| return; | ||
| } | ||
| const options = client.getOptions(); | ||
| const timeout = | ||
| options?.shutdownTimeout && options.shutdownTimeout > 0 ? options.shutdownTimeout : DEFAULT_SHUTDOWN_TIMEOUT; | ||
| const timeout = options?.shutdownTimeout && options.shutdownTimeout > 0 ? options.shutdownTimeout : DEFAULT_SHUTDOWN_TIMEOUT; | ||
| client.close(timeout).then( | ||
| (result) => { | ||
| if (!result) { | ||
| debugBuild.DEBUG_BUILD && core.debug.warn('We reached the timeout for emptying the request buffer, still exiting now!'); | ||
| debugBuild.DEBUG_BUILD && core.debug.warn("We reached the timeout for emptying the request buffer, still exiting now!"); | ||
| } | ||
| global.process.exit(1); | ||
| }, | ||
| error => { | ||
| debugBuild.DEBUG_BUILD && core.debug.error(error); | ||
| }, | ||
| (error2) => { | ||
| debugBuild.DEBUG_BUILD && core.debug.error(error2); | ||
| } | ||
| ); | ||
@@ -40,0 +31,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"errorhandling.js","sources":["../../../src/utils/errorhandling.ts"],"sourcesContent":["import { consoleSandbox, debug, getClient } from '@sentry/core';\nimport { DEBUG_BUILD } from '../debug-build';\nimport type { NodeClient } from '../sdk/client';\n\nconst DEFAULT_SHUTDOWN_TIMEOUT = 2000;\n\n/**\n * @hidden\n */\nexport function logAndExitProcess(error: unknown): void {\n consoleSandbox(() => {\n // eslint-disable-next-line no-console\n console.error(error);\n });\n\n const client = getClient<NodeClient>();\n\n if (client === undefined) {\n DEBUG_BUILD && debug.warn('No NodeClient was defined, we are exiting the process now.');\n global.process.exit(1);\n return;\n }\n\n const options = client.getOptions();\n const timeout =\n options?.shutdownTimeout && options.shutdownTimeout > 0 ? options.shutdownTimeout : DEFAULT_SHUTDOWN_TIMEOUT;\n client.close(timeout).then(\n (result: boolean) => {\n if (!result) {\n DEBUG_BUILD && debug.warn('We reached the timeout for emptying the request buffer, still exiting now!');\n }\n global.process.exit(1);\n },\n error => {\n DEBUG_BUILD && debug.error(error);\n },\n );\n}\n"],"names":["consoleSandbox","getClient","DEBUG_BUILD","debug"],"mappings":";;;;;AAIA,MAAM,wBAAA,GAA2B,IAAI;;AAErC;AACA;AACA;AACO,SAAS,iBAAiB,CAAC,KAAK,EAAiB;AACxD,EAAEA,mBAAc,CAAC,MAAM;AACvB;AACA,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;AACxB,EAAE,CAAC,CAAC;;AAEJ,EAAE,MAAM,MAAA,GAASC,cAAS,EAAc;;AAExC,EAAE,IAAI,MAAA,KAAW,SAAS,EAAE;AAC5B,IAAIC,0BAAeC,UAAK,CAAC,IAAI,CAAC,4DAA4D,CAAC;AAC3F,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;AAC1B,IAAI;AACJ,EAAE;;AAEF,EAAE,MAAM,OAAA,GAAU,MAAM,CAAC,UAAU,EAAE;AACrC,EAAE,MAAM,OAAA;AACR,IAAI,OAAO,EAAE,eAAA,IAAmB,OAAO,CAAC,eAAA,GAAkB,CAAA,GAAI,OAAO,CAAC,eAAA,GAAkB,wBAAwB;AAChH,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI;AAC5B,IAAI,CAAC,MAAM,KAAc;AACzB,MAAM,IAAI,CAAC,MAAM,EAAE;AACnB,QAAQD,0BAAeC,UAAK,CAAC,IAAI,CAAC,4EAA4E,CAAC;AAC/G,MAAM;AACN,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;AAC5B,IAAI,CAAC;AACL,IAAI,SAAS;AACb,MAAMD,0BAAeC,UAAK,CAAC,KAAK,CAAC,KAAK,CAAC;AACvC,IAAI,CAAC;AACL,GAAG;AACH;;;;"} | ||
| {"version":3,"file":"errorhandling.js","sources":["../../../src/utils/errorhandling.ts"],"sourcesContent":["import { consoleSandbox, debug, getClient } from '@sentry/core';\nimport { DEBUG_BUILD } from '../debug-build';\nimport type { NodeClient } from '../sdk/client';\n\nconst DEFAULT_SHUTDOWN_TIMEOUT = 2000;\n\n/**\n * @hidden\n */\nexport function logAndExitProcess(error: unknown): void {\n consoleSandbox(() => {\n // eslint-disable-next-line no-console\n console.error(error);\n });\n\n const client = getClient<NodeClient>();\n\n if (client === undefined) {\n DEBUG_BUILD && debug.warn('No NodeClient was defined, we are exiting the process now.');\n global.process.exit(1);\n return;\n }\n\n const options = client.getOptions();\n const timeout =\n options?.shutdownTimeout && options.shutdownTimeout > 0 ? options.shutdownTimeout : DEFAULT_SHUTDOWN_TIMEOUT;\n client.close(timeout).then(\n (result: boolean) => {\n if (!result) {\n DEBUG_BUILD && debug.warn('We reached the timeout for emptying the request buffer, still exiting now!');\n }\n global.process.exit(1);\n },\n error => {\n DEBUG_BUILD && debug.error(error);\n },\n );\n}\n"],"names":["consoleSandbox","getClient","DEBUG_BUILD","debug","error"],"mappings":";;;;;AAIA,MAAM,wBAAA,GAA2B,GAAA;AAK1B,SAAS,kBAAkB,KAAA,EAAsB;AACtD,EAAAA,mBAAA,CAAe,MAAM;AAEnB,IAAA,OAAA,CAAQ,MAAM,KAAK,CAAA;AAAA,EACrB,CAAC,CAAA;AAED,EAAA,MAAM,SAASC,cAAA,EAAsB;AAErC,EAAA,IAAI,WAAW,MAAA,EAAW;AACxB,IAAAC,sBAAA,IAAeC,UAAA,CAAM,KAAK,4DAA4D,CAAA;AACtF,IAAA,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAC,CAAA;AACrB,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,OAAA,GAAU,OAAO,UAAA,EAAW;AAClC,EAAA,MAAM,UACJ,OAAA,EAAS,eAAA,IAAmB,QAAQ,eAAA,GAAkB,CAAA,GAAI,QAAQ,eAAA,GAAkB,wBAAA;AACtF,EAAA,MAAA,CAAO,KAAA,CAAM,OAAO,CAAA,CAAE,IAAA;AAAA,IACpB,CAAC,MAAA,KAAoB;AACnB,MAAA,IAAI,CAAC,MAAA,EAAQ;AACX,QAAAD,sBAAA,IAAeC,UAAA,CAAM,KAAK,4EAA4E,CAAA;AAAA,MACxG;AACA,MAAA,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,IACvB,CAAA;AAAA,IACA,CAAAC,MAAAA,KAAS;AACP,MAAAF,sBAAA,IAAeC,UAAA,CAAM,MAAMC,MAAK,CAAA;AAAA,IAClC;AAAA,GACF;AACF;;;;"} |
@@ -6,16 +6,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| /** normalizes Windows paths */ | ||
| function normalizeWindowsPath(path) { | ||
| return path | ||
| .replace(/^[A-Z]:/, '') // remove Windows-style prefix | ||
| .replace(/\\/g, '/'); // replace all `\` instances with `/` | ||
| return path.replace(/^[A-Z]:/, "").replace(/\\/g, "/"); | ||
| } | ||
| /** Creates a function that gets the module name from a filename */ | ||
| function createGetModuleFromFilename( | ||
| basePath = process.argv[1] ? core.dirname(process.argv[1]) : process.cwd(), | ||
| isWindows = node_path.sep === '\\', | ||
| ) { | ||
| function createGetModuleFromFilename(basePath = process.argv[1] ? core.dirname(process.argv[1]) : process.cwd(), isWindows = node_path.sep === "\\") { | ||
| const normalizedBase = isWindows ? normalizeWindowsPath(basePath) : basePath; | ||
| return (filename) => { | ||
@@ -25,33 +16,19 @@ if (!filename) { | ||
| } | ||
| const normalizedFilename = isWindows ? normalizeWindowsPath(filename) : filename; | ||
| // eslint-disable-next-line prefer-const | ||
| let { dir, base: file, ext } = node_path.posix.parse(normalizedFilename); | ||
| if (ext === '.js' || ext === '.mjs' || ext === '.cjs') { | ||
| if (ext === ".js" || ext === ".mjs" || ext === ".cjs") { | ||
| file = file.slice(0, ext.length * -1); | ||
| } | ||
| // The file name might be URI-encoded which we want to decode to | ||
| // the original file name. | ||
| const decodedFile = decodeURIComponent(file); | ||
| if (!dir) { | ||
| // No dirname whatsoever | ||
| dir = '.'; | ||
| dir = "."; | ||
| } | ||
| const n = dir.lastIndexOf('/node_modules'); | ||
| const n = dir.lastIndexOf("/node_modules"); | ||
| if (n > -1) { | ||
| return `${dir.slice(n + 14).replace(/\//g, '.')}:${decodedFile}`; | ||
| return `${dir.slice(n + 14).replace(/\//g, ".")}:${decodedFile}`; | ||
| } | ||
| // Let's see if it's a part of the main module | ||
| // To be a part of main module, it has to share the same base | ||
| if (dir.startsWith(normalizedBase)) { | ||
| const moduleName = dir.slice(normalizedBase.length + 1).replace(/\//g, '.'); | ||
| const moduleName = dir.slice(normalizedBase.length + 1).replace(/\//g, "."); | ||
| return moduleName ? `${moduleName}:${decodedFile}` : decodedFile; | ||
| } | ||
| return decodedFile; | ||
@@ -58,0 +35,0 @@ }; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"module.js","sources":["../../../src/utils/module.ts"],"sourcesContent":["import { posix, sep } from 'node:path';\nimport { dirname } from '@sentry/core';\n\n/** normalizes Windows paths */\nfunction normalizeWindowsPath(path: string): string {\n return path\n .replace(/^[A-Z]:/, '') // remove Windows-style prefix\n .replace(/\\\\/g, '/'); // replace all `\\` instances with `/`\n}\n\n/** Creates a function that gets the module name from a filename */\nexport function createGetModuleFromFilename(\n basePath: string = process.argv[1] ? dirname(process.argv[1]) : process.cwd(),\n isWindows: boolean = sep === '\\\\',\n): (filename: string | undefined) => string | undefined {\n const normalizedBase = isWindows ? normalizeWindowsPath(basePath) : basePath;\n\n return (filename: string | undefined) => {\n if (!filename) {\n return;\n }\n\n const normalizedFilename = isWindows ? normalizeWindowsPath(filename) : filename;\n\n // eslint-disable-next-line prefer-const\n let { dir, base: file, ext } = posix.parse(normalizedFilename);\n\n if (ext === '.js' || ext === '.mjs' || ext === '.cjs') {\n file = file.slice(0, ext.length * -1);\n }\n\n // The file name might be URI-encoded which we want to decode to\n // the original file name.\n const decodedFile = decodeURIComponent(file);\n\n if (!dir) {\n // No dirname whatsoever\n dir = '.';\n }\n\n const n = dir.lastIndexOf('/node_modules');\n if (n > -1) {\n return `${dir.slice(n + 14).replace(/\\//g, '.')}:${decodedFile}`;\n }\n\n // Let's see if it's a part of the main module\n // To be a part of main module, it has to share the same base\n if (dir.startsWith(normalizedBase)) {\n const moduleName = dir.slice(normalizedBase.length + 1).replace(/\\//g, '.');\n return moduleName ? `${moduleName}:${decodedFile}` : decodedFile;\n }\n\n return decodedFile;\n };\n}\n"],"names":["dirname","sep","posix"],"mappings":";;;;;AAGA;AACA,SAAS,oBAAoB,CAAC,IAAI,EAAkB;AACpD,EAAE,OAAO;AACT,KAAK,OAAO,CAAC,SAAS,EAAE,EAAE,CAAA;AAC1B,KAAK,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;AACxB;;AAEA;AACO,SAAS,2BAA2B;AAC3C,EAAE,QAAQ,GAAW,OAAO,CAAC,IAAI,CAAC,CAAC,CAAA,GAAIA,YAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA,GAAI,OAAO,CAAC,GAAG,EAAE;AAC/E,EAAE,SAAS,GAAYC,aAAA,KAAQ,IAAI;AACnC,EAAwD;AACxD,EAAE,MAAM,cAAA,GAAiB,SAAA,GAAY,oBAAoB,CAAC,QAAQ,CAAA,GAAI,QAAQ;;AAE9E,EAAE,OAAO,CAAC,QAAQ,KAAyB;AAC3C,IAAI,IAAI,CAAC,QAAQ,EAAE;AACnB,MAAM;AACN,IAAI;;AAEJ,IAAI,MAAM,kBAAA,GAAqB,SAAA,GAAY,oBAAoB,CAAC,QAAQ,CAAA,GAAI,QAAQ;;AAEpF;AACA,IAAI,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,GAAA,EAAI,GAAIC,eAAK,CAAC,KAAK,CAAC,kBAAkB,CAAC;;AAElE,IAAI,IAAI,GAAA,KAAQ,KAAA,IAAS,GAAA,KAAQ,MAAA,IAAU,GAAA,KAAQ,MAAM,EAAE;AAC3D,MAAM,IAAA,GAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,MAAA,GAAS,EAAE,CAAC;AAC3C,IAAI;;AAEJ;AACA;AACA,IAAI,MAAM,WAAA,GAAc,kBAAkB,CAAC,IAAI,CAAC;;AAEhD,IAAI,IAAI,CAAC,GAAG,EAAE;AACd;AACA,MAAM,GAAA,GAAM,GAAG;AACf,IAAI;;AAEJ,IAAI,MAAM,IAAI,GAAG,CAAC,WAAW,CAAC,eAAe,CAAC;AAC9C,IAAI,IAAI,CAAA,GAAI,EAAE,EAAE;AAChB,MAAM,OAAO,CAAC,EAAA,GAAA,CAAA,KAAA,CAAA,CAAA,GAAA,EAAA,CAAA,CAAA,OAAA,CAAA,KAAA,EAAA,GAAA,CAAA,CAAA,CAAA,EAAA,WAAA,CAAA,CAAA;AACA,IAAA;;AAEA;AACA;AACA,IAAA,IAAA,GAAA,CAAA,UAAA,CAAA,cAAA,CAAA,EAAA;AACA,MAAA,MAAA,UAAA,GAAA,GAAA,CAAA,KAAA,CAAA,cAAA,CAAA,MAAA,GAAA,CAAA,CAAA,CAAA,OAAA,CAAA,KAAA,EAAA,GAAA,CAAA;AACA,MAAA,OAAA,UAAA,GAAA,CAAA,EAAA,UAAA,CAAA,CAAA,EAAA,WAAA,CAAA,CAAA,GAAA,WAAA;AACA,IAAA;;AAEA,IAAA,OAAA,WAAA;AACA,EAAA,CAAA;AACA;;;;"} | ||
| {"version":3,"file":"module.js","sources":["../../../src/utils/module.ts"],"sourcesContent":["import { posix, sep } from 'node:path';\nimport { dirname } from '@sentry/core';\n\n/** normalizes Windows paths */\nfunction normalizeWindowsPath(path: string): string {\n return path\n .replace(/^[A-Z]:/, '') // remove Windows-style prefix\n .replace(/\\\\/g, '/'); // replace all `\\` instances with `/`\n}\n\n/** Creates a function that gets the module name from a filename */\nexport function createGetModuleFromFilename(\n basePath: string = process.argv[1] ? dirname(process.argv[1]) : process.cwd(),\n isWindows: boolean = sep === '\\\\',\n): (filename: string | undefined) => string | undefined {\n const normalizedBase = isWindows ? normalizeWindowsPath(basePath) : basePath;\n\n return (filename: string | undefined) => {\n if (!filename) {\n return;\n }\n\n const normalizedFilename = isWindows ? normalizeWindowsPath(filename) : filename;\n\n // eslint-disable-next-line prefer-const\n let { dir, base: file, ext } = posix.parse(normalizedFilename);\n\n if (ext === '.js' || ext === '.mjs' || ext === '.cjs') {\n file = file.slice(0, ext.length * -1);\n }\n\n // The file name might be URI-encoded which we want to decode to\n // the original file name.\n const decodedFile = decodeURIComponent(file);\n\n if (!dir) {\n // No dirname whatsoever\n dir = '.';\n }\n\n const n = dir.lastIndexOf('/node_modules');\n if (n > -1) {\n return `${dir.slice(n + 14).replace(/\\//g, '.')}:${decodedFile}`;\n }\n\n // Let's see if it's a part of the main module\n // To be a part of main module, it has to share the same base\n if (dir.startsWith(normalizedBase)) {\n const moduleName = dir.slice(normalizedBase.length + 1).replace(/\\//g, '.');\n return moduleName ? `${moduleName}:${decodedFile}` : decodedFile;\n }\n\n return decodedFile;\n };\n}\n"],"names":["dirname","sep","posix"],"mappings":";;;;;AAIA,SAAS,qBAAqB,IAAA,EAAsB;AAClD,EAAA,OAAO,KACJ,OAAA,CAAQ,SAAA,EAAW,EAAE,CAAA,CACrB,OAAA,CAAQ,OAAO,GAAG,CAAA;AACvB;AAGO,SAAS,4BACd,QAAA,GAAmB,OAAA,CAAQ,IAAA,CAAK,CAAC,IAAIA,YAAA,CAAQ,OAAA,CAAQ,IAAA,CAAK,CAAC,CAAC,CAAA,GAAI,OAAA,CAAQ,KAAI,EAC5E,SAAA,GAAqBC,kBAAQ,IAAA,EACyB;AACtD,EAAA,MAAM,cAAA,GAAiB,SAAA,GAAY,oBAAA,CAAqB,QAAQ,CAAA,GAAI,QAAA;AAEpE,EAAA,OAAO,CAAC,QAAA,KAAiC;AACvC,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,kBAAA,GAAqB,SAAA,GAAY,oBAAA,CAAqB,QAAQ,CAAA,GAAI,QAAA;AAGxE,IAAA,IAAI,EAAE,KAAK,IAAA,EAAM,IAAA,EAAM,KAAI,GAAIC,eAAA,CAAM,MAAM,kBAAkB,CAAA;AAE7D,IAAA,IAAI,GAAA,KAAQ,KAAA,IAAS,GAAA,KAAQ,MAAA,IAAU,QAAQ,MAAA,EAAQ;AACrD,MAAA,IAAA,GAAO,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,GAAA,CAAI,SAAS,EAAE,CAAA;AAAA,IACtC;AAIA,IAAA,MAAM,WAAA,GAAc,mBAAmB,IAAI,CAAA;AAE3C,IAAA,IAAI,CAAC,GAAA,EAAK;AAER,MAAA,GAAA,GAAM,GAAA;AAAA,IACR;AAEA,IAAA,MAAM,CAAA,GAAI,GAAA,CAAI,WAAA,CAAY,eAAe,CAAA;AACzC,IAAA,IAAI,IAAI,EAAA,EAAI;AACV,MAAA,OAAO,CAAA,EAAG,GAAA,CAAI,KAAA,CAAM,CAAA,GAAI,EAAE,CAAA,CAAE,OAAA,CAAQ,KAAA,EAAO,GAAG,CAAC,CAAA,CAAA,EAAI,WAAW,CAAA,CAAA;AAAA,IAChE;AAIA,IAAA,IAAI,GAAA,CAAI,UAAA,CAAW,cAAc,CAAA,EAAG;AAClC,MAAA,MAAM,UAAA,GAAa,IAAI,KAAA,CAAM,cAAA,CAAe,SAAS,CAAC,CAAA,CAAE,OAAA,CAAQ,KAAA,EAAO,GAAG,CAAA;AAC1E,MAAA,OAAO,UAAA,GAAa,CAAA,EAAG,UAAU,CAAA,CAAA,EAAI,WAAW,CAAA,CAAA,GAAK,WAAA;AAAA,IACvD;AAEA,IAAA,OAAO,WAAA;AAAA,EACT,CAAA;AACF;;;;"} |
@@ -5,45 +5,14 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const SENTRY_TRACE_HEADER = 'sentry-trace'; | ||
| const SENTRY_BAGGAGE_HEADER = 'baggage'; | ||
| const W3C_TRACEPARENT_HEADER = 'traceparent'; | ||
| /** | ||
| * Add trace propagation headers to an outgoing fetch/undici request. | ||
| * | ||
| * Checks if the request URL matches trace propagation targets, | ||
| * then injects sentry-trace, traceparent, and baggage headers. | ||
| */ | ||
| // eslint-disable-next-line complexity | ||
| function addTracePropagationHeadersToFetchRequest( | ||
| request, | ||
| propagationDecisionMap, | ||
| ) { | ||
| const SENTRY_TRACE_HEADER = "sentry-trace"; | ||
| const SENTRY_BAGGAGE_HEADER = "baggage"; | ||
| const W3C_TRACEPARENT_HEADER = "traceparent"; | ||
| function addTracePropagationHeadersToFetchRequest(request, propagationDecisionMap) { | ||
| const url = getAbsoluteUrl(request.origin, request.path); | ||
| // Manually add the trace headers, if it applies | ||
| // Note: We do not use `propagation.inject()` here, because our propagator relies on an active span | ||
| // Which we do not have in this case | ||
| // The propagator _may_ overwrite this, but this should be fine as it is the same data | ||
| const { tracePropagationTargets, propagateTraceparent } = core.getClient()?.getOptions() || {}; | ||
| const addedHeaders = core.shouldPropagateTraceForUrl(url, tracePropagationTargets, propagationDecisionMap) | ||
| ? core.getTraceData({ propagateTraceparent }) | ||
| : undefined; | ||
| const addedHeaders = core.shouldPropagateTraceForUrl(url, tracePropagationTargets, propagationDecisionMap) ? core.getTraceData({ propagateTraceparent }) : void 0; | ||
| if (!addedHeaders) { | ||
| return; | ||
| } | ||
| const { 'sentry-trace': sentryTrace, baggage, traceparent } = addedHeaders; | ||
| // Undici can expose headers either as a raw string (v5-style) or as a flat array of pairs (v6-style). | ||
| // In the array form, even indices are header names and odd indices are values; in undici v6 a value | ||
| // may be `string | string[]` when a header has multiple values. The helpers below (_deduplicateArrayHeader, | ||
| // push, etc.) expect each value slot to be a single string, so we normalize array headers first. | ||
| const requestHeaders = Array.isArray(request.headers) | ||
| ? normalizeUndiciHeaderPairs(request.headers) | ||
| : stringToArrayHeaders(request.headers); | ||
| // OTel's UndiciInstrumentation calls propagation.inject() which unconditionally | ||
| // appends headers to the request. When the user also sets headers via getTraceData(), | ||
| // this results in duplicate sentry-trace and baggage (and optionally traceparent) entries. | ||
| // We clean these up before applying our own logic. | ||
| const { "sentry-trace": sentryTrace, baggage, traceparent } = addedHeaders; | ||
| const requestHeaders = Array.isArray(request.headers) ? normalizeUndiciHeaderPairs(request.headers) : stringToArrayHeaders(request.headers); | ||
| _deduplicateArrayHeader(requestHeaders, SENTRY_TRACE_HEADER); | ||
@@ -54,10 +23,3 @@ _deduplicateArrayHeader(requestHeaders, SENTRY_BAGGAGE_HEADER); | ||
| } | ||
| // We do not want to overwrite existing headers here | ||
| // If the core UndiciInstrumentation is registered, it will already have set the headers | ||
| // We do not want to add any then | ||
| const hasExistingSentryTraceHeader = _findExistingHeaderIndex(requestHeaders, SENTRY_TRACE_HEADER) !== -1; | ||
| // We do not want to set any headers if we already have an existing sentry-trace header. | ||
| // sentry-trace is still the source of truth, otherwise we risk mixing up baggage and sentry-trace values. | ||
| if (!hasExistingSentryTraceHeader) { | ||
@@ -67,8 +29,5 @@ if (sentryTrace) { | ||
| } | ||
| if (traceparent && _findExistingHeaderIndex(requestHeaders, 'traceparent') === -1) { | ||
| requestHeaders.push('traceparent', traceparent); | ||
| if (traceparent && _findExistingHeaderIndex(requestHeaders, "traceparent") === -1) { | ||
| requestHeaders.push("traceparent", traceparent); | ||
| } | ||
| // For baggage, we make sure to merge this into a possibly existing header | ||
| const existingBaggageIndex = _findExistingHeaderIndex(requestHeaders, SENTRY_BAGGAGE_HEADER); | ||
@@ -78,3 +37,2 @@ if (baggage && existingBaggageIndex === -1) { | ||
| } else if (baggage) { | ||
| // headers in format [key_0, value_0, key_1, value_1, ...], hence the +1 here | ||
| const existingBaggageValue = requestHeaders[existingBaggageIndex + 1]; | ||
@@ -87,6 +45,3 @@ const merged = core.mergeBaggageHeaders(existingBaggageValue, baggage); | ||
| } | ||
| if (Array.isArray(request.headers)) { | ||
| // Replace contents in place so we keep the same array reference undici/fetch still holds. | ||
| // `requestHeaders` is already normalized (string pairs only); splice writes them back. | ||
| request.headers.splice(0, request.headers.length, ...requestHeaders); | ||
@@ -97,10 +52,2 @@ } else { | ||
| } | ||
| /** | ||
| * Convert undici’s header array into `[name, value, name, value, ...]` where every value is a string. | ||
| * | ||
| * Undici v6 uses this shape: `[k1, v1, k2, v2, ...]`. Types allow each `v` to be `string | string[]` when | ||
| * that header has multiple values. Sentry’s dedupe/merge helpers expect one string per value slot, so | ||
| * multi-value arrays are joined with `', '`. Missing value slots become `''`. | ||
| */ | ||
| function normalizeUndiciHeaderPairs(headers) { | ||
@@ -111,7 +58,5 @@ const out = []; | ||
| if (i % 2 === 0) { | ||
| // Header name (should always be a string; coerce defensively). | ||
| out.push(typeof entry === 'string' ? entry : String(entry)); | ||
| out.push(typeof entry === "string" ? entry : String(entry)); | ||
| } else { | ||
| // Header value: flatten `string[]` to a single string for downstream string-only helpers. | ||
| out.push(Array.isArray(entry) ? entry.join(', ') : (entry ?? '')); | ||
| out.push(Array.isArray(entry) ? entry.join(", ") : entry ?? ""); | ||
| } | ||
@@ -121,9 +66,8 @@ } | ||
| } | ||
| function stringToArrayHeaders(requestHeaders) { | ||
| const headersArray = requestHeaders.split('\r\n'); | ||
| const headersArray = requestHeaders.split("\r\n"); | ||
| const headers = []; | ||
| for (const header of headersArray) { | ||
| try { | ||
| const colonIndex = header.indexOf(':'); | ||
| const colonIndex = header.indexOf(":"); | ||
| if (colonIndex === -1) { | ||
@@ -143,6 +87,4 @@ continue; | ||
| } | ||
| function arrayToStringHeaders(headers) { | ||
| const headerPairs = []; | ||
| for (let i = 0; i < headers.length; i += 2) { | ||
@@ -152,3 +94,2 @@ const key = headers[i]; | ||
| if (!key || value == null) { | ||
| // skip falsy keys but only null/undefined values | ||
| continue; | ||
@@ -158,16 +99,7 @@ } | ||
| } | ||
| if (!headerPairs.length) { | ||
| return ''; | ||
| return ""; | ||
| } | ||
| return headerPairs.join('\r\n').concat('\r\n'); | ||
| return headerPairs.join("\r\n").concat("\r\n"); | ||
| } | ||
| /** | ||
| * For a given header name, if there are multiple entries in the [key, value, key, value, ...] array, | ||
| * keep the first entry and remove the rest. | ||
| * For baggage, values are merged to preserve all entries but to dedupe sentry- values, and always | ||
| * keep the first occurrence of them | ||
| */ | ||
| function _deduplicateArrayHeader(headers, headerName) { | ||
@@ -179,3 +111,2 @@ let firstIndex = -1; | ||
| } | ||
| if (firstIndex === -1) { | ||
@@ -185,8 +116,4 @@ firstIndex = i; | ||
| } | ||
| const firstHeaderValue = headers[firstIndex + 1]; | ||
| if (headerName === SENTRY_BAGGAGE_HEADER && firstHeaderValue) { | ||
| // mergeBaggageHeaders always takes sentry- values from the new baggage (2nd param) and merges | ||
| // it with the existing one (1st param). Here, we want to keep the first header's existing | ||
| // sentry- values in favor of the new ones. Hence we swap the parameters. | ||
| const merged = core.mergeBaggageHeaders(headers[i + 1], firstHeaderValue); | ||
@@ -201,37 +128,26 @@ if (merged) { | ||
| } | ||
| /** | ||
| * Find the index of an existing header in an array of headers. | ||
| * Only take even indices, because headers are in format [key_0, value_0, key_1, value_1, ...] | ||
| * otherwise we could match a header _value_ with @param name | ||
| */ | ||
| function _findExistingHeaderIndex(headers, name) { | ||
| return headers.findIndex((header, i) => i % 2 === 0 && header === name); | ||
| } | ||
| /** Add a breadcrumb for an outgoing fetch/undici request. */ | ||
| function addFetchRequestBreadcrumb(request, response) { | ||
| const data = getBreadcrumbData(request); | ||
| const statusCode = response.statusCode; | ||
| const level = core.getBreadcrumbLogLevelFromHttpStatusCode(statusCode); | ||
| core.addBreadcrumb( | ||
| { | ||
| category: 'http', | ||
| category: "http", | ||
| data: { | ||
| status_code: statusCode, | ||
| ...data, | ||
| ...data | ||
| }, | ||
| type: 'http', | ||
| level, | ||
| type: "http", | ||
| level | ||
| }, | ||
| { | ||
| event: 'response', | ||
| event: "response", | ||
| request, | ||
| response, | ||
| }, | ||
| response | ||
| } | ||
| ); | ||
| } | ||
| function getBreadcrumbData(request) { | ||
@@ -241,15 +157,12 @@ try { | ||
| const parsedUrl = core.parseUrl(url); | ||
| const data = { | ||
| url: core.getSanitizedUrlString(parsedUrl), | ||
| 'http.method': request.method || 'GET', | ||
| "http.method": request.method || "GET" | ||
| }; | ||
| if (parsedUrl.search) { | ||
| data['http.query'] = parsedUrl.search; | ||
| data["http.query"] = parsedUrl.search; | ||
| } | ||
| if (parsedUrl.hash) { | ||
| data['http.fragment'] = parsedUrl.hash; | ||
| data["http.fragment"] = parsedUrl.hash; | ||
| } | ||
| return data; | ||
@@ -260,5 +173,3 @@ } catch { | ||
| } | ||
| /** Get the absolute URL from an origin and path. */ | ||
| function getAbsoluteUrl(origin, path = '/') { | ||
| function getAbsoluteUrl(origin, path = "/") { | ||
| try { | ||
@@ -268,13 +179,9 @@ const url = new URL(path, origin); | ||
| } catch { | ||
| // fallback: Construct it on our own | ||
| const url = `${origin}`; | ||
| if (url.endsWith('/') && path.startsWith('/')) { | ||
| if (url.endsWith("/") && path.startsWith("/")) { | ||
| return `${url}${path.slice(1)}`; | ||
| } | ||
| if (!url.endsWith('/') && !path.startsWith('/')) { | ||
| if (!url.endsWith("/") && !path.startsWith("/")) { | ||
| return `${url}/${path}`; | ||
| } | ||
| return `${url}${path}`; | ||
@@ -281,0 +188,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"outgoingFetchRequest.js","sources":["../../../src/utils/outgoingFetchRequest.ts"],"sourcesContent":["import type { LRUMap, SanitizedRequestData } from '@sentry/core';\nimport {\n addBreadcrumb,\n getBreadcrumbLogLevelFromHttpStatusCode,\n getClient,\n getSanitizedUrlString,\n getTraceData,\n parseUrl,\n shouldPropagateTraceForUrl,\n mergeBaggageHeaders,\n} from '@sentry/core';\nimport type { UndiciRequest, UndiciResponse } from '../integrations/node-fetch/types';\nimport { debug } from '@sentry/core';\nconst SENTRY_TRACE_HEADER = 'sentry-trace';\nconst SENTRY_BAGGAGE_HEADER = 'baggage';\nconst W3C_TRACEPARENT_HEADER = 'traceparent';\n/**\n * Add trace propagation headers to an outgoing fetch/undici request.\n *\n * Checks if the request URL matches trace propagation targets,\n * then injects sentry-trace, traceparent, and baggage headers.\n */\n// eslint-disable-next-line complexity\nexport function addTracePropagationHeadersToFetchRequest(\n request: UndiciRequest,\n propagationDecisionMap: LRUMap<string, boolean>,\n): void {\n const url = getAbsoluteUrl(request.origin, request.path);\n\n // Manually add the trace headers, if it applies\n // Note: We do not use `propagation.inject()` here, because our propagator relies on an active span\n // Which we do not have in this case\n // The propagator _may_ overwrite this, but this should be fine as it is the same data\n const { tracePropagationTargets, propagateTraceparent } = getClient()?.getOptions() || {};\n const addedHeaders = shouldPropagateTraceForUrl(url, tracePropagationTargets, propagationDecisionMap)\n ? getTraceData({ propagateTraceparent })\n : undefined;\n\n if (!addedHeaders) {\n return;\n }\n\n const { 'sentry-trace': sentryTrace, baggage, traceparent } = addedHeaders;\n\n // Undici can expose headers either as a raw string (v5-style) or as a flat array of pairs (v6-style).\n // In the array form, even indices are header names and odd indices are values; in undici v6 a value\n // may be `string | string[]` when a header has multiple values. The helpers below (_deduplicateArrayHeader,\n // push, etc.) expect each value slot to be a single string, so we normalize array headers first.\n const requestHeaders: string[] = Array.isArray(request.headers)\n ? normalizeUndiciHeaderPairs(request.headers)\n : stringToArrayHeaders(request.headers);\n\n // OTel's UndiciInstrumentation calls propagation.inject() which unconditionally\n // appends headers to the request. When the user also sets headers via getTraceData(),\n // this results in duplicate sentry-trace and baggage (and optionally traceparent) entries.\n // We clean these up before applying our own logic.\n _deduplicateArrayHeader(requestHeaders, SENTRY_TRACE_HEADER);\n _deduplicateArrayHeader(requestHeaders, SENTRY_BAGGAGE_HEADER);\n if (propagateTraceparent) {\n _deduplicateArrayHeader(requestHeaders, W3C_TRACEPARENT_HEADER);\n }\n\n // We do not want to overwrite existing headers here\n // If the core UndiciInstrumentation is registered, it will already have set the headers\n // We do not want to add any then\n const hasExistingSentryTraceHeader = _findExistingHeaderIndex(requestHeaders, SENTRY_TRACE_HEADER) !== -1;\n\n // We do not want to set any headers if we already have an existing sentry-trace header.\n // sentry-trace is still the source of truth, otherwise we risk mixing up baggage and sentry-trace values.\n if (!hasExistingSentryTraceHeader) {\n if (sentryTrace) {\n requestHeaders.push(SENTRY_TRACE_HEADER, sentryTrace);\n }\n\n if (traceparent && _findExistingHeaderIndex(requestHeaders, 'traceparent') === -1) {\n requestHeaders.push('traceparent', traceparent);\n }\n\n // For baggage, we make sure to merge this into a possibly existing header\n const existingBaggageIndex = _findExistingHeaderIndex(requestHeaders, SENTRY_BAGGAGE_HEADER);\n if (baggage && existingBaggageIndex === -1) {\n requestHeaders.push(SENTRY_BAGGAGE_HEADER, baggage);\n } else if (baggage) {\n // headers in format [key_0, value_0, key_1, value_1, ...], hence the +1 here\n const existingBaggageValue = requestHeaders[existingBaggageIndex + 1];\n const merged = mergeBaggageHeaders(existingBaggageValue, baggage);\n if (merged) {\n requestHeaders[existingBaggageIndex + 1] = merged;\n }\n }\n }\n\n if (Array.isArray(request.headers)) {\n // Replace contents in place so we keep the same array reference undici/fetch still holds.\n // `requestHeaders` is already normalized (string pairs only); splice writes them back.\n request.headers.splice(0, request.headers.length, ...requestHeaders);\n } else {\n request.headers = arrayToStringHeaders(requestHeaders);\n }\n}\n\n/**\n * Convert undici’s header array into `[name, value, name, value, ...]` where every value is a string.\n *\n * Undici v6 uses this shape: `[k1, v1, k2, v2, ...]`. Types allow each `v` to be `string | string[]` when\n * that header has multiple values. Sentry’s dedupe/merge helpers expect one string per value slot, so\n * multi-value arrays are joined with `', '`. Missing value slots become `''`.\n */\nfunction normalizeUndiciHeaderPairs(headers: (string | string[])[]): string[] {\n const out: string[] = [];\n for (let i = 0; i < headers.length; i++) {\n const entry = headers[i];\n if (i % 2 === 0) {\n // Header name (should always be a string; coerce defensively).\n out.push(typeof entry === 'string' ? entry : String(entry));\n } else {\n // Header value: flatten `string[]` to a single string for downstream string-only helpers.\n out.push(Array.isArray(entry) ? entry.join(', ') : (entry ?? ''));\n }\n }\n return out;\n}\n\nfunction stringToArrayHeaders(requestHeaders: string): string[] {\n const headersArray = requestHeaders.split('\\r\\n');\n const headers: string[] = [];\n for (const header of headersArray) {\n try {\n const colonIndex = header.indexOf(':');\n if (colonIndex === -1) {\n continue;\n }\n const key = header.slice(0, colonIndex).trim();\n const value = header.slice(colonIndex + 1).trim();\n if (key) {\n headers.push(key, value);\n }\n } catch {\n debug.warn(`Failed to convert string request header to array header: ${header}`);\n }\n }\n return headers;\n}\n\nfunction arrayToStringHeaders(headers: string[]): string {\n const headerPairs: string[] = [];\n\n for (let i = 0; i < headers.length; i += 2) {\n const key = headers[i];\n const value = headers[i + 1];\n if (!key || value == null) {\n // skip falsy keys but only null/undefined values\n continue;\n }\n headerPairs.push(`${key}: ${value}`);\n }\n\n if (!headerPairs.length) {\n return '';\n }\n\n return headerPairs.join('\\r\\n').concat('\\r\\n');\n}\n\n/**\n * For a given header name, if there are multiple entries in the [key, value, key, value, ...] array,\n * keep the first entry and remove the rest.\n * For baggage, values are merged to preserve all entries but to dedupe sentry- values, and always\n * keep the first occurrence of them\n */\nfunction _deduplicateArrayHeader(headers: string[], headerName: string): void {\n let firstIndex = -1;\n for (let i = 0; i < headers.length; i += 2) {\n if (headers[i] !== headerName) {\n continue;\n }\n\n if (firstIndex === -1) {\n firstIndex = i;\n continue;\n }\n\n const firstHeaderValue = headers[firstIndex + 1];\n if (headerName === SENTRY_BAGGAGE_HEADER && firstHeaderValue) {\n // mergeBaggageHeaders always takes sentry- values from the new baggage (2nd param) and merges\n // it with the existing one (1st param). Here, we want to keep the first header's existing\n // sentry- values in favor of the new ones. Hence we swap the parameters.\n const merged = mergeBaggageHeaders(headers[i + 1], firstHeaderValue);\n if (merged) {\n headers[firstIndex + 1] = merged;\n }\n }\n headers.splice(i, 2);\n i -= 2;\n }\n}\n\n/**\n * Find the index of an existing header in an array of headers.\n * Only take even indices, because headers are in format [key_0, value_0, key_1, value_1, ...]\n * otherwise we could match a header _value_ with @param name\n */\nfunction _findExistingHeaderIndex(headers: string[], name: string): number {\n return headers.findIndex((header, i) => i % 2 === 0 && header === name);\n}\n\n/** Add a breadcrumb for an outgoing fetch/undici request. */\nexport function addFetchRequestBreadcrumb(request: UndiciRequest, response: UndiciResponse): void {\n const data = getBreadcrumbData(request);\n\n const statusCode = response.statusCode;\n const level = getBreadcrumbLogLevelFromHttpStatusCode(statusCode);\n\n addBreadcrumb(\n {\n category: 'http',\n data: {\n status_code: statusCode,\n ...data,\n },\n type: 'http',\n level,\n },\n {\n event: 'response',\n request,\n response,\n },\n );\n}\n\nfunction getBreadcrumbData(request: UndiciRequest): Partial<SanitizedRequestData> {\n try {\n const url = getAbsoluteUrl(request.origin, request.path);\n const parsedUrl = parseUrl(url);\n\n const data: Partial<SanitizedRequestData> = {\n url: getSanitizedUrlString(parsedUrl),\n 'http.method': request.method || 'GET',\n };\n\n if (parsedUrl.search) {\n data['http.query'] = parsedUrl.search;\n }\n if (parsedUrl.hash) {\n data['http.fragment'] = parsedUrl.hash;\n }\n\n return data;\n } catch {\n return {};\n }\n}\n\n/** Get the absolute URL from an origin and path. */\nexport function getAbsoluteUrl(origin: string, path: string = '/'): string {\n try {\n const url = new URL(path, origin);\n return url.toString();\n } catch {\n // fallback: Construct it on our own\n const url = `${origin}`;\n\n if (url.endsWith('/') && path.startsWith('/')) {\n return `${url}${path.slice(1)}`;\n }\n\n if (!url.endsWith('/') && !path.startsWith('/')) {\n return `${url}/${path}`;\n }\n\n return `${url}${path}`;\n }\n}\n"],"names":["getClient","shouldPropagateTraceForUrl","getTraceData","mergeBaggageHeaders","debug","getBreadcrumbLogLevelFromHttpStatusCode","addBreadcrumb","parseUrl","getSanitizedUrlString"],"mappings":"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"} | ||
| {"version":3,"file":"outgoingFetchRequest.js","sources":["../../../src/utils/outgoingFetchRequest.ts"],"sourcesContent":["import type { LRUMap, SanitizedRequestData } from '@sentry/core';\nimport {\n addBreadcrumb,\n getBreadcrumbLogLevelFromHttpStatusCode,\n getClient,\n getSanitizedUrlString,\n getTraceData,\n parseUrl,\n shouldPropagateTraceForUrl,\n mergeBaggageHeaders,\n} from '@sentry/core';\nimport type { UndiciRequest, UndiciResponse } from '../integrations/node-fetch/types';\nimport { debug } from '@sentry/core';\nconst SENTRY_TRACE_HEADER = 'sentry-trace';\nconst SENTRY_BAGGAGE_HEADER = 'baggage';\nconst W3C_TRACEPARENT_HEADER = 'traceparent';\n/**\n * Add trace propagation headers to an outgoing fetch/undici request.\n *\n * Checks if the request URL matches trace propagation targets,\n * then injects sentry-trace, traceparent, and baggage headers.\n */\n// eslint-disable-next-line complexity\nexport function addTracePropagationHeadersToFetchRequest(\n request: UndiciRequest,\n propagationDecisionMap: LRUMap<string, boolean>,\n): void {\n const url = getAbsoluteUrl(request.origin, request.path);\n\n // Manually add the trace headers, if it applies\n // Note: We do not use `propagation.inject()` here, because our propagator relies on an active span\n // Which we do not have in this case\n // The propagator _may_ overwrite this, but this should be fine as it is the same data\n const { tracePropagationTargets, propagateTraceparent } = getClient()?.getOptions() || {};\n const addedHeaders = shouldPropagateTraceForUrl(url, tracePropagationTargets, propagationDecisionMap)\n ? getTraceData({ propagateTraceparent })\n : undefined;\n\n if (!addedHeaders) {\n return;\n }\n\n const { 'sentry-trace': sentryTrace, baggage, traceparent } = addedHeaders;\n\n // Undici can expose headers either as a raw string (v5-style) or as a flat array of pairs (v6-style).\n // In the array form, even indices are header names and odd indices are values; in undici v6 a value\n // may be `string | string[]` when a header has multiple values. The helpers below (_deduplicateArrayHeader,\n // push, etc.) expect each value slot to be a single string, so we normalize array headers first.\n const requestHeaders: string[] = Array.isArray(request.headers)\n ? normalizeUndiciHeaderPairs(request.headers)\n : stringToArrayHeaders(request.headers);\n\n // OTel's UndiciInstrumentation calls propagation.inject() which unconditionally\n // appends headers to the request. When the user also sets headers via getTraceData(),\n // this results in duplicate sentry-trace and baggage (and optionally traceparent) entries.\n // We clean these up before applying our own logic.\n _deduplicateArrayHeader(requestHeaders, SENTRY_TRACE_HEADER);\n _deduplicateArrayHeader(requestHeaders, SENTRY_BAGGAGE_HEADER);\n if (propagateTraceparent) {\n _deduplicateArrayHeader(requestHeaders, W3C_TRACEPARENT_HEADER);\n }\n\n // We do not want to overwrite existing headers here\n // If the core UndiciInstrumentation is registered, it will already have set the headers\n // We do not want to add any then\n const hasExistingSentryTraceHeader = _findExistingHeaderIndex(requestHeaders, SENTRY_TRACE_HEADER) !== -1;\n\n // We do not want to set any headers if we already have an existing sentry-trace header.\n // sentry-trace is still the source of truth, otherwise we risk mixing up baggage and sentry-trace values.\n if (!hasExistingSentryTraceHeader) {\n if (sentryTrace) {\n requestHeaders.push(SENTRY_TRACE_HEADER, sentryTrace);\n }\n\n if (traceparent && _findExistingHeaderIndex(requestHeaders, 'traceparent') === -1) {\n requestHeaders.push('traceparent', traceparent);\n }\n\n // For baggage, we make sure to merge this into a possibly existing header\n const existingBaggageIndex = _findExistingHeaderIndex(requestHeaders, SENTRY_BAGGAGE_HEADER);\n if (baggage && existingBaggageIndex === -1) {\n requestHeaders.push(SENTRY_BAGGAGE_HEADER, baggage);\n } else if (baggage) {\n // headers in format [key_0, value_0, key_1, value_1, ...], hence the +1 here\n const existingBaggageValue = requestHeaders[existingBaggageIndex + 1];\n const merged = mergeBaggageHeaders(existingBaggageValue, baggage);\n if (merged) {\n requestHeaders[existingBaggageIndex + 1] = merged;\n }\n }\n }\n\n if (Array.isArray(request.headers)) {\n // Replace contents in place so we keep the same array reference undici/fetch still holds.\n // `requestHeaders` is already normalized (string pairs only); splice writes them back.\n request.headers.splice(0, request.headers.length, ...requestHeaders);\n } else {\n request.headers = arrayToStringHeaders(requestHeaders);\n }\n}\n\n/**\n * Convert undici’s header array into `[name, value, name, value, ...]` where every value is a string.\n *\n * Undici v6 uses this shape: `[k1, v1, k2, v2, ...]`. Types allow each `v` to be `string | string[]` when\n * that header has multiple values. Sentry’s dedupe/merge helpers expect one string per value slot, so\n * multi-value arrays are joined with `', '`. Missing value slots become `''`.\n */\nfunction normalizeUndiciHeaderPairs(headers: (string | string[])[]): string[] {\n const out: string[] = [];\n for (let i = 0; i < headers.length; i++) {\n const entry = headers[i];\n if (i % 2 === 0) {\n // Header name (should always be a string; coerce defensively).\n out.push(typeof entry === 'string' ? entry : String(entry));\n } else {\n // Header value: flatten `string[]` to a single string for downstream string-only helpers.\n out.push(Array.isArray(entry) ? entry.join(', ') : (entry ?? ''));\n }\n }\n return out;\n}\n\nfunction stringToArrayHeaders(requestHeaders: string): string[] {\n const headersArray = requestHeaders.split('\\r\\n');\n const headers: string[] = [];\n for (const header of headersArray) {\n try {\n const colonIndex = header.indexOf(':');\n if (colonIndex === -1) {\n continue;\n }\n const key = header.slice(0, colonIndex).trim();\n const value = header.slice(colonIndex + 1).trim();\n if (key) {\n headers.push(key, value);\n }\n } catch {\n debug.warn(`Failed to convert string request header to array header: ${header}`);\n }\n }\n return headers;\n}\n\nfunction arrayToStringHeaders(headers: string[]): string {\n const headerPairs: string[] = [];\n\n for (let i = 0; i < headers.length; i += 2) {\n const key = headers[i];\n const value = headers[i + 1];\n if (!key || value == null) {\n // skip falsy keys but only null/undefined values\n continue;\n }\n headerPairs.push(`${key}: ${value}`);\n }\n\n if (!headerPairs.length) {\n return '';\n }\n\n return headerPairs.join('\\r\\n').concat('\\r\\n');\n}\n\n/**\n * For a given header name, if there are multiple entries in the [key, value, key, value, ...] array,\n * keep the first entry and remove the rest.\n * For baggage, values are merged to preserve all entries but to dedupe sentry- values, and always\n * keep the first occurrence of them\n */\nfunction _deduplicateArrayHeader(headers: string[], headerName: string): void {\n let firstIndex = -1;\n for (let i = 0; i < headers.length; i += 2) {\n if (headers[i] !== headerName) {\n continue;\n }\n\n if (firstIndex === -1) {\n firstIndex = i;\n continue;\n }\n\n const firstHeaderValue = headers[firstIndex + 1];\n if (headerName === SENTRY_BAGGAGE_HEADER && firstHeaderValue) {\n // mergeBaggageHeaders always takes sentry- values from the new baggage (2nd param) and merges\n // it with the existing one (1st param). Here, we want to keep the first header's existing\n // sentry- values in favor of the new ones. Hence we swap the parameters.\n const merged = mergeBaggageHeaders(headers[i + 1], firstHeaderValue);\n if (merged) {\n headers[firstIndex + 1] = merged;\n }\n }\n headers.splice(i, 2);\n i -= 2;\n }\n}\n\n/**\n * Find the index of an existing header in an array of headers.\n * Only take even indices, because headers are in format [key_0, value_0, key_1, value_1, ...]\n * otherwise we could match a header _value_ with @param name\n */\nfunction _findExistingHeaderIndex(headers: string[], name: string): number {\n return headers.findIndex((header, i) => i % 2 === 0 && header === name);\n}\n\n/** Add a breadcrumb for an outgoing fetch/undici request. */\nexport function addFetchRequestBreadcrumb(request: UndiciRequest, response: UndiciResponse): void {\n const data = getBreadcrumbData(request);\n\n const statusCode = response.statusCode;\n const level = getBreadcrumbLogLevelFromHttpStatusCode(statusCode);\n\n addBreadcrumb(\n {\n category: 'http',\n data: {\n status_code: statusCode,\n ...data,\n },\n type: 'http',\n level,\n },\n {\n event: 'response',\n request,\n response,\n },\n );\n}\n\nfunction getBreadcrumbData(request: UndiciRequest): Partial<SanitizedRequestData> {\n try {\n const url = getAbsoluteUrl(request.origin, request.path);\n const parsedUrl = parseUrl(url);\n\n const data: Partial<SanitizedRequestData> = {\n url: getSanitizedUrlString(parsedUrl),\n 'http.method': request.method || 'GET',\n };\n\n if (parsedUrl.search) {\n data['http.query'] = parsedUrl.search;\n }\n if (parsedUrl.hash) {\n data['http.fragment'] = parsedUrl.hash;\n }\n\n return data;\n } catch {\n return {};\n }\n}\n\n/** Get the absolute URL from an origin and path. */\nexport function getAbsoluteUrl(origin: string, path: string = '/'): string {\n try {\n const url = new URL(path, origin);\n return url.toString();\n } catch {\n // fallback: Construct it on our own\n const url = `${origin}`;\n\n if (url.endsWith('/') && path.startsWith('/')) {\n return `${url}${path.slice(1)}`;\n }\n\n if (!url.endsWith('/') && !path.startsWith('/')) {\n return `${url}/${path}`;\n }\n\n return `${url}${path}`;\n }\n}\n"],"names":["getClient","shouldPropagateTraceForUrl","getTraceData","mergeBaggageHeaders","debug","getBreadcrumbLogLevelFromHttpStatusCode","addBreadcrumb","parseUrl","getSanitizedUrlString"],"mappings":"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"} |
@@ -5,8 +5,2 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| /** | ||
| * Parse the spotlight option with proper precedence: | ||
| * - `false` or explicit string from options: use as-is | ||
| * - `true`: enable spotlight, but prefer a custom URL from the env var if set | ||
| * - `undefined`: defer entirely to the env var (bool or URL) | ||
| */ | ||
| function getSpotlightConfig(optionsSpotlight) { | ||
@@ -16,14 +10,8 @@ if (optionsSpotlight === false) { | ||
| } | ||
| if (typeof optionsSpotlight === 'string') { | ||
| if (typeof optionsSpotlight === "string") { | ||
| return optionsSpotlight; | ||
| } | ||
| // optionsSpotlight is true or undefined | ||
| const envBool = core.envToBool(process.env.SENTRY_SPOTLIGHT, { strict: true }); | ||
| const envUrl = envBool === null && process.env.SENTRY_SPOTLIGHT ? process.env.SENTRY_SPOTLIGHT : undefined; | ||
| return optionsSpotlight === true | ||
| ? (envUrl ?? true) // true: use env URL if present, otherwise true | ||
| : (envBool ?? envUrl); // undefined: use env var (bool or URL) | ||
| const envUrl = envBool === null && process.env.SENTRY_SPOTLIGHT ? process.env.SENTRY_SPOTLIGHT : void 0; | ||
| return optionsSpotlight === true ? envUrl ?? true : envBool ?? envUrl; | ||
| } | ||
@@ -30,0 +18,0 @@ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"spotlight.js","sources":["../../../src/utils/spotlight.ts"],"sourcesContent":["import { envToBool } from '@sentry/core';\n\n/**\n * Parse the spotlight option with proper precedence:\n * - `false` or explicit string from options: use as-is\n * - `true`: enable spotlight, but prefer a custom URL from the env var if set\n * - `undefined`: defer entirely to the env var (bool or URL)\n */\nexport function getSpotlightConfig(optionsSpotlight: boolean | string | undefined): boolean | string | undefined {\n if (optionsSpotlight === false) {\n return false;\n }\n\n if (typeof optionsSpotlight === 'string') {\n return optionsSpotlight;\n }\n\n // optionsSpotlight is true or undefined\n const envBool = envToBool(process.env.SENTRY_SPOTLIGHT, { strict: true });\n const envUrl = envBool === null && process.env.SENTRY_SPOTLIGHT ? process.env.SENTRY_SPOTLIGHT : 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":["envToBool"],"mappings":";;;;AAEA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,kBAAkB,CAAC,gBAAgB,EAA8D;AACjH,EAAE,IAAI,gBAAA,KAAqB,KAAK,EAAE;AAClC,IAAI,OAAO,KAAK;AAChB,EAAE;;AAEF,EAAE,IAAI,OAAO,gBAAA,KAAqB,QAAQ,EAAE;AAC5C,IAAI,OAAO,gBAAgB;AAC3B,EAAE;;AAEF;AACA,EAAE,MAAM,OAAA,GAAUA,cAAS,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,EAAE,MAAM,EAAE,IAAA,EAAM,CAAC;AAC3E,EAAE,MAAM,MAAA,GAAS,YAAY,IAAA,IAAQ,OAAO,CAAC,GAAG,CAAC,gBAAA,GAAmB,OAAO,CAAC,GAAG,CAAC,gBAAA,GAAmB,SAAS;;AAE5G,EAAE,OAAO,qBAAqB;AAC9B,OAAO,MAAA,IAAU,IAAI;AACrB,OAAO,OAAA,IAAW,MAAM,CAAC,CAAA;AACzB;;;;"} | ||
| {"version":3,"file":"spotlight.js","sources":["../../../src/utils/spotlight.ts"],"sourcesContent":["import { envToBool } from '@sentry/core';\n\n/**\n * Parse the spotlight option with proper precedence:\n * - `false` or explicit string from options: use as-is\n * - `true`: enable spotlight, but prefer a custom URL from the env var if set\n * - `undefined`: defer entirely to the env var (bool or URL)\n */\nexport function getSpotlightConfig(optionsSpotlight: boolean | string | undefined): boolean | string | undefined {\n if (optionsSpotlight === false) {\n return false;\n }\n\n if (typeof optionsSpotlight === 'string') {\n return optionsSpotlight;\n }\n\n // optionsSpotlight is true or undefined\n const envBool = envToBool(process.env.SENTRY_SPOTLIGHT, { strict: true });\n const envUrl = envBool === null && process.env.SENTRY_SPOTLIGHT ? process.env.SENTRY_SPOTLIGHT : 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":["envToBool"],"mappings":";;;;AAQO,SAAS,mBAAmB,gBAAA,EAA8E;AAC/G,EAAA,IAAI,qBAAqB,KAAA,EAAO;AAC9B,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,IAAI,OAAO,qBAAqB,QAAA,EAAU;AACxC,IAAA,OAAO,gBAAA;AAAA,EACT;AAGA,EAAA,MAAM,OAAA,GAAUA,eAAU,OAAA,CAAQ,GAAA,CAAI,kBAAkB,EAAE,MAAA,EAAQ,MAAM,CAAA;AACxE,EAAA,MAAM,MAAA,GAAS,YAAY,IAAA,IAAQ,OAAA,CAAQ,IAAI,gBAAA,GAAmB,OAAA,CAAQ,IAAI,gBAAA,GAAmB,MAAA;AAEjG,EAAA,OAAO,gBAAA,KAAqB,IAAA,GACvB,MAAA,IAAU,IAAA,GACV,OAAA,IAAW,MAAA;AAClB;;;;"} |
+40
-44
| const replacements = [ | ||
| ['january', '1'], | ||
| ['february', '2'], | ||
| ['march', '3'], | ||
| ['april', '4'], | ||
| ['may', '5'], | ||
| ['june', '6'], | ||
| ['july', '7'], | ||
| ['august', '8'], | ||
| ['september', '9'], | ||
| ['october', '10'], | ||
| ['november', '11'], | ||
| ['december', '12'], | ||
| ['jan', '1'], | ||
| ['feb', '2'], | ||
| ['mar', '3'], | ||
| ['apr', '4'], | ||
| ['may', '5'], | ||
| ['jun', '6'], | ||
| ['jul', '7'], | ||
| ['aug', '8'], | ||
| ['sep', '9'], | ||
| ['oct', '10'], | ||
| ['nov', '11'], | ||
| ['dec', '12'], | ||
| ['sunday', '0'], | ||
| ['monday', '1'], | ||
| ['tuesday', '2'], | ||
| ['wednesday', '3'], | ||
| ['thursday', '4'], | ||
| ['friday', '5'], | ||
| ['saturday', '6'], | ||
| ['sun', '0'], | ||
| ['mon', '1'], | ||
| ['tue', '2'], | ||
| ['wed', '3'], | ||
| ['thu', '4'], | ||
| ['fri', '5'], | ||
| ['sat', '6'], | ||
| ["january", "1"], | ||
| ["february", "2"], | ||
| ["march", "3"], | ||
| ["april", "4"], | ||
| ["may", "5"], | ||
| ["june", "6"], | ||
| ["july", "7"], | ||
| ["august", "8"], | ||
| ["september", "9"], | ||
| ["october", "10"], | ||
| ["november", "11"], | ||
| ["december", "12"], | ||
| ["jan", "1"], | ||
| ["feb", "2"], | ||
| ["mar", "3"], | ||
| ["apr", "4"], | ||
| ["may", "5"], | ||
| ["jun", "6"], | ||
| ["jul", "7"], | ||
| ["aug", "8"], | ||
| ["sep", "9"], | ||
| ["oct", "10"], | ||
| ["nov", "11"], | ||
| ["dec", "12"], | ||
| ["sunday", "0"], | ||
| ["monday", "1"], | ||
| ["tuesday", "2"], | ||
| ["wednesday", "3"], | ||
| ["thursday", "4"], | ||
| ["friday", "5"], | ||
| ["saturday", "6"], | ||
| ["sun", "0"], | ||
| ["mon", "1"], | ||
| ["tue", "2"], | ||
| ["wed", "3"], | ||
| ["thu", "4"], | ||
| ["fri", "5"], | ||
| ["sat", "6"] | ||
| ]; | ||
| /** | ||
| * Replaces names in cron expressions | ||
| */ | ||
| function replaceCronNames(cronExpression) { | ||
| return replacements.reduce( | ||
| // oxlint-disable-next-line sdk/no-regexp-constructor | ||
| (acc, [name, replacement]) => acc.replace(new RegExp(name, 'gi'), replacement), | ||
| cronExpression, | ||
| (acc, [name, replacement]) => acc.replace(new RegExp(name, "gi"), replacement), | ||
| cronExpression | ||
| ); | ||
@@ -51,0 +47,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"common.js","sources":["../../../src/cron/common.ts"],"sourcesContent":["const replacements: [string, string][] = [\n ['january', '1'],\n ['february', '2'],\n ['march', '3'],\n ['april', '4'],\n ['may', '5'],\n ['june', '6'],\n ['july', '7'],\n ['august', '8'],\n ['september', '9'],\n ['october', '10'],\n ['november', '11'],\n ['december', '12'],\n ['jan', '1'],\n ['feb', '2'],\n ['mar', '3'],\n ['apr', '4'],\n ['may', '5'],\n ['jun', '6'],\n ['jul', '7'],\n ['aug', '8'],\n ['sep', '9'],\n ['oct', '10'],\n ['nov', '11'],\n ['dec', '12'],\n ['sunday', '0'],\n ['monday', '1'],\n ['tuesday', '2'],\n ['wednesday', '3'],\n ['thursday', '4'],\n ['friday', '5'],\n ['saturday', '6'],\n ['sun', '0'],\n ['mon', '1'],\n ['tue', '2'],\n ['wed', '3'],\n ['thu', '4'],\n ['fri', '5'],\n ['sat', '6'],\n];\n\n/**\n * Replaces names in cron expressions\n */\nexport function replaceCronNames(cronExpression: string): string {\n return replacements.reduce(\n // oxlint-disable-next-line sdk/no-regexp-constructor\n (acc, [name, replacement]) => acc.replace(new RegExp(name, 'gi'), replacement),\n cronExpression,\n );\n}\n"],"names":[],"mappings":"AAAA,MAAM,YAAY,GAAuB;AACzC,EAAE,CAAC,SAAS,EAAE,GAAG,CAAC;AAClB,EAAE,CAAC,UAAU,EAAE,GAAG,CAAC;AACnB,EAAE,CAAC,OAAO,EAAE,GAAG,CAAC;AAChB,EAAE,CAAC,OAAO,EAAE,GAAG,CAAC;AAChB,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC;AACd,EAAE,CAAC,MAAM,EAAE,GAAG,CAAC;AACf,EAAE,CAAC,MAAM,EAAE,GAAG,CAAC;AACf,EAAE,CAAC,QAAQ,EAAE,GAAG,CAAC;AACjB,EAAE,CAAC,WAAW,EAAE,GAAG,CAAC;AACpB,EAAE,CAAC,SAAS,EAAE,IAAI,CAAC;AACnB,EAAE,CAAC,UAAU,EAAE,IAAI,CAAC;AACpB,EAAE,CAAC,UAAU,EAAE,IAAI,CAAC;AACpB,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC;AACd,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC;AACd,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC;AACd,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC;AACd,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC;AACd,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC;AACd,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC;AACd,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC;AACd,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC;AACd,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC;AACf,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC;AACf,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC;AACf,EAAE,CAAC,QAAQ,EAAE,GAAG,CAAC;AACjB,EAAE,CAAC,QAAQ,EAAE,GAAG,CAAC;AACjB,EAAE,CAAC,SAAS,EAAE,GAAG,CAAC;AAClB,EAAE,CAAC,WAAW,EAAE,GAAG,CAAC;AACpB,EAAE,CAAC,UAAU,EAAE,GAAG,CAAC;AACnB,EAAE,CAAC,QAAQ,EAAE,GAAG,CAAC;AACjB,EAAE,CAAC,UAAU,EAAE,GAAG,CAAC;AACnB,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC;AACd,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC;AACd,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC;AACd,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC;AACd,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC;AACd,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC;AACd,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC;AACd,CAAC;;AAED;AACA;AACA;AACO,SAAS,gBAAgB,CAAC,cAAc,EAAkB;AACjE,EAAE,OAAO,YAAY,CAAC,MAAM;AAC5B;AACA,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,WAAW,CAAC,KAAK,GAAG,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,WAAW,CAAC;AAClF,IAAI,cAAc;AAClB,GAAG;AACH;;;;"} | ||
| {"version":3,"file":"common.js","sources":["../../../src/cron/common.ts"],"sourcesContent":["const replacements: [string, string][] = [\n ['january', '1'],\n ['february', '2'],\n ['march', '3'],\n ['april', '4'],\n ['may', '5'],\n ['june', '6'],\n ['july', '7'],\n ['august', '8'],\n ['september', '9'],\n ['october', '10'],\n ['november', '11'],\n ['december', '12'],\n ['jan', '1'],\n ['feb', '2'],\n ['mar', '3'],\n ['apr', '4'],\n ['may', '5'],\n ['jun', '6'],\n ['jul', '7'],\n ['aug', '8'],\n ['sep', '9'],\n ['oct', '10'],\n ['nov', '11'],\n ['dec', '12'],\n ['sunday', '0'],\n ['monday', '1'],\n ['tuesday', '2'],\n ['wednesday', '3'],\n ['thursday', '4'],\n ['friday', '5'],\n ['saturday', '6'],\n ['sun', '0'],\n ['mon', '1'],\n ['tue', '2'],\n ['wed', '3'],\n ['thu', '4'],\n ['fri', '5'],\n ['sat', '6'],\n];\n\n/**\n * Replaces names in cron expressions\n */\nexport function replaceCronNames(cronExpression: string): string {\n return replacements.reduce(\n // oxlint-disable-next-line sdk/no-regexp-constructor\n (acc, [name, replacement]) => acc.replace(new RegExp(name, 'gi'), replacement),\n cronExpression,\n );\n}\n"],"names":[],"mappings":"AAAA,MAAM,YAAA,GAAmC;AAAA,EACvC,CAAC,WAAW,GAAG,CAAA;AAAA,EACf,CAAC,YAAY,GAAG,CAAA;AAAA,EAChB,CAAC,SAAS,GAAG,CAAA;AAAA,EACb,CAAC,SAAS,GAAG,CAAA;AAAA,EACb,CAAC,OAAO,GAAG,CAAA;AAAA,EACX,CAAC,QAAQ,GAAG,CAAA;AAAA,EACZ,CAAC,QAAQ,GAAG,CAAA;AAAA,EACZ,CAAC,UAAU,GAAG,CAAA;AAAA,EACd,CAAC,aAAa,GAAG,CAAA;AAAA,EACjB,CAAC,WAAW,IAAI,CAAA;AAAA,EAChB,CAAC,YAAY,IAAI,CAAA;AAAA,EACjB,CAAC,YAAY,IAAI,CAAA;AAAA,EACjB,CAAC,OAAO,GAAG,CAAA;AAAA,EACX,CAAC,OAAO,GAAG,CAAA;AAAA,EACX,CAAC,OAAO,GAAG,CAAA;AAAA,EACX,CAAC,OAAO,GAAG,CAAA;AAAA,EACX,CAAC,OAAO,GAAG,CAAA;AAAA,EACX,CAAC,OAAO,GAAG,CAAA;AAAA,EACX,CAAC,OAAO,GAAG,CAAA;AAAA,EACX,CAAC,OAAO,GAAG,CAAA;AAAA,EACX,CAAC,OAAO,GAAG,CAAA;AAAA,EACX,CAAC,OAAO,IAAI,CAAA;AAAA,EACZ,CAAC,OAAO,IAAI,CAAA;AAAA,EACZ,CAAC,OAAO,IAAI,CAAA;AAAA,EACZ,CAAC,UAAU,GAAG,CAAA;AAAA,EACd,CAAC,UAAU,GAAG,CAAA;AAAA,EACd,CAAC,WAAW,GAAG,CAAA;AAAA,EACf,CAAC,aAAa,GAAG,CAAA;AAAA,EACjB,CAAC,YAAY,GAAG,CAAA;AAAA,EAChB,CAAC,UAAU,GAAG,CAAA;AAAA,EACd,CAAC,YAAY,GAAG,CAAA;AAAA,EAChB,CAAC,OAAO,GAAG,CAAA;AAAA,EACX,CAAC,OAAO,GAAG,CAAA;AAAA,EACX,CAAC,OAAO,GAAG,CAAA;AAAA,EACX,CAAC,OAAO,GAAG,CAAA;AAAA,EACX,CAAC,OAAO,GAAG,CAAA;AAAA,EACX,CAAC,OAAO,GAAG,CAAA;AAAA,EACX,CAAC,OAAO,GAAG;AACb,CAAA;AAKO,SAAS,iBAAiB,cAAA,EAAgC;AAC/D,EAAA,OAAO,YAAA,CAAa,MAAA;AAAA;AAAA,IAElB,CAAC,GAAA,EAAK,CAAC,IAAA,EAAM,WAAW,CAAA,KAAM,GAAA,CAAI,OAAA,CAAQ,IAAI,MAAA,CAAO,IAAA,EAAM,IAAI,GAAG,WAAW,CAAA;AAAA,IAC7E;AAAA,GACF;AACF;;;;"} |
+17
-51
| import { withMonitor, captureException } from '@sentry/core'; | ||
| import { replaceCronNames } from './common.js'; | ||
| const ERROR_TEXT = 'Automatic instrumentation of CronJob only supports crontab string'; | ||
| /** | ||
| * Instruments the `cron` library to send a check-in event to Sentry for each job execution. | ||
| * | ||
| * ```ts | ||
| * import * as Sentry from '@sentry/node'; | ||
| * import { CronJob } from 'cron'; | ||
| * | ||
| * const CronJobWithCheckIn = Sentry.cron.instrumentCron(CronJob, 'my-cron-job'); | ||
| * | ||
| * // use the constructor | ||
| * const job = new CronJobWithCheckIn('* * * * *', () => { | ||
| * console.log('You will see this message every minute'); | ||
| * }); | ||
| * | ||
| * // or from | ||
| * const job = CronJobWithCheckIn.from({ cronTime: '* * * * *', onTick: () => { | ||
| * console.log('You will see this message every minute'); | ||
| * }); | ||
| * ``` | ||
| */ | ||
| const ERROR_TEXT = "Automatic instrumentation of CronJob only supports crontab string"; | ||
| function instrumentCron(lib, monitorSlug) { | ||
| let jobScheduled = false; | ||
| return new Proxy(lib, { | ||
| construct(target, args) { | ||
| const [cronTime, onTick, onComplete, start, timeZone, ...rest] = args; | ||
| if (typeof cronTime !== 'string') { | ||
| if (typeof cronTime !== "string") { | ||
| throw new Error(ERROR_TEXT); | ||
| } | ||
| if (jobScheduled) { | ||
| throw new Error(`A job named '${monitorSlug}' has already been scheduled`); | ||
| } | ||
| jobScheduled = true; | ||
| const cronString = replaceCronNames(cronTime); | ||
| async function monitoredTick(context, onComplete) { | ||
| async function monitoredTick(context, onComplete2) { | ||
| return withMonitor( | ||
@@ -50,3 +23,3 @@ monitorSlug, | ||
| try { | ||
| await onTick(context, onComplete); | ||
| await onTick(context, onComplete2); | ||
| } catch (e) { | ||
@@ -56,4 +29,4 @@ captureException(e, { | ||
| handled: false, | ||
| type: 'auto.function.cron.instrumentCron', | ||
| }, | ||
| type: "auto.function.cron.instrumentCron" | ||
| } | ||
| }); | ||
@@ -64,27 +37,21 @@ throw e; | ||
| { | ||
| schedule: { type: 'crontab', value: cronString }, | ||
| timezone: timeZone || undefined, | ||
| }, | ||
| schedule: { type: "crontab", value: cronString }, | ||
| timezone: timeZone || void 0 | ||
| } | ||
| ); | ||
| } | ||
| return new target(cronTime, monitoredTick, onComplete, start, timeZone, ...rest); | ||
| }, | ||
| get(target, prop) { | ||
| if (prop === 'from') { | ||
| if (prop === "from") { | ||
| return (param) => { | ||
| const { cronTime, onTick, timeZone } = param; | ||
| if (typeof cronTime !== 'string') { | ||
| if (typeof cronTime !== "string") { | ||
| throw new Error(ERROR_TEXT); | ||
| } | ||
| if (jobScheduled) { | ||
| throw new Error(`A job named '${monitorSlug}' has already been scheduled`); | ||
| } | ||
| jobScheduled = true; | ||
| const cronString = replaceCronNames(cronTime); | ||
| param.onTick = async (context, onComplete) => { | ||
@@ -100,4 +67,4 @@ return withMonitor( | ||
| handled: false, | ||
| type: 'auto.function.cron.instrumentCron', | ||
| }, | ||
| type: "auto.function.cron.instrumentCron" | ||
| } | ||
| }); | ||
@@ -108,8 +75,7 @@ throw e; | ||
| { | ||
| schedule: { type: 'crontab', value: cronString }, | ||
| timezone: timeZone || undefined, | ||
| }, | ||
| schedule: { type: "crontab", value: cronString }, | ||
| timezone: timeZone || void 0 | ||
| } | ||
| ); | ||
| }; | ||
| return target.from(param); | ||
@@ -120,3 +86,3 @@ }; | ||
| } | ||
| }, | ||
| } | ||
| }); | ||
@@ -123,0 +89,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"cron.js","sources":["../../../src/cron/cron.ts"],"sourcesContent":["import { captureException, withMonitor } from '@sentry/core';\nimport { replaceCronNames } from './common';\n\nexport type CronJobParams = {\n cronTime: string | Date;\n onTick: (context: unknown, onComplete?: unknown) => void | Promise<void>;\n onComplete?: () => void | Promise<void>;\n start?: boolean | null;\n context?: unknown;\n runOnInit?: boolean | null;\n unrefTimeout?: boolean | null;\n} & (\n | {\n timeZone?: string | null;\n utcOffset?: never;\n }\n | {\n timeZone?: never;\n utcOffset?: number | null;\n }\n);\n\nexport type CronJob = {\n //\n};\n\nexport type CronJobConstructor = {\n from: (param: CronJobParams) => CronJob;\n\n new (\n cronTime: CronJobParams['cronTime'],\n onTick: CronJobParams['onTick'],\n onComplete?: CronJobParams['onComplete'],\n start?: CronJobParams['start'],\n timeZone?: CronJobParams['timeZone'],\n context?: CronJobParams['context'],\n runOnInit?: CronJobParams['runOnInit'],\n utcOffset?: null,\n unrefTimeout?: CronJobParams['unrefTimeout'],\n ): CronJob;\n new (\n cronTime: CronJobParams['cronTime'],\n onTick: CronJobParams['onTick'],\n onComplete?: CronJobParams['onComplete'],\n start?: CronJobParams['start'],\n timeZone?: null,\n context?: CronJobParams['context'],\n runOnInit?: CronJobParams['runOnInit'],\n utcOffset?: CronJobParams['utcOffset'],\n unrefTimeout?: CronJobParams['unrefTimeout'],\n ): CronJob;\n};\n\nconst ERROR_TEXT = 'Automatic instrumentation of CronJob only supports crontab string';\n\n/**\n * Instruments the `cron` library to send a check-in event to Sentry for each job execution.\n *\n * ```ts\n * import * as Sentry from '@sentry/node';\n * import { CronJob } from 'cron';\n *\n * const CronJobWithCheckIn = Sentry.cron.instrumentCron(CronJob, 'my-cron-job');\n *\n * // use the constructor\n * const job = new CronJobWithCheckIn('* * * * *', () => {\n * console.log('You will see this message every minute');\n * });\n *\n * // or from\n * const job = CronJobWithCheckIn.from({ cronTime: '* * * * *', onTick: () => {\n * console.log('You will see this message every minute');\n * });\n * ```\n */\nexport function instrumentCron<T>(lib: T & CronJobConstructor, monitorSlug: string): T {\n let jobScheduled = false;\n\n return new Proxy(lib, {\n construct(target, args: ConstructorParameters<CronJobConstructor>) {\n const [cronTime, onTick, onComplete, start, timeZone, ...rest] = args;\n\n if (typeof cronTime !== 'string') {\n throw new Error(ERROR_TEXT);\n }\n\n if (jobScheduled) {\n throw new Error(`A job named '${monitorSlug}' has already been scheduled`);\n }\n\n jobScheduled = true;\n\n const cronString = replaceCronNames(cronTime);\n\n async function monitoredTick(context: unknown, onComplete?: unknown): Promise<void> {\n return withMonitor(\n monitorSlug,\n async () => {\n try {\n await onTick(context, onComplete);\n } catch (e) {\n captureException(e, {\n mechanism: {\n handled: false,\n type: 'auto.function.cron.instrumentCron',\n },\n });\n throw e;\n }\n },\n {\n schedule: { type: 'crontab', value: cronString },\n timezone: timeZone || undefined,\n },\n );\n }\n\n return new target(cronTime, monitoredTick, onComplete, start, timeZone, ...rest);\n },\n get(target, prop: keyof CronJobConstructor) {\n if (prop === 'from') {\n return (param: CronJobParams) => {\n const { cronTime, onTick, timeZone } = param;\n\n if (typeof cronTime !== 'string') {\n throw new Error(ERROR_TEXT);\n }\n\n if (jobScheduled) {\n throw new Error(`A job named '${monitorSlug}' has already been scheduled`);\n }\n\n jobScheduled = true;\n\n const cronString = replaceCronNames(cronTime);\n\n param.onTick = async (context: unknown, onComplete?: unknown) => {\n return withMonitor(\n monitorSlug,\n async () => {\n try {\n await onTick(context, onComplete);\n } catch (e) {\n captureException(e, {\n mechanism: {\n handled: false,\n type: 'auto.function.cron.instrumentCron',\n },\n });\n throw e;\n }\n },\n {\n schedule: { type: 'crontab', value: cronString },\n timezone: timeZone || undefined,\n },\n );\n };\n\n return target.from(param);\n };\n } else {\n return target[prop];\n }\n },\n });\n}\n"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"cron.js","sources":["../../../src/cron/cron.ts"],"sourcesContent":["import { captureException, withMonitor } from '@sentry/core';\nimport { replaceCronNames } from './common';\n\nexport type CronJobParams = {\n cronTime: string | Date;\n onTick: (context: unknown, onComplete?: unknown) => void | Promise<void>;\n onComplete?: () => void | Promise<void>;\n start?: boolean | null;\n context?: unknown;\n runOnInit?: boolean | null;\n unrefTimeout?: boolean | null;\n} & (\n | {\n timeZone?: string | null;\n utcOffset?: never;\n }\n | {\n timeZone?: never;\n utcOffset?: number | null;\n }\n);\n\nexport type CronJob = {\n //\n};\n\nexport type CronJobConstructor = {\n from: (param: CronJobParams) => CronJob;\n\n new (\n cronTime: CronJobParams['cronTime'],\n onTick: CronJobParams['onTick'],\n onComplete?: CronJobParams['onComplete'],\n start?: CronJobParams['start'],\n timeZone?: CronJobParams['timeZone'],\n context?: CronJobParams['context'],\n runOnInit?: CronJobParams['runOnInit'],\n utcOffset?: null,\n unrefTimeout?: CronJobParams['unrefTimeout'],\n ): CronJob;\n new (\n cronTime: CronJobParams['cronTime'],\n onTick: CronJobParams['onTick'],\n onComplete?: CronJobParams['onComplete'],\n start?: CronJobParams['start'],\n timeZone?: null,\n context?: CronJobParams['context'],\n runOnInit?: CronJobParams['runOnInit'],\n utcOffset?: CronJobParams['utcOffset'],\n unrefTimeout?: CronJobParams['unrefTimeout'],\n ): CronJob;\n};\n\nconst ERROR_TEXT = 'Automatic instrumentation of CronJob only supports crontab string';\n\n/**\n * Instruments the `cron` library to send a check-in event to Sentry for each job execution.\n *\n * ```ts\n * import * as Sentry from '@sentry/node';\n * import { CronJob } from 'cron';\n *\n * const CronJobWithCheckIn = Sentry.cron.instrumentCron(CronJob, 'my-cron-job');\n *\n * // use the constructor\n * const job = new CronJobWithCheckIn('* * * * *', () => {\n * console.log('You will see this message every minute');\n * });\n *\n * // or from\n * const job = CronJobWithCheckIn.from({ cronTime: '* * * * *', onTick: () => {\n * console.log('You will see this message every minute');\n * });\n * ```\n */\nexport function instrumentCron<T>(lib: T & CronJobConstructor, monitorSlug: string): T {\n let jobScheduled = false;\n\n return new Proxy(lib, {\n construct(target, args: ConstructorParameters<CronJobConstructor>) {\n const [cronTime, onTick, onComplete, start, timeZone, ...rest] = args;\n\n if (typeof cronTime !== 'string') {\n throw new Error(ERROR_TEXT);\n }\n\n if (jobScheduled) {\n throw new Error(`A job named '${monitorSlug}' has already been scheduled`);\n }\n\n jobScheduled = true;\n\n const cronString = replaceCronNames(cronTime);\n\n async function monitoredTick(context: unknown, onComplete?: unknown): Promise<void> {\n return withMonitor(\n monitorSlug,\n async () => {\n try {\n await onTick(context, onComplete);\n } catch (e) {\n captureException(e, {\n mechanism: {\n handled: false,\n type: 'auto.function.cron.instrumentCron',\n },\n });\n throw e;\n }\n },\n {\n schedule: { type: 'crontab', value: cronString },\n timezone: timeZone || undefined,\n },\n );\n }\n\n return new target(cronTime, monitoredTick, onComplete, start, timeZone, ...rest);\n },\n get(target, prop: keyof CronJobConstructor) {\n if (prop === 'from') {\n return (param: CronJobParams) => {\n const { cronTime, onTick, timeZone } = param;\n\n if (typeof cronTime !== 'string') {\n throw new Error(ERROR_TEXT);\n }\n\n if (jobScheduled) {\n throw new Error(`A job named '${monitorSlug}' has already been scheduled`);\n }\n\n jobScheduled = true;\n\n const cronString = replaceCronNames(cronTime);\n\n param.onTick = async (context: unknown, onComplete?: unknown) => {\n return withMonitor(\n monitorSlug,\n async () => {\n try {\n await onTick(context, onComplete);\n } catch (e) {\n captureException(e, {\n mechanism: {\n handled: false,\n type: 'auto.function.cron.instrumentCron',\n },\n });\n throw e;\n }\n },\n {\n schedule: { type: 'crontab', value: cronString },\n timezone: timeZone || undefined,\n },\n );\n };\n\n return target.from(param);\n };\n } else {\n return target[prop];\n }\n },\n });\n}\n"],"names":["onComplete"],"mappings":"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"} |
@@ -5,7 +5,6 @@ import { instrumentCron } from './cron.js'; | ||
| /** Methods to instrument cron libraries for Sentry check-ins */ | ||
| const cron = { | ||
| instrumentCron, | ||
| instrumentNodeCron, | ||
| instrumentNodeSchedule, | ||
| instrumentNodeSchedule | ||
| }; | ||
@@ -12,0 +11,0 @@ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.js","sources":["../../../src/cron/index.ts"],"sourcesContent":["import { instrumentCron } from './cron';\nimport { instrumentNodeCron } from './node-cron';\nimport { instrumentNodeSchedule } from './node-schedule';\n\n/** Methods to instrument cron libraries for Sentry check-ins */\nexport const cron = {\n instrumentCron,\n instrumentNodeCron,\n instrumentNodeSchedule,\n};\n"],"names":[],"mappings":";;;;AAIA;AACO,MAAM,OAAO;AACpB,EAAE,cAAc;AAChB,EAAE,kBAAkB;AACpB,EAAE,sBAAsB;AACxB;;;;"} | ||
| {"version":3,"file":"index.js","sources":["../../../src/cron/index.ts"],"sourcesContent":["import { instrumentCron } from './cron';\nimport { instrumentNodeCron } from './node-cron';\nimport { instrumentNodeSchedule } from './node-schedule';\n\n/** Methods to instrument cron libraries for Sentry check-ins */\nexport const cron = {\n instrumentCron,\n instrumentNodeCron,\n instrumentNodeSchedule,\n};\n"],"names":[],"mappings":";;;;AAKO,MAAM,IAAA,GAAO;AAAA,EAClB,cAAA;AAAA,EACA,kBAAA;AAAA,EACA;AACF;;;;"} |
| import { withMonitor, captureException } from '@sentry/core'; | ||
| import { replaceCronNames } from './common.js'; | ||
| /** | ||
| * Wraps the `node-cron` library with check-in monitoring. | ||
| * | ||
| * ```ts | ||
| * import * as Sentry from "@sentry/node"; | ||
| * import * as cron from "node-cron"; | ||
| * | ||
| * const cronWithCheckIn = Sentry.cron.instrumentNodeCron(cron); | ||
| * | ||
| * cronWithCheckIn.schedule( | ||
| * "* * * * *", | ||
| * () => { | ||
| * console.log("running a task every minute"); | ||
| * }, | ||
| * { name: "my-cron-job" }, | ||
| * ); | ||
| * ``` | ||
| */ | ||
| function instrumentNodeCron( | ||
| lib, | ||
| monitorConfig = {}, | ||
| ) { | ||
| function instrumentNodeCron(lib, monitorConfig = {}) { | ||
| return new Proxy(lib, { | ||
| get(target, prop) { | ||
| if (prop === 'schedule' && target.schedule) { | ||
| // When 'get' is called for schedule, return a proxied version of the schedule function | ||
| if (prop === "schedule" && target.schedule) { | ||
| return new Proxy(target.schedule, { | ||
| apply(target, thisArg, argArray) { | ||
| apply(target2, thisArg, argArray) { | ||
| const [expression, callback, options] = argArray; | ||
| const name = options?.name; | ||
| const timezone = options?.timezone; | ||
| if (!name) { | ||
| throw new Error('Missing "name" for scheduled job. A name is required for Sentry check-in monitoring.'); | ||
| } | ||
| const monitoredCallback = async (...args) => { | ||
@@ -45,6 +20,3 @@ return withMonitor( | ||
| async () => { | ||
| // We have to manually catch here and capture the exception because node-cron swallows errors | ||
| // https://github.com/node-cron/node-cron/issues/399 | ||
| try { | ||
| // oxlint-disable-next-line typescript/await-thenable -- callback may be async at runtime | ||
| return await callback(...args); | ||
@@ -55,4 +27,4 @@ } catch (e) { | ||
| handled: false, | ||
| type: 'auto.function.node-cron.instrumentNodeCron', | ||
| }, | ||
| type: "auto.function.node-cron.instrumentNodeCron" | ||
| } | ||
| }); | ||
@@ -63,16 +35,15 @@ throw e; | ||
| { | ||
| schedule: { type: 'crontab', value: replaceCronNames(expression) }, | ||
| schedule: { type: "crontab", value: replaceCronNames(expression) }, | ||
| timezone, | ||
| ...monitorConfig, | ||
| }, | ||
| ...monitorConfig | ||
| } | ||
| ); | ||
| }; | ||
| return target.apply(thisArg, [expression, monitoredCallback, options]); | ||
| }, | ||
| return target2.apply(thisArg, [expression, monitoredCallback, options]); | ||
| } | ||
| }); | ||
| } else { | ||
| return target[prop ]; | ||
| return target[prop]; | ||
| } | ||
| }, | ||
| } | ||
| }); | ||
@@ -79,0 +50,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"node-cron.js","sources":["../../../src/cron/node-cron.ts"],"sourcesContent":["import { captureException, type MonitorConfig, withMonitor } from '@sentry/core';\nimport { replaceCronNames } from './common';\n\nexport interface NodeCronOptions {\n name: string;\n timezone?: string;\n}\n\nexport interface NodeCron {\n schedule: (\n cronExpression: string,\n callback: (context?: unknown) => void,\n options: NodeCronOptions | undefined,\n ) => unknown;\n}\n\n/**\n * Wraps the `node-cron` library with check-in monitoring.\n *\n * ```ts\n * import * as Sentry from \"@sentry/node\";\n * import * as cron from \"node-cron\";\n *\n * const cronWithCheckIn = Sentry.cron.instrumentNodeCron(cron);\n *\n * cronWithCheckIn.schedule(\n * \"* * * * *\",\n * () => {\n * console.log(\"running a task every minute\");\n * },\n * { name: \"my-cron-job\" },\n * );\n * ```\n */\nexport function instrumentNodeCron<T>(\n lib: Partial<NodeCron> & T,\n monitorConfig: Pick<MonitorConfig, 'isolateTrace'> = {},\n): T {\n return new Proxy(lib, {\n get(target, prop) {\n if (prop === 'schedule' && target.schedule) {\n // When 'get' is called for schedule, return a proxied version of the schedule function\n return new Proxy(target.schedule, {\n apply(target, thisArg, argArray: Parameters<NodeCron['schedule']>) {\n const [expression, callback, options] = argArray;\n\n const name = options?.name;\n const timezone = options?.timezone;\n\n if (!name) {\n throw new Error('Missing \"name\" for scheduled job. A name is required for Sentry check-in monitoring.');\n }\n\n const monitoredCallback = async (...args: Parameters<typeof callback>): Promise<void> => {\n return withMonitor(\n name,\n async () => {\n // We have to manually catch here and capture the exception because node-cron swallows errors\n // https://github.com/node-cron/node-cron/issues/399\n try {\n // oxlint-disable-next-line typescript/await-thenable -- callback may be async at runtime\n return await callback(...args);\n } catch (e) {\n captureException(e, {\n mechanism: {\n handled: false,\n type: 'auto.function.node-cron.instrumentNodeCron',\n },\n });\n throw e;\n }\n },\n {\n schedule: { type: 'crontab', value: replaceCronNames(expression) },\n timezone,\n ...monitorConfig,\n },\n );\n };\n\n return target.apply(thisArg, [expression, monitoredCallback, options]);\n },\n });\n } else {\n return target[prop as keyof T];\n }\n },\n });\n}\n"],"names":[],"mappings":";;;AAgBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,kBAAkB;AAClC,EAAE,GAAG;AACL,EAAE,aAAa,GAAwC,EAAE;AACzD,EAAK;AACL,EAAE,OAAO,IAAI,KAAK,CAAC,GAAG,EAAE;AACxB,IAAI,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE;AACtB,MAAM,IAAI,IAAA,KAAS,cAAc,MAAM,CAAC,QAAQ,EAAE;AAClD;AACA,QAAQ,OAAO,IAAI,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE;AAC1C,UAAU,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAoC;AAC7E,YAAY,MAAM,CAAC,UAAU,EAAE,QAAQ,EAAE,OAAO,CAAA,GAAI,QAAQ;;AAE5D,YAAY,MAAM,IAAA,GAAO,OAAO,EAAE,IAAI;AACtC,YAAY,MAAM,QAAA,GAAW,OAAO,EAAE,QAAQ;;AAE9C,YAAY,IAAI,CAAC,IAAI,EAAE;AACvB,cAAc,MAAM,IAAI,KAAK,CAAC,sFAAsF,CAAC;AACrH,YAAY;;AAEZ,YAAY,MAAM,iBAAA,GAAoB,OAAO,GAAG,IAAI,KAAiD;AACrG,cAAc,OAAO,WAAW;AAChC,gBAAgB,IAAI;AACpB,gBAAgB,YAAY;AAC5B;AACA;AACA,kBAAkB,IAAI;AACtB;AACA,oBAAoB,OAAO,MAAM,QAAQ,CAAC,GAAG,IAAI,CAAC;AAClD,kBAAkB,CAAA,CAAE,OAAO,CAAC,EAAE;AAC9B,oBAAoB,gBAAgB,CAAC,CAAC,EAAE;AACxC,sBAAsB,SAAS,EAAE;AACjC,wBAAwB,OAAO,EAAE,KAAK;AACtC,wBAAwB,IAAI,EAAE,4CAA4C;AAC1E,uBAAuB;AACvB,qBAAqB,CAAC;AACtB,oBAAoB,MAAM,CAAC;AAC3B,kBAAkB;AAClB,gBAAgB,CAAC;AACjB,gBAAgB;AAChB,kBAAkB,QAAQ,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,gBAAgB,CAAC,UAAU,GAAG;AACpF,kBAAkB,QAAQ;AAC1B,kBAAkB,GAAG,aAAa;AAClC,iBAAiB;AACjB,eAAe;AACf,YAAY,CAAC;;AAEb,YAAY,OAAO,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,UAAU,EAAE,iBAAiB,EAAE,OAAO,CAAC,CAAC;AAClF,UAAU,CAAC;AACX,SAAS,CAAC;AACV,MAAM,OAAO;AACb,QAAQ,OAAO,MAAM,CAAC,IAAA,EAAgB;AACtC,MAAM;AACN,IAAI,CAAC;AACL,GAAG,CAAC;AACJ;;;;"} | ||
| {"version":3,"file":"node-cron.js","sources":["../../../src/cron/node-cron.ts"],"sourcesContent":["import { captureException, type MonitorConfig, withMonitor } from '@sentry/core';\nimport { replaceCronNames } from './common';\n\nexport interface NodeCronOptions {\n name: string;\n timezone?: string;\n}\n\nexport interface NodeCron {\n schedule: (\n cronExpression: string,\n callback: (context?: unknown) => void,\n options: NodeCronOptions | undefined,\n ) => unknown;\n}\n\n/**\n * Wraps the `node-cron` library with check-in monitoring.\n *\n * ```ts\n * import * as Sentry from \"@sentry/node\";\n * import * as cron from \"node-cron\";\n *\n * const cronWithCheckIn = Sentry.cron.instrumentNodeCron(cron);\n *\n * cronWithCheckIn.schedule(\n * \"* * * * *\",\n * () => {\n * console.log(\"running a task every minute\");\n * },\n * { name: \"my-cron-job\" },\n * );\n * ```\n */\nexport function instrumentNodeCron<T>(\n lib: Partial<NodeCron> & T,\n monitorConfig: Pick<MonitorConfig, 'isolateTrace'> = {},\n): T {\n return new Proxy(lib, {\n get(target, prop) {\n if (prop === 'schedule' && target.schedule) {\n // When 'get' is called for schedule, return a proxied version of the schedule function\n return new Proxy(target.schedule, {\n apply(target, thisArg, argArray: Parameters<NodeCron['schedule']>) {\n const [expression, callback, options] = argArray;\n\n const name = options?.name;\n const timezone = options?.timezone;\n\n if (!name) {\n throw new Error('Missing \"name\" for scheduled job. A name is required for Sentry check-in monitoring.');\n }\n\n const monitoredCallback = async (...args: Parameters<typeof callback>): Promise<void> => {\n return withMonitor(\n name,\n async () => {\n // We have to manually catch here and capture the exception because node-cron swallows errors\n // https://github.com/node-cron/node-cron/issues/399\n try {\n // oxlint-disable-next-line typescript/await-thenable -- callback may be async at runtime\n return await callback(...args);\n } catch (e) {\n captureException(e, {\n mechanism: {\n handled: false,\n type: 'auto.function.node-cron.instrumentNodeCron',\n },\n });\n throw e;\n }\n },\n {\n schedule: { type: 'crontab', value: replaceCronNames(expression) },\n timezone,\n ...monitorConfig,\n },\n );\n };\n\n return target.apply(thisArg, [expression, monitoredCallback, options]);\n },\n });\n } else {\n return target[prop as keyof T];\n }\n },\n });\n}\n"],"names":["target"],"mappings":";;;AAkCO,SAAS,kBAAA,CACd,GAAA,EACA,aAAA,GAAqD,EAAC,EACnD;AACH,EAAA,OAAO,IAAI,MAAM,GAAA,EAAK;AAAA,IACpB,GAAA,CAAI,QAAQ,IAAA,EAAM;AAChB,MAAA,IAAI,IAAA,KAAS,UAAA,IAAc,MAAA,CAAO,QAAA,EAAU;AAE1C,QAAA,OAAO,IAAI,KAAA,CAAM,MAAA,CAAO,QAAA,EAAU;AAAA,UAChC,KAAA,CAAMA,OAAAA,EAAQ,OAAA,EAAS,QAAA,EAA4C;AACjE,YAAA,MAAM,CAAC,UAAA,EAAY,QAAA,EAAU,OAAO,CAAA,GAAI,QAAA;AAExC,YAAA,MAAM,OAAO,OAAA,EAAS,IAAA;AACtB,YAAA,MAAM,WAAW,OAAA,EAAS,QAAA;AAE1B,YAAA,IAAI,CAAC,IAAA,EAAM;AACT,cAAA,MAAM,IAAI,MAAM,sFAAsF,CAAA;AAAA,YACxG;AAEA,YAAA,MAAM,iBAAA,GAAoB,UAAU,IAAA,KAAqD;AACvF,cAAA,OAAO,WAAA;AAAA,gBACL,IAAA;AAAA,gBACA,YAAY;AAGV,kBAAA,IAAI;AAEF,oBAAA,OAAO,MAAM,QAAA,CAAS,GAAG,IAAI,CAAA;AAAA,kBAC/B,SAAS,CAAA,EAAG;AACV,oBAAA,gBAAA,CAAiB,CAAA,EAAG;AAAA,sBAClB,SAAA,EAAW;AAAA,wBACT,OAAA,EAAS,KAAA;AAAA,wBACT,IAAA,EAAM;AAAA;AACR,qBACD,CAAA;AACD,oBAAA,MAAM,CAAA;AAAA,kBACR;AAAA,gBACF,CAAA;AAAA,gBACA;AAAA,kBACE,UAAU,EAAE,IAAA,EAAM,WAAW,KAAA,EAAO,gBAAA,CAAiB,UAAU,CAAA,EAAE;AAAA,kBACjE,QAAA;AAAA,kBACA,GAAG;AAAA;AACL,eACF;AAAA,YACF,CAAA;AAEA,YAAA,OAAOA,QAAO,KAAA,CAAM,OAAA,EAAS,CAAC,UAAA,EAAY,iBAAA,EAAmB,OAAO,CAAC,CAAA;AAAA,UACvE;AAAA,SACD,CAAA;AAAA,MACH,CAAA,MAAO;AACL,QAAA,OAAO,OAAO,IAAe,CAAA;AAAA,MAC/B;AAAA,IACF;AAAA,GACD,CAAA;AACH;;;;"} |
| import { withMonitor } from '@sentry/core'; | ||
| import { replaceCronNames } from './common.js'; | ||
| /** | ||
| * Instruments the `node-schedule` library to send a check-in event to Sentry for each job execution. | ||
| * | ||
| * ```ts | ||
| * import * as Sentry from '@sentry/node'; | ||
| * import * as schedule from 'node-schedule'; | ||
| * | ||
| * const scheduleWithCheckIn = Sentry.cron.instrumentNodeSchedule(schedule); | ||
| * | ||
| * const job = scheduleWithCheckIn.scheduleJob('my-cron-job', '* * * * *', () => { | ||
| * console.log('You will see this message every minute'); | ||
| * }); | ||
| * ``` | ||
| */ | ||
| function instrumentNodeSchedule(lib) { | ||
| return new Proxy(lib, { | ||
| get(target, prop) { | ||
| if (prop === 'scheduleJob') { | ||
| // eslint-disable-next-line @typescript-eslint/unbound-method | ||
| if (prop === "scheduleJob") { | ||
| return new Proxy(target.scheduleJob, { | ||
| apply(target, thisArg, argArray) { | ||
| apply(target2, thisArg, argArray) { | ||
| const [nameOrExpression, expressionOrCallback, callback] = argArray; | ||
| if ( | ||
| typeof nameOrExpression !== 'string' || | ||
| typeof expressionOrCallback !== 'string' || | ||
| typeof callback !== 'function' | ||
| ) { | ||
| if (typeof nameOrExpression !== "string" || typeof expressionOrCallback !== "string" || typeof callback !== "function") { | ||
| throw new Error( | ||
| "Automatic instrumentation of 'node-schedule' requires the first parameter of 'scheduleJob' to be a job name string and the second parameter to be a crontab string", | ||
| "Automatic instrumentation of 'node-schedule' requires the first parameter of 'scheduleJob' to be a job name string and the second parameter to be a crontab string" | ||
| ); | ||
| } | ||
| const monitorSlug = nameOrExpression; | ||
| const expression = expressionOrCallback; | ||
| async function monitoredCallback() { | ||
@@ -44,18 +22,15 @@ return withMonitor( | ||
| async () => { | ||
| // oxlint-disable-next-line typescript/await-thenable -- callback may be async at runtime | ||
| await callback?.(); | ||
| }, | ||
| { | ||
| schedule: { type: 'crontab', value: replaceCronNames(expression) }, | ||
| }, | ||
| schedule: { type: "crontab", value: replaceCronNames(expression) } | ||
| } | ||
| ); | ||
| } | ||
| return target.apply(thisArg, [monitorSlug, expression, monitoredCallback]); | ||
| }, | ||
| return target2.apply(thisArg, [monitorSlug, expression, monitoredCallback]); | ||
| } | ||
| }); | ||
| } | ||
| return target[prop]; | ||
| }, | ||
| } | ||
| }); | ||
@@ -62,0 +37,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"node-schedule.js","sources":["../../../src/cron/node-schedule.ts"],"sourcesContent":["import { withMonitor } from '@sentry/core';\nimport { replaceCronNames } from './common';\n\nexport interface NodeSchedule {\n scheduleJob(\n nameOrExpression: string | Date | object,\n expressionOrCallback: string | Date | object | (() => void),\n callback?: () => void,\n ): unknown;\n}\n\n/**\n * Instruments the `node-schedule` library to send a check-in event to Sentry for each job execution.\n *\n * ```ts\n * import * as Sentry from '@sentry/node';\n * import * as schedule from 'node-schedule';\n *\n * const scheduleWithCheckIn = Sentry.cron.instrumentNodeSchedule(schedule);\n *\n * const job = scheduleWithCheckIn.scheduleJob('my-cron-job', '* * * * *', () => {\n * console.log('You will see this message every minute');\n * });\n * ```\n */\nexport function instrumentNodeSchedule<T>(lib: T & NodeSchedule): T {\n return new Proxy(lib, {\n get(target, prop: keyof NodeSchedule) {\n if (prop === 'scheduleJob') {\n // eslint-disable-next-line @typescript-eslint/unbound-method\n return new Proxy(target.scheduleJob, {\n apply(target, thisArg, argArray: Parameters<NodeSchedule['scheduleJob']>) {\n const [nameOrExpression, expressionOrCallback, callback] = argArray;\n\n if (\n typeof nameOrExpression !== 'string' ||\n typeof expressionOrCallback !== 'string' ||\n typeof callback !== 'function'\n ) {\n throw new Error(\n \"Automatic instrumentation of 'node-schedule' requires the first parameter of 'scheduleJob' to be a job name string and the second parameter to be a crontab string\",\n );\n }\n\n const monitorSlug = nameOrExpression;\n const expression = expressionOrCallback;\n\n async function monitoredCallback(): Promise<void> {\n return withMonitor(\n monitorSlug,\n async () => {\n // oxlint-disable-next-line typescript/await-thenable -- callback may be async at runtime\n await callback?.();\n },\n {\n schedule: { type: 'crontab', value: replaceCronNames(expression) },\n },\n );\n }\n\n return target.apply(thisArg, [monitorSlug, expression, monitoredCallback]);\n },\n });\n }\n\n return target[prop];\n },\n });\n}\n"],"names":[],"mappings":";;;AAWA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,sBAAsB,CAAI,GAAG,EAAuB;AACpE,EAAE,OAAO,IAAI,KAAK,CAAC,GAAG,EAAE;AACxB,IAAI,GAAG,CAAC,MAAM,EAAE,IAAI,EAAsB;AAC1C,MAAM,IAAI,IAAA,KAAS,aAAa,EAAE;AAClC;AACA,QAAQ,OAAO,IAAI,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE;AAC7C,UAAU,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAA2C;AACpF,YAAY,MAAM,CAAC,gBAAgB,EAAE,oBAAoB,EAAE,QAAQ,CAAA,GAAI,QAAQ;;AAE/E,YAAY;AACZ,cAAc,OAAO,gBAAA,KAAqB,QAAA;AAC1C,cAAc,OAAO,oBAAA,KAAyB,QAAA;AAC9C,cAAc,OAAO,aAAa;AAClC,cAAc;AACd,cAAc,MAAM,IAAI,KAAK;AAC7B,gBAAgB,oKAAoK;AACpL,eAAe;AACf,YAAY;;AAEZ,YAAY,MAAM,WAAA,GAAc,gBAAgB;AAChD,YAAY,MAAM,UAAA,GAAa,oBAAoB;;AAEnD,YAAY,eAAe,iBAAiB,GAAkB;AAC9D,cAAc,OAAO,WAAW;AAChC,gBAAgB,WAAW;AAC3B,gBAAgB,YAAY;AAC5B;AACA,kBAAkB,MAAM,QAAQ,IAAI;AACpC,gBAAgB,CAAC;AACjB,gBAAgB;AAChB,kBAAkB,QAAQ,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,gBAAgB,CAAC,UAAU,GAAG;AACpF,iBAAiB;AACjB,eAAe;AACf,YAAY;;AAEZ,YAAY,OAAO,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,WAAW,EAAE,UAAU,EAAE,iBAAiB,CAAC,CAAC;AACtF,UAAU,CAAC;AACX,SAAS,CAAC;AACV,MAAM;;AAEN,MAAM,OAAO,MAAM,CAAC,IAAI,CAAC;AACzB,IAAI,CAAC;AACL,GAAG,CAAC;AACJ;;;;"} | ||
| {"version":3,"file":"node-schedule.js","sources":["../../../src/cron/node-schedule.ts"],"sourcesContent":["import { withMonitor } from '@sentry/core';\nimport { replaceCronNames } from './common';\n\nexport interface NodeSchedule {\n scheduleJob(\n nameOrExpression: string | Date | object,\n expressionOrCallback: string | Date | object | (() => void),\n callback?: () => void,\n ): unknown;\n}\n\n/**\n * Instruments the `node-schedule` library to send a check-in event to Sentry for each job execution.\n *\n * ```ts\n * import * as Sentry from '@sentry/node';\n * import * as schedule from 'node-schedule';\n *\n * const scheduleWithCheckIn = Sentry.cron.instrumentNodeSchedule(schedule);\n *\n * const job = scheduleWithCheckIn.scheduleJob('my-cron-job', '* * * * *', () => {\n * console.log('You will see this message every minute');\n * });\n * ```\n */\nexport function instrumentNodeSchedule<T>(lib: T & NodeSchedule): T {\n return new Proxy(lib, {\n get(target, prop: keyof NodeSchedule) {\n if (prop === 'scheduleJob') {\n // eslint-disable-next-line @typescript-eslint/unbound-method\n return new Proxy(target.scheduleJob, {\n apply(target, thisArg, argArray: Parameters<NodeSchedule['scheduleJob']>) {\n const [nameOrExpression, expressionOrCallback, callback] = argArray;\n\n if (\n typeof nameOrExpression !== 'string' ||\n typeof expressionOrCallback !== 'string' ||\n typeof callback !== 'function'\n ) {\n throw new Error(\n \"Automatic instrumentation of 'node-schedule' requires the first parameter of 'scheduleJob' to be a job name string and the second parameter to be a crontab string\",\n );\n }\n\n const monitorSlug = nameOrExpression;\n const expression = expressionOrCallback;\n\n async function monitoredCallback(): Promise<void> {\n return withMonitor(\n monitorSlug,\n async () => {\n // oxlint-disable-next-line typescript/await-thenable -- callback may be async at runtime\n await callback?.();\n },\n {\n schedule: { type: 'crontab', value: replaceCronNames(expression) },\n },\n );\n }\n\n return target.apply(thisArg, [monitorSlug, expression, monitoredCallback]);\n },\n });\n }\n\n return target[prop];\n },\n });\n}\n"],"names":["target"],"mappings":";;;AAyBO,SAAS,uBAA0B,GAAA,EAA0B;AAClE,EAAA,OAAO,IAAI,MAAM,GAAA,EAAK;AAAA,IACpB,GAAA,CAAI,QAAQ,IAAA,EAA0B;AACpC,MAAA,IAAI,SAAS,aAAA,EAAe;AAE1B,QAAA,OAAO,IAAI,KAAA,CAAM,MAAA,CAAO,WAAA,EAAa;AAAA,UACnC,KAAA,CAAMA,OAAAA,EAAQ,OAAA,EAAS,QAAA,EAAmD;AACxE,YAAA,MAAM,CAAC,gBAAA,EAAkB,oBAAA,EAAsB,QAAQ,CAAA,GAAI,QAAA;AAE3D,YAAA,IACE,OAAO,qBAAqB,QAAA,IAC5B,OAAO,yBAAyB,QAAA,IAChC,OAAO,aAAa,UAAA,EACpB;AACA,cAAA,MAAM,IAAI,KAAA;AAAA,gBACR;AAAA,eACF;AAAA,YACF;AAEA,YAAA,MAAM,WAAA,GAAc,gBAAA;AACpB,YAAA,MAAM,UAAA,GAAa,oBAAA;AAEnB,YAAA,eAAe,iBAAA,GAAmC;AAChD,cAAA,OAAO,WAAA;AAAA,gBACL,WAAA;AAAA,gBACA,YAAY;AAEV,kBAAA,MAAM,QAAA,IAAW;AAAA,gBACnB,CAAA;AAAA,gBACA;AAAA,kBACE,UAAU,EAAE,IAAA,EAAM,WAAW,KAAA,EAAO,gBAAA,CAAiB,UAAU,CAAA;AAAE;AACnE,eACF;AAAA,YACF;AAEA,YAAA,OAAOA,QAAO,KAAA,CAAM,OAAA,EAAS,CAAC,WAAA,EAAa,UAAA,EAAY,iBAAiB,CAAC,CAAA;AAAA,UAC3E;AAAA,SACD,CAAA;AAAA,MACH;AAEA,MAAA,OAAO,OAAO,IAAI,CAAA;AAAA,IACpB;AAAA,GACD,CAAA;AACH;;;;"} |
@@ -1,6 +0,1 @@ | ||
| /** | ||
| * This serves as a build time flag that will be true by default, but false in non-debug builds or if users replace `__SENTRY_DEBUG__` in their generated code. | ||
| * | ||
| * ATTENTION: This constant must never cross package boundaries (i.e. be exported) to guarantee that it can be used for tree shaking. | ||
| */ | ||
| const DEBUG_BUILD = (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__); | ||
@@ -7,0 +2,0 @@ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"debug-build.js","sources":["../../src/debug-build.ts"],"sourcesContent":["declare const __DEBUG_BUILD__: boolean;\n\n/**\n * This serves as a build time flag that will be true by default, but false in non-debug builds or if users replace `__SENTRY_DEBUG__` in their generated code.\n *\n * ATTENTION: This constant must never cross package boundaries (i.e. be exported) to guarantee that it can be used for tree shaking.\n */\nexport const DEBUG_BUILD = __DEBUG_BUILD__;\n"],"names":[],"mappings":"AAEA;AACA;AACA;AACA;AACA;AACO,MAAM,WAAA,IAAc,OAAA,gBAAA,KAAA,WAAA,IAAA,gBAAA;;;;"} | ||
| {"version":3,"file":"debug-build.js","sources":["../../src/debug-build.ts"],"sourcesContent":["declare const __DEBUG_BUILD__: boolean;\n\n/**\n * This serves as a build time flag that will be true by default, but false in non-debug builds or if users replace `__SENTRY_DEBUG__` in their generated code.\n *\n * ATTENTION: This constant must never cross package boundaries (i.e. be exported) to guarantee that it can be used for tree shaking.\n */\nexport const DEBUG_BUILD = __DEBUG_BUILD__;\n"],"names":[],"mappings":"AAOO,MAAM,WAAA,IAAc,OAAA,gBAAA,KAAA,WAAA,IAAA,gBAAA;;;;"} |
| import { init } from './sdk/index.js'; | ||
| /** | ||
| * The @sentry/node-core/init export can be used with the node --import and --require args to initialize the SDK entirely via | ||
| * environment variables. | ||
| * | ||
| * > SENTRY_DSN=https://examplePublicKey@o0.ingest.sentry.io/0 SENTRY_TRACES_SAMPLE_RATE=1.0 node --import=@sentry/node/init app.mjs | ||
| */ | ||
| init(); | ||
| //# sourceMappingURL=init.js.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"init.js","sources":["../../src/init.ts"],"sourcesContent":["import { init } from './sdk';\n\n/**\n * The @sentry/node-core/init export can be used with the node --import and --require args to initialize the SDK entirely via\n * environment variables.\n *\n * > SENTRY_DSN=https://examplePublicKey@o0.ingest.sentry.io/0 SENTRY_TRACES_SAMPLE_RATE=1.0 node --import=@sentry/node/init app.mjs\n */\ninit();\n"],"names":[],"mappings":";;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,EAAE"} | ||
| {"version":3,"file":"init.js","sources":["../../src/init.ts"],"sourcesContent":["import { init } from './sdk';\n\n/**\n * The @sentry/node-core/init export can be used with the node --import and --require args to initialize the SDK entirely via\n * environment variables.\n *\n * > SENTRY_DSN=https://examplePublicKey@o0.ingest.sentry.io/0 SENTRY_TRACES_SAMPLE_RATE=1.0 node --import=@sentry/node/init app.mjs\n */\ninit();\n"],"names":[],"mappings":";;AAQA,IAAA,EAAK"} |
@@ -8,36 +8,20 @@ import { types } from 'node:util'; | ||
| const { isPromise } = types; | ||
| // This string is a placeholder that gets overwritten with the worker code. | ||
| const base64WorkerScript = 'LyohIEBzZW50cnkvbm9kZS1jb3JlIDEwLjUzLjEgKGNkOTc0MDgpIHwgaHR0cHM6Ly9naXRodWIuY29tL2dldHNlbnRyeS9zZW50cnktamF2YXNjcmlwdCAqLwppbXBvcnR7U2Vzc2lvbiBhcyB0fWZyb20ibm9kZTppbnNwZWN0b3IiO2ltcG9ydHt3b3JrZXJEYXRhIGFzIG4scGFyZW50UG9ydCBhcyBlfWZyb20ibm9kZTp3b3JrZXJfdGhyZWFkcyI7aW1wb3J0e3Bvc2l4IGFzIHIsc2VwIGFzIG99ZnJvbSJub2RlOnBhdGgiO2ltcG9ydCphcyBpIGZyb20ibm9kZTpodHRwIjtpbXBvcnQqYXMgcyBmcm9tIm5vZGU6aHR0cHMiO2ltcG9ydHtSZWFkYWJsZSBhcyBjfWZyb20ibm9kZTpzdHJlYW0iO2ltcG9ydHtjcmVhdGVHemlwIGFzIHV9ZnJvbSJub2RlOnpsaWIiO2ltcG9ydCphcyBhIGZyb20ibm9kZTpuZXQiO2ltcG9ydCphcyBmIGZyb20ibm9kZTp0bHMiO2NvbnN0IGg9InVuZGVmaW5lZCI9PXR5cGVvZiBfX1NFTlRSWV9ERUJVR19ffHxfX1NFTlRSWV9ERUJVR19fLHA9Z2xvYmFsVGhpcyxsPSIxMC41My4xIjtmdW5jdGlvbiBkKCl7cmV0dXJuIG0ocCkscH1mdW5jdGlvbiBtKHQpe2NvbnN0IG49dC5fX1NFTlRSWV9fPXQuX19TRU5UUllfX3x8e307cmV0dXJuIG4udmVyc2lvbj1uLnZlcnNpb258fGwsbltsXT1uW2xdfHx7fX1mdW5jdGlvbiBnKHQsbixlPXApe2NvbnN0IHI9ZS5fX1NFTlRSWV9fPWUuX19TRU5UUllfX3x8e30sbz1yW2xdPXJbbF18fHt9O3JldHVybiBvW3RdfHwob1t0XT1uKCkpfWNvbnN0IHk9e307ZnVuY3Rpb24gYih0KXtpZighKCJjb25zb2xlImluIHApKXJldHVybiB0KCk7Y29uc3Qgbj1wLmNvbnNvbGUsZT17fSxyPU9iamVjdC5rZXlzKHkpO3IuZm9yRWFjaCh0PT57Y29uc3Qgcj15W3RdO2VbdF09blt0XSxuW3RdPXJ9KTt0cnl7cmV0dXJuIHQoKX1maW5hbGx5e3IuZm9yRWFjaCh0PT57blt0XT1lW3RdfSl9fWZ1bmN0aW9uIHYoKXtyZXR1cm4gdygpLmVuYWJsZWR9ZnVuY3Rpb24gXyh0LC4uLm4pe2gmJnYoKSYmYigoKT0+e3AuY29uc29sZVt0XShgU2VudHJ5IExvZ2dlciBbJHt0fV06YCwuLi5uKX0pfWZ1bmN0aW9uIHcoKXtyZXR1cm4gaD9nKCJsb2dnZXJTZXR0aW5ncyIsKCk9Pih7ZW5hYmxlZDohMX0pKTp7ZW5hYmxlZDohMX19Y29uc3QgUz17ZW5hYmxlOmZ1bmN0aW9uKCl7dygpLmVuYWJsZWQ9ITB9LGRpc2FibGU6ZnVuY3Rpb24oKXt3KCkuZW5hYmxlZD0hMX0saXNFbmFibGVkOnYsbG9nOmZ1bmN0aW9uKC4uLnQpe18oImxvZyIsLi4udCl9LHdhcm46ZnVuY3Rpb24oLi4udCl7Xygid2FybiIsLi4udCl9LGVycm9yOmZ1bmN0aW9uKC4uLnQpe18oImVycm9yIiwuLi50KX19LCQ9L2NhcHR1cmVNZXNzYWdlfGNhcHR1cmVFeGNlcHRpb24vO2Z1bmN0aW9uIEUodCl7cmV0dXJuIHRbdC5sZW5ndGgtMV18fHt9fWNvbnN0IHg9Ijxhbm9ueW1vdXM+Ijtjb25zdCBOPU9iamVjdC5wcm90b3R5cGUudG9TdHJpbmc7ZnVuY3Rpb24gaih0LG4pe3JldHVybiBOLmNhbGwodCk9PT1gW29iamVjdCAke259XWB9ZnVuY3Rpb24gQyh0KXtyZXR1cm4gaih0LCJTdHJpbmciKX1mdW5jdGlvbiBBKHQpe3JldHVybiBqKHQsIk9iamVjdCIpfWZ1bmN0aW9uIGsodCl7cmV0dXJuIEJvb2xlYW4odD8udGhlbiYmImZ1bmN0aW9uIj09dHlwZW9mIHQudGhlbil9ZnVuY3Rpb24gVCh0LG4pe3RyeXtyZXR1cm4gdCBpbnN0YW5jZW9mIG59Y2F0Y2h7cmV0dXJuITF9fWNvbnN0IEk9cDtmdW5jdGlvbiBPKHQsbil7Y29uc3QgZT10LHI9W107aWYoIWU/LnRhZ05hbWUpcmV0dXJuIiI7aWYoSS5IVE1MRWxlbWVudCYmZSBpbnN0YW5jZW9mIEhUTUxFbGVtZW50JiZlLmRhdGFzZXQpe2lmKGUuZGF0YXNldC5zZW50cnlDb21wb25lbnQpcmV0dXJuIGUuZGF0YXNldC5zZW50cnlDb21wb25lbnQ7aWYoZS5kYXRhc2V0LnNlbnRyeUVsZW1lbnQpcmV0dXJuIGUuZGF0YXNldC5zZW50cnlFbGVtZW50fXIucHVzaChlLnRhZ05hbWUudG9Mb3dlckNhc2UoKSk7Y29uc3Qgbz1uPy5sZW5ndGg/bi5maWx0ZXIodD0+ZS5nZXRBdHRyaWJ1dGUodCkpLm1hcCh0PT5bdCxlLmdldEF0dHJpYnV0ZSh0KV0pOm51bGw7aWYobz8ubGVuZ3RoKW8uZm9yRWFjaCh0PT57ci5wdXNoKGBbJHt0WzBdfT0iJHt0WzFdfSJdYCl9KTtlbHNle2UuaWQmJnIucHVzaChgIyR7ZS5pZH1gKTtjb25zdCB0PWUuY2xhc3NOYW1lO2lmKHQmJkModCkpe2NvbnN0IG49dC5zcGxpdCgvXHMrLyk7Zm9yKGNvbnN0IHQgb2YgbilyLnB1c2goYC4ke3R9YCl9fWZvcihjb25zdCB0IG9mWyJhcmlhLWxhYmVsIiwidHlwZSIsIm5hbWUiLCJ0aXRsZSIsImFsdCJdKXtjb25zdCBuPWUuZ2V0QXR0cmlidXRlKHQpO24mJnIucHVzaChgWyR7dH09IiR7bn0iXWApfXJldHVybiByLmpvaW4oIiIpfWZ1bmN0aW9uIFIodCl7aWYoZnVuY3Rpb24odCl7c3dpdGNoKE4uY2FsbCh0KSl7Y2FzZSJbb2JqZWN0IEVycm9yXSI6Y2FzZSJbb2JqZWN0IEV4Y2VwdGlvbl0iOmNhc2UiW29iamVjdCBET01FeGNlcHRpb25dIjpjYXNlIltvYmplY3QgV2ViQXNzZW1ibHkuRXhjZXB0aW9uXSI6cmV0dXJuITA7ZGVmYXVsdDpyZXR1cm4gVCh0LEVycm9yKX19KHQpKXJldHVybnttZXNzYWdlOnQubWVzc2FnZSxuYW1lOnQubmFtZSxzdGFjazp0LnN0YWNrLC4uLkQodCl9O2lmKG49dCwidW5kZWZpbmVkIiE9dHlwZW9mIEV2ZW50JiZUKG4sRXZlbnQpKXtjb25zdCBuPXt0eXBlOnQudHlwZSx0YXJnZXQ6UCh0LnRhcmdldCksY3VycmVudFRhcmdldDpQKHQuY3VycmVudFRhcmdldCksLi4uRCh0KX07cmV0dXJuInVuZGVmaW5lZCIhPXR5cGVvZiBDdXN0b21FdmVudCYmVCh0LEN1c3RvbUV2ZW50KSYmKG4uZGV0YWlsPXQuZGV0YWlsKSxufXJldHVybiB0O3ZhciBufWZ1bmN0aW9uIFAodCl7dHJ5e3JldHVybiBuPXQsInVuZGVmaW5lZCIhPXR5cGVvZiBFbGVtZW50JiZUKG4sRWxlbWVudCk/ZnVuY3Rpb24odCxuPXt9KXtpZighdClyZXR1cm4iPHVua25vd24+Ijt0cnl7bGV0IGU9dDtjb25zdCByPTUsbz1bXTtsZXQgaT0wLHM9MDtjb25zdCBjPSIgPiAiLHU9Yy5sZW5ndGg7bGV0IGE7Y29uc3QgZj1BcnJheS5pc0FycmF5KG4pP246bi5rZXlBdHRycyxoPSFBcnJheS5pc0FycmF5KG4pJiZuLm1heFN0cmluZ0xlbmd0aHx8ODA7Zm9yKDtlJiZpKys8ciYmKGE9TyhlLGYpLCEoImh0bWwiPT09YXx8aT4xJiZzK28ubGVuZ3RoKnUrYS5sZW5ndGg+PWgpKTspby5wdXNoKGEpLHMrPWEubGVuZ3RoLGU9ZS5wYXJlbnROb2RlO3JldHVybiBvLnJldmVyc2UoKS5qb2luKGMpfWNhdGNoe3JldHVybiI8dW5rbm93bj4ifX0odCk6T2JqZWN0LnByb3RvdHlwZS50b1N0cmluZy5jYWxsKHQpfWNhdGNoe3JldHVybiI8dW5rbm93bj4ifXZhciBufWZ1bmN0aW9uIEQodCl7cmV0dXJuIm9iamVjdCI9PXR5cGVvZiB0JiZudWxsIT09dD9PYmplY3QuZnJvbUVudHJpZXMoT2JqZWN0LmVudHJpZXModCkpOnt9fWxldCBVLEw7ZnVuY3Rpb24gTSh0KXtpZih2b2lkIDAhPT1VKXJldHVybiBVP1UodCk6dCgpO2NvbnN0IG49U3ltYm9sLmZvcigiX19TRU5UUllfU0FGRV9SQU5ET01fSURfV1JBUFBFUl9fIiksZT1wO3JldHVybiBuIGluIGUmJiJmdW5jdGlvbiI9PXR5cGVvZiBlW25dPyhVPWVbbl0sVSh0KSk6KFU9bnVsbCx0KCkpfWZ1bmN0aW9uIEIoKXtyZXR1cm4gTSgoKT0+TWF0aC5yYW5kb20oKSl9ZnVuY3Rpb24geigpe3JldHVybiBNKCgpPT5EYXRlLm5vdygpKX1mdW5jdGlvbiBXKHQsbj0wKXtyZXR1cm4ic3RyaW5nIiE9dHlwZW9mIHR8fDA9PT1ufHx0Lmxlbmd0aDw9bj90OmAke3Quc2xpY2UoMCxuKX0uLi5gfWZ1bmN0aW9uIEYodD1mdW5jdGlvbigpe2NvbnN0IHQ9cDtyZXR1cm4gdC5jcnlwdG98fHQubXNDcnlwdG99KCkpe3RyeXtpZih0Py5yYW5kb21VVUlEKXJldHVybiBNKCgpPT50LnJhbmRvbVVVSUQoKSkucmVwbGFjZSgvLS9nLCIiKX1jYXRjaHt9cmV0dXJuIEx8fChMPVsxZTddKzFlMys0ZTMrOGUzKzFlMTEpLEwucmVwbGFjZSgvWzAxOF0vZyx0PT4odF4oMTYqQigpJjE1KT4+dC80KS50b1N0cmluZygxNikpfWZ1bmN0aW9uIEcoKXtyZXR1cm4geigpLzFlM31sZXQgSDtmdW5jdGlvbiBKKCl7cmV0dXJuKEg/PyhIPWZ1bmN0aW9uKCl7Y29uc3R7cGVyZm9ybWFuY2U6dH09cDtpZighdD8ubm93fHwhdC50aW1lT3JpZ2luKXJldHVybiBHO2NvbnN0IG49dC50aW1lT3JpZ2luO3JldHVybigpPT4obitNKCgpPT50Lm5vdygpKSkvMWUzfSgpKSkoKX1mdW5jdGlvbiBZKHQpe2NvbnN0IG49SigpLGU9e3NpZDpGKCksaW5pdDohMCx0aW1lc3RhbXA6bixzdGFydGVkOm4sZHVyYXRpb246MCxzdGF0dXM6Im9rIixlcnJvcnM6MCxpZ25vcmVEdXJhdGlvbjohMSx0b0pTT046KCk9PmZ1bmN0aW9uKHQpe3JldHVybntzaWQ6YCR7dC5zaWR9YCxpbml0OnQuaW5pdCxzdGFydGVkOm5ldyBEYXRlKDFlMyp0LnN0YXJ0ZWQpLnRvSVNPU3RyaW5nKCksdGltZXN0YW1wOm5ldyBEYXRlKDFlMyp0LnRpbWVzdGFtcCkudG9JU09TdHJpbmcoKSxzdGF0dXM6dC5zdGF0dXMsZXJyb3JzOnQuZXJyb3JzLGRpZDoibnVtYmVyIj09dHlwZW9mIHQuZGlkfHwic3RyaW5nIj09dHlwZW9mIHQuZGlkP2Ake3QuZGlkfWA6dm9pZCAwLGR1cmF0aW9uOnQuZHVyYXRpb24sYWJub3JtYWxfbWVjaGFuaXNtOnQuYWJub3JtYWxfbWVjaGFuaXNtLGF0dHJzOntyZWxlYXNlOnQucmVsZWFzZSxlbnZpcm9ubWVudDp0LmVudmlyb25tZW50LGlwX2FkZHJlc3M6dC5pcEFkZHJlc3MsdXNlcl9hZ2VudDp0LnVzZXJBZ2VudH19fShlKX07cmV0dXJuIHQmJlYoZSx0KSxlfWZ1bmN0aW9uIFYodCxuPXt9KXtpZihuLnVzZXImJighdC5pcEFkZHJlc3MmJm4udXNlci5pcF9hZGRyZXNzJiYodC5pcEFkZHJlc3M9bi51c2VyLmlwX2FkZHJlc3MpLHQuZGlkfHxuLmRpZHx8KHQuZGlkPW4udXNlci5pZHx8bi51c2VyLmVtYWlsfHxuLnVzZXIudXNlcm5hbWUpKSx0LnRpbWVzdGFtcD1uLnRpbWVzdGFtcHx8SigpLG4uYWJub3JtYWxfbWVjaGFuaXNtJiYodC5hYm5vcm1hbF9tZWNoYW5pc209bi5hYm5vcm1hbF9tZWNoYW5pc20pLG4uaWdub3JlRHVyYXRpb24mJih0Lmlnbm9yZUR1cmF0aW9uPW4uaWdub3JlRHVyYXRpb24pLG4uc2lkJiYodC5zaWQ9MzI9PT1uLnNpZC5sZW5ndGg/bi5zaWQ6RigpKSx2b2lkIDAhPT1uLmluaXQmJih0LmluaXQ9bi5pbml0KSwhdC5kaWQmJm4uZGlkJiYodC5kaWQ9YCR7bi5kaWR9YCksIm51bWJlciI9PXR5cGVvZiBuLnN0YXJ0ZWQmJih0LnN0YXJ0ZWQ9bi5zdGFydGVkKSx0Lmlnbm9yZUR1cmF0aW9uKXQuZHVyYXRpb249dm9pZCAwO2Vsc2UgaWYoIm51bWJlciI9PXR5cGVvZiBuLmR1cmF0aW9uKXQuZHVyYXRpb249bi5kdXJhdGlvbjtlbHNle2NvbnN0IG49dC50aW1lc3RhbXAtdC5zdGFydGVkO3QuZHVyYXRpb249bj49MD9uOjB9bi5yZWxlYXNlJiYodC5yZWxlYXNlPW4ucmVsZWFzZSksbi5lbnZpcm9ubWVudCYmKHQuZW52aXJvbm1lbnQ9bi5lbnZpcm9ubWVudCksIXQuaXBBZGRyZXNzJiZuLmlwQWRkcmVzcyYmKHQuaXBBZGRyZXNzPW4uaXBBZGRyZXNzKSwhdC51c2VyQWdlbnQmJm4udXNlckFnZW50JiYodC51c2VyQWdlbnQ9bi51c2VyQWdlbnQpLCJudW1iZXIiPT10eXBlb2Ygbi5lcnJvcnMmJih0LmVycm9ycz1uLmVycm9ycyksbi5zdGF0dXMmJih0LnN0YXR1cz1uLnN0YXR1cyl9ZnVuY3Rpb24gSyh0LG4sZT0yKXtpZighbnx8Im9iamVjdCIhPXR5cGVvZiBufHxlPD0wKXJldHVybiBuO2lmKHQmJjA9PT1PYmplY3Qua2V5cyhuKS5sZW5ndGgpcmV0dXJuIHQ7Y29uc3Qgcj17Li4udH07Zm9yKGNvbnN0IHQgaW4gbilPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwobix0KSYmKHJbdF09SyhyW3RdLG5bdF0sZS0xKSk7cmV0dXJuIHJ9ZnVuY3Rpb24gWigpe3JldHVybiBGKCl9ZnVuY3Rpb24gcSgpe3JldHVybiBGKCkuc3Vic3RyaW5nKDE2KX1jb25zdCBRPSJfc2VudHJ5U3BhbiI7ZnVuY3Rpb24gWCh0LG4pe24/ZnVuY3Rpb24odCxuLGUpe3RyeXtPYmplY3QuZGVmaW5lUHJvcGVydHkodCxuLHt2YWx1ZTplLHdyaXRhYmxlOiEwLGNvbmZpZ3VyYWJsZTohMH0pfWNhdGNoe2gmJlMubG9nKGBGYWlsZWQgdG8gYWRkIG5vbi1lbnVtZXJhYmxlIHByb3BlcnR5ICIke1N0cmluZyhuKX0iIHRvIG9iamVjdGAsdCl9fSh0LFEsbik6ZGVsZXRlIHRbUV19ZnVuY3Rpb24gdHQodCl7cmV0dXJuIHRbUV19Y2xhc3MgbnR7Y29uc3RydWN0b3IoKXt0aGlzLnQ9ITEsdGhpcy5vPVtdLHRoaXMuaT1bXSx0aGlzLnU9W10sdGhpcy5oPVtdLHRoaXMucD17fSx0aGlzLmw9e30sdGhpcy5tPXt9LHRoaXMudj17fSx0aGlzLl89e30sdGhpcy5TPXt9LHRoaXMuTj17dHJhY2VJZDpaKCksc2FtcGxlUmFuZDpCKCl9fWNsb25lKCl7Y29uc3QgdD1uZXcgbnQ7cmV0dXJuIHQudT1bLi4udGhpcy51XSx0Lmw9ey4uLnRoaXMubH0sdC5tPXsuLi50aGlzLm19LHQudj17Li4udGhpcy52fSx0Ll89ey4uLnRoaXMuX30sdGhpcy5fLmZsYWdzJiYodC5fLmZsYWdzPXt2YWx1ZXM6Wy4uLnRoaXMuXy5mbGFncy52YWx1ZXNdfSksdC5wPXRoaXMucCx0Lmo9dGhpcy5qLHQuQz10aGlzLkMsdC5BPXRoaXMuQSx0Lms9dGhpcy5rLHQuaT1bLi4udGhpcy5pXSx0Lmg9Wy4uLnRoaXMuaF0sdC5TPXsuLi50aGlzLlN9LHQuTj17Li4udGhpcy5OfSx0LlQ9dGhpcy5ULHQuST10aGlzLkksdC5PPXRoaXMuTyxYKHQsdHQodGhpcykpLHR9c2V0Q2xpZW50KHQpe3RoaXMuVD10fXNldExhc3RFdmVudElkKHQpe3RoaXMuST10fWdldENsaWVudCgpe3JldHVybiB0aGlzLlR9bGFzdEV2ZW50SWQoKXtyZXR1cm4gdGhpcy5JfWFkZFNjb3BlTGlzdGVuZXIodCl7dGhpcy5vLnB1c2godCl9YWRkRXZlbnRQcm9jZXNzb3IodCl7cmV0dXJuIHRoaXMuaS5wdXNoKHQpLHRoaXN9c2V0VXNlcih0KXtyZXR1cm4gdGhpcy5wPXR8fHtlbWFpbDp2b2lkIDAsaWQ6dm9pZCAwLGlwX2FkZHJlc3M6dm9pZCAwLHVzZXJuYW1lOnZvaWQgMH0sdGhpcy5DJiZWKHRoaXMuQyx7dXNlcjp0fSksdGhpcy5SKCksdGhpc31nZXRVc2VyKCl7cmV0dXJuIHRoaXMucH1zZXRDb252ZXJzYXRpb25JZCh0KXtyZXR1cm4gdGhpcy5PPXR8fHZvaWQgMCx0aGlzLlIoKSx0aGlzfXNldFRhZ3ModCl7cmV0dXJuIHRoaXMubD17Li4udGhpcy5sLC4uLnR9LHRoaXMuUigpLHRoaXN9c2V0VGFnKHQsbil7cmV0dXJuIHRoaXMuc2V0VGFncyh7W3RdOm59KX1zZXRBdHRyaWJ1dGVzKHQpe3JldHVybiB0aGlzLm09ey4uLnRoaXMubSwuLi50fSx0aGlzLlIoKSx0aGlzfXNldEF0dHJpYnV0ZSh0LG4pe3JldHVybiB0aGlzLnNldEF0dHJpYnV0ZXMoe1t0XTpufSl9cmVtb3ZlQXR0cmlidXRlKHQpe3JldHVybiB0IGluIHRoaXMubSYmKGRlbGV0ZSB0aGlzLm1bdF0sdGhpcy5SKCkpLHRoaXN9c2V0RXh0cmFzKHQpe3JldHVybiB0aGlzLnY9ey4uLnRoaXMudiwuLi50fSx0aGlzLlIoKSx0aGlzfXNldEV4dHJhKHQsbil7cmV0dXJuIHRoaXMudj17Li4udGhpcy52LFt0XTpufSx0aGlzLlIoKSx0aGlzfXNldEZpbmdlcnByaW50KHQpe3JldHVybiB0aGlzLms9dCx0aGlzLlIoKSx0aGlzfXNldExldmVsKHQpe3JldHVybiB0aGlzLmo9dCx0aGlzLlIoKSx0aGlzfXNldFRyYW5zYWN0aW9uTmFtZSh0KXtyZXR1cm4gdGhpcy5BPXQsdGhpcy5SKCksdGhpc31zZXRDb250ZXh0KHQsbil7cmV0dXJuIG51bGw9PT1uP2RlbGV0ZSB0aGlzLl9bdF06dGhpcy5fW3RdPW4sdGhpcy5SKCksdGhpc31zZXRTZXNzaW9uKHQpe3JldHVybiB0P3RoaXMuQz10OmRlbGV0ZSB0aGlzLkMsdGhpcy5SKCksdGhpc31nZXRTZXNzaW9uKCl7cmV0dXJuIHRoaXMuQ311cGRhdGUodCl7aWYoIXQpcmV0dXJuIHRoaXM7Y29uc3Qgbj0iZnVuY3Rpb24iPT10eXBlb2YgdD90KHRoaXMpOnQsZT1uIGluc3RhbmNlb2YgbnQ/bi5nZXRTY29wZURhdGEoKTpBKG4pP3Q6dm9pZCAwLHt0YWdzOnIsYXR0cmlidXRlczpvLGV4dHJhOmksdXNlcjpzLGNvbnRleHRzOmMsbGV2ZWw6dSxmaW5nZXJwcmludDphPVtdLHByb3BhZ2F0aW9uQ29udGV4dDpmLGNvbnZlcnNhdGlvbklkOmh9PWV8fHt9O3JldHVybiB0aGlzLmw9ey4uLnRoaXMubCwuLi5yfSx0aGlzLm09ey4uLnRoaXMubSwuLi5vfSx0aGlzLnY9ey4uLnRoaXMudiwuLi5pfSx0aGlzLl89ey4uLnRoaXMuXywuLi5jfSxzJiZPYmplY3Qua2V5cyhzKS5sZW5ndGgmJih0aGlzLnA9cyksdSYmKHRoaXMuaj11KSxhLmxlbmd0aCYmKHRoaXMuaz1hKSxmJiYodGhpcy5OPWYpLGgmJih0aGlzLk89aCksdGhpc31jbGVhcigpe3JldHVybiB0aGlzLnU9W10sdGhpcy5sPXt9LHRoaXMubT17fSx0aGlzLnY9e30sdGhpcy5wPXt9LHRoaXMuXz17fSx0aGlzLmo9dm9pZCAwLHRoaXMuQT12b2lkIDAsdGhpcy5rPXZvaWQgMCx0aGlzLkM9dm9pZCAwLHRoaXMuTz12b2lkIDAsWCh0aGlzLHZvaWQgMCksdGhpcy5oPVtdLHRoaXMuc2V0UHJvcGFnYXRpb25Db250ZXh0KHt0cmFjZUlkOlooKSxzYW1wbGVSYW5kOkIoKX0pLHRoaXMuUigpLHRoaXN9YWRkQnJlYWRjcnVtYih0LG4pe2NvbnN0IGU9Im51bWJlciI9PXR5cGVvZiBuP246MTAwO2lmKGU8PTApcmV0dXJuIHRoaXM7Y29uc3Qgcj17dGltZXN0YW1wOkcoKSwuLi50LG1lc3NhZ2U6dC5tZXNzYWdlP1codC5tZXNzYWdlLDIwNDgpOnQubWVzc2FnZX07cmV0dXJuIHRoaXMudS5wdXNoKHIpLHRoaXMudS5sZW5ndGg+ZSYmKHRoaXMudT10aGlzLnUuc2xpY2UoLWUpLHRoaXMuVD8ucmVjb3JkRHJvcHBlZEV2ZW50KCJidWZmZXJfb3ZlcmZsb3ciLCJsb2dfaXRlbSIpKSx0aGlzLlIoKSx0aGlzfWdldExhc3RCcmVhZGNydW1iKCl7cmV0dXJuIHRoaXMudVt0aGlzLnUubGVuZ3RoLTFdfWNsZWFyQnJlYWRjcnVtYnMoKXtyZXR1cm4gdGhpcy51PVtdLHRoaXMuUigpLHRoaXN9YWRkQXR0YWNobWVudCh0KXtyZXR1cm4gdGhpcy5oLnB1c2godCksdGhpc31jbGVhckF0dGFjaG1lbnRzKCl7cmV0dXJuIHRoaXMuaD1bXSx0aGlzfWdldFNjb3BlRGF0YSgpe3JldHVybnticmVhZGNydW1iczp0aGlzLnUsYXR0YWNobWVudHM6dGhpcy5oLGNvbnRleHRzOnRoaXMuXyx0YWdzOnRoaXMubCxhdHRyaWJ1dGVzOnRoaXMubSxleHRyYTp0aGlzLnYsdXNlcjp0aGlzLnAsbGV2ZWw6dGhpcy5qLGZpbmdlcnByaW50OnRoaXMua3x8W10sZXZlbnRQcm9jZXNzb3JzOnRoaXMuaSxwcm9wYWdhdGlvbkNvbnRleHQ6dGhpcy5OLHNka1Byb2Nlc3NpbmdNZXRhZGF0YTp0aGlzLlMsdHJhbnNhY3Rpb25OYW1lOnRoaXMuQSxzcGFuOnR0KHRoaXMpLGNvbnZlcnNhdGlvbklkOnRoaXMuT319c2V0U0RLUHJvY2Vzc2luZ01ldGFkYXRhKHQpe3JldHVybiB0aGlzLlM9Syh0aGlzLlMsdCwyKSx0aGlzfXNldFByb3BhZ2F0aW9uQ29udGV4dCh0KXtyZXR1cm4gdGhpcy5OPXQsdGhpc31nZXRQcm9wYWdhdGlvbkNvbnRleHQoKXtyZXR1cm4gdGhpcy5OfWNhcHR1cmVFeGNlcHRpb24odCxuKXtjb25zdCBlPW4/LmV2ZW50X2lkfHxGKCk7aWYoIXRoaXMuVClyZXR1cm4gaCYmUy53YXJuKCJObyBjbGllbnQgY29uZmlndXJlZCBvbiBzY29wZSAtIHdpbGwgbm90IGNhcHR1cmUgZXhjZXB0aW9uISIpLGU7Y29uc3Qgcj1uZXcgRXJyb3IoIlNlbnRyeSBzeW50aGV0aWNFeGNlcHRpb24iKTtyZXR1cm4gdGhpcy5ULmNhcHR1cmVFeGNlcHRpb24odCx7b3JpZ2luYWxFeGNlcHRpb246dCxzeW50aGV0aWNFeGNlcHRpb246ciwuLi5uLGV2ZW50X2lkOmV9LHRoaXMpLGV9Y2FwdHVyZU1lc3NhZ2UodCxuLGUpe2NvbnN0IHI9ZT8uZXZlbnRfaWR8fEYoKTtpZighdGhpcy5UKXJldHVybiBoJiZTLndhcm4oIk5vIGNsaWVudCBjb25maWd1cmVkIG9uIHNjb3BlIC0gd2lsbCBub3QgY2FwdHVyZSBtZXNzYWdlISIpLHI7Y29uc3Qgbz1lPy5zeW50aGV0aWNFeGNlcHRpb24/P25ldyBFcnJvcih0KTtyZXR1cm4gdGhpcy5ULmNhcHR1cmVNZXNzYWdlKHQsbix7b3JpZ2luYWxFeGNlcHRpb246dCxzeW50aGV0aWNFeGNlcHRpb246bywuLi5lLGV2ZW50X2lkOnJ9LHRoaXMpLHJ9Y2FwdHVyZUV2ZW50KHQsbil7Y29uc3QgZT10LmV2ZW50X2lkfHxuPy5ldmVudF9pZHx8RigpO3JldHVybiB0aGlzLlQ/KHRoaXMuVC5jYXB0dXJlRXZlbnQodCx7Li4ubixldmVudF9pZDplfSx0aGlzKSxlKTooaCYmUy53YXJuKCJObyBjbGllbnQgY29uZmlndXJlZCBvbiBzY29wZSAtIHdpbGwgbm90IGNhcHR1cmUgZXZlbnQhIiksZSl9Uigpe3RoaXMudHx8KHRoaXMudD0hMCx0aGlzLm8uZm9yRWFjaCh0PT57dCh0aGlzKX0pLHRoaXMudD0hMSl9fWNvbnN0IGV0PXQ9PnQgaW5zdGFuY2VvZiBQcm9taXNlJiYhdFtydF0scnQ9U3ltYm9sKCJjaGFpbmVkIFByb21pc2VMaWtlIiksb3Q9KHQsbik9PntpZighbilyZXR1cm4gdDtsZXQgZT0hMTtmb3IoY29uc3QgciBpbiB0KXtpZihyIGluIG4pY29udGludWU7ZT0hMDtjb25zdCBvPXRbcl07ImZ1bmN0aW9uIj09dHlwZW9mIG8/T2JqZWN0LmRlZmluZVByb3BlcnR5KG4scix7dmFsdWU6KC4uLm4pPT5vLmFwcGx5KHQsbiksZW51bWVyYWJsZTohMCxjb25maWd1cmFibGU6ITAsd3JpdGFibGU6ITB9KTpuW3JdPW99cmV0dXJuIGUmJk9iamVjdC5hc3NpZ24obix7W3J0XTohMH0pLG59O2NsYXNzIGl0e2NvbnN0cnVjdG9yKHQsbil7bGV0IGUscjtlPXR8fG5ldyBudCxyPW58fG5ldyBudCx0aGlzLlA9W3tzY29wZTplfV0sdGhpcy5EPXJ9d2l0aFNjb3BlKHQpe2NvbnN0IG49dGhpcy5VKCk7bGV0IGU7dHJ5e2U9dChuKX1jYXRjaCh0KXt0aHJvdyB0aGlzLkwoKSx0fXJldHVybiBrKGUpPygodCxuLGUpPT57Y29uc3Qgcj10LnRoZW4odD0+KG4odCksdCksdD0+e3Rocm93IGUodCksdH0pO3JldHVybiBldChyKSYmZXQodCk/cjpvdCh0LHIpfSkoZSwoKT0+dGhpcy5MKCksKCk9PnRoaXMuTCgpKToodGhpcy5MKCksZSl9Z2V0Q2xpZW50KCl7cmV0dXJuIHRoaXMuZ2V0U3RhY2tUb3AoKS5jbGllbnR9Z2V0U2NvcGUoKXtyZXR1cm4gdGhpcy5nZXRTdGFja1RvcCgpLnNjb3BlfWdldElzb2xhdGlvblNjb3BlKCl7cmV0dXJuIHRoaXMuRH1nZXRTdGFja1RvcCgpe3JldHVybiB0aGlzLlBbdGhpcy5QLmxlbmd0aC0xXX1VKCl7Y29uc3QgdD10aGlzLmdldFNjb3BlKCkuY2xvbmUoKTtyZXR1cm4gdGhpcy5QLnB1c2goe2NsaWVudDp0aGlzLmdldENsaWVudCgpLHNjb3BlOnR9KSx0fUwoKXtyZXR1cm4hKHRoaXMuUC5sZW5ndGg8PTEpJiYhIXRoaXMuUC5wb3AoKX19ZnVuY3Rpb24gc3QoKXtjb25zdCB0PW0oZCgpKTtyZXR1cm4gdC5zdGFjaz10LnN0YWNrfHxuZXcgaXQoZygiZGVmYXVsdEN1cnJlbnRTY29wZSIsKCk9Pm5ldyBudCksZygiZGVmYXVsdElzb2xhdGlvblNjb3BlIiwoKT0+bmV3IG50KSl9ZnVuY3Rpb24gY3QodCl7cmV0dXJuIHN0KCkud2l0aFNjb3BlKHQpfWZ1bmN0aW9uIHV0KHQsbil7Y29uc3QgZT1zdCgpO3JldHVybiBlLndpdGhTY29wZSgoKT0+KGUuZ2V0U3RhY2tUb3AoKS5zY29wZT10LG4odCkpKX1mdW5jdGlvbiBhdCh0KXtyZXR1cm4gc3QoKS53aXRoU2NvcGUoKCk9PnQoc3QoKS5nZXRJc29sYXRpb25TY29wZSgpKSl9ZnVuY3Rpb24gZnQodCl7Y29uc3Qgbj1tKHQpO3JldHVybiBuLmFjcz9uLmFjczp7d2l0aElzb2xhdGlvblNjb3BlOmF0LHdpdGhTY29wZTpjdCx3aXRoU2V0U2NvcGU6dXQsd2l0aFNldElzb2xhdGlvblNjb3BlOih0LG4pPT5hdChuKSxnZXRDdXJyZW50U2NvcGU6KCk9PnN0KCkuZ2V0U2NvcGUoKSxnZXRJc29sYXRpb25TY29wZTooKT0+c3QoKS5nZXRJc29sYXRpb25TY29wZSgpfX1mdW5jdGlvbiBodCgpe3JldHVybiBmdChkKCkpLmdldEN1cnJlbnRTY29wZSgpLmdldENsaWVudCgpfWZ1bmN0aW9uIHB0KHQpe2lmKHQpe2lmKCJvYmplY3QiPT10eXBlb2YgdCYmImRlcmVmImluIHQmJiJmdW5jdGlvbiI9PXR5cGVvZiB0LmRlcmVmKXRyeXtyZXR1cm4gdC5kZXJlZigpfWNhdGNoe3JldHVybn1yZXR1cm4gdH19ZnVuY3Rpb24gbHQodCl7Y29uc3Qgbj10O3JldHVybntzY29wZTpuLl9zZW50cnlTY29wZSxpc29sYXRpb25TY29wZTpwdChuLl9zZW50cnlJc29sYXRpb25TY29wZSl9fWNvbnN0IGR0PSJzZW50cnktIjtmdW5jdGlvbiBtdCh0KXtjb25zdCBuPWZ1bmN0aW9uKHQpe2lmKCF0fHwhQyh0KSYmIUFycmF5LmlzQXJyYXkodCkpcmV0dXJuO2lmKEFycmF5LmlzQXJyYXkodCkpcmV0dXJuIHQucmVkdWNlKCh0LG4pPT57Y29uc3QgZT1ndChuKTtyZXR1cm4gT2JqZWN0LmVudHJpZXMoZSkuZm9yRWFjaCgoW24sZV0pPT57dFtuXT1lfSksdH0se30pO3JldHVybiBndCh0KX0odCk7aWYoIW4pcmV0dXJuO2NvbnN0IGU9T2JqZWN0LmVudHJpZXMobikucmVkdWNlKCh0LFtuLGVdKT0+e2lmKG4uc3RhcnRzV2l0aChkdCkpe3Rbbi5zbGljZSg3KV09ZX1yZXR1cm4gdH0se30pO3JldHVybiBPYmplY3Qua2V5cyhlKS5sZW5ndGg+MD9lOnZvaWQgMH1mdW5jdGlvbiBndCh0KXtyZXR1cm4gdC5zcGxpdCgiLCIpLm1hcCh0PT57Y29uc3Qgbj10LmluZGV4T2YoIj0iKTtpZigtMT09PW4pcmV0dXJuW107cmV0dXJuW3Quc2xpY2UoMCxuKSx0LnNsaWNlKG4rMSldLm1hcCh0PT57dHJ5e3JldHVybiBkZWNvZGVVUklDb21wb25lbnQodC50cmltKCkpfWNhdGNoe3JldHVybn19KX0pLnJlZHVjZSgodCxbbixlXSk9PihuJiZlJiYodFtuXT1lKSx0KSx7fSl9Y29uc3QgeXQ9L15vKFxkKylcLi87ZnVuY3Rpb24gYnQodCxuPSExKXtjb25zdHtob3N0OmUscGF0aDpyLHBhc3M6byxwb3J0OmkscHJvamVjdElkOnMscHJvdG9jb2w6YyxwdWJsaWNLZXk6dX09dDtyZXR1cm5gJHtjfTovLyR7dX0ke24mJm8/YDoke299YDoiIn1AJHtlfSR7aT9gOiR7aX1gOiIifS8ke3I/YCR7cn0vYDpyfSR7c31gfWZ1bmN0aW9uIHZ0KHQpe2NvbnN0IG49dC5nZXRPcHRpb25zKCkse2hvc3Q6ZX09dC5nZXREc24oKXx8e307bGV0IHI7cmV0dXJuIG4ub3JnSWQ/cj1TdHJpbmcobi5vcmdJZCk6ZSYmKHI9ZnVuY3Rpb24odCl7Y29uc3Qgbj10Lm1hdGNoKHl0KTtyZXR1cm4gbj8uWzFdfShlKSkscn1mdW5jdGlvbiBfdCh0KXtjb25zdHtzcGFuSWQ6bix0cmFjZUlkOmUsaXNSZW1vdGU6cn09dC5zcGFuQ29udGV4dCgpLG89cj9uOkV0KHQpLnBhcmVudF9zcGFuX2lkLGk9bHQodCkuc2NvcGU7cmV0dXJue3BhcmVudF9zcGFuX2lkOm8sc3Bhbl9pZDpyP2k/LmdldFByb3BhZ2F0aW9uQ29udGV4dCgpLnByb3BhZ2F0aW9uU3BhbklkfHxxKCk6bix0cmFjZV9pZDplfX1mdW5jdGlvbiB3dCh0KXtyZXR1cm4gdCYmdC5sZW5ndGg+MD90Lm1hcCgoe2NvbnRleHQ6e3NwYW5JZDp0LHRyYWNlSWQ6bix0cmFjZUZsYWdzOmUsLi4ucn0sYXR0cmlidXRlczpvfSk9Pih7c3Bhbl9pZDp0LHRyYWNlX2lkOm4sc2FtcGxlZDoxPT09ZSxhdHRyaWJ1dGVzOm8sLi4ucn0pKTp2b2lkIDB9ZnVuY3Rpb24gU3QodCl7cmV0dXJuIm51bWJlciI9PXR5cGVvZiB0PyR0KHQpOkFycmF5LmlzQXJyYXkodCk/dFswXSt0WzFdLzFlOTp0IGluc3RhbmNlb2YgRGF0ZT8kdCh0LmdldFRpbWUoKSk6SigpfWZ1bmN0aW9uICR0KHQpe3JldHVybiB0Pjk5OTk5OTk5OTk/dC8xZTM6dH1mdW5jdGlvbiBFdCh0KXtpZihmdW5jdGlvbih0KXtyZXR1cm4iZnVuY3Rpb24iPT10eXBlb2YgdC5nZXRTcGFuSlNPTn0odCkpcmV0dXJuIHQuZ2V0U3BhbkpTT04oKTtjb25zdHtzcGFuSWQ6bix0cmFjZUlkOmV9PXQuc3BhbkNvbnRleHQoKTtpZihmdW5jdGlvbih0KXtjb25zdCBuPXQ7cmV0dXJuISEobi5hdHRyaWJ1dGVzJiZuLnN0YXJ0VGltZSYmbi5uYW1lJiZuLmVuZFRpbWUmJm4uc3RhdHVzKX0odCkpe2NvbnN0e2F0dHJpYnV0ZXM6cixzdGFydFRpbWU6byxuYW1lOmksZW5kVGltZTpzLHN0YXR1czpjLGxpbmtzOnV9PXQ7cmV0dXJue3NwYW5faWQ6bix0cmFjZV9pZDplLGRhdGE6cixkZXNjcmlwdGlvbjppLHBhcmVudF9zcGFuX2lkOnh0KHQpLHN0YXJ0X3RpbWVzdGFtcDpTdChvKSx0aW1lc3RhbXA6U3Qocyl8fHZvaWQgMCxzdGF0dXM6TnQoYyksb3A6clsic2VudHJ5Lm9wIl0sb3JpZ2luOnJbInNlbnRyeS5vcmlnaW4iXSxsaW5rczp3dCh1KX19cmV0dXJue3NwYW5faWQ6bix0cmFjZV9pZDplLHN0YXJ0X3RpbWVzdGFtcDowLGRhdGE6e319fWZ1bmN0aW9uIHh0KHQpe3JldHVybiJwYXJlbnRTcGFuSWQiaW4gdD90LnBhcmVudFNwYW5JZDoicGFyZW50U3BhbkNvbnRleHQiaW4gdD90LnBhcmVudFNwYW5Db250ZXh0Py5zcGFuSWQ6dm9pZCAwfWZ1bmN0aW9uIE50KHQpe2lmKHQmJjAhPT10LmNvZGUpcmV0dXJuIDE9PT10LmNvZGU/Im9rIjp0Lm1lc3NhZ2V8fCJpbnRlcm5hbF9lcnJvciJ9Y29uc3QganQ9ZnVuY3Rpb24odCl7cmV0dXJuIHQuX3NlbnRyeVJvb3RTcGFufHx0fTtmdW5jdGlvbiBDdCh0KXtjb25zdCBuPWh0KCk7aWYoIW4pcmV0dXJue307Y29uc3QgZT1qdCh0KSxyPUV0KGUpLG89ci5kYXRhLGk9ZS5zcGFuQ29udGV4dCgpLnRyYWNlU3RhdGUscz1pPy5nZXQoInNlbnRyeS5zYW1wbGVfcmF0ZSIpPz9vWyJzZW50cnkuc2FtcGxlX3JhdGUiXT8/b1sic2VudHJ5LnByZXZpb3VzX3RyYWNlX3NhbXBsZV9yYXRlIl07ZnVuY3Rpb24gYyh0KXtyZXR1cm4ibnVtYmVyIiE9dHlwZW9mIHMmJiJzdHJpbmciIT10eXBlb2Ygc3x8KHQuc2FtcGxlX3JhdGU9YCR7c31gKSx0fWNvbnN0IHU9ZS5fZnJvemVuRHNjO2lmKHUpcmV0dXJuIGModSk7Y29uc3QgYT1pPy5nZXQoInNlbnRyeS5kc2MiKSxmPWEmJm10KGEpO2lmKGYpcmV0dXJuIGMoZik7Y29uc3QgaD1mdW5jdGlvbih0LG4pe2NvbnN0IGU9bi5nZXRPcHRpb25zKCkse3B1YmxpY0tleTpyfT1uLmdldERzbigpfHx7fSxvPXtlbnZpcm9ubWVudDplLmVudmlyb25tZW50fHwicHJvZHVjdGlvbiIscmVsZWFzZTplLnJlbGVhc2UscHVibGljX2tleTpyLHRyYWNlX2lkOnQsb3JnX2lkOnZ0KG4pfTtyZXR1cm4gbi5lbWl0KCJjcmVhdGVEc2MiLG8pLG99KHQuc3BhbkNvbnRleHQoKS50cmFjZUlkLG4pLHA9b1sic2VudHJ5LnNvdXJjZSJdPz9vWyJzZW50cnkuc3Bhbi5zb3VyY2UiXSxsPXIuZGVzY3JpcHRpb247cmV0dXJuInVybCIhPT1wJiZsJiYoaC50cmFuc2FjdGlvbj1sKSxmdW5jdGlvbigpe2lmKCJib29sZWFuIj09dHlwZW9mIF9fU0VOVFJZX1RSQUNJTkdfXyYmIV9fU0VOVFJZX1RSQUNJTkdfXylyZXR1cm4hMTtjb25zdCB0PWh0KCk/LmdldE9wdGlvbnMoKTtyZXR1cm4hKCF0fHxudWxsPT10LnRyYWNlc1NhbXBsZVJhdGUmJiF0LnRyYWNlc1NhbXBsZXIpfSgpJiYoaC5zYW1wbGVkPVN0cmluZyhmdW5jdGlvbih0KXtjb25zdHt0cmFjZUZsYWdzOm59PXQuc3BhbkNvbnRleHQoKTtyZXR1cm4gMT09PW59KGUpKSxoLnNhbXBsZV9yYW5kPWk/LmdldCgic2VudHJ5LnNhbXBsZV9yYW5kIik/P2x0KGUpLnNjb3BlPy5nZXRQcm9wYWdhdGlvbkNvbnRleHQoKS5zYW1wbGVSYW5kLnRvU3RyaW5nKCkpLGMoaCksbi5lbWl0KCJjcmVhdGVEc2MiLGgsZSksaH1jb25zdCBBdD1TeW1ib2wuZm9yKCJzZW50cnkuc2tpcE5vcm1hbGl6YXRpb24iKSxrdD1TeW1ib2wuZm9yKCJzZW50cnkub3ZlcnJpZGVOb3JtYWxpemF0aW9uRGVwdGgiKTtmdW5jdGlvbiBUdCh0LG49MTAwLGU9MS8wKXt0cnl7cmV0dXJuIEl0KCIiLHQsbixlKX1jYXRjaCh0KXtyZXR1cm57RVJST1I6YCoqbm9uLXNlcmlhbGl6YWJsZSoqICgke3R9KWB9fX1mdW5jdGlvbiBJdCh0LG4sZT0xLzAscj0xLzAsbz1mdW5jdGlvbigpe2NvbnN0IHQ9bmV3IFdlYWtTZXQ7ZnVuY3Rpb24gbihuKXtyZXR1cm4hIXQuaGFzKG4pfHwodC5hZGQobiksITEpfWZ1bmN0aW9uIGUobil7dC5kZWxldGUobil9cmV0dXJuW24sZV19KCkpe2NvbnN0W2ksc109bztpZihudWxsPT1ufHxbImJvb2xlYW4iLCJzdHJpbmciXS5pbmNsdWRlcyh0eXBlb2Ygbil8fCJudW1iZXIiPT10eXBlb2YgbiYmTnVtYmVyLmlzRmluaXRlKG4pKXJldHVybiBuO2NvbnN0IGM9ZnVuY3Rpb24odCxuKXt0cnl7aWYoImRvbWFpbiI9PT10JiZuJiYib2JqZWN0Ij09dHlwZW9mIG4mJm4uTSlyZXR1cm4iW0RvbWFpbl0iO2lmKCJkb21haW5FbWl0dGVyIj09PXQpcmV0dXJuIltEb21haW5FbWl0dGVyXSI7aWYoInVuZGVmaW5lZCIhPXR5cGVvZiBnbG9iYWwmJm49PT1nbG9iYWwpcmV0dXJuIltHbG9iYWxdIjtpZigidW5kZWZpbmVkIiE9dHlwZW9mIHdpbmRvdyYmbj09PXdpbmRvdylyZXR1cm4iW1dpbmRvd10iO2lmKCJ1bmRlZmluZWQiIT10eXBlb2YgZG9jdW1lbnQmJm49PT1kb2N1bWVudClyZXR1cm4iW0RvY3VtZW50XSI7aWYoIm9iamVjdCI9PXR5cGVvZihlPW4pJiZudWxsIT09ZSYmKGUuX19pc1Z1ZXx8ZS5CfHxlLl9fdl9pc1ZOb2RlKSlyZXR1cm4gZnVuY3Rpb24odCl7cmV0dXJuIl9fdl9pc1ZOb2RlImluIHQmJnQuX192X2lzVk5vZGU/IltWdWVWTm9kZV0iOiJbVnVlVmlld01vZGVsXSJ9KG4pO2lmKGZ1bmN0aW9uKHQpe3JldHVybiBBKHQpJiYibmF0aXZlRXZlbnQiaW4gdCYmInByZXZlbnREZWZhdWx0ImluIHQmJiJzdG9wUHJvcGFnYXRpb24iaW4gdH0obikpcmV0dXJuIltTeW50aGV0aWNFdmVudF0iO2lmKCJudW1iZXIiPT10eXBlb2YgbiYmIU51bWJlci5pc0Zpbml0ZShuKSlyZXR1cm5gWyR7bn1dYDtpZigiZnVuY3Rpb24iPT10eXBlb2YgbilyZXR1cm5gW0Z1bmN0aW9uOiAke2Z1bmN0aW9uKHQpe3RyeXtyZXR1cm4gdCYmImZ1bmN0aW9uIj09dHlwZW9mIHQmJnQubmFtZXx8eH1jYXRjaHtyZXR1cm4geH19KG4pfV1gO2lmKCJzeW1ib2wiPT10eXBlb2YgbilyZXR1cm5gWyR7U3RyaW5nKG4pfV1gO2lmKCJiaWdpbnQiPT10eXBlb2YgbilyZXR1cm5gW0JpZ0ludDogJHtTdHJpbmcobil9XWA7Y29uc3Qgcj1mdW5jdGlvbih0KXtjb25zdCBuPU9iamVjdC5nZXRQcm90b3R5cGVPZih0KTtyZXR1cm4gbj8uY29uc3RydWN0b3I/bi5jb25zdHJ1Y3Rvci5uYW1lOiJudWxsIHByb3RvdHlwZSJ9KG4pO3JldHVybi9eSFRNTChcdyopRWxlbWVudCQvLnRlc3Qocik/YFtIVE1MRWxlbWVudDogJHtyfV1gOmBbb2JqZWN0ICR7cn1dYH1jYXRjaCh0KXtyZXR1cm5gKipub24tc2VyaWFsaXphYmxlKiogKCR7dH0pYH12YXIgZX0odCxuKTtpZighYy5zdGFydHNXaXRoKCJbb2JqZWN0ICIpKXJldHVybiBjO2lmKGZ1bmN0aW9uKHQpe3JldHVybiBCb29sZWFuKHRbQXRdKX0obikpcmV0dXJuIG47Y29uc3QgdT1mdW5jdGlvbih0KXtjb25zdCBuPXRba3RdO3JldHVybiJudW1iZXIiPT10eXBlb2Ygbj9uOnZvaWQgMH0obiksYT12b2lkIDAhPT11P3U6ZTtpZigwPT09YSlyZXR1cm4gYy5yZXBsYWNlKCJvYmplY3QgIiwiIik7aWYoaShuKSlyZXR1cm4iW0NpcmN1bGFyIH5dIjtjb25zdCBmPW47aWYoZiYmImZ1bmN0aW9uIj09dHlwZW9mIGYudG9KU09OKXRyeXtyZXR1cm4gSXQoIiIsZi50b0pTT04oKSxhLTEscixvKX1jYXRjaHt9Y29uc3QgaD1BcnJheS5pc0FycmF5KG4pP1tdOnt9O2xldCBwPTA7Y29uc3QgbD1SKG4pO2Zvcihjb25zdCB0IGluIGwpe2lmKCFPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwobCx0KSljb250aW51ZTtpZihwPj1yKXtoW3RdPSJbTWF4UHJvcGVydGllcyB+XSI7YnJlYWt9Y29uc3Qgbj1sW3RdO2hbdF09SXQodCxuLGEtMSxyLG8pLHArK31yZXR1cm4gcyhuKSxofWZ1bmN0aW9uIE90KHQsbil7Y29uc3QgZT1uLnJlcGxhY2UoL1xcL2csIi8iKS5yZXBsYWNlKC9bfFxce30oKVtcXV4kKyo/Ll0vZywiXFwkJiIpO2xldCByPXQ7dHJ5e3I9ZGVjb2RlVVJJKHQpfWNhdGNoe31yZXR1cm4gci5yZXBsYWNlKC9cXC9nLCIvIikucmVwbGFjZSgvd2VicGFjazpcLz8vZywiIikucmVwbGFjZShuZXcgUmVnRXhwKGAoZmlsZTovLyk/Lyoke2V9LypgLCJpZyIpLCJhcHA6Ly8vIil9ZnVuY3Rpb24gUnQodCxuPVtdKXtyZXR1cm5bdCxuXX1mdW5jdGlvbiBQdCh0LG4pe2NvbnN0IGU9dFsxXTtmb3IoY29uc3QgdCBvZiBlKXtpZihuKHQsdFswXS50eXBlKSlyZXR1cm4hMH1yZXR1cm4hMX1mdW5jdGlvbiBEdCh0KXtjb25zdCBuPW0ocCk7cmV0dXJuIG4uZW5jb2RlUG9seWZpbGw/bi5lbmNvZGVQb2x5ZmlsbCh0KToobmV3IFRleHRFbmNvZGVyKS5lbmNvZGUodCl9ZnVuY3Rpb24gVXQodCl7Y29uc3RbbixlXT10O2xldCByPUpTT04uc3RyaW5naWZ5KG4pO2Z1bmN0aW9uIG8odCl7InN0cmluZyI9PXR5cGVvZiByP3I9InN0cmluZyI9PXR5cGVvZiB0P3IrdDpbRHQociksdF06ci5wdXNoKCJzdHJpbmciPT10eXBlb2YgdD9EdCh0KTp0KX1mb3IoY29uc3QgdCBvZiBlKXtjb25zdFtuLGVdPXQ7aWYobyhgXG4ke0pTT04uc3RyaW5naWZ5KG4pfVxuYCksInN0cmluZyI9PXR5cGVvZiBlfHxlIGluc3RhbmNlb2YgVWludDhBcnJheSlvKGUpO2Vsc2V7bGV0IHQ7dHJ5e3Q9SlNPTi5zdHJpbmdpZnkoZSl9Y2F0Y2h7dD1KU09OLnN0cmluZ2lmeShUdChlKSl9byh0KX19cmV0dXJuInN0cmluZyI9PXR5cGVvZiByP3I6ZnVuY3Rpb24odCl7Y29uc3Qgbj10LnJlZHVjZSgodCxuKT0+dCtuLmxlbmd0aCwwKSxlPW5ldyBVaW50OEFycmF5KG4pO2xldCByPTA7Zm9yKGNvbnN0IG4gb2YgdCllLnNldChuLHIpLHIrPW4ubGVuZ3RoO3JldHVybiBlfShyKX1jb25zdCBMdD17c2Vzc2lvbnM6InNlc3Npb24iLGV2ZW50OiJlcnJvciIsY2xpZW50X3JlcG9ydDoiaW50ZXJuYWwiLHVzZXJfcmVwb3J0OiJkZWZhdWx0Iixwcm9maWxlX2NodW5rOiJwcm9maWxlIixyZXBsYXlfZXZlbnQ6InJlcGxheSIscmVwbGF5X3JlY29yZGluZzoicmVwbGF5IixjaGVja19pbjoibW9uaXRvciIscmF3X3NlY3VyaXR5OiJzZWN1cml0eSIsbG9nOiJsb2dfaXRlbSIsdHJhY2VfbWV0cmljOiJtZXRyaWMifTtmdW5jdGlvbiBNdCh0KXtyZXR1cm4gZnVuY3Rpb24odCl7cmV0dXJuIHQgaW4gTHR9KHQpP0x0W3RdOnR9ZnVuY3Rpb24gQnQodCl7aWYoIXQ/LnNkaylyZXR1cm47Y29uc3R7bmFtZTpuLHZlcnNpb246ZX09dC5zZGs7cmV0dXJue25hbWU6bix2ZXJzaW9uOmV9fWZ1bmN0aW9uIHp0KHQsbixlLHIpe2NvbnN0IG89QnQoZSksaT10LnR5cGUmJiJyZXBsYXlfZXZlbnQiIT09dC50eXBlP3QudHlwZToiZXZlbnQiOyFmdW5jdGlvbih0LG4pe2lmKCFuKXJldHVybiB0O2NvbnN0IGU9dC5zZGt8fHt9O3Quc2RrPXsuLi5lLG5hbWU6ZS5uYW1lfHxuLm5hbWUsdmVyc2lvbjplLnZlcnNpb258fG4udmVyc2lvbixpbnRlZ3JhdGlvbnM6Wy4uLnQuc2RrPy5pbnRlZ3JhdGlvbnN8fFtdLC4uLm4uaW50ZWdyYXRpb25zfHxbXV0scGFja2FnZXM6Wy4uLnQuc2RrPy5wYWNrYWdlc3x8W10sLi4ubi5wYWNrYWdlc3x8W11dLHNldHRpbmdzOnQuc2RrPy5zZXR0aW5nc3x8bi5zZXR0aW5ncz97Li4udC5zZGs/LnNldHRpbmdzLC4uLm4uc2V0dGluZ3N9OnZvaWQgMH19KHQsZT8uc2RrKTtjb25zdCBzPWZ1bmN0aW9uKHQsbixlLHIpe2NvbnN0IG89dC5zZGtQcm9jZXNzaW5nTWV0YWRhdGE/LmR5bmFtaWNTYW1wbGluZ0NvbnRleHQ7cmV0dXJue2V2ZW50X2lkOnQuZXZlbnRfaWQsc2VudF9hdDoobmV3IERhdGUpLnRvSVNPU3RyaW5nKCksLi4ubiYme3NkazpufSwuLi4hIWUmJnImJntkc246YnQocil9LC4uLm8mJnt0cmFjZTpvfX19KHQsbyxyLG4pO2RlbGV0ZSB0LnNka1Byb2Nlc3NpbmdNZXRhZGF0YTtyZXR1cm4gUnQocyxbW3t0eXBlOml9LHRdXSl9Y29uc3QgV3Q9Il9fU0VOVFJZX1NVUFBSRVNTX1RSQUNJTkdfXyI7ZnVuY3Rpb24gRnQodCl7Y29uc3Qgbj1mdChkKCkpO3JldHVybiBuLnN1cHByZXNzVHJhY2luZz9uLnN1cHByZXNzVHJhY2luZyh0KTpmdW5jdGlvbiguLi50KXtjb25zdCBuPWZ0KGQoKSk7aWYoMj09PXQubGVuZ3RoKXtjb25zdFtlLHJdPXQ7cmV0dXJuIGU/bi53aXRoU2V0U2NvcGUoZSxyKTpuLndpdGhTY29wZShyKX1yZXR1cm4gbi53aXRoU2NvcGUodFswXSl9KG49PntuLnNldFNES1Byb2Nlc3NpbmdNZXRhZGF0YSh7W1d0XTohMH0pO2NvbnN0IGU9dCgpO3JldHVybiBuLnNldFNES1Byb2Nlc3NpbmdNZXRhZGF0YSh7W1d0XTp2b2lkIDB9KSxlfSl9ZnVuY3Rpb24gR3QodCxuKXtjb25zdHtmaW5nZXJwcmludDplLHNwYW46cixicmVhZGNydW1iczpvLHNka1Byb2Nlc3NpbmdNZXRhZGF0YTppfT1uOyFmdW5jdGlvbih0LG4pe2NvbnN0e2V4dHJhOmUsdGFnczpyLHVzZXI6byxjb250ZXh0czppLGxldmVsOnMsdHJhbnNhY3Rpb25OYW1lOmN9PW47T2JqZWN0LmtleXMoZSkubGVuZ3RoJiYodC5leHRyYT17Li4uZSwuLi50LmV4dHJhfSk7T2JqZWN0LmtleXMocikubGVuZ3RoJiYodC50YWdzPXsuLi5yLC4uLnQudGFnc30pO09iamVjdC5rZXlzKG8pLmxlbmd0aCYmKHQudXNlcj17Li4ubywuLi50LnVzZXJ9KTtPYmplY3Qua2V5cyhpKS5sZW5ndGgmJih0LmNvbnRleHRzPXsuLi5pLC4uLnQuY29udGV4dHN9KTtzJiYodC5sZXZlbD1zKTtjJiYidHJhbnNhY3Rpb24iIT09dC50eXBlJiYodC50cmFuc2FjdGlvbj1jKX0odCxuKSxyJiZmdW5jdGlvbih0LG4pe3QuY29udGV4dHM9e3RyYWNlOl90KG4pLC4uLnQuY29udGV4dHN9LHQuc2RrUHJvY2Vzc2luZ01ldGFkYXRhPXtkeW5hbWljU2FtcGxpbmdDb250ZXh0OkN0KG4pLC4uLnQuc2RrUHJvY2Vzc2luZ01ldGFkYXRhfTtjb25zdCBlPWp0KG4pLHI9RXQoZSkuZGVzY3JpcHRpb247ciYmIXQudHJhbnNhY3Rpb24mJiJ0cmFuc2FjdGlvbiI9PT10LnR5cGUmJih0LnRyYW5zYWN0aW9uPXIpfSh0LHIpLGZ1bmN0aW9uKHQsbil7dC5maW5nZXJwcmludD10LmZpbmdlcnByaW50P0FycmF5LmlzQXJyYXkodC5maW5nZXJwcmludCk/dC5maW5nZXJwcmludDpbdC5maW5nZXJwcmludF06W10sbiYmKHQuZmluZ2VycHJpbnQ9dC5maW5nZXJwcmludC5jb25jYXQobikpO3QuZmluZ2VycHJpbnQubGVuZ3RofHxkZWxldGUgdC5maW5nZXJwcmludH0odCxlKSxmdW5jdGlvbih0LG4pe2NvbnN0IGU9Wy4uLnQuYnJlYWRjcnVtYnN8fFtdLC4uLm5dO3QuYnJlYWRjcnVtYnM9ZS5sZW5ndGg/ZTp2b2lkIDB9KHQsbyksZnVuY3Rpb24odCxuKXt0LnNka1Byb2Nlc3NpbmdNZXRhZGF0YT17Li4udC5zZGtQcm9jZXNzaW5nTWV0YWRhdGEsLi4ubn19KHQsaSl9Y2xhc3MgSHR7Y29uc3RydWN0b3IodCl7dGhpcy5XPTAsdGhpcy5GPVtdLHRoaXMuRyh0KX10aGVuKHQsbil7cmV0dXJuIG5ldyBIdCgoZSxyKT0+e3RoaXMuRi5wdXNoKFshMSxuPT57aWYodCl0cnl7ZSh0KG4pKX1jYXRjaCh0KXtyKHQpfWVsc2UgZShuKX0sdD0+e2lmKG4pdHJ5e2Uobih0KSl9Y2F0Y2godCl7cih0KX1lbHNlIHIodCl9XSksdGhpcy5IKCl9KX1jYXRjaCh0KXtyZXR1cm4gdGhpcy50aGVuKHQ9PnQsdCl9ZmluYWxseSh0KXtyZXR1cm4gbmV3IEh0KChuLGUpPT57bGV0IHIsbztyZXR1cm4gdGhpcy50aGVuKG49PntvPSExLHI9bix0JiZ0KCl9LG49PntvPSEwLHI9bix0JiZ0KCl9KS50aGVuKCgpPT57bz9lKHIpOm4ocil9KX0pfUgoKXtpZigwPT09dGhpcy5XKXJldHVybjtjb25zdCB0PXRoaXMuRi5zbGljZSgpO3RoaXMuRj1bXSx0LmZvckVhY2godD0+e3RbMF18fCgxPT09dGhpcy5XJiZ0WzFdKHRoaXMuSiksMj09PXRoaXMuVyYmdFsyXSh0aGlzLkopLHRbMF09ITApfSl9Ryh0KXtjb25zdCBuPSh0LG4pPT57MD09PXRoaXMuVyYmKGsobik/bi50aGVuKGUscik6KHRoaXMuVz10LHRoaXMuSj1uLHRoaXMuSCgpKSl9LGU9dD0+e24oMSx0KX0scj10PT57bigyLHQpfTt0cnl7dChlLHIpfWNhdGNoKHQpe3IodCl9fX1jb25zdCBKdD1TeW1ib2wuZm9yKCJTZW50cnlCdWZmZXJGdWxsRXJyb3IiKTtmdW5jdGlvbiBZdCh0PTEwMCl7Y29uc3Qgbj1uZXcgU2V0O2Z1bmN0aW9uIGUodCl7bi5kZWxldGUodCl9cmV0dXJue2dldCAkKCl7cmV0dXJuIEFycmF5LmZyb20obil9LGFkZDpmdW5jdGlvbihyKXtpZighKG4uc2l6ZTx0KSlyZXR1cm4gbz1KdCxuZXcgSHQoKHQsbik9PntuKG8pfSk7dmFyIG87Y29uc3QgaT1yKCk7cmV0dXJuIG4uYWRkKGkpLGkudGhlbigoKT0+ZShpKSwoKT0+ZShpKSksaX0sZHJhaW46ZnVuY3Rpb24odCl7aWYoIW4uc2l6ZSlyZXR1cm4gZT0hMCxuZXcgSHQodD0+e3QoZSl9KTt2YXIgZTtjb25zdCByPVByb21pc2UuYWxsU2V0dGxlZChBcnJheS5mcm9tKG4pKS50aGVuKCgpPT4hMCk7aWYoIXQpcmV0dXJuIHI7Y29uc3Qgbz1bcixuZXcgUHJvbWlzZShuPT57cmV0dXJuIm9iamVjdCI9PXR5cGVvZihlPXNldFRpbWVvdXQoKCk9Pm4oITEpLHQpKSYmImZ1bmN0aW9uIj09dHlwZW9mIGUudW5yZWYmJmUudW5yZWYoKSxlO3ZhciBlfSldO3JldHVybiBQcm9taXNlLnJhY2Uobyl9fX1mdW5jdGlvbiBWdCh0LHtzdGF0dXNDb2RlOm4saGVhZGVyczplfSxyPXooKSl7Y29uc3Qgbz17Li4udH0saT1lPy5bIngtc2VudHJ5LXJhdGUtbGltaXRzIl0scz1lPy5bInJldHJ5LWFmdGVyIl07aWYoaSlmb3IoY29uc3QgdCBvZiBpLnRyaW0oKS5zcGxpdCgiLCIpKXtjb25zdFtuLGUsLCxpXT10LnNwbGl0KCI6Iiw1KSxzPXBhcnNlSW50KG4sMTApLGM9MWUzKihpc05hTihzKT82MDpzKTtpZihlKWZvcihjb25zdCB0IG9mIGUuc3BsaXQoIjsiKSkibWV0cmljX2J1Y2tldCI9PT10JiZpJiYhaS5zcGxpdCgiOyIpLmluY2x1ZGVzKCJjdXN0b20iKXx8KG9bdF09citjKTtlbHNlIG8uYWxsPXIrY31lbHNlIHM/by5hbGw9citmdW5jdGlvbih0LG49eigpKXtjb25zdCBlPXBhcnNlSW50KGAke3R9YCwxMCk7aWYoIWlzTmFOKGUpKXJldHVybiAxZTMqZTtjb25zdCByPURhdGUucGFyc2UoYCR7dH1gKTtyZXR1cm4gaXNOYU4ocik/NmU0OnItbn0ocyxyKTo0Mjk9PT1uJiYoby5hbGw9cis2ZTQpO3JldHVybiBvfWZ1bmN0aW9uIEt0KHQsbixlPVl0KHQuYnVmZmVyU2l6ZXx8NjQpKXtsZXQgcj17fTtyZXR1cm57c2VuZDpmdW5jdGlvbih0KXtjb25zdCBvPVtdO2lmKFB0KHQsKHQsbik9Pntjb25zdCBlPU10KG4pOyhmdW5jdGlvbih0LG4sZT16KCkpe3JldHVybiBmdW5jdGlvbih0LG4pe3JldHVybiB0W25dfHx0LmFsbHx8MH0odCxuKT5lfSkocixlKXx8by5wdXNoKHQpfSksMD09PW8ubGVuZ3RoKXJldHVybiBQcm9taXNlLnJlc29sdmUoe30pO2NvbnN0IGk9UnQodFswXSxvKSxzPXQ9PnshZnVuY3Rpb24odCxuKXtyZXR1cm4gUHQodCwodCxlKT0+bi5pbmNsdWRlcyhlKSl9KGksWyJjbGllbnRfcmVwb3J0Il0pP1B0KGksKHQsbik9Pnt9KTpoJiZTLndhcm4oYERyb3BwaW5nIGNsaWVudCByZXBvcnQuIFdpbGwgbm90IHNlbmQgb3V0Y29tZXMgKHJlYXNvbjogJHt0fSkuYCl9O3JldHVybiBlLmFkZCgoKT0+bih7Ym9keTpVdChpKX0pLnRoZW4odD0+NDEzPT09dC5zdGF0dXNDb2RlPyhoJiZTLmVycm9yKCJTZW50cnkgcmVzcG9uZGVkIHdpdGggc3RhdHVzIGNvZGUgNDEzLiBFbnZlbG9wZSB3YXMgZGlzY2FyZGVkIGR1ZSB0byBleGNlZWRpbmcgc2l6ZSBsaW1pdHMuIikscygic2VuZF9lcnJvciIpLHQpOihoJiZ2b2lkIDAhPT10LnN0YXR1c0NvZGUmJih0LnN0YXR1c0NvZGU8MjAwfHx0LnN0YXR1c0NvZGU+PTMwMCkmJlMud2FybihgU2VudHJ5IHJlc3BvbmRlZCB3aXRoIHN0YXR1cyBjb2RlICR7dC5zdGF0dXNDb2RlfSB0byBzZW50IGV2ZW50LmApLHI9VnQocix0KSx0KSx0PT57dGhyb3cgcygibmV0d29ya19lcnJvciIpLGgmJlMuZXJyb3IoIkVuY291bnRlcmVkIGVycm9yIHJ1bm5pbmcgdHJhbnNwb3J0IHJlcXVlc3Q6Iix0KSx0fSkpLnRoZW4odD0+dCx0PT57aWYodD09PUp0KXJldHVybiBoJiZTLmVycm9yKCJTa2lwcGVkIHNlbmRpbmcgZXZlbnQgYmVjYXVzZSBidWZmZXIgaXMgZnVsbC4iKSxzKCJxdWV1ZV9vdmVyZmxvdyIpLFByb21pc2UucmVzb2x2ZSh7fSk7dGhyb3cgdH0pfSxmbHVzaDp0PT5lLmRyYWluKHQpfX1jb25zdCBadD0vXihcUys6XFx8XC8/KShbXHNcU10qPykoKD86XC57MSwyfXxbXi9cXF0rP3wpKFwuW14uL1xcXSp8KSkoPzpbL1xcXSopJC87ZnVuY3Rpb24gcXQodCl7Y29uc3Qgbj1mdW5jdGlvbih0KXtjb25zdCBuPXQubGVuZ3RoPjEwMjQ/YDx0cnVuY2F0ZWQ+JHt0LnNsaWNlKC0xMDI0KX1gOnQsZT1adC5leGVjKG4pO3JldHVybiBlP2Uuc2xpY2UoMSk6W119KHQpLGU9blswXXx8IiI7bGV0IHI9blsxXTtyZXR1cm4gZXx8cj8ociYmKHI9ci5zbGljZSgwLHIubGVuZ3RoLTEpKSxlK3IpOiIuIn1mdW5jdGlvbiBRdCh0LG49ITEpe3JldHVybiEobnx8dCYmIXQuc3RhcnRzV2l0aCgiLyIpJiYhdC5tYXRjaCgvXltBLVpdOi8pJiYhdC5zdGFydHNXaXRoKCIuIikmJiF0Lm1hdGNoKC9eW2EtekEtWl0oW2EtekEtWjAtOS5cLStdKSo6XC9cLy8pKSYmdm9pZCAwIT09dCYmIXQuaW5jbHVkZXMoIm5vZGVfbW9kdWxlcy8iKX1jb25zdCBYdD1TeW1ib2woIkFnZW50QmFzZUludGVybmFsU3RhdGUiKTtjbGFzcyB0biBleHRlbmRzIGkuQWdlbnR7Y29uc3RydWN0b3IodCl7c3VwZXIodCksdGhpc1tYdF09e319aXNTZWN1cmVFbmRwb2ludCh0KXtpZih0KXtpZigiYm9vbGVhbiI9PXR5cGVvZiB0LnNlY3VyZUVuZHBvaW50KXJldHVybiB0LnNlY3VyZUVuZHBvaW50O2lmKCJzdHJpbmciPT10eXBlb2YgdC5wcm90b2NvbClyZXR1cm4iaHR0cHM6Ij09PXQucHJvdG9jb2x9Y29uc3R7c3RhY2s6bn09bmV3IEVycm9yO3JldHVybiJzdHJpbmciPT10eXBlb2YgbiYmbi5zcGxpdCgiXG4iKS5zb21lKHQ9Pi0xIT09dC5pbmRleE9mKCIoaHR0cHMuanM6Iil8fC0xIT09dC5pbmRleE9mKCJub2RlOmh0dHBzOiIpKX1jcmVhdGVTb2NrZXQodCxuLGUpe2NvbnN0IHI9ey4uLm4sc2VjdXJlRW5kcG9pbnQ6dGhpcy5pc1NlY3VyZUVuZHBvaW50KG4pfTtQcm9taXNlLnJlc29sdmUoKS50aGVuKCgpPT50aGlzLmNvbm5lY3QodCxyKSkudGhlbihvPT57aWYobyBpbnN0YW5jZW9mIGkuQWdlbnQpcmV0dXJuIG8uYWRkUmVxdWVzdCh0LHIpO3RoaXNbWHRdLmN1cnJlbnRTb2NrZXQ9byxzdXBlci5jcmVhdGVTb2NrZXQodCxuLGUpfSxlKX1jcmVhdGVDb25uZWN0aW9uKCl7Y29uc3QgdD10aGlzW1h0XS5jdXJyZW50U29ja2V0O2lmKHRoaXNbWHRdLmN1cnJlbnRTb2NrZXQ9dm9pZCAwLCF0KXRocm93IG5ldyBFcnJvcigiTm8gc29ja2V0IHdhcyByZXR1cm5lZCBpbiB0aGUgYGNvbm5lY3QoKWAgZnVuY3Rpb24iKTtyZXR1cm4gdH1nZXQgZGVmYXVsdFBvcnQoKXtyZXR1cm4gdGhpc1tYdF0uZGVmYXVsdFBvcnQ/PygiaHR0cHM6Ij09PXRoaXMucHJvdG9jb2w/NDQzOjgwKX1zZXQgZGVmYXVsdFBvcnQodCl7dGhpc1tYdF0mJih0aGlzW1h0XS5kZWZhdWx0UG9ydD10KX1nZXQgcHJvdG9jb2woKXtyZXR1cm4gdGhpc1tYdF0ucHJvdG9jb2w/Pyh0aGlzLmlzU2VjdXJlRW5kcG9pbnQoKT8iaHR0cHM6IjoiaHR0cDoiKX1zZXQgcHJvdG9jb2wodCl7dGhpc1tYdF0mJih0aGlzW1h0XS5wcm90b2NvbD10KX19ZnVuY3Rpb24gbm4oLi4udCl7Uy5sb2coIltodHRwcy1wcm94eS1hZ2VudDpwYXJzZS1wcm94eS1yZXNwb25zZV0iLC4uLnQpfWZ1bmN0aW9uIGVuKHQpe3JldHVybiBuZXcgUHJvbWlzZSgobixlKT0+e2xldCByPTA7Y29uc3Qgbz1bXTtmdW5jdGlvbiBpKCl7Y29uc3QgYz10LnJlYWQoKTtjP2Z1bmN0aW9uKGMpe28ucHVzaChjKSxyKz1jLmxlbmd0aDtjb25zdCB1PUJ1ZmZlci5jb25jYXQobyxyKSxhPXUuaW5kZXhPZigiXHJcblxyXG4iKTtpZigtMT09PWEpcmV0dXJuIG5uKCJoYXZlIG5vdCByZWNlaXZlZCBlbmQgb2YgSFRUUCBoZWFkZXJzIHlldC4uLiIpLHZvaWQgaSgpO2NvbnN0IGY9dS5zdWJhcnJheSgwLGEpLnRvU3RyaW5nKCJhc2NpaSIpLnNwbGl0KCJcclxuIiksaD1mLnNoaWZ0KCk7aWYoIWgpcmV0dXJuIHQuZGVzdHJveSgpLGUobmV3IEVycm9yKCJObyBoZWFkZXIgcmVjZWl2ZWQgZnJvbSBwcm94eSBDT05ORUNUIHJlc3BvbnNlIikpO2NvbnN0IHA9aC5zcGxpdCgiICIpLGw9KyhwWzFdfHwwKSxkPXAuc2xpY2UoMikuam9pbigiICIpLG09e307Zm9yKGNvbnN0IG4gb2YgZil7aWYoIW4pY29udGludWU7Y29uc3Qgcj1uLmluZGV4T2YoIjoiKTtpZigtMT09PXIpcmV0dXJuIHQuZGVzdHJveSgpLGUobmV3IEVycm9yKGBJbnZhbGlkIGhlYWRlciBmcm9tIHByb3h5IENPTk5FQ1QgcmVzcG9uc2U6ICIke259ImApKTtjb25zdCBvPW4uc2xpY2UoMCxyKS50b0xvd2VyQ2FzZSgpLGk9bi5zbGljZShyKzEpLnRyaW1TdGFydCgpLHM9bVtvXTsic3RyaW5nIj09dHlwZW9mIHM/bVtvXT1bcyxpXTpBcnJheS5pc0FycmF5KHMpP3MucHVzaChpKTptW29dPWl9bm4oImdvdCBwcm94eSBzZXJ2ZXIgcmVzcG9uc2U6ICVvICVvIixoLG0pLHMoKSxuKHtjb25uZWN0OntzdGF0dXNDb2RlOmwsc3RhdHVzVGV4dDpkLGhlYWRlcnM6bX0sYnVmZmVyZWQ6dX0pfShjKTp0Lm9uY2UoInJlYWRhYmxlIixpKX1mdW5jdGlvbiBzKCl7dC5yZW1vdmVMaXN0ZW5lcigiZW5kIixjKSx0LnJlbW92ZUxpc3RlbmVyKCJlcnJvciIsdSksdC5yZW1vdmVMaXN0ZW5lcigicmVhZGFibGUiLGkpfWZ1bmN0aW9uIGMoKXtzKCksbm4oIm9uZW5kIiksZShuZXcgRXJyb3IoIlByb3h5IGNvbm5lY3Rpb24gZW5kZWQgYmVmb3JlIHJlY2VpdmluZyBDT05ORUNUIHJlc3BvbnNlIikpfWZ1bmN0aW9uIHUodCl7cygpLG5uKCJvbmVycm9yICVvIix0KSxlKHQpfXQub24oImVycm9yIix1KSx0Lm9uKCJlbmQiLGMpLGkoKX0pfWZ1bmN0aW9uIHJuKC4uLnQpe1MubG9nKCJbaHR0cHMtcHJveHktYWdlbnRdIiwuLi50KX1jbGFzcyBvbiBleHRlbmRzIHRue3N0YXRpYyBfX2luaXRTdGF0aWMoKXt0aGlzLnByb3RvY29scz1bImh0dHAiLCJodHRwcyJdfWNvbnN0cnVjdG9yKHQsbil7c3VwZXIobiksdGhpcy5vcHRpb25zPXt9LHRoaXMucHJveHk9InN0cmluZyI9PXR5cGVvZiB0P25ldyBVUkwodCk6dCx0aGlzLnByb3h5SGVhZGVycz1uPy5oZWFkZXJzPz97fSxybigiQ3JlYXRpbmcgbmV3IEh0dHBzUHJveHlBZ2VudCBpbnN0YW5jZTogJW8iLHRoaXMucHJveHkuaHJlZik7Y29uc3QgZT0odGhpcy5wcm94eS5ob3N0bmFtZXx8dGhpcy5wcm94eS5ob3N0KS5yZXBsYWNlKC9eXFt8XF0kL2csIiIpLHI9dGhpcy5wcm94eS5wb3J0P3BhcnNlSW50KHRoaXMucHJveHkucG9ydCwxMCk6Imh0dHBzOiI9PT10aGlzLnByb3h5LnByb3RvY29sPzQ0Mzo4MDt0aGlzLmNvbm5lY3RPcHRzPXtBTFBOUHJvdG9jb2xzOlsiaHR0cC8xLjEiXSwuLi5uP2NuKG4sImhlYWRlcnMiKTpudWxsLGhvc3Q6ZSxwb3J0OnJ9fWFzeW5jIGNvbm5lY3QodCxuKXtjb25zdHtwcm94eTplfT10aGlzO2lmKCFuLmhvc3QpdGhyb3cgbmV3IFR5cGVFcnJvcignTm8gImhvc3QiIHByb3ZpZGVkJyk7bGV0IHI7aWYoImh0dHBzOiI9PT1lLnByb3RvY29sKXtybigiQ3JlYXRpbmcgYHRscy5Tb2NrZXRgOiAlbyIsdGhpcy5jb25uZWN0T3B0cyk7Y29uc3QgdD10aGlzLmNvbm5lY3RPcHRzLnNlcnZlcm5hbWV8fHRoaXMuY29ubmVjdE9wdHMuaG9zdDtyPWYuY29ubmVjdCh7Li4udGhpcy5jb25uZWN0T3B0cyxzZXJ2ZXJuYW1lOnQmJmEuaXNJUCh0KT92b2lkIDA6dH0pfWVsc2Ugcm4oIkNyZWF0aW5nIGBuZXQuU29ja2V0YDogJW8iLHRoaXMuY29ubmVjdE9wdHMpLHI9YS5jb25uZWN0KHRoaXMuY29ubmVjdE9wdHMpO2NvbnN0IG89ImZ1bmN0aW9uIj09dHlwZW9mIHRoaXMucHJveHlIZWFkZXJzP3RoaXMucHJveHlIZWFkZXJzKCk6ey4uLnRoaXMucHJveHlIZWFkZXJzfSxpPWEuaXNJUHY2KG4uaG9zdCk/YFske24uaG9zdH1dYDpuLmhvc3Q7bGV0IHM9YENPTk5FQ1QgJHtpfToke24ucG9ydH0gSFRUUC8xLjFcclxuYDtpZihlLnVzZXJuYW1lfHxlLnBhc3N3b3JkKXtjb25zdCB0PWAke2RlY29kZVVSSUNvbXBvbmVudChlLnVzZXJuYW1lKX06JHtkZWNvZGVVUklDb21wb25lbnQoZS5wYXNzd29yZCl9YDtvWyJQcm94eS1BdXRob3JpemF0aW9uIl09YEJhc2ljICR7QnVmZmVyLmZyb20odCkudG9TdHJpbmcoImJhc2U2NCIpfWB9by5Ib3N0PWAke2l9OiR7bi5wb3J0fWAsb1siUHJveHktQ29ubmVjdGlvbiJdfHwob1siUHJveHktQ29ubmVjdGlvbiJdPXRoaXMua2VlcEFsaXZlPyJLZWVwLUFsaXZlIjoiY2xvc2UiKTtmb3IoY29uc3QgdCBvZiBPYmplY3Qua2V5cyhvKSlzKz1gJHt0fTogJHtvW3RdfVxyXG5gO2NvbnN0IGM9ZW4ocik7ci53cml0ZShgJHtzfVxyXG5gKTtjb25zdHtjb25uZWN0OnUsYnVmZmVyZWQ6aH09YXdhaXQgYztpZih0LmVtaXQoInByb3h5Q29ubmVjdCIsdSksdGhpcy5lbWl0KCJwcm94eUNvbm5lY3QiLHUsdCksMjAwPT09dS5zdGF0dXNDb2RlKXtpZih0Lm9uY2UoInNvY2tldCIsc24pLG4uc2VjdXJlRW5kcG9pbnQpe3JuKCJVcGdyYWRpbmcgc29ja2V0IGNvbm5lY3Rpb24gdG8gVExTIik7Y29uc3QgdD1uLnNlcnZlcm5hbWV8fG4uaG9zdDtyZXR1cm4gZi5jb25uZWN0KHsuLi5jbihuLCJob3N0IiwicGF0aCIsInBvcnQiKSxzb2NrZXQ6cixzZXJ2ZXJuYW1lOmEuaXNJUCh0KT92b2lkIDA6dH0pfXJldHVybiByfXIuZGVzdHJveSgpO2NvbnN0IHA9bmV3IGEuU29ja2V0KHt3cml0YWJsZTohMX0pO3JldHVybiBwLnJlYWRhYmxlPSEwLHQub25jZSgic29ja2V0Iix0PT57cm4oIlJlcGxheWluZyBwcm94eSBidWZmZXIgZm9yIGZhaWxlZCByZXF1ZXN0IiksdC5wdXNoKGgpLHQucHVzaChudWxsKX0pLHB9fWZ1bmN0aW9uIHNuKHQpe3QucmVzdW1lKCl9ZnVuY3Rpb24gY24odCwuLi5uKXtjb25zdCBlPXt9O2xldCByO2ZvcihyIGluIHQpbi5pbmNsdWRlcyhyKXx8KGVbcl09dFtyXSk7cmV0dXJuIGV9b24uX19pbml0U3RhdGljKCk7ZnVuY3Rpb24gdW4odCl7cmV0dXJuIHQucmVwbGFjZSgvXltBLVpdOi8sIiIpLnJlcGxhY2UoL1xcL2csIi8iKX1jb25zdCBhbj1uO2xldCBmbixobj0wLHBuPXt9O2Z1bmN0aW9uIGxuKHQpe2FuLmRlYnVnJiZjb25zb2xlLmxvZyhgW0FOUiBXb3JrZXJdICR7dH1gKX12YXIgZG4sbW4sZ247Y29uc3QgeW49ZnVuY3Rpb24odCl7bGV0IG47dHJ5e249bmV3IFVSTCh0LnVybCl9Y2F0Y2gobil7cmV0dXJuIGIoKCk9Pntjb25zb2xlLndhcm4oIltAc2VudHJ5L25vZGVdOiBJbnZhbGlkIGRzbiBvciB0dW5uZWwgb3B0aW9uLCB3aWxsIG5vdCBzZW5kIGFueSBldmVudHMuIFRoZSB0dW5uZWwgb3B0aW9uIG11c3QgYmUgYSBmdWxsIFVSTCB3aGVuIHVzZWQuIil9KSxLdCh0LCgpPT5Qcm9taXNlLnJlc29sdmUoe30pKX1jb25zdCBlPSJodHRwczoiPT09bi5wcm90b2NvbCxyPWZ1bmN0aW9uKHQsbil7Y29uc3R7bm9fcHJveHk6ZX09cHJvY2Vzcy5lbnYscj1lPy5zcGxpdCgiLCIpLnNvbWUobj0+dC5ob3N0LmVuZHNXaXRoKG4pfHx0Lmhvc3RuYW1lLmVuZHNXaXRoKG4pKTtyZXR1cm4gcj92b2lkIDA6bn0obix0LnByb3h5fHwoZT9wcm9jZXNzLmVudi5odHRwc19wcm94eTp2b2lkIDApfHxwcm9jZXNzLmVudi5odHRwX3Byb3h5KSxvPWU/czppLGE9dm9pZCAwIT09dC5rZWVwQWxpdmUmJnQua2VlcEFsaXZlLGY9cj9uZXcgb24ocik6bmV3IG8uQWdlbnQoe2tlZXBBbGl2ZTphLG1heFNvY2tldHM6MzAsdGltZW91dDoyZTN9KSxoPWZ1bmN0aW9uKHQsbixlKXtjb25zdHtob3N0bmFtZTpyLHBhdGhuYW1lOm8scG9ydDppLHByb3RvY29sOnMsc2VhcmNoOmF9PW5ldyBVUkwodC51cmwpO3JldHVybiBmdW5jdGlvbihmKXtyZXR1cm4gbmV3IFByb21pc2UoKGgscCk9PntGdCgoKT0+e2xldCBsPWZ1bmN0aW9uKHQpe3JldHVybiBuZXcgYyh7cmVhZCgpe3RoaXMucHVzaCh0KSx0aGlzLnB1c2gobnVsbCl9fSl9KGYuYm9keSk7Y29uc3QgZD17Li4udC5oZWFkZXJzfTtmLmJvZHkubGVuZ3RoPjMyNzY4JiYoZFsiY29udGVudC1lbmNvZGluZyJdPSJnemlwIixsPWwucGlwZSh1KCkpKTtjb25zdCBtPXIuc3RhcnRzV2l0aCgiWyIpLGc9bi5yZXF1ZXN0KHttZXRob2Q6IlBPU1QiLGFnZW50OmUsaGVhZGVyczpkLGhvc3RuYW1lOm0/ci5zbGljZSgxLC0xKTpyLHBhdGg6YCR7b30ke2F9YCxwb3J0OmkscHJvdG9jb2w6cyxjYTp0LmNhQ2VydHN9LHQ9Pnt0Lm9uKCJkYXRhIiwoKT0+e30pLHQub24oImVuZCIsKCk9Pnt9KSx0LnNldEVuY29kaW5nKCJ1dGY4Iik7Y29uc3Qgbj10LmhlYWRlcnNbInJldHJ5LWFmdGVyIl0/P251bGwsZT10LmhlYWRlcnNbIngtc2VudHJ5LXJhdGUtbGltaXRzIl0/P251bGw7aCh7c3RhdHVzQ29kZTp0LnN0YXR1c0NvZGUsaGVhZGVyczp7InJldHJ5LWFmdGVyIjpuLCJ4LXNlbnRyeS1yYXRlLWxpbWl0cyI6QXJyYXkuaXNBcnJheShlKT9lWzBdfHxudWxsOmV9fSl9KTtnLm9uKCJlcnJvciIscCksbC5waXBlKGcpfSl9KX19KHQsdC5odHRwTW9kdWxlPz9vLGYpO3JldHVybiBLdCh0LGgpfSh7dXJsOihkbj1hbi5kc24sbW49YW4udHVubmVsLGduPWFuLnNka01ldGFkYXRhLnNkayxtbnx8YCR7ZnVuY3Rpb24odCl7cmV0dXJuYCR7ZnVuY3Rpb24odCl7Y29uc3Qgbj10LnByb3RvY29sP2Ake3QucHJvdG9jb2x9OmA6IiIsZT10LnBvcnQ/YDoke3QucG9ydH1gOiIiO3JldHVybmAke259Ly8ke3QuaG9zdH0ke2V9JHt0LnBhdGg/YC8ke3QucGF0aH1gOiIifS9hcGkvYH0odCl9JHt0LnByb2plY3RJZH0vZW52ZWxvcGUvYH0oZG4pfT8ke2Z1bmN0aW9uKHQsbil7Y29uc3QgZT17c2VudHJ5X3ZlcnNpb246IjcifTtyZXR1cm4gdC5wdWJsaWNLZXkmJihlLnNlbnRyeV9rZXk9dC5wdWJsaWNLZXkpLG4mJihlLnNlbnRyeV9jbGllbnQ9YCR7bi5uYW1lfS8ke24udmVyc2lvbn1gKSxuZXcgVVJMU2VhcmNoUGFyYW1zKGUpLnRvU3RyaW5nKCl9KGRuLGduKX1gKX0pO2FzeW5jIGZ1bmN0aW9uIGJuKCl7aWYoZm4pe2xuKCJTZW5kaW5nIGFibm9ybWFsIHNlc3Npb24iKSxWKGZuLHtzdGF0dXM6ImFibm9ybWFsIixhYm5vcm1hbF9tZWNoYW5pc206ImFucl9mb3JlZ3JvdW5kIixyZWxlYXNlOmFuLnJlbGVhc2UsZW52aXJvbm1lbnQ6YW4uZW52aXJvbm1lbnR9KTtjb25zdCB0PWZ1bmN0aW9uKHQsbixlLHIpe2NvbnN0IG89QnQoZSk7cmV0dXJuIFJ0KHtzZW50X2F0OihuZXcgRGF0ZSkudG9JU09TdHJpbmcoKSwuLi5vJiZ7c2RrOm99LC4uLiEhciYmbiYme2RzbjpidChuKX19LFsiYWdncmVnYXRlcyJpbiB0P1t7dHlwZToic2Vzc2lvbnMifSx0XTpbe3R5cGU6InNlc3Npb24ifSx0LnRvSlNPTigpXV0pfShmbixhbi5kc24sYW4uc2RrTWV0YWRhdGEsYW4udHVubmVsKTtsbihKU09OLnN0cmluZ2lmeSh0KSksYXdhaXQgeW4uc2VuZCh0KTt0cnl7ZT8ucG9zdE1lc3NhZ2UoInNlc3Npb24tZW5kZWQiKX1jYXRjaHt9fX1mdW5jdGlvbiB2bih0KXtpZighdClyZXR1cm47Y29uc3Qgbj1mdW5jdGlvbih0KXtpZighdC5sZW5ndGgpcmV0dXJuW107Y29uc3Qgbj1BcnJheS5mcm9tKHQpO3JldHVybi9zZW50cnlXcmFwcGVkLy50ZXN0KEUobikuZnVuY3Rpb258fCIiKSYmbi5wb3AoKSxuLnJldmVyc2UoKSwkLnRlc3QoRShuKS5mdW5jdGlvbnx8IiIpJiYobi5wb3AoKSwkLnRlc3QoRShuKS5mdW5jdGlvbnx8IiIpJiZuLnBvcCgpKSxuLnNsaWNlKDAsNTApLm1hcCh0PT4oey4uLnQsZmlsZW5hbWU6dC5maWxlbmFtZXx8RShuKS5maWxlbmFtZSxmdW5jdGlvbjp0LmZ1bmN0aW9ufHwiPyJ9KSl9KHQpO2lmKGFuLmFwcFJvb3RQYXRoKWZvcihjb25zdCB0IG9mIG4pdC5maWxlbmFtZSYmKHQuZmlsZW5hbWU9T3QodC5maWxlbmFtZSxhbi5hcHBSb290UGF0aCkpO3JldHVybiBufWFzeW5jIGZ1bmN0aW9uIF9uKHQsbil7aWYoaG4+PWFuLm1heEFuckV2ZW50cylyZXR1cm47aG4rPTEsYXdhaXQgYm4oKSxsbigiU2VuZGluZyBldmVudCIpO2NvbnN0IGU9e2V2ZW50X2lkOkYoKSxjb250ZXh0czphbi5jb250ZXh0cyxyZWxlYXNlOmFuLnJlbGVhc2UsZW52aXJvbm1lbnQ6YW4uZW52aXJvbm1lbnQsZGlzdDphbi5kaXN0LHBsYXRmb3JtOiJub2RlIixsZXZlbDoiZXJyb3IiLGV4Y2VwdGlvbjp7dmFsdWVzOlt7dHlwZToiQXBwbGljYXRpb25Ob3RSZXNwb25kaW5nIix2YWx1ZTpgQXBwbGljYXRpb24gTm90IFJlc3BvbmRpbmcgZm9yIGF0IGxlYXN0ICR7YW4uYW5yVGhyZXNob2xkfSBtc2Asc3RhY2t0cmFjZTp7ZnJhbWVzOnZuKHQpfSxtZWNoYW5pc206e3R5cGU6IkFOUiJ9fV19LHRhZ3M6YW4uc3RhdGljVGFnc307biYmZnVuY3Rpb24odCxuKXtpZihHdCh0LG4pLCF0LmNvbnRleHRzPy50cmFjZSl7Y29uc3R7dHJhY2VJZDplLHBhcmVudFNwYW5JZDpyLHByb3BhZ2F0aW9uU3BhbklkOm99PW4ucHJvcGFnYXRpb25Db250ZXh0O3QuY29udGV4dHM9e3RyYWNlOnt0cmFjZV9pZDplLHNwYW5faWQ6b3x8cSgpLHBhcmVudF9zcGFuX2lkOnJ9LC4uLnQuY29udGV4dHN9fX0oZSxuKSxmdW5jdGlvbih0KXtpZigwPT09T2JqZWN0LmtleXMocG4pLmxlbmd0aClyZXR1cm47Y29uc3Qgbj1hbi5hcHBSb290UGF0aD97fTpwbjtpZihhbi5hcHBSb290UGF0aClmb3IoY29uc3RbdCxlXW9mIE9iamVjdC5lbnRyaWVzKHBuKSluW090KHQsYW4uYXBwUm9vdFBhdGgpXT1lO2NvbnN0IGU9bmV3IE1hcDtmb3IoY29uc3QgciBvZiB0LmV4Y2VwdGlvbj8udmFsdWVzfHxbXSlmb3IoY29uc3QgdCBvZiByLnN0YWNrdHJhY2U/LmZyYW1lc3x8W10pe2NvbnN0IHI9dC5hYnNfcGF0aHx8dC5maWxlbmFtZTtyJiZuW3JdJiZlLnNldChyLG5bcl0pfWlmKGUuc2l6ZT4wKXtjb25zdCBuPVtdO2Zvcihjb25zdFt0LHJdb2YgZS5lbnRyaWVzKCkpbi5wdXNoKHt0eXBlOiJzb3VyY2VtYXAiLGNvZGVfZmlsZTp0LGRlYnVnX2lkOnJ9KTt0LmRlYnVnX21ldGE9e2ltYWdlczpufX19KGUpO2NvbnN0IHI9enQoZSxhbi5kc24sYW4uc2RrTWV0YWRhdGEsYW4udHVubmVsKTtsbihKU09OLnN0cmluZ2lmeShyKSksYXdhaXQgeW4uc2VuZChyKSxhd2FpdCB5bi5mbHVzaCgyZTMpLGhuPj1hbi5tYXhBbnJFdmVudHMmJnNldFRpbWVvdXQoKCk9Pntwcm9jZXNzLmV4aXQoMCl9LDVlMyl9bGV0IHduO2lmKGxuKCJTdGFydGVkIiksYW4uY2FwdHVyZVN0YWNrVHJhY2Upe2xuKCJDb25uZWN0aW5nIHRvIGRlYnVnZ2VyIik7Y29uc3Qgbj1uZXcgdDtuLmNvbm5lY3RUb01haW5UaHJlYWQoKSxsbigiQ29ubmVjdGVkIHRvIGRlYnVnZ2VyIik7Y29uc3QgZT1uZXcgTWFwO24ub24oIkRlYnVnZ2VyLnNjcmlwdFBhcnNlZCIsdD0+e2Uuc2V0KHQucGFyYW1zLnNjcmlwdElkLHQucGFyYW1zLnVybCl9KSxuLm9uKCJEZWJ1Z2dlci5wYXVzZWQiLHQ9PntpZigib3RoZXIiPT09dC5wYXJhbXMucmVhc29uKXRyeXtsbigiRGVidWdnZXIgcGF1c2VkIik7Y29uc3QgaT1bLi4udC5wYXJhbXMuY2FsbEZyYW1lc10scz1hbi5hcHBSb290UGF0aD9mdW5jdGlvbih0PShwcm9jZXNzLmFyZ3ZbMV0/cXQocHJvY2Vzcy5hcmd2WzFdKTpwcm9jZXNzLmN3ZCgpKSxuPSJcXCI9PT1vKXtjb25zdCBlPW4/dW4odCk6dDtyZXR1cm4gdD0+e2lmKCF0KXJldHVybjtjb25zdCBvPW4/dW4odCk6dDtsZXR7ZGlyOmksYmFzZTpzLGV4dDpjfT1yLnBhcnNlKG8pOyIuanMiIT09YyYmIi5tanMiIT09YyYmIi5janMiIT09Y3x8KHM9cy5zbGljZSgwLC0xKmMubGVuZ3RoKSk7Y29uc3QgdT1kZWNvZGVVUklDb21wb25lbnQocyk7aXx8KGk9Ii4iKTtjb25zdCBhPWkubGFzdEluZGV4T2YoIi9ub2RlX21vZHVsZXMiKTtpZihhPi0xKXJldHVybmAke2kuc2xpY2UoYSsxNCkucmVwbGFjZSgvXC8vZywiLiIpfToke3V9YDtpZihpLnN0YXJ0c1dpdGgoZSkpe2NvbnN0IHQ9aS5zbGljZShlLmxlbmd0aCsxKS5yZXBsYWNlKC9cLy9nLCIuIik7cmV0dXJuIHQ/YCR7dH06JHt1fWA6dX1yZXR1cm4gdX19KGFuLmFwcFJvb3RQYXRoKTooKT0+e30sYz1pLm1hcCh0PT5mdW5jdGlvbih0LG4sZSl7Y29uc3Qgcj1uP24ucmVwbGFjZSgvXmZpbGU6XC9cLy8sIiIpOnZvaWQgMCxvPXQubG9jYXRpb24uY29sdW1uTnVtYmVyP3QubG9jYXRpb24uY29sdW1uTnVtYmVyKzE6dm9pZCAwLGk9dC5sb2NhdGlvbi5saW5lTnVtYmVyP3QubG9jYXRpb24ubGluZU51bWJlcisxOnZvaWQgMDtyZXR1cm57ZmlsZW5hbWU6cixtb2R1bGU6ZShyKSxmdW5jdGlvbjp0LmZ1bmN0aW9uTmFtZXx8Ij8iLGNvbG5vOm8sbGluZW5vOmksaW5fYXBwOnI/UXQocik6dm9pZCAwfX0odCxlLmdldCh0LmxvY2F0aW9uLnNjcmlwdElkKSxzKSksdT1zZXRUaW1lb3V0KCgpPT57X24oYykudGhlbihudWxsLCgpPT57bG4oIlNlbmRpbmcgQU5SIGV2ZW50IGZhaWxlZC4iKX0pfSw1ZTMpO24ucG9zdCgiUnVudGltZS5ldmFsdWF0ZSIse2V4cHJlc3Npb246Imdsb2JhbC5fX1NFTlRSWV9HRVRfU0NPUEVTX18oKTsiLHNpbGVudDohMCxyZXR1cm5CeVZhbHVlOiEwfSwodCxlKT0+e3QmJmxuKGBFcnJvciBleGVjdXRpbmcgc2NyaXB0OiAnJHt0Lm1lc3NhZ2V9J2ApLGNsZWFyVGltZW91dCh1KTtjb25zdCByPWU/LnJlc3VsdD9lLnJlc3VsdC52YWx1ZTp2b2lkIDA7bi5wb3N0KCJEZWJ1Z2dlci5yZXN1bWUiKSxuLnBvc3QoIkRlYnVnZ2VyLmRpc2FibGUiKSxfbihjLHIpLnRoZW4obnVsbCwoKT0+e2xuKCJTZW5kaW5nIEFOUiBldmVudCBmYWlsZWQuIil9KX0pfWNhdGNoKHQpe3Rocm93IG4ucG9zdCgiRGVidWdnZXIucmVzdW1lIiksbi5wb3N0KCJEZWJ1Z2dlci5kaXNhYmxlIiksdH19KSx3bj0oKT0+e3RyeXtuLnBvc3QoIkRlYnVnZ2VyLmVuYWJsZSIsKCk9PntuLnBvc3QoIkRlYnVnZ2VyLnBhdXNlIil9KX1jYXRjaHt9fX1jb25zdHtwb2xsOlNufT1mdW5jdGlvbih0LG4sZSxyKXtjb25zdCBvPXQoKTtsZXQgaT0hMSxzPSEwO3JldHVybiBzZXRJbnRlcnZhbCgoKT0+e2NvbnN0IHQ9by5nZXRUaW1lTXMoKTshMT09PWkmJnQ+bitlJiYoaT0hMCxzJiZyKCkpLHQ8bitlJiYoaT0hMSl9LDIwKSx7cG9sbDooKT0+e28ucmVzZXQoKX0sZW5hYmxlZDp0PT57cz10fX19KGZ1bmN0aW9uKCl7bGV0IHQ9cHJvY2Vzcy5ocnRpbWUoKTtyZXR1cm57Z2V0VGltZU1zOigpPT57Y29uc3RbbixlXT1wcm9jZXNzLmhydGltZSh0KTtyZXR1cm4gTWF0aC5mbG9vcigxZTMqbitlLzFlNil9LHJlc2V0OigpPT57dD1wcm9jZXNzLmhydGltZSgpfX19LGFuLnBvbGxJbnRlcnZhbCxhbi5hbnJUaHJlc2hvbGQsZnVuY3Rpb24oKXtsbigiV2F0Y2hkb2cgdGltZW91dCIpLHduPyhsbigiUGF1c2luZyBkZWJ1Z2dlciB0byBjYXB0dXJlIHN0YWNrIHRyYWNlIiksd24oKSk6KGxuKCJDYXB0dXJpbmcgZXZlbnQgd2l0aG91dCBhIHN0YWNrIHRyYWNlIiksX24oKS50aGVuKG51bGwsKCk9PntsbigiU2VuZGluZyBBTlIgZXZlbnQgZmFpbGVkIG9uIHdhdGNoZG9nIHRpbWVvdXQuIil9KSl9KTtlPy5vbigibWVzc2FnZSIsdD0+e3Quc2Vzc2lvbiYmKGZuPVkodC5zZXNzaW9uKSksdC5kZWJ1Z0ltYWdlcyYmKHBuPXQuZGVidWdJbWFnZXMpLFNuKCl9KTs='; | ||
| const base64WorkerScript = "LyohIEBzZW50cnkvbm9kZS1jb3JlIDEwLjU0LjAgKGYzMTY4NmIpIHwgaHR0cHM6Ly9naXRodWIuY29tL2dldHNlbnRyeS9zZW50cnktamF2YXNjcmlwdCAqLwppbXBvcnR7U2Vzc2lvbiBhcyB0fWZyb20ibm9kZTppbnNwZWN0b3IiO2ltcG9ydHt3b3JrZXJEYXRhIGFzIG4scGFyZW50UG9ydCBhcyBlfWZyb20ibm9kZTp3b3JrZXJfdGhyZWFkcyI7aW1wb3J0e3Bvc2l4IGFzIHIsc2VwIGFzIG99ZnJvbSJub2RlOnBhdGgiO2ltcG9ydCphcyBpIGZyb20ibm9kZTpodHRwIjtpbXBvcnQqYXMgcyBmcm9tIm5vZGU6aHR0cHMiO2ltcG9ydHtSZWFkYWJsZSBhcyBjfWZyb20ibm9kZTpzdHJlYW0iO2ltcG9ydHtjcmVhdGVHemlwIGFzIHV9ZnJvbSJub2RlOnpsaWIiO2ltcG9ydCphcyBhIGZyb20ibm9kZTpuZXQiO2ltcG9ydCphcyBmIGZyb20ibm9kZTp0bHMiO2NvbnN0IGg9InVuZGVmaW5lZCI9PXR5cGVvZiBfX1NFTlRSWV9ERUJVR19ffHxfX1NFTlRSWV9ERUJVR19fLHA9Z2xvYmFsVGhpcyxsPSIxMC41NC4wIjtmdW5jdGlvbiBkKCl7cmV0dXJuIG0ocCkscH1mdW5jdGlvbiBtKHQpe2NvbnN0IG49dC5fX1NFTlRSWV9fPXQuX19TRU5UUllfX3x8e307cmV0dXJuIG4udmVyc2lvbj1uLnZlcnNpb258fGwsbltsXT1uW2xdfHx7fX1mdW5jdGlvbiBnKHQsbixlPXApe2NvbnN0IHI9ZS5fX1NFTlRSWV9fPWUuX19TRU5UUllfX3x8e30sbz1yW2xdPXJbbF18fHt9O3JldHVybiBvW3RdfHwob1t0XT1uKCkpfWNvbnN0IHk9e307ZnVuY3Rpb24gYih0KXtpZighKCJjb25zb2xlImluIHApKXJldHVybiB0KCk7Y29uc3Qgbj1wLmNvbnNvbGUsZT17fSxyPU9iamVjdC5rZXlzKHkpO3IuZm9yRWFjaCh0PT57Y29uc3Qgcj15W3RdO2VbdF09blt0XSxuW3RdPXJ9KTt0cnl7cmV0dXJuIHQoKX1maW5hbGx5e3IuZm9yRWFjaCh0PT57blt0XT1lW3RdfSl9fWZ1bmN0aW9uIHYoKXtyZXR1cm4gdygpLmVuYWJsZWR9ZnVuY3Rpb24gXyh0LC4uLm4pe2gmJnYoKSYmYigoKT0+e3AuY29uc29sZVt0XShgU2VudHJ5IExvZ2dlciBbJHt0fV06YCwuLi5uKX0pfWZ1bmN0aW9uIHcoKXtyZXR1cm4gaD9nKCJsb2dnZXJTZXR0aW5ncyIsKCk9Pih7ZW5hYmxlZDohMX0pKTp7ZW5hYmxlZDohMX19Y29uc3QgUz17ZW5hYmxlOmZ1bmN0aW9uKCl7dygpLmVuYWJsZWQ9ITB9LGRpc2FibGU6ZnVuY3Rpb24oKXt3KCkuZW5hYmxlZD0hMX0saXNFbmFibGVkOnYsbG9nOmZ1bmN0aW9uKC4uLnQpe18oImxvZyIsLi4udCl9LHdhcm46ZnVuY3Rpb24oLi4udCl7Xygid2FybiIsLi4udCl9LGVycm9yOmZ1bmN0aW9uKC4uLnQpe18oImVycm9yIiwuLi50KX19LCQ9L2NhcHR1cmVNZXNzYWdlfGNhcHR1cmVFeGNlcHRpb24vO2Z1bmN0aW9uIEUodCl7cmV0dXJuIHRbdC5sZW5ndGgtMV18fHt9fWNvbnN0IHg9Ijxhbm9ueW1vdXM+Ijtjb25zdCBOPU9iamVjdC5wcm90b3R5cGUudG9TdHJpbmc7ZnVuY3Rpb24gaih0LG4pe3JldHVybiBOLmNhbGwodCk9PT1gW29iamVjdCAke259XWB9ZnVuY3Rpb24gQyh0KXtyZXR1cm4gaih0LCJTdHJpbmciKX1mdW5jdGlvbiBBKHQpe3JldHVybiBqKHQsIk9iamVjdCIpfWZ1bmN0aW9uIGsodCl7cmV0dXJuIEJvb2xlYW4odD8udGhlbiYmImZ1bmN0aW9uIj09dHlwZW9mIHQudGhlbil9ZnVuY3Rpb24gVCh0LG4pe3RyeXtyZXR1cm4gdCBpbnN0YW5jZW9mIG59Y2F0Y2h7cmV0dXJuITF9fWNvbnN0IEk9cDtmdW5jdGlvbiBPKHQsbil7Y29uc3QgZT10LHI9W107aWYoIWU/LnRhZ05hbWUpcmV0dXJuIiI7aWYoSS5IVE1MRWxlbWVudCYmZSBpbnN0YW5jZW9mIEhUTUxFbGVtZW50JiZlLmRhdGFzZXQpe2lmKGUuZGF0YXNldC5zZW50cnlDb21wb25lbnQpcmV0dXJuIGUuZGF0YXNldC5zZW50cnlDb21wb25lbnQ7aWYoZS5kYXRhc2V0LnNlbnRyeUVsZW1lbnQpcmV0dXJuIGUuZGF0YXNldC5zZW50cnlFbGVtZW50fXIucHVzaChlLnRhZ05hbWUudG9Mb3dlckNhc2UoKSk7Y29uc3Qgbz1uPy5sZW5ndGg/bi5maWx0ZXIodD0+ZS5nZXRBdHRyaWJ1dGUodCkpLm1hcCh0PT5bdCxlLmdldEF0dHJpYnV0ZSh0KV0pOm51bGw7aWYobz8ubGVuZ3RoKW8uZm9yRWFjaCh0PT57ci5wdXNoKGBbJHt0WzBdfT0iJHt0WzFdfSJdYCl9KTtlbHNle2UuaWQmJnIucHVzaChgIyR7ZS5pZH1gKTtjb25zdCB0PWUuY2xhc3NOYW1lO2lmKHQmJkModCkpe2NvbnN0IG49dC5zcGxpdCgvXHMrLyk7Zm9yKGNvbnN0IHQgb2YgbilyLnB1c2goYC4ke3R9YCl9fWZvcihjb25zdCB0IG9mWyJhcmlhLWxhYmVsIiwidHlwZSIsIm5hbWUiLCJ0aXRsZSIsImFsdCJdKXtjb25zdCBuPWUuZ2V0QXR0cmlidXRlKHQpO24mJnIucHVzaChgWyR7dH09IiR7bn0iXWApfXJldHVybiByLmpvaW4oIiIpfWZ1bmN0aW9uIFIodCl7aWYoZnVuY3Rpb24odCl7c3dpdGNoKE4uY2FsbCh0KSl7Y2FzZSJbb2JqZWN0IEVycm9yXSI6Y2FzZSJbb2JqZWN0IEV4Y2VwdGlvbl0iOmNhc2UiW29iamVjdCBET01FeGNlcHRpb25dIjpjYXNlIltvYmplY3QgV2ViQXNzZW1ibHkuRXhjZXB0aW9uXSI6cmV0dXJuITA7ZGVmYXVsdDpyZXR1cm4gVCh0LEVycm9yKX19KHQpKXJldHVybnttZXNzYWdlOnQubWVzc2FnZSxuYW1lOnQubmFtZSxzdGFjazp0LnN0YWNrLC4uLkQodCl9O2lmKG49dCwidW5kZWZpbmVkIiE9dHlwZW9mIEV2ZW50JiZUKG4sRXZlbnQpKXtjb25zdCBuPXt0eXBlOnQudHlwZSx0YXJnZXQ6UCh0LnRhcmdldCksY3VycmVudFRhcmdldDpQKHQuY3VycmVudFRhcmdldCksLi4uRCh0KX07cmV0dXJuInVuZGVmaW5lZCIhPXR5cGVvZiBDdXN0b21FdmVudCYmVCh0LEN1c3RvbUV2ZW50KSYmKG4uZGV0YWlsPXQuZGV0YWlsKSxufXJldHVybiB0O3ZhciBufWZ1bmN0aW9uIFAodCl7dHJ5e3JldHVybiBuPXQsInVuZGVmaW5lZCIhPXR5cGVvZiBFbGVtZW50JiZUKG4sRWxlbWVudCk/ZnVuY3Rpb24odCxuPXt9KXtpZighdClyZXR1cm4iPHVua25vd24+Ijt0cnl7bGV0IGU9dDtjb25zdCByPTUsbz1bXTtsZXQgaT0wLHM9MDtjb25zdCBjPSIgPiAiLHU9Yy5sZW5ndGg7bGV0IGE7Y29uc3QgZj1BcnJheS5pc0FycmF5KG4pP246bi5rZXlBdHRycyxoPSFBcnJheS5pc0FycmF5KG4pJiZuLm1heFN0cmluZ0xlbmd0aHx8ODA7Zm9yKDtlJiZpKys8ciYmKGE9TyhlLGYpLCEoImh0bWwiPT09YXx8aT4xJiZzK28ubGVuZ3RoKnUrYS5sZW5ndGg+PWgpKTspby5wdXNoKGEpLHMrPWEubGVuZ3RoLGU9ZS5wYXJlbnROb2RlO3JldHVybiBvLnJldmVyc2UoKS5qb2luKGMpfWNhdGNoe3JldHVybiI8dW5rbm93bj4ifX0odCk6T2JqZWN0LnByb3RvdHlwZS50b1N0cmluZy5jYWxsKHQpfWNhdGNoe3JldHVybiI8dW5rbm93bj4ifXZhciBufWZ1bmN0aW9uIEQodCl7cmV0dXJuIm9iamVjdCI9PXR5cGVvZiB0JiZudWxsIT09dD9PYmplY3QuZnJvbUVudHJpZXMoT2JqZWN0LmVudHJpZXModCkpOnt9fWxldCBVLEw7ZnVuY3Rpb24gTSh0KXtpZih2b2lkIDAhPT1VKXJldHVybiBVP1UodCk6dCgpO2NvbnN0IG49U3ltYm9sLmZvcigiX19TRU5UUllfU0FGRV9SQU5ET01fSURfV1JBUFBFUl9fIiksZT1wO3JldHVybiBuIGluIGUmJiJmdW5jdGlvbiI9PXR5cGVvZiBlW25dPyhVPWVbbl0sVSh0KSk6KFU9bnVsbCx0KCkpfWZ1bmN0aW9uIEIoKXtyZXR1cm4gTSgoKT0+TWF0aC5yYW5kb20oKSl9ZnVuY3Rpb24geigpe3JldHVybiBNKCgpPT5EYXRlLm5vdygpKX1mdW5jdGlvbiBXKHQsbj0wKXtyZXR1cm4ic3RyaW5nIiE9dHlwZW9mIHR8fDA9PT1ufHx0Lmxlbmd0aDw9bj90OmAke3Quc2xpY2UoMCxuKX0uLi5gfWZ1bmN0aW9uIEYodD1mdW5jdGlvbigpe2NvbnN0IHQ9cDtyZXR1cm4gdC5jcnlwdG98fHQubXNDcnlwdG99KCkpe3RyeXtpZih0Py5yYW5kb21VVUlEKXJldHVybiBNKCgpPT50LnJhbmRvbVVVSUQoKSkucmVwbGFjZSgvLS9nLCIiKX1jYXRjaHt9cmV0dXJuIEx8fChMPSIxMDAwMDAwMDEwMDA0MDAwODAwMDEwMDAwMDAwMDAwMCIpLEwucmVwbGFjZSgvWzAxOF0vZyx0PT4odF4oMTYqQigpJjE1KT4+dC80KS50b1N0cmluZygxNikpfWZ1bmN0aW9uIEcoKXtyZXR1cm4geigpLzFlM31sZXQgSDtmdW5jdGlvbiBKKCl7cmV0dXJuKEg/PyhIPWZ1bmN0aW9uKCl7Y29uc3R7cGVyZm9ybWFuY2U6dH09cDtpZighdD8ubm93fHwhdC50aW1lT3JpZ2luKXJldHVybiBHO2NvbnN0IG49dC50aW1lT3JpZ2luO3JldHVybigpPT4obitNKCgpPT50Lm5vdygpKSkvMWUzfSgpKSkoKX1mdW5jdGlvbiBZKHQpe2NvbnN0IG49SigpLGU9e3NpZDpGKCksaW5pdDohMCx0aW1lc3RhbXA6bixzdGFydGVkOm4sZHVyYXRpb246MCxzdGF0dXM6Im9rIixlcnJvcnM6MCxpZ25vcmVEdXJhdGlvbjohMSx0b0pTT046KCk9PmZ1bmN0aW9uKHQpe3JldHVybntzaWQ6YCR7dC5zaWR9YCxpbml0OnQuaW5pdCxzdGFydGVkOm5ldyBEYXRlKDFlMyp0LnN0YXJ0ZWQpLnRvSVNPU3RyaW5nKCksdGltZXN0YW1wOm5ldyBEYXRlKDFlMyp0LnRpbWVzdGFtcCkudG9JU09TdHJpbmcoKSxzdGF0dXM6dC5zdGF0dXMsZXJyb3JzOnQuZXJyb3JzLGRpZDoibnVtYmVyIj09dHlwZW9mIHQuZGlkfHwic3RyaW5nIj09dHlwZW9mIHQuZGlkP2Ake3QuZGlkfWA6dm9pZCAwLGR1cmF0aW9uOnQuZHVyYXRpb24sYWJub3JtYWxfbWVjaGFuaXNtOnQuYWJub3JtYWxfbWVjaGFuaXNtLGF0dHJzOntyZWxlYXNlOnQucmVsZWFzZSxlbnZpcm9ubWVudDp0LmVudmlyb25tZW50LGlwX2FkZHJlc3M6dC5pcEFkZHJlc3MsdXNlcl9hZ2VudDp0LnVzZXJBZ2VudH19fShlKX07cmV0dXJuIHQmJlYoZSx0KSxlfWZ1bmN0aW9uIFYodCxuPXt9KXtpZihuLnVzZXImJighdC5pcEFkZHJlc3MmJm4udXNlci5pcF9hZGRyZXNzJiYodC5pcEFkZHJlc3M9bi51c2VyLmlwX2FkZHJlc3MpLHQuZGlkfHxuLmRpZHx8KHQuZGlkPW4udXNlci5pZHx8bi51c2VyLmVtYWlsfHxuLnVzZXIudXNlcm5hbWUpKSx0LnRpbWVzdGFtcD1uLnRpbWVzdGFtcHx8SigpLG4uYWJub3JtYWxfbWVjaGFuaXNtJiYodC5hYm5vcm1hbF9tZWNoYW5pc209bi5hYm5vcm1hbF9tZWNoYW5pc20pLG4uaWdub3JlRHVyYXRpb24mJih0Lmlnbm9yZUR1cmF0aW9uPW4uaWdub3JlRHVyYXRpb24pLG4uc2lkJiYodC5zaWQ9MzI9PT1uLnNpZC5sZW5ndGg/bi5zaWQ6RigpKSx2b2lkIDAhPT1uLmluaXQmJih0LmluaXQ9bi5pbml0KSwhdC5kaWQmJm4uZGlkJiYodC5kaWQ9YCR7bi5kaWR9YCksIm51bWJlciI9PXR5cGVvZiBuLnN0YXJ0ZWQmJih0LnN0YXJ0ZWQ9bi5zdGFydGVkKSx0Lmlnbm9yZUR1cmF0aW9uKXQuZHVyYXRpb249dm9pZCAwO2Vsc2UgaWYoIm51bWJlciI9PXR5cGVvZiBuLmR1cmF0aW9uKXQuZHVyYXRpb249bi5kdXJhdGlvbjtlbHNle2NvbnN0IG49dC50aW1lc3RhbXAtdC5zdGFydGVkO3QuZHVyYXRpb249bj49MD9uOjB9bi5yZWxlYXNlJiYodC5yZWxlYXNlPW4ucmVsZWFzZSksbi5lbnZpcm9ubWVudCYmKHQuZW52aXJvbm1lbnQ9bi5lbnZpcm9ubWVudCksIXQuaXBBZGRyZXNzJiZuLmlwQWRkcmVzcyYmKHQuaXBBZGRyZXNzPW4uaXBBZGRyZXNzKSwhdC51c2VyQWdlbnQmJm4udXNlckFnZW50JiYodC51c2VyQWdlbnQ9bi51c2VyQWdlbnQpLCJudW1iZXIiPT10eXBlb2Ygbi5lcnJvcnMmJih0LmVycm9ycz1uLmVycm9ycyksbi5zdGF0dXMmJih0LnN0YXR1cz1uLnN0YXR1cyl9ZnVuY3Rpb24gSyh0LG4sZT0yKXtpZighbnx8Im9iamVjdCIhPXR5cGVvZiBufHxlPD0wKXJldHVybiBuO2lmKHQmJjA9PT1PYmplY3Qua2V5cyhuKS5sZW5ndGgpcmV0dXJuIHQ7Y29uc3Qgcj17Li4udH07Zm9yKGNvbnN0IHQgaW4gbilPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwobix0KSYmKHJbdF09SyhyW3RdLG5bdF0sZS0xKSk7cmV0dXJuIHJ9ZnVuY3Rpb24gWigpe3JldHVybiBGKCl9ZnVuY3Rpb24gcSgpe3JldHVybiBGKCkuc3Vic3RyaW5nKDE2KX1jb25zdCBRPSJfc2VudHJ5U3BhbiI7ZnVuY3Rpb24gWCh0LG4pe24/ZnVuY3Rpb24odCxuLGUpe3RyeXtPYmplY3QuZGVmaW5lUHJvcGVydHkodCxuLHt2YWx1ZTplLHdyaXRhYmxlOiEwLGNvbmZpZ3VyYWJsZTohMH0pfWNhdGNoe2gmJlMubG9nKGBGYWlsZWQgdG8gYWRkIG5vbi1lbnVtZXJhYmxlIHByb3BlcnR5ICIke1N0cmluZyhuKX0iIHRvIG9iamVjdGAsdCl9fSh0LFEsbik6ZGVsZXRlIHRbUV19ZnVuY3Rpb24gdHQodCl7cmV0dXJuIHRbUV19Y2xhc3MgbnR7Y29uc3RydWN0b3IoKXt0aGlzLnQ9ITEsdGhpcy5vPVtdLHRoaXMuaT1bXSx0aGlzLnU9W10sdGhpcy5oPVtdLHRoaXMucD17fSx0aGlzLmw9e30sdGhpcy5tPXt9LHRoaXMudj17fSx0aGlzLl89e30sdGhpcy5TPXt9LHRoaXMuTj17dHJhY2VJZDpaKCksc2FtcGxlUmFuZDpCKCl9fWNsb25lKCl7Y29uc3QgdD1uZXcgbnQ7cmV0dXJuIHQudT1bLi4udGhpcy51XSx0Lmw9ey4uLnRoaXMubH0sdC5tPXsuLi50aGlzLm19LHQudj17Li4udGhpcy52fSx0Ll89ey4uLnRoaXMuX30sdGhpcy5fLmZsYWdzJiYodC5fLmZsYWdzPXt2YWx1ZXM6Wy4uLnRoaXMuXy5mbGFncy52YWx1ZXNdfSksdC5wPXRoaXMucCx0Lmo9dGhpcy5qLHQuQz10aGlzLkMsdC5BPXRoaXMuQSx0Lms9dGhpcy5rLHQuaT1bLi4udGhpcy5pXSx0Lmg9Wy4uLnRoaXMuaF0sdC5TPXsuLi50aGlzLlN9LHQuTj17Li4udGhpcy5OfSx0LlQ9dGhpcy5ULHQuST10aGlzLkksdC5PPXRoaXMuTyxYKHQsdHQodGhpcykpLHR9c2V0Q2xpZW50KHQpe3RoaXMuVD10fXNldExhc3RFdmVudElkKHQpe3RoaXMuST10fWdldENsaWVudCgpe3JldHVybiB0aGlzLlR9bGFzdEV2ZW50SWQoKXtyZXR1cm4gdGhpcy5JfWFkZFNjb3BlTGlzdGVuZXIodCl7dGhpcy5vLnB1c2godCl9YWRkRXZlbnRQcm9jZXNzb3IodCl7cmV0dXJuIHRoaXMuaS5wdXNoKHQpLHRoaXN9c2V0VXNlcih0KXtyZXR1cm4gdGhpcy5wPXR8fHtlbWFpbDp2b2lkIDAsaWQ6dm9pZCAwLGlwX2FkZHJlc3M6dm9pZCAwLHVzZXJuYW1lOnZvaWQgMH0sdGhpcy5DJiZWKHRoaXMuQyx7dXNlcjp0fSksdGhpcy5SKCksdGhpc31nZXRVc2VyKCl7cmV0dXJuIHRoaXMucH1zZXRDb252ZXJzYXRpb25JZCh0KXtyZXR1cm4gdGhpcy5PPXR8fHZvaWQgMCx0aGlzLlIoKSx0aGlzfXNldFRhZ3ModCl7cmV0dXJuIHRoaXMubD17Li4udGhpcy5sLC4uLnR9LHRoaXMuUigpLHRoaXN9c2V0VGFnKHQsbil7cmV0dXJuIHRoaXMuc2V0VGFncyh7W3RdOm59KX1zZXRBdHRyaWJ1dGVzKHQpe3JldHVybiB0aGlzLm09ey4uLnRoaXMubSwuLi50fSx0aGlzLlIoKSx0aGlzfXNldEF0dHJpYnV0ZSh0LG4pe3JldHVybiB0aGlzLnNldEF0dHJpYnV0ZXMoe1t0XTpufSl9cmVtb3ZlQXR0cmlidXRlKHQpe3JldHVybiB0IGluIHRoaXMubSYmKGRlbGV0ZSB0aGlzLm1bdF0sdGhpcy5SKCkpLHRoaXN9c2V0RXh0cmFzKHQpe3JldHVybiB0aGlzLnY9ey4uLnRoaXMudiwuLi50fSx0aGlzLlIoKSx0aGlzfXNldEV4dHJhKHQsbil7cmV0dXJuIHRoaXMudj17Li4udGhpcy52LFt0XTpufSx0aGlzLlIoKSx0aGlzfXNldEZpbmdlcnByaW50KHQpe3JldHVybiB0aGlzLms9dCx0aGlzLlIoKSx0aGlzfXNldExldmVsKHQpe3JldHVybiB0aGlzLmo9dCx0aGlzLlIoKSx0aGlzfXNldFRyYW5zYWN0aW9uTmFtZSh0KXtyZXR1cm4gdGhpcy5BPXQsdGhpcy5SKCksdGhpc31zZXRDb250ZXh0KHQsbil7cmV0dXJuIG51bGw9PT1uP2RlbGV0ZSB0aGlzLl9bdF06dGhpcy5fW3RdPW4sdGhpcy5SKCksdGhpc31zZXRTZXNzaW9uKHQpe3JldHVybiB0P3RoaXMuQz10OmRlbGV0ZSB0aGlzLkMsdGhpcy5SKCksdGhpc31nZXRTZXNzaW9uKCl7cmV0dXJuIHRoaXMuQ311cGRhdGUodCl7aWYoIXQpcmV0dXJuIHRoaXM7Y29uc3Qgbj0iZnVuY3Rpb24iPT10eXBlb2YgdD90KHRoaXMpOnQsZT1uIGluc3RhbmNlb2YgbnQ/bi5nZXRTY29wZURhdGEoKTpBKG4pP3Q6dm9pZCAwLHt0YWdzOnIsYXR0cmlidXRlczpvLGV4dHJhOmksdXNlcjpzLGNvbnRleHRzOmMsbGV2ZWw6dSxmaW5nZXJwcmludDphPVtdLHByb3BhZ2F0aW9uQ29udGV4dDpmLGNvbnZlcnNhdGlvbklkOmh9PWV8fHt9O3JldHVybiB0aGlzLmw9ey4uLnRoaXMubCwuLi5yfSx0aGlzLm09ey4uLnRoaXMubSwuLi5vfSx0aGlzLnY9ey4uLnRoaXMudiwuLi5pfSx0aGlzLl89ey4uLnRoaXMuXywuLi5jfSxzJiZPYmplY3Qua2V5cyhzKS5sZW5ndGgmJih0aGlzLnA9cyksdSYmKHRoaXMuaj11KSxhLmxlbmd0aCYmKHRoaXMuaz1hKSxmJiYodGhpcy5OPWYpLGgmJih0aGlzLk89aCksdGhpc31jbGVhcigpe3JldHVybiB0aGlzLnU9W10sdGhpcy5sPXt9LHRoaXMubT17fSx0aGlzLnY9e30sdGhpcy5wPXt9LHRoaXMuXz17fSx0aGlzLmo9dm9pZCAwLHRoaXMuQT12b2lkIDAsdGhpcy5rPXZvaWQgMCx0aGlzLkM9dm9pZCAwLHRoaXMuTz12b2lkIDAsWCh0aGlzLHZvaWQgMCksdGhpcy5oPVtdLHRoaXMuc2V0UHJvcGFnYXRpb25Db250ZXh0KHt0cmFjZUlkOlooKSxzYW1wbGVSYW5kOkIoKX0pLHRoaXMuUigpLHRoaXN9YWRkQnJlYWRjcnVtYih0LG4pe2NvbnN0IGU9Im51bWJlciI9PXR5cGVvZiBuP246MTAwO2lmKGU8PTApcmV0dXJuIHRoaXM7Y29uc3Qgcj17dGltZXN0YW1wOkcoKSwuLi50LG1lc3NhZ2U6dC5tZXNzYWdlP1codC5tZXNzYWdlLDIwNDgpOnQubWVzc2FnZX07cmV0dXJuIHRoaXMudS5wdXNoKHIpLHRoaXMudS5sZW5ndGg+ZSYmKHRoaXMudT10aGlzLnUuc2xpY2UoLWUpLHRoaXMuVD8ucmVjb3JkRHJvcHBlZEV2ZW50KCJidWZmZXJfb3ZlcmZsb3ciLCJsb2dfaXRlbSIpKSx0aGlzLlIoKSx0aGlzfWdldExhc3RCcmVhZGNydW1iKCl7cmV0dXJuIHRoaXMudVt0aGlzLnUubGVuZ3RoLTFdfWNsZWFyQnJlYWRjcnVtYnMoKXtyZXR1cm4gdGhpcy51PVtdLHRoaXMuUigpLHRoaXN9YWRkQXR0YWNobWVudCh0KXtyZXR1cm4gdGhpcy5oLnB1c2godCksdGhpc31jbGVhckF0dGFjaG1lbnRzKCl7cmV0dXJuIHRoaXMuaD1bXSx0aGlzfWdldFNjb3BlRGF0YSgpe3JldHVybnticmVhZGNydW1iczp0aGlzLnUsYXR0YWNobWVudHM6dGhpcy5oLGNvbnRleHRzOnRoaXMuXyx0YWdzOnRoaXMubCxhdHRyaWJ1dGVzOnRoaXMubSxleHRyYTp0aGlzLnYsdXNlcjp0aGlzLnAsbGV2ZWw6dGhpcy5qLGZpbmdlcnByaW50OnRoaXMua3x8W10sZXZlbnRQcm9jZXNzb3JzOnRoaXMuaSxwcm9wYWdhdGlvbkNvbnRleHQ6dGhpcy5OLHNka1Byb2Nlc3NpbmdNZXRhZGF0YTp0aGlzLlMsdHJhbnNhY3Rpb25OYW1lOnRoaXMuQSxzcGFuOnR0KHRoaXMpLGNvbnZlcnNhdGlvbklkOnRoaXMuT319c2V0U0RLUHJvY2Vzc2luZ01ldGFkYXRhKHQpe3JldHVybiB0aGlzLlM9Syh0aGlzLlMsdCwyKSx0aGlzfXNldFByb3BhZ2F0aW9uQ29udGV4dCh0KXtyZXR1cm4gdGhpcy5OPXQsdGhpc31nZXRQcm9wYWdhdGlvbkNvbnRleHQoKXtyZXR1cm4gdGhpcy5OfWNhcHR1cmVFeGNlcHRpb24odCxuKXtjb25zdCBlPW4/LmV2ZW50X2lkfHxGKCk7aWYoIXRoaXMuVClyZXR1cm4gaCYmUy53YXJuKCJObyBjbGllbnQgY29uZmlndXJlZCBvbiBzY29wZSAtIHdpbGwgbm90IGNhcHR1cmUgZXhjZXB0aW9uISIpLGU7Y29uc3Qgcj1uZXcgRXJyb3IoIlNlbnRyeSBzeW50aGV0aWNFeGNlcHRpb24iKTtyZXR1cm4gdGhpcy5ULmNhcHR1cmVFeGNlcHRpb24odCx7b3JpZ2luYWxFeGNlcHRpb246dCxzeW50aGV0aWNFeGNlcHRpb246ciwuLi5uLGV2ZW50X2lkOmV9LHRoaXMpLGV9Y2FwdHVyZU1lc3NhZ2UodCxuLGUpe2NvbnN0IHI9ZT8uZXZlbnRfaWR8fEYoKTtpZighdGhpcy5UKXJldHVybiBoJiZTLndhcm4oIk5vIGNsaWVudCBjb25maWd1cmVkIG9uIHNjb3BlIC0gd2lsbCBub3QgY2FwdHVyZSBtZXNzYWdlISIpLHI7Y29uc3Qgbz1lPy5zeW50aGV0aWNFeGNlcHRpb24/P25ldyBFcnJvcih0KTtyZXR1cm4gdGhpcy5ULmNhcHR1cmVNZXNzYWdlKHQsbix7b3JpZ2luYWxFeGNlcHRpb246dCxzeW50aGV0aWNFeGNlcHRpb246bywuLi5lLGV2ZW50X2lkOnJ9LHRoaXMpLHJ9Y2FwdHVyZUV2ZW50KHQsbil7Y29uc3QgZT10LmV2ZW50X2lkfHxuPy5ldmVudF9pZHx8RigpO3JldHVybiB0aGlzLlQ/KHRoaXMuVC5jYXB0dXJlRXZlbnQodCx7Li4ubixldmVudF9pZDplfSx0aGlzKSxlKTooaCYmUy53YXJuKCJObyBjbGllbnQgY29uZmlndXJlZCBvbiBzY29wZSAtIHdpbGwgbm90IGNhcHR1cmUgZXZlbnQhIiksZSl9Uigpe3RoaXMudHx8KHRoaXMudD0hMCx0aGlzLm8uZm9yRWFjaCh0PT57dCh0aGlzKX0pLHRoaXMudD0hMSl9fWNvbnN0IGV0PXQ9PnQgaW5zdGFuY2VvZiBQcm9taXNlJiYhdFtydF0scnQ9U3ltYm9sKCJjaGFpbmVkIFByb21pc2VMaWtlIiksb3Q9KHQsbik9PntpZighbilyZXR1cm4gdDtsZXQgZT0hMTtmb3IoY29uc3QgciBpbiB0KXtpZihyIGluIG4pY29udGludWU7ZT0hMDtjb25zdCBvPXRbcl07ImZ1bmN0aW9uIj09dHlwZW9mIG8/T2JqZWN0LmRlZmluZVByb3BlcnR5KG4scix7dmFsdWU6KC4uLm4pPT5vLmFwcGx5KHQsbiksZW51bWVyYWJsZTohMCxjb25maWd1cmFibGU6ITAsd3JpdGFibGU6ITB9KTpuW3JdPW99cmV0dXJuIGUmJk9iamVjdC5hc3NpZ24obix7W3J0XTohMH0pLG59O2NsYXNzIGl0e2NvbnN0cnVjdG9yKHQsbil7bGV0IGUscjtlPXR8fG5ldyBudCxyPW58fG5ldyBudCx0aGlzLlA9W3tzY29wZTplfV0sdGhpcy5EPXJ9d2l0aFNjb3BlKHQpe2NvbnN0IG49dGhpcy5VKCk7bGV0IGU7dHJ5e2U9dChuKX1jYXRjaCh0KXt0aHJvdyB0aGlzLkwoKSx0fXJldHVybiBrKGUpPygodCxuLGUpPT57Y29uc3Qgcj10LnRoZW4odD0+KG4odCksdCksdD0+e3Rocm93IGUodCksdH0pO3JldHVybiBldChyKSYmZXQodCk/cjpvdCh0LHIpfSkoZSwoKT0+dGhpcy5MKCksKCk9PnRoaXMuTCgpKToodGhpcy5MKCksZSl9Z2V0Q2xpZW50KCl7cmV0dXJuIHRoaXMuZ2V0U3RhY2tUb3AoKS5jbGllbnR9Z2V0U2NvcGUoKXtyZXR1cm4gdGhpcy5nZXRTdGFja1RvcCgpLnNjb3BlfWdldElzb2xhdGlvblNjb3BlKCl7cmV0dXJuIHRoaXMuRH1nZXRTdGFja1RvcCgpe3JldHVybiB0aGlzLlBbdGhpcy5QLmxlbmd0aC0xXX1VKCl7Y29uc3QgdD10aGlzLmdldFNjb3BlKCkuY2xvbmUoKTtyZXR1cm4gdGhpcy5QLnB1c2goe2NsaWVudDp0aGlzLmdldENsaWVudCgpLHNjb3BlOnR9KSx0fUwoKXtyZXR1cm4hKHRoaXMuUC5sZW5ndGg8PTEpJiYhIXRoaXMuUC5wb3AoKX19ZnVuY3Rpb24gc3QoKXtjb25zdCB0PW0oZCgpKTtyZXR1cm4gdC5zdGFjaz10LnN0YWNrfHxuZXcgaXQoZygiZGVmYXVsdEN1cnJlbnRTY29wZSIsKCk9Pm5ldyBudCksZygiZGVmYXVsdElzb2xhdGlvblNjb3BlIiwoKT0+bmV3IG50KSl9ZnVuY3Rpb24gY3QodCl7cmV0dXJuIHN0KCkud2l0aFNjb3BlKHQpfWZ1bmN0aW9uIHV0KHQsbil7Y29uc3QgZT1zdCgpO3JldHVybiBlLndpdGhTY29wZSgoKT0+KGUuZ2V0U3RhY2tUb3AoKS5zY29wZT10LG4odCkpKX1mdW5jdGlvbiBhdCh0KXtyZXR1cm4gc3QoKS53aXRoU2NvcGUoKCk9PnQoc3QoKS5nZXRJc29sYXRpb25TY29wZSgpKSl9ZnVuY3Rpb24gZnQodCl7Y29uc3Qgbj1tKHQpO3JldHVybiBuLmFjcz9uLmFjczp7d2l0aElzb2xhdGlvblNjb3BlOmF0LHdpdGhTY29wZTpjdCx3aXRoU2V0U2NvcGU6dXQsd2l0aFNldElzb2xhdGlvblNjb3BlOih0LG4pPT5hdChuKSxnZXRDdXJyZW50U2NvcGU6KCk9PnN0KCkuZ2V0U2NvcGUoKSxnZXRJc29sYXRpb25TY29wZTooKT0+c3QoKS5nZXRJc29sYXRpb25TY29wZSgpfX1mdW5jdGlvbiBodCgpe3JldHVybiBmdChkKCkpLmdldEN1cnJlbnRTY29wZSgpLmdldENsaWVudCgpfWZ1bmN0aW9uIHB0KHQpe2lmKHQpe2lmKCJvYmplY3QiPT10eXBlb2YgdCYmImRlcmVmImluIHQmJiJmdW5jdGlvbiI9PXR5cGVvZiB0LmRlcmVmKXRyeXtyZXR1cm4gdC5kZXJlZigpfWNhdGNoe3JldHVybn1yZXR1cm4gdH19ZnVuY3Rpb24gbHQodCl7Y29uc3Qgbj10O3JldHVybntzY29wZTpuLl9zZW50cnlTY29wZSxpc29sYXRpb25TY29wZTpwdChuLl9zZW50cnlJc29sYXRpb25TY29wZSl9fWNvbnN0IGR0PSJzZW50cnktIjtmdW5jdGlvbiBtdCh0KXtjb25zdCBuPWZ1bmN0aW9uKHQpe2lmKCF0fHwhQyh0KSYmIUFycmF5LmlzQXJyYXkodCkpcmV0dXJuO2lmKEFycmF5LmlzQXJyYXkodCkpcmV0dXJuIHQucmVkdWNlKCh0LG4pPT57Y29uc3QgZT1ndChuKTtyZXR1cm4gT2JqZWN0LmVudHJpZXMoZSkuZm9yRWFjaCgoW24sZV0pPT57dFtuXT1lfSksdH0se30pO3JldHVybiBndCh0KX0odCk7aWYoIW4pcmV0dXJuO2NvbnN0IGU9T2JqZWN0LmVudHJpZXMobikucmVkdWNlKCh0LFtuLGVdKT0+e2lmKG4uc3RhcnRzV2l0aChkdCkpe3Rbbi5zbGljZSg3KV09ZX1yZXR1cm4gdH0se30pO3JldHVybiBPYmplY3Qua2V5cyhlKS5sZW5ndGg+MD9lOnZvaWQgMH1mdW5jdGlvbiBndCh0KXtyZXR1cm4gdC5zcGxpdCgiLCIpLm1hcCh0PT57Y29uc3Qgbj10LmluZGV4T2YoIj0iKTtpZigtMT09PW4pcmV0dXJuW107cmV0dXJuW3Quc2xpY2UoMCxuKSx0LnNsaWNlKG4rMSldLm1hcCh0PT57dHJ5e3JldHVybiBkZWNvZGVVUklDb21wb25lbnQodC50cmltKCkpfWNhdGNoe3JldHVybn19KX0pLnJlZHVjZSgodCxbbixlXSk9PihuJiZlJiYodFtuXT1lKSx0KSx7fSl9Y29uc3QgeXQ9L15vKFxkKylcLi87ZnVuY3Rpb24gYnQodCxuPSExKXtjb25zdHtob3N0OmUscGF0aDpyLHBhc3M6byxwb3J0OmkscHJvamVjdElkOnMscHJvdG9jb2w6YyxwdWJsaWNLZXk6dX09dDtyZXR1cm5gJHtjfTovLyR7dX0ke24mJm8/YDoke299YDoiIn1AJHtlfSR7aT9gOiR7aX1gOiIifS8ke3I/YCR7cn0vYDpyfSR7c31gfWZ1bmN0aW9uIHZ0KHQpe2NvbnN0IG49dC5nZXRPcHRpb25zKCkse2hvc3Q6ZX09dC5nZXREc24oKXx8e307bGV0IHI7cmV0dXJuIG4ub3JnSWQ/cj1TdHJpbmcobi5vcmdJZCk6ZSYmKHI9ZnVuY3Rpb24odCl7Y29uc3Qgbj10Lm1hdGNoKHl0KTtyZXR1cm4gbj8uWzFdfShlKSkscn1mdW5jdGlvbiBfdCh0KXtjb25zdHtzcGFuSWQ6bix0cmFjZUlkOmUsaXNSZW1vdGU6cn09dC5zcGFuQ29udGV4dCgpLG89cj9uOkV0KHQpLnBhcmVudF9zcGFuX2lkLGk9bHQodCkuc2NvcGU7cmV0dXJue3BhcmVudF9zcGFuX2lkOm8sc3Bhbl9pZDpyP2k/LmdldFByb3BhZ2F0aW9uQ29udGV4dCgpLnByb3BhZ2F0aW9uU3BhbklkfHxxKCk6bix0cmFjZV9pZDplfX1mdW5jdGlvbiB3dCh0KXtyZXR1cm4gdCYmdC5sZW5ndGg+MD90Lm1hcCgoe2NvbnRleHQ6e3NwYW5JZDp0LHRyYWNlSWQ6bix0cmFjZUZsYWdzOmUsLi4ucn0sYXR0cmlidXRlczpvfSk9Pih7c3Bhbl9pZDp0LHRyYWNlX2lkOm4sc2FtcGxlZDoxPT09ZSxhdHRyaWJ1dGVzOm8sLi4ucn0pKTp2b2lkIDB9ZnVuY3Rpb24gU3QodCl7cmV0dXJuIm51bWJlciI9PXR5cGVvZiB0PyR0KHQpOkFycmF5LmlzQXJyYXkodCk/dFswXSt0WzFdLzFlOTp0IGluc3RhbmNlb2YgRGF0ZT8kdCh0LmdldFRpbWUoKSk6SigpfWZ1bmN0aW9uICR0KHQpe3JldHVybiB0Pjk5OTk5OTk5OTk/dC8xZTM6dH1mdW5jdGlvbiBFdCh0KXtpZihmdW5jdGlvbih0KXtyZXR1cm4iZnVuY3Rpb24iPT10eXBlb2YgdC5nZXRTcGFuSlNPTn0odCkpcmV0dXJuIHQuZ2V0U3BhbkpTT04oKTtjb25zdHtzcGFuSWQ6bix0cmFjZUlkOmV9PXQuc3BhbkNvbnRleHQoKTtpZihmdW5jdGlvbih0KXtjb25zdCBuPXQ7cmV0dXJuISEobi5hdHRyaWJ1dGVzJiZuLnN0YXJ0VGltZSYmbi5uYW1lJiZuLmVuZFRpbWUmJm4uc3RhdHVzKX0odCkpe2NvbnN0e2F0dHJpYnV0ZXM6cixzdGFydFRpbWU6byxuYW1lOmksZW5kVGltZTpzLHN0YXR1czpjLGxpbmtzOnV9PXQ7cmV0dXJue3NwYW5faWQ6bix0cmFjZV9pZDplLGRhdGE6cixkZXNjcmlwdGlvbjppLHBhcmVudF9zcGFuX2lkOnh0KHQpLHN0YXJ0X3RpbWVzdGFtcDpTdChvKSx0aW1lc3RhbXA6U3Qocyl8fHZvaWQgMCxzdGF0dXM6TnQoYyksb3A6clsic2VudHJ5Lm9wIl0sb3JpZ2luOnJbInNlbnRyeS5vcmlnaW4iXSxsaW5rczp3dCh1KX19cmV0dXJue3NwYW5faWQ6bix0cmFjZV9pZDplLHN0YXJ0X3RpbWVzdGFtcDowLGRhdGE6e319fWZ1bmN0aW9uIHh0KHQpe3JldHVybiJwYXJlbnRTcGFuSWQiaW4gdD90LnBhcmVudFNwYW5JZDoicGFyZW50U3BhbkNvbnRleHQiaW4gdD90LnBhcmVudFNwYW5Db250ZXh0Py5zcGFuSWQ6dm9pZCAwfWZ1bmN0aW9uIE50KHQpe2lmKHQmJjAhPT10LmNvZGUpcmV0dXJuIDE9PT10LmNvZGU/Im9rIjp0Lm1lc3NhZ2V8fCJpbnRlcm5hbF9lcnJvciJ9Y29uc3QganQ9ZnVuY3Rpb24odCl7cmV0dXJuIHQuX3NlbnRyeVJvb3RTcGFufHx0fTtmdW5jdGlvbiBDdCh0KXtjb25zdCBuPWh0KCk7aWYoIW4pcmV0dXJue307Y29uc3QgZT1qdCh0KSxyPUV0KGUpLG89ci5kYXRhLGk9ZS5zcGFuQ29udGV4dCgpLnRyYWNlU3RhdGUscz1pPy5nZXQoInNlbnRyeS5zYW1wbGVfcmF0ZSIpPz9vWyJzZW50cnkuc2FtcGxlX3JhdGUiXT8/b1sic2VudHJ5LnByZXZpb3VzX3RyYWNlX3NhbXBsZV9yYXRlIl07ZnVuY3Rpb24gYyh0KXtyZXR1cm4ibnVtYmVyIiE9dHlwZW9mIHMmJiJzdHJpbmciIT10eXBlb2Ygc3x8KHQuc2FtcGxlX3JhdGU9YCR7c31gKSx0fWNvbnN0IHU9ZS5fZnJvemVuRHNjO2lmKHUpcmV0dXJuIGModSk7Y29uc3QgYT1pPy5nZXQoInNlbnRyeS5kc2MiKSxmPWEmJm10KGEpO2lmKGYpcmV0dXJuIGMoZik7Y29uc3QgaD1mdW5jdGlvbih0LG4pe2NvbnN0IGU9bi5nZXRPcHRpb25zKCkse3B1YmxpY0tleTpyfT1uLmdldERzbigpfHx7fSxvPXtlbnZpcm9ubWVudDplLmVudmlyb25tZW50fHwicHJvZHVjdGlvbiIscmVsZWFzZTplLnJlbGVhc2UscHVibGljX2tleTpyLHRyYWNlX2lkOnQsb3JnX2lkOnZ0KG4pfTtyZXR1cm4gbi5lbWl0KCJjcmVhdGVEc2MiLG8pLG99KHQuc3BhbkNvbnRleHQoKS50cmFjZUlkLG4pLHA9b1sic2VudHJ5LnNvdXJjZSJdPz9vWyJzZW50cnkuc3Bhbi5zb3VyY2UiXSxsPXIuZGVzY3JpcHRpb247cmV0dXJuInVybCIhPT1wJiZsJiYoaC50cmFuc2FjdGlvbj1sKSxmdW5jdGlvbigpe2lmKCJib29sZWFuIj09dHlwZW9mIF9fU0VOVFJZX1RSQUNJTkdfXyYmIV9fU0VOVFJZX1RSQUNJTkdfXylyZXR1cm4hMTtjb25zdCB0PWh0KCk/LmdldE9wdGlvbnMoKTtyZXR1cm4hKCF0fHxudWxsPT10LnRyYWNlc1NhbXBsZVJhdGUmJiF0LnRyYWNlc1NhbXBsZXIpfSgpJiYoaC5zYW1wbGVkPVN0cmluZyhmdW5jdGlvbih0KXtjb25zdHt0cmFjZUZsYWdzOm59PXQuc3BhbkNvbnRleHQoKTtyZXR1cm4gMT09PW59KGUpKSxoLnNhbXBsZV9yYW5kPWk/LmdldCgic2VudHJ5LnNhbXBsZV9yYW5kIik/P2x0KGUpLnNjb3BlPy5nZXRQcm9wYWdhdGlvbkNvbnRleHQoKS5zYW1wbGVSYW5kLnRvU3RyaW5nKCkpLGMoaCksbi5lbWl0KCJjcmVhdGVEc2MiLGgsZSksaH1jb25zdCBBdD1TeW1ib2wuZm9yKCJzZW50cnkuc2tpcE5vcm1hbGl6YXRpb24iKSxrdD1TeW1ib2wuZm9yKCJzZW50cnkub3ZlcnJpZGVOb3JtYWxpemF0aW9uRGVwdGgiKTtmdW5jdGlvbiBUdCh0LG49MTAwLGU9MS8wKXt0cnl7cmV0dXJuIEl0KCIiLHQsbixlKX1jYXRjaCh0KXtyZXR1cm57RVJST1I6YCoqbm9uLXNlcmlhbGl6YWJsZSoqICgke3R9KWB9fX1mdW5jdGlvbiBJdCh0LG4sZT0xLzAscj0xLzAsbz1mdW5jdGlvbigpe2NvbnN0IHQ9bmV3IFdlYWtTZXQ7ZnVuY3Rpb24gbihuKXtyZXR1cm4hIXQuaGFzKG4pfHwodC5hZGQobiksITEpfWZ1bmN0aW9uIGUobil7dC5kZWxldGUobil9cmV0dXJuW24sZV19KCkpe2NvbnN0W2ksc109bztpZihudWxsPT1ufHxbImJvb2xlYW4iLCJzdHJpbmciXS5pbmNsdWRlcyh0eXBlb2Ygbil8fCJudW1iZXIiPT10eXBlb2YgbiYmTnVtYmVyLmlzRmluaXRlKG4pKXJldHVybiBuO2NvbnN0IGM9ZnVuY3Rpb24odCxuKXt0cnl7aWYoImRvbWFpbiI9PT10JiZuJiYib2JqZWN0Ij09dHlwZW9mIG4mJm4uTSlyZXR1cm4iW0RvbWFpbl0iO2lmKCJkb21haW5FbWl0dGVyIj09PXQpcmV0dXJuIltEb21haW5FbWl0dGVyXSI7aWYoInVuZGVmaW5lZCIhPXR5cGVvZiBnbG9iYWwmJm49PT1nbG9iYWwpcmV0dXJuIltHbG9iYWxdIjtpZigidW5kZWZpbmVkIiE9dHlwZW9mIHdpbmRvdyYmbj09PXdpbmRvdylyZXR1cm4iW1dpbmRvd10iO2lmKCJ1bmRlZmluZWQiIT10eXBlb2YgZG9jdW1lbnQmJm49PT1kb2N1bWVudClyZXR1cm4iW0RvY3VtZW50XSI7aWYoIm9iamVjdCI9PXR5cGVvZihlPW4pJiZudWxsIT09ZSYmKGUuX19pc1Z1ZXx8ZS5CfHxlLl9fdl9pc1ZOb2RlKSlyZXR1cm4gZnVuY3Rpb24odCl7cmV0dXJuIl9fdl9pc1ZOb2RlImluIHQmJnQuX192X2lzVk5vZGU/IltWdWVWTm9kZV0iOiJbVnVlVmlld01vZGVsXSJ9KG4pO2lmKGZ1bmN0aW9uKHQpe3JldHVybiBBKHQpJiYibmF0aXZlRXZlbnQiaW4gdCYmInByZXZlbnREZWZhdWx0ImluIHQmJiJzdG9wUHJvcGFnYXRpb24iaW4gdH0obikpcmV0dXJuIltTeW50aGV0aWNFdmVudF0iO2lmKCJudW1iZXIiPT10eXBlb2YgbiYmIU51bWJlci5pc0Zpbml0ZShuKSlyZXR1cm5gWyR7bn1dYDtpZigiZnVuY3Rpb24iPT10eXBlb2YgbilyZXR1cm5gW0Z1bmN0aW9uOiAke2Z1bmN0aW9uKHQpe3RyeXtyZXR1cm4gdCYmImZ1bmN0aW9uIj09dHlwZW9mIHQmJnQubmFtZXx8eH1jYXRjaHtyZXR1cm4geH19KG4pfV1gO2lmKCJzeW1ib2wiPT10eXBlb2YgbilyZXR1cm5gWyR7U3RyaW5nKG4pfV1gO2lmKCJiaWdpbnQiPT10eXBlb2YgbilyZXR1cm5gW0JpZ0ludDogJHtTdHJpbmcobil9XWA7Y29uc3Qgcj1mdW5jdGlvbih0KXtjb25zdCBuPU9iamVjdC5nZXRQcm90b3R5cGVPZih0KTtyZXR1cm4gbj8uY29uc3RydWN0b3I/bi5jb25zdHJ1Y3Rvci5uYW1lOiJudWxsIHByb3RvdHlwZSJ9KG4pO3JldHVybi9eSFRNTChcdyopRWxlbWVudCQvLnRlc3Qocik/YFtIVE1MRWxlbWVudDogJHtyfV1gOmBbb2JqZWN0ICR7cn1dYH1jYXRjaCh0KXtyZXR1cm5gKipub24tc2VyaWFsaXphYmxlKiogKCR7dH0pYH12YXIgZX0odCxuKTtpZighYy5zdGFydHNXaXRoKCJbb2JqZWN0ICIpKXJldHVybiBjO2lmKGZ1bmN0aW9uKHQpe3JldHVybiBCb29sZWFuKHRbQXRdKX0obikpcmV0dXJuIG47Y29uc3QgdT1mdW5jdGlvbih0KXtjb25zdCBuPXRba3RdO3JldHVybiJudW1iZXIiPT10eXBlb2Ygbj9uOnZvaWQgMH0obiksYT12b2lkIDAhPT11P3U6ZTtpZigwPT09YSlyZXR1cm4gYy5yZXBsYWNlKCJvYmplY3QgIiwiIik7aWYoaShuKSlyZXR1cm4iW0NpcmN1bGFyIH5dIjtjb25zdCBmPW47aWYoZiYmImZ1bmN0aW9uIj09dHlwZW9mIGYudG9KU09OKXRyeXtyZXR1cm4gSXQoIiIsZi50b0pTT04oKSxhLTEscixvKX1jYXRjaHt9Y29uc3QgaD1BcnJheS5pc0FycmF5KG4pP1tdOnt9O2xldCBwPTA7Y29uc3QgbD1SKG4pO2Zvcihjb25zdCB0IGluIGwpe2lmKCFPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwobCx0KSljb250aW51ZTtpZihwPj1yKXtoW3RdPSJbTWF4UHJvcGVydGllcyB+XSI7YnJlYWt9Y29uc3Qgbj1sW3RdO2hbdF09SXQodCxuLGEtMSxyLG8pLHArK31yZXR1cm4gcyhuKSxofWZ1bmN0aW9uIE90KHQsbil7Y29uc3QgZT1uLnJlcGxhY2UoL1xcL2csIi8iKS5yZXBsYWNlKC9bfFxce30oKVtcXV4kKyo/Ll0vZywiXFwkJiIpO2xldCByPXQ7dHJ5e3I9ZGVjb2RlVVJJKHQpfWNhdGNoe31yZXR1cm4gci5yZXBsYWNlKC9cXC9nLCIvIikucmVwbGFjZSgvd2VicGFjazpcLz8vZywiIikucmVwbGFjZShuZXcgUmVnRXhwKGAoZmlsZTovLyk/Lyoke2V9LypgLCJpZyIpLCJhcHA6Ly8vIil9ZnVuY3Rpb24gUnQodCxuPVtdKXtyZXR1cm5bdCxuXX1mdW5jdGlvbiBQdCh0LG4pe2NvbnN0IGU9dFsxXTtmb3IoY29uc3QgdCBvZiBlKXtpZihuKHQsdFswXS50eXBlKSlyZXR1cm4hMH1yZXR1cm4hMX1mdW5jdGlvbiBEdCh0KXtjb25zdCBuPW0ocCk7cmV0dXJuIG4uZW5jb2RlUG9seWZpbGw/bi5lbmNvZGVQb2x5ZmlsbCh0KToobmV3IFRleHRFbmNvZGVyKS5lbmNvZGUodCl9ZnVuY3Rpb24gVXQodCl7Y29uc3RbbixlXT10O2xldCByPUpTT04uc3RyaW5naWZ5KG4pO2Z1bmN0aW9uIG8odCl7InN0cmluZyI9PXR5cGVvZiByP3I9InN0cmluZyI9PXR5cGVvZiB0P3IrdDpbRHQociksdF06ci5wdXNoKCJzdHJpbmciPT10eXBlb2YgdD9EdCh0KTp0KX1mb3IoY29uc3QgdCBvZiBlKXtjb25zdFtuLGVdPXQ7aWYobyhgXG4ke0pTT04uc3RyaW5naWZ5KG4pfVxuYCksInN0cmluZyI9PXR5cGVvZiBlfHxlIGluc3RhbmNlb2YgVWludDhBcnJheSlvKGUpO2Vsc2V7bGV0IHQ7dHJ5e3Q9SlNPTi5zdHJpbmdpZnkoZSl9Y2F0Y2h7dD1KU09OLnN0cmluZ2lmeShUdChlKSl9byh0KX19cmV0dXJuInN0cmluZyI9PXR5cGVvZiByP3I6ZnVuY3Rpb24odCl7Y29uc3Qgbj10LnJlZHVjZSgodCxuKT0+dCtuLmxlbmd0aCwwKSxlPW5ldyBVaW50OEFycmF5KG4pO2xldCByPTA7Zm9yKGNvbnN0IG4gb2YgdCllLnNldChuLHIpLHIrPW4ubGVuZ3RoO3JldHVybiBlfShyKX1jb25zdCBMdD17c2Vzc2lvbnM6InNlc3Npb24iLGV2ZW50OiJlcnJvciIsY2xpZW50X3JlcG9ydDoiaW50ZXJuYWwiLHVzZXJfcmVwb3J0OiJkZWZhdWx0Iixwcm9maWxlX2NodW5rOiJwcm9maWxlIixyZXBsYXlfZXZlbnQ6InJlcGxheSIscmVwbGF5X3JlY29yZGluZzoicmVwbGF5IixjaGVja19pbjoibW9uaXRvciIscmF3X3NlY3VyaXR5OiJzZWN1cml0eSIsbG9nOiJsb2dfaXRlbSIsdHJhY2VfbWV0cmljOiJtZXRyaWMifTtmdW5jdGlvbiBNdCh0KXtyZXR1cm4gZnVuY3Rpb24odCl7cmV0dXJuIHQgaW4gTHR9KHQpP0x0W3RdOnR9ZnVuY3Rpb24gQnQodCl7aWYoIXQ/LnNkaylyZXR1cm47Y29uc3R7bmFtZTpuLHZlcnNpb246ZX09dC5zZGs7cmV0dXJue25hbWU6bix2ZXJzaW9uOmV9fWZ1bmN0aW9uIHp0KHQsbixlLHIpe2NvbnN0IG89QnQoZSksaT10LnR5cGUmJiJyZXBsYXlfZXZlbnQiIT09dC50eXBlP3QudHlwZToiZXZlbnQiOyFmdW5jdGlvbih0LG4pe2lmKCFuKXJldHVybiB0O2NvbnN0IGU9dC5zZGt8fHt9O3Quc2RrPXsuLi5lLG5hbWU6ZS5uYW1lfHxuLm5hbWUsdmVyc2lvbjplLnZlcnNpb258fG4udmVyc2lvbixpbnRlZ3JhdGlvbnM6Wy4uLnQuc2RrPy5pbnRlZ3JhdGlvbnN8fFtdLC4uLm4uaW50ZWdyYXRpb25zfHxbXV0scGFja2FnZXM6Wy4uLnQuc2RrPy5wYWNrYWdlc3x8W10sLi4ubi5wYWNrYWdlc3x8W11dLHNldHRpbmdzOnQuc2RrPy5zZXR0aW5nc3x8bi5zZXR0aW5ncz97Li4udC5zZGs/LnNldHRpbmdzLC4uLm4uc2V0dGluZ3N9OnZvaWQgMH19KHQsZT8uc2RrKTtjb25zdCBzPWZ1bmN0aW9uKHQsbixlLHIpe2NvbnN0IG89dC5zZGtQcm9jZXNzaW5nTWV0YWRhdGE/LmR5bmFtaWNTYW1wbGluZ0NvbnRleHQ7cmV0dXJue2V2ZW50X2lkOnQuZXZlbnRfaWQsc2VudF9hdDoobmV3IERhdGUpLnRvSVNPU3RyaW5nKCksLi4ubiYme3NkazpufSwuLi4hIWUmJnImJntkc246YnQocil9LC4uLm8mJnt0cmFjZTpvfX19KHQsbyxyLG4pO2RlbGV0ZSB0LnNka1Byb2Nlc3NpbmdNZXRhZGF0YTtyZXR1cm4gUnQocyxbW3t0eXBlOml9LHRdXSl9Y29uc3QgV3Q9Il9fU0VOVFJZX1NVUFBSRVNTX1RSQUNJTkdfXyI7ZnVuY3Rpb24gRnQodCl7Y29uc3Qgbj1mdChkKCkpO3JldHVybiBuLnN1cHByZXNzVHJhY2luZz9uLnN1cHByZXNzVHJhY2luZyh0KTpmdW5jdGlvbiguLi50KXtjb25zdCBuPWZ0KGQoKSk7aWYoMj09PXQubGVuZ3RoKXtjb25zdFtlLHJdPXQ7cmV0dXJuIGU/bi53aXRoU2V0U2NvcGUoZSxyKTpuLndpdGhTY29wZShyKX1yZXR1cm4gbi53aXRoU2NvcGUodFswXSl9KG49PntuLnNldFNES1Byb2Nlc3NpbmdNZXRhZGF0YSh7W1d0XTohMH0pO2NvbnN0IGU9dCgpO3JldHVybiBuLnNldFNES1Byb2Nlc3NpbmdNZXRhZGF0YSh7W1d0XTp2b2lkIDB9KSxlfSl9ZnVuY3Rpb24gR3QodCxuKXtjb25zdHtmaW5nZXJwcmludDplLHNwYW46cixicmVhZGNydW1iczpvLHNka1Byb2Nlc3NpbmdNZXRhZGF0YTppfT1uOyFmdW5jdGlvbih0LG4pe2NvbnN0e2V4dHJhOmUsdGFnczpyLHVzZXI6byxjb250ZXh0czppLGxldmVsOnMsdHJhbnNhY3Rpb25OYW1lOmN9PW47T2JqZWN0LmtleXMoZSkubGVuZ3RoJiYodC5leHRyYT17Li4uZSwuLi50LmV4dHJhfSk7T2JqZWN0LmtleXMocikubGVuZ3RoJiYodC50YWdzPXsuLi5yLC4uLnQudGFnc30pO09iamVjdC5rZXlzKG8pLmxlbmd0aCYmKHQudXNlcj17Li4ubywuLi50LnVzZXJ9KTtPYmplY3Qua2V5cyhpKS5sZW5ndGgmJih0LmNvbnRleHRzPXsuLi5pLC4uLnQuY29udGV4dHN9KTtzJiYodC5sZXZlbD1zKTtjJiYidHJhbnNhY3Rpb24iIT09dC50eXBlJiYodC50cmFuc2FjdGlvbj1jKX0odCxuKSxyJiZmdW5jdGlvbih0LG4pe3QuY29udGV4dHM9e3RyYWNlOl90KG4pLC4uLnQuY29udGV4dHN9LHQuc2RrUHJvY2Vzc2luZ01ldGFkYXRhPXtkeW5hbWljU2FtcGxpbmdDb250ZXh0OkN0KG4pLC4uLnQuc2RrUHJvY2Vzc2luZ01ldGFkYXRhfTtjb25zdCBlPWp0KG4pLHI9RXQoZSkuZGVzY3JpcHRpb247ciYmIXQudHJhbnNhY3Rpb24mJiJ0cmFuc2FjdGlvbiI9PT10LnR5cGUmJih0LnRyYW5zYWN0aW9uPXIpfSh0LHIpLGZ1bmN0aW9uKHQsbil7dC5maW5nZXJwcmludD10LmZpbmdlcnByaW50P0FycmF5LmlzQXJyYXkodC5maW5nZXJwcmludCk/dC5maW5nZXJwcmludDpbdC5maW5nZXJwcmludF06W10sbiYmKHQuZmluZ2VycHJpbnQ9dC5maW5nZXJwcmludC5jb25jYXQobikpO3QuZmluZ2VycHJpbnQubGVuZ3RofHxkZWxldGUgdC5maW5nZXJwcmludH0odCxlKSxmdW5jdGlvbih0LG4pe2NvbnN0IGU9Wy4uLnQuYnJlYWRjcnVtYnN8fFtdLC4uLm5dO3QuYnJlYWRjcnVtYnM9ZS5sZW5ndGg/ZTp2b2lkIDB9KHQsbyksZnVuY3Rpb24odCxuKXt0LnNka1Byb2Nlc3NpbmdNZXRhZGF0YT17Li4udC5zZGtQcm9jZXNzaW5nTWV0YWRhdGEsLi4ubn19KHQsaSl9Y2xhc3MgSHR7Y29uc3RydWN0b3IodCl7dGhpcy5XPTAsdGhpcy5GPVtdLHRoaXMuRyh0KX10aGVuKHQsbil7cmV0dXJuIG5ldyBIdCgoZSxyKT0+e3RoaXMuRi5wdXNoKFshMSxuPT57aWYodCl0cnl7ZSh0KG4pKX1jYXRjaCh0KXtyKHQpfWVsc2UgZShuKX0sdD0+e2lmKG4pdHJ5e2Uobih0KSl9Y2F0Y2godCl7cih0KX1lbHNlIHIodCl9XSksdGhpcy5IKCl9KX1jYXRjaCh0KXtyZXR1cm4gdGhpcy50aGVuKHQ9PnQsdCl9ZmluYWxseSh0KXtyZXR1cm4gbmV3IEh0KChuLGUpPT57bGV0IHIsbztyZXR1cm4gdGhpcy50aGVuKG49PntvPSExLHI9bix0JiZ0KCl9LG49PntvPSEwLHI9bix0JiZ0KCl9KS50aGVuKCgpPT57bz9lKHIpOm4ocil9KX0pfUgoKXtpZigwPT09dGhpcy5XKXJldHVybjtjb25zdCB0PXRoaXMuRi5zbGljZSgpO3RoaXMuRj1bXSx0LmZvckVhY2godD0+e3RbMF18fCgxPT09dGhpcy5XJiZ0WzFdKHRoaXMuSiksMj09PXRoaXMuVyYmdFsyXSh0aGlzLkopLHRbMF09ITApfSl9Ryh0KXtjb25zdCBuPSh0LG4pPT57MD09PXRoaXMuVyYmKGsobik/bi50aGVuKGUscik6KHRoaXMuVz10LHRoaXMuSj1uLHRoaXMuSCgpKSl9LGU9dD0+e24oMSx0KX0scj10PT57bigyLHQpfTt0cnl7dChlLHIpfWNhdGNoKHQpe3IodCl9fX1jb25zdCBKdD1TeW1ib2wuZm9yKCJTZW50cnlCdWZmZXJGdWxsRXJyb3IiKTtmdW5jdGlvbiBZdCh0PTEwMCl7Y29uc3Qgbj1uZXcgU2V0O2Z1bmN0aW9uIGUodCl7bi5kZWxldGUodCl9cmV0dXJue2dldCAkKCl7cmV0dXJuIEFycmF5LmZyb20obil9LGFkZDpmdW5jdGlvbihyKXtpZighKG4uc2l6ZTx0KSlyZXR1cm4gbz1KdCxuZXcgSHQoKHQsbik9PntuKG8pfSk7dmFyIG87Y29uc3QgaT1yKCk7cmV0dXJuIG4uYWRkKGkpLGkudGhlbigoKT0+ZShpKSwoKT0+ZShpKSksaX0sZHJhaW46ZnVuY3Rpb24odCl7aWYoIW4uc2l6ZSlyZXR1cm4gZT0hMCxuZXcgSHQodD0+e3QoZSl9KTt2YXIgZTtjb25zdCByPVByb21pc2UuYWxsU2V0dGxlZChBcnJheS5mcm9tKG4pKS50aGVuKCgpPT4hMCk7aWYoIXQpcmV0dXJuIHI7Y29uc3Qgbz1bcixuZXcgUHJvbWlzZShuPT57cmV0dXJuIm9iamVjdCI9PXR5cGVvZihlPXNldFRpbWVvdXQoKCk9Pm4oITEpLHQpKSYmImZ1bmN0aW9uIj09dHlwZW9mIGUudW5yZWYmJmUudW5yZWYoKSxlO3ZhciBlfSldO3JldHVybiBQcm9taXNlLnJhY2Uobyl9fX1mdW5jdGlvbiBWdCh0LHtzdGF0dXNDb2RlOm4saGVhZGVyczplfSxyPXooKSl7Y29uc3Qgbz17Li4udH0saT1lPy5bIngtc2VudHJ5LXJhdGUtbGltaXRzIl0scz1lPy5bInJldHJ5LWFmdGVyIl07aWYoaSlmb3IoY29uc3QgdCBvZiBpLnRyaW0oKS5zcGxpdCgiLCIpKXtjb25zdFtuLGUsLCxpXT10LnNwbGl0KCI6Iiw1KSxzPXBhcnNlSW50KG4sMTApLGM9MWUzKihpc05hTihzKT82MDpzKTtpZihlKWZvcihjb25zdCB0IG9mIGUuc3BsaXQoIjsiKSkibWV0cmljX2J1Y2tldCI9PT10JiZpJiYhaS5zcGxpdCgiOyIpLmluY2x1ZGVzKCJjdXN0b20iKXx8KG9bdF09citjKTtlbHNlIG8uYWxsPXIrY31lbHNlIHM/by5hbGw9citmdW5jdGlvbih0LG49eigpKXtjb25zdCBlPXBhcnNlSW50KGAke3R9YCwxMCk7aWYoIWlzTmFOKGUpKXJldHVybiAxZTMqZTtjb25zdCByPURhdGUucGFyc2UoYCR7dH1gKTtyZXR1cm4gaXNOYU4ocik/NmU0OnItbn0ocyxyKTo0Mjk9PT1uJiYoby5hbGw9cis2ZTQpO3JldHVybiBvfWZ1bmN0aW9uIEt0KHQsbixlPVl0KHQuYnVmZmVyU2l6ZXx8NjQpKXtsZXQgcj17fTtyZXR1cm57c2VuZDpmdW5jdGlvbih0KXtjb25zdCBvPVtdO2lmKFB0KHQsKHQsbik9Pntjb25zdCBlPU10KG4pOyhmdW5jdGlvbih0LG4sZT16KCkpe3JldHVybiBmdW5jdGlvbih0LG4pe3JldHVybiB0W25dfHx0LmFsbHx8MH0odCxuKT5lfSkocixlKXx8by5wdXNoKHQpfSksMD09PW8ubGVuZ3RoKXJldHVybiBQcm9taXNlLnJlc29sdmUoe30pO2NvbnN0IGk9UnQodFswXSxvKSxzPXQ9PnshZnVuY3Rpb24odCxuKXtyZXR1cm4gUHQodCwodCxlKT0+bi5pbmNsdWRlcyhlKSl9KGksWyJjbGllbnRfcmVwb3J0Il0pP1B0KGksKHQsbik9Pnt9KTpoJiZTLndhcm4oYERyb3BwaW5nIGNsaWVudCByZXBvcnQuIFdpbGwgbm90IHNlbmQgb3V0Y29tZXMgKHJlYXNvbjogJHt0fSkuYCl9O3JldHVybiBlLmFkZCgoKT0+bih7Ym9keTpVdChpKX0pLnRoZW4odD0+NDEzPT09dC5zdGF0dXNDb2RlPyhoJiZTLmVycm9yKCJTZW50cnkgcmVzcG9uZGVkIHdpdGggc3RhdHVzIGNvZGUgNDEzLiBFbnZlbG9wZSB3YXMgZGlzY2FyZGVkIGR1ZSB0byBleGNlZWRpbmcgc2l6ZSBsaW1pdHMuIikscygic2VuZF9lcnJvciIpLHQpOihoJiZ2b2lkIDAhPT10LnN0YXR1c0NvZGUmJih0LnN0YXR1c0NvZGU8MjAwfHx0LnN0YXR1c0NvZGU+PTMwMCkmJlMud2FybihgU2VudHJ5IHJlc3BvbmRlZCB3aXRoIHN0YXR1cyBjb2RlICR7dC5zdGF0dXNDb2RlfSB0byBzZW50IGV2ZW50LmApLHI9VnQocix0KSx0KSx0PT57dGhyb3cgcygibmV0d29ya19lcnJvciIpLGgmJlMuZXJyb3IoIkVuY291bnRlcmVkIGVycm9yIHJ1bm5pbmcgdHJhbnNwb3J0IHJlcXVlc3Q6Iix0KSx0fSkpLnRoZW4odD0+dCx0PT57aWYodD09PUp0KXJldHVybiBoJiZTLmVycm9yKCJTa2lwcGVkIHNlbmRpbmcgZXZlbnQgYmVjYXVzZSBidWZmZXIgaXMgZnVsbC4iKSxzKCJxdWV1ZV9vdmVyZmxvdyIpLFByb21pc2UucmVzb2x2ZSh7fSk7dGhyb3cgdH0pfSxmbHVzaDp0PT5lLmRyYWluKHQpfX1jb25zdCBadD0vXihcUys6XFx8XC8/KShbXHNcU10qPykoKD86XC57MSwyfXxbXi9cXF0rP3wpKFwuW14uL1xcXSp8KSkoPzpbL1xcXSopJC87ZnVuY3Rpb24gcXQodCl7Y29uc3Qgbj1mdW5jdGlvbih0KXtjb25zdCBuPXQubGVuZ3RoPjEwMjQ/YDx0cnVuY2F0ZWQ+JHt0LnNsaWNlKC0xMDI0KX1gOnQsZT1adC5leGVjKG4pO3JldHVybiBlP2Uuc2xpY2UoMSk6W119KHQpLGU9blswXXx8IiI7bGV0IHI9blsxXTtyZXR1cm4gZXx8cj8ociYmKHI9ci5zbGljZSgwLHIubGVuZ3RoLTEpKSxlK3IpOiIuIn1mdW5jdGlvbiBRdCh0LG49ITEpe3JldHVybiEobnx8dCYmIXQuc3RhcnRzV2l0aCgiLyIpJiYhdC5tYXRjaCgvXltBLVpdOi8pJiYhdC5zdGFydHNXaXRoKCIuIikmJiF0Lm1hdGNoKC9eW2EtekEtWl0oW2EtekEtWjAtOS5cLStdKSo6XC9cLy8pKSYmdm9pZCAwIT09dCYmIXQuaW5jbHVkZXMoIm5vZGVfbW9kdWxlcy8iKX12YXIgWHQ7Y29uc3QgdG49U3ltYm9sKCJBZ2VudEJhc2VJbnRlcm5hbFN0YXRlIik7Y2xhc3Mgbm4gZXh0ZW5kcyhYdD1pLkFnZW50LFh0KXtjb25zdHJ1Y3Rvcih0KXtzdXBlcih0KSx0aGlzW3RuXT17fX1pc1NlY3VyZUVuZHBvaW50KHQpe2lmKHQpe2lmKCJib29sZWFuIj09dHlwZW9mIHQuc2VjdXJlRW5kcG9pbnQpcmV0dXJuIHQuc2VjdXJlRW5kcG9pbnQ7aWYoInN0cmluZyI9PXR5cGVvZiB0LnByb3RvY29sKXJldHVybiJodHRwczoiPT09dC5wcm90b2NvbH1jb25zdHtzdGFjazpufT1uZXcgRXJyb3I7cmV0dXJuInN0cmluZyI9PXR5cGVvZiBuJiZuLnNwbGl0KCJcbiIpLnNvbWUodD0+LTEhPT10LmluZGV4T2YoIihodHRwcy5qczoiKXx8LTEhPT10LmluZGV4T2YoIm5vZGU6aHR0cHM6IikpfWNyZWF0ZVNvY2tldCh0LG4sZSl7Y29uc3Qgcj17Li4ubixzZWN1cmVFbmRwb2ludDp0aGlzLmlzU2VjdXJlRW5kcG9pbnQobil9O1Byb21pc2UucmVzb2x2ZSgpLnRoZW4oKCk9PnRoaXMuY29ubmVjdCh0LHIpKS50aGVuKG89PntpZihvIGluc3RhbmNlb2YgaS5BZ2VudClyZXR1cm4gby5hZGRSZXF1ZXN0KHQscik7dGhpc1t0bl0uY3VycmVudFNvY2tldD1vLHN1cGVyLmNyZWF0ZVNvY2tldCh0LG4sZSl9LGUpfWNyZWF0ZUNvbm5lY3Rpb24oKXtjb25zdCB0PXRoaXNbdG5dLmN1cnJlbnRTb2NrZXQ7aWYodGhpc1t0bl0uY3VycmVudFNvY2tldD12b2lkIDAsIXQpdGhyb3cgbmV3IEVycm9yKCJObyBzb2NrZXQgd2FzIHJldHVybmVkIGluIHRoZSBgY29ubmVjdCgpYCBmdW5jdGlvbiIpO3JldHVybiB0fWdldCBkZWZhdWx0UG9ydCgpe3JldHVybiB0aGlzW3RuXS5kZWZhdWx0UG9ydD8/KCJodHRwczoiPT09dGhpcy5wcm90b2NvbD80NDM6ODApfXNldCBkZWZhdWx0UG9ydCh0KXt0aGlzW3RuXSYmKHRoaXNbdG5dLmRlZmF1bHRQb3J0PXQpfWdldCBwcm90b2NvbCgpe3JldHVybiB0aGlzW3RuXS5wcm90b2NvbD8/KHRoaXMuaXNTZWN1cmVFbmRwb2ludCgpPyJodHRwczoiOiJodHRwOiIpfXNldCBwcm90b2NvbCh0KXt0aGlzW3RuXSYmKHRoaXNbdG5dLnByb3RvY29sPXQpfX1mdW5jdGlvbiBlbiguLi50KXtTLmxvZygiW2h0dHBzLXByb3h5LWFnZW50OnBhcnNlLXByb3h5LXJlc3BvbnNlXSIsLi4udCl9ZnVuY3Rpb24gcm4odCl7cmV0dXJuIG5ldyBQcm9taXNlKChuLGUpPT57bGV0IHI9MDtjb25zdCBvPVtdO2Z1bmN0aW9uIGkoKXtjb25zdCBjPXQucmVhZCgpO2M/ZnVuY3Rpb24oYyl7by5wdXNoKGMpLHIrPWMubGVuZ3RoO2NvbnN0IHU9QnVmZmVyLmNvbmNhdChvLHIpLGE9dS5pbmRleE9mKCJcclxuXHJcbiIpO2lmKC0xPT09YSlyZXR1cm4gZW4oImhhdmUgbm90IHJlY2VpdmVkIGVuZCBvZiBIVFRQIGhlYWRlcnMgeWV0Li4uIiksdm9pZCBpKCk7Y29uc3QgZj11LnN1YmFycmF5KDAsYSkudG9TdHJpbmcoImFzY2lpIikuc3BsaXQoIlxyXG4iKSxoPWYuc2hpZnQoKTtpZighaClyZXR1cm4gdC5kZXN0cm95KCksZShuZXcgRXJyb3IoIk5vIGhlYWRlciByZWNlaXZlZCBmcm9tIHByb3h5IENPTk5FQ1QgcmVzcG9uc2UiKSk7Y29uc3QgcD1oLnNwbGl0KCIgIiksbD0rKHBbMV18fDApLGQ9cC5zbGljZSgyKS5qb2luKCIgIiksbT17fTtmb3IoY29uc3QgbiBvZiBmKXtpZighbiljb250aW51ZTtjb25zdCByPW4uaW5kZXhPZigiOiIpO2lmKC0xPT09cilyZXR1cm4gdC5kZXN0cm95KCksZShuZXcgRXJyb3IoYEludmFsaWQgaGVhZGVyIGZyb20gcHJveHkgQ09OTkVDVCByZXNwb25zZTogIiR7bn0iYCkpO2NvbnN0IG89bi5zbGljZSgwLHIpLnRvTG93ZXJDYXNlKCksaT1uLnNsaWNlKHIrMSkudHJpbVN0YXJ0KCkscz1tW29dOyJzdHJpbmciPT10eXBlb2Ygcz9tW29dPVtzLGldOkFycmF5LmlzQXJyYXkocyk/cy5wdXNoKGkpOm1bb109aX1lbigiZ290IHByb3h5IHNlcnZlciByZXNwb25zZTogJW8gJW8iLGgsbSkscygpLG4oe2Nvbm5lY3Q6e3N0YXR1c0NvZGU6bCxzdGF0dXNUZXh0OmQsaGVhZGVyczptfSxidWZmZXJlZDp1fSl9KGMpOnQub25jZSgicmVhZGFibGUiLGkpfWZ1bmN0aW9uIHMoKXt0LnJlbW92ZUxpc3RlbmVyKCJlbmQiLGMpLHQucmVtb3ZlTGlzdGVuZXIoImVycm9yIix1KSx0LnJlbW92ZUxpc3RlbmVyKCJyZWFkYWJsZSIsaSl9ZnVuY3Rpb24gYygpe3MoKSxlbigib25lbmQiKSxlKG5ldyBFcnJvcigiUHJveHkgY29ubmVjdGlvbiBlbmRlZCBiZWZvcmUgcmVjZWl2aW5nIENPTk5FQ1QgcmVzcG9uc2UiKSl9ZnVuY3Rpb24gdSh0KXtzKCksZW4oIm9uZXJyb3IgJW8iLHQpLGUodCl9dC5vbigiZXJyb3IiLHUpLHQub24oImVuZCIsYyksaSgpfSl9ZnVuY3Rpb24gb24oLi4udCl7Uy5sb2coIltodHRwcy1wcm94eS1hZ2VudF0iLC4uLnQpfWNsYXNzIHNuIGV4dGVuZHMgbm57Y29uc3RydWN0b3IodCxuKXtzdXBlcihuKSx0aGlzLm9wdGlvbnM9e30sdGhpcy5wcm94eT0ic3RyaW5nIj09dHlwZW9mIHQ/bmV3IFVSTCh0KTp0LHRoaXMucHJveHlIZWFkZXJzPW4/LmhlYWRlcnM/P3t9LG9uKCJDcmVhdGluZyBuZXcgSHR0cHNQcm94eUFnZW50IGluc3RhbmNlOiAlbyIsdGhpcy5wcm94eS5ocmVmKTtjb25zdCBlPSh0aGlzLnByb3h5Lmhvc3RuYW1lfHx0aGlzLnByb3h5Lmhvc3QpLnJlcGxhY2UoL15cW3xcXSQvZywiIikscj10aGlzLnByb3h5LnBvcnQ/cGFyc2VJbnQodGhpcy5wcm94eS5wb3J0LDEwKToiaHR0cHM6Ij09PXRoaXMucHJveHkucHJvdG9jb2w/NDQzOjgwO3RoaXMuY29ubmVjdE9wdHM9e0FMUE5Qcm90b2NvbHM6WyJodHRwLzEuMSJdLC4uLm4/dW4obiwiaGVhZGVycyIpOm51bGwsaG9zdDplLHBvcnQ6cn19YXN5bmMgY29ubmVjdCh0LG4pe2NvbnN0e3Byb3h5OmV9PXRoaXM7aWYoIW4uaG9zdCl0aHJvdyBuZXcgVHlwZUVycm9yKCdObyAiaG9zdCIgcHJvdmlkZWQnKTtsZXQgcjtpZigiaHR0cHM6Ij09PWUucHJvdG9jb2wpe29uKCJDcmVhdGluZyBgdGxzLlNvY2tldGA6ICVvIix0aGlzLmNvbm5lY3RPcHRzKTtjb25zdCB0PXRoaXMuY29ubmVjdE9wdHMuc2VydmVybmFtZXx8dGhpcy5jb25uZWN0T3B0cy5ob3N0O3I9Zi5jb25uZWN0KHsuLi50aGlzLmNvbm5lY3RPcHRzLHNlcnZlcm5hbWU6dCYmYS5pc0lQKHQpP3ZvaWQgMDp0fSl9ZWxzZSBvbigiQ3JlYXRpbmcgYG5ldC5Tb2NrZXRgOiAlbyIsdGhpcy5jb25uZWN0T3B0cykscj1hLmNvbm5lY3QodGhpcy5jb25uZWN0T3B0cyk7Y29uc3Qgbz0iZnVuY3Rpb24iPT10eXBlb2YgdGhpcy5wcm94eUhlYWRlcnM/dGhpcy5wcm94eUhlYWRlcnMoKTp7Li4udGhpcy5wcm94eUhlYWRlcnN9LGk9YS5pc0lQdjYobi5ob3N0KT9gWyR7bi5ob3N0fV1gOm4uaG9zdDtsZXQgcz1gQ09OTkVDVCAke2l9OiR7bi5wb3J0fSBIVFRQLzEuMVxyXG5gO2lmKGUudXNlcm5hbWV8fGUucGFzc3dvcmQpe2NvbnN0IHQ9YCR7ZGVjb2RlVVJJQ29tcG9uZW50KGUudXNlcm5hbWUpfToke2RlY29kZVVSSUNvbXBvbmVudChlLnBhc3N3b3JkKX1gO29bIlByb3h5LUF1dGhvcml6YXRpb24iXT1gQmFzaWMgJHtCdWZmZXIuZnJvbSh0KS50b1N0cmluZygiYmFzZTY0Iil9YH1vLkhvc3Q9YCR7aX06JHtuLnBvcnR9YCxvWyJQcm94eS1Db25uZWN0aW9uIl18fChvWyJQcm94eS1Db25uZWN0aW9uIl09dGhpcy5rZWVwQWxpdmU/IktlZXAtQWxpdmUiOiJjbG9zZSIpO2Zvcihjb25zdCB0IG9mIE9iamVjdC5rZXlzKG8pKXMrPWAke3R9OiAke29bdF19XHJcbmA7Y29uc3QgYz1ybihyKTtyLndyaXRlKGAke3N9XHJcbmApO2NvbnN0e2Nvbm5lY3Q6dSxidWZmZXJlZDpofT1hd2FpdCBjO2lmKHQuZW1pdCgicHJveHlDb25uZWN0Iix1KSx0aGlzLmVtaXQoInByb3h5Q29ubmVjdCIsdSx0KSwyMDA9PT11LnN0YXR1c0NvZGUpe2lmKHQub25jZSgic29ja2V0Iixjbiksbi5zZWN1cmVFbmRwb2ludCl7b24oIlVwZ3JhZGluZyBzb2NrZXQgY29ubmVjdGlvbiB0byBUTFMiKTtjb25zdCB0PW4uc2VydmVybmFtZXx8bi5ob3N0O3JldHVybiBmLmNvbm5lY3Qoey4uLnVuKG4sImhvc3QiLCJwYXRoIiwicG9ydCIpLHNvY2tldDpyLHNlcnZlcm5hbWU6YS5pc0lQKHQpP3ZvaWQgMDp0fSl9cmV0dXJuIHJ9ci5kZXN0cm95KCk7Y29uc3QgcD1uZXcgYS5Tb2NrZXQoe3dyaXRhYmxlOiExfSk7cmV0dXJuIHAucmVhZGFibGU9ITAsdC5vbmNlKCJzb2NrZXQiLHQ9PntvbigiUmVwbGF5aW5nIHByb3h5IGJ1ZmZlciBmb3IgZmFpbGVkIHJlcXVlc3QiKSx0LnB1c2goaCksdC5wdXNoKG51bGwpfSkscH19ZnVuY3Rpb24gY24odCl7dC5yZXN1bWUoKX1mdW5jdGlvbiB1bih0LC4uLm4pe2NvbnN0IGU9e307bGV0IHI7Zm9yKHIgaW4gdCluLmluY2x1ZGVzKHIpfHwoZVtyXT10W3JdKTtyZXR1cm4gZX1zbi5wcm90b2NvbHM9WyJodHRwIiwiaHR0cHMiXTtmdW5jdGlvbiBhbih0KXtyZXR1cm4gdC5yZXBsYWNlKC9eW0EtWl06LywiIikucmVwbGFjZSgvXFwvZywiLyIpfWNvbnN0IGZuPW47bGV0IGhuLHBuPTAsbG49e307ZnVuY3Rpb24gZG4odCl7Zm4uZGVidWcmJmNvbnNvbGUubG9nKGBbQU5SIFdvcmtlcl0gJHt0fWApfXZhciBtbixnbix5bjtjb25zdCBibj1mdW5jdGlvbih0KXtsZXQgbjt0cnl7bj1uZXcgVVJMKHQudXJsKX1jYXRjaChuKXtyZXR1cm4gYigoKT0+e2NvbnNvbGUud2FybigiW0BzZW50cnkvbm9kZV06IEludmFsaWQgZHNuIG9yIHR1bm5lbCBvcHRpb24sIHdpbGwgbm90IHNlbmQgYW55IGV2ZW50cy4gVGhlIHR1bm5lbCBvcHRpb24gbXVzdCBiZSBhIGZ1bGwgVVJMIHdoZW4gdXNlZC4iKX0pLEt0KHQsKCk9PlByb21pc2UucmVzb2x2ZSh7fSkpfWNvbnN0IGU9Imh0dHBzOiI9PT1uLnByb3RvY29sLHI9ZnVuY3Rpb24odCxuKXtjb25zdHtub19wcm94eTplfT1wcm9jZXNzLmVudixyPWU/LnNwbGl0KCIsIikuc29tZShuPT50Lmhvc3QuZW5kc1dpdGgobil8fHQuaG9zdG5hbWUuZW5kc1dpdGgobikpO3JldHVybiByP3ZvaWQgMDpufShuLHQucHJveHl8fChlP3Byb2Nlc3MuZW52Lmh0dHBzX3Byb3h5OnZvaWQgMCl8fHByb2Nlc3MuZW52Lmh0dHBfcHJveHkpLG89ZT9zOmksYT12b2lkIDAhPT10LmtlZXBBbGl2ZSYmdC5rZWVwQWxpdmUsZj1yP25ldyBzbihyKTpuZXcgby5BZ2VudCh7a2VlcEFsaXZlOmEsbWF4U29ja2V0czozMCx0aW1lb3V0OjJlM30pLGg9ZnVuY3Rpb24odCxuLGUpe2NvbnN0e2hvc3RuYW1lOnIscGF0aG5hbWU6byxwb3J0OmkscHJvdG9jb2w6cyxzZWFyY2g6YX09bmV3IFVSTCh0LnVybCk7cmV0dXJuIGZ1bmN0aW9uKGYpe3JldHVybiBuZXcgUHJvbWlzZSgoaCxwKT0+e0Z0KCgpPT57bGV0IGw9ZnVuY3Rpb24odCl7cmV0dXJuIG5ldyBjKHtyZWFkKCl7dGhpcy5wdXNoKHQpLHRoaXMucHVzaChudWxsKX19KX0oZi5ib2R5KTtjb25zdCBkPXsuLi50LmhlYWRlcnN9O2YuYm9keS5sZW5ndGg+MzI3NjgmJihkWyJjb250ZW50LWVuY29kaW5nIl09Imd6aXAiLGw9bC5waXBlKHUoKSkpO2NvbnN0IG09ci5zdGFydHNXaXRoKCJbIiksZz1uLnJlcXVlc3Qoe21ldGhvZDoiUE9TVCIsYWdlbnQ6ZSxoZWFkZXJzOmQsaG9zdG5hbWU6bT9yLnNsaWNlKDEsLTEpOnIscGF0aDpgJHtvfSR7YX1gLHBvcnQ6aSxwcm90b2NvbDpzLGNhOnQuY2FDZXJ0c30sdD0+e3Qub24oImRhdGEiLCgpPT57fSksdC5vbigiZW5kIiwoKT0+e30pLHQuc2V0RW5jb2RpbmcoInV0ZjgiKTtjb25zdCBuPXQuaGVhZGVyc1sicmV0cnktYWZ0ZXIiXT8/bnVsbCxlPXQuaGVhZGVyc1sieC1zZW50cnktcmF0ZS1saW1pdHMiXT8/bnVsbDtoKHtzdGF0dXNDb2RlOnQuc3RhdHVzQ29kZSxoZWFkZXJzOnsicmV0cnktYWZ0ZXIiOm4sIngtc2VudHJ5LXJhdGUtbGltaXRzIjpBcnJheS5pc0FycmF5KGUpP2VbMF18fG51bGw6ZX19KX0pO2cub24oImVycm9yIixwKSxsLnBpcGUoZyl9KX0pfX0odCx0Lmh0dHBNb2R1bGU/P28sZik7cmV0dXJuIEt0KHQsaCl9KHt1cmw6KG1uPWZuLmRzbixnbj1mbi50dW5uZWwseW49Zm4uc2RrTWV0YWRhdGEuc2RrLGdufHxgJHtmdW5jdGlvbih0KXtyZXR1cm5gJHtmdW5jdGlvbih0KXtjb25zdCBuPXQucHJvdG9jb2w/YCR7dC5wcm90b2NvbH06YDoiIixlPXQucG9ydD9gOiR7dC5wb3J0fWA6IiI7cmV0dXJuYCR7bn0vLyR7dC5ob3N0fSR7ZX0ke3QucGF0aD9gLyR7dC5wYXRofWA6IiJ9L2FwaS9gfSh0KX0ke3QucHJvamVjdElkfS9lbnZlbG9wZS9gfShtbil9PyR7ZnVuY3Rpb24odCxuKXtjb25zdCBlPXtzZW50cnlfdmVyc2lvbjoiNyJ9O3JldHVybiB0LnB1YmxpY0tleSYmKGUuc2VudHJ5X2tleT10LnB1YmxpY0tleSksbiYmKGUuc2VudHJ5X2NsaWVudD1gJHtuLm5hbWV9LyR7bi52ZXJzaW9ufWApLG5ldyBVUkxTZWFyY2hQYXJhbXMoZSkudG9TdHJpbmcoKX0obW4seW4pfWApfSk7YXN5bmMgZnVuY3Rpb24gdm4oKXtpZihobil7ZG4oIlNlbmRpbmcgYWJub3JtYWwgc2Vzc2lvbiIpLFYoaG4se3N0YXR1czoiYWJub3JtYWwiLGFibm9ybWFsX21lY2hhbmlzbToiYW5yX2ZvcmVncm91bmQiLHJlbGVhc2U6Zm4ucmVsZWFzZSxlbnZpcm9ubWVudDpmbi5lbnZpcm9ubWVudH0pO2NvbnN0IHQ9ZnVuY3Rpb24odCxuLGUscil7Y29uc3Qgbz1CdChlKTtyZXR1cm4gUnQoe3NlbnRfYXQ6KG5ldyBEYXRlKS50b0lTT1N0cmluZygpLC4uLm8mJntzZGs6b30sLi4uISFyJiZuJiZ7ZHNuOmJ0KG4pfX0sWyJhZ2dyZWdhdGVzImluIHQ/W3t0eXBlOiJzZXNzaW9ucyJ9LHRdOlt7dHlwZToic2Vzc2lvbiJ9LHQudG9KU09OKCldXSl9KGhuLGZuLmRzbixmbi5zZGtNZXRhZGF0YSxmbi50dW5uZWwpO2RuKEpTT04uc3RyaW5naWZ5KHQpKSxhd2FpdCBibi5zZW5kKHQpO3RyeXtlPy5wb3N0TWVzc2FnZSgic2Vzc2lvbi1lbmRlZCIpfWNhdGNoe319fWZ1bmN0aW9uIF9uKHQpe2lmKCF0KXJldHVybjtjb25zdCBuPWZ1bmN0aW9uKHQpe2lmKCF0Lmxlbmd0aClyZXR1cm5bXTtjb25zdCBuPUFycmF5LmZyb20odCk7cmV0dXJuL3NlbnRyeVdyYXBwZWQvLnRlc3QoRShuKS5mdW5jdGlvbnx8IiIpJiZuLnBvcCgpLG4ucmV2ZXJzZSgpLCQudGVzdChFKG4pLmZ1bmN0aW9ufHwiIikmJihuLnBvcCgpLCQudGVzdChFKG4pLmZ1bmN0aW9ufHwiIikmJm4ucG9wKCkpLG4uc2xpY2UoMCw1MCkubWFwKHQ9Pih7Li4udCxmaWxlbmFtZTp0LmZpbGVuYW1lfHxFKG4pLmZpbGVuYW1lLGZ1bmN0aW9uOnQuZnVuY3Rpb258fCI/In0pKX0odCk7aWYoZm4uYXBwUm9vdFBhdGgpZm9yKGNvbnN0IHQgb2Ygbil0LmZpbGVuYW1lJiYodC5maWxlbmFtZT1PdCh0LmZpbGVuYW1lLGZuLmFwcFJvb3RQYXRoKSk7cmV0dXJuIG59YXN5bmMgZnVuY3Rpb24gd24odCxuKXtpZihwbj49Zm4ubWF4QW5yRXZlbnRzKXJldHVybjtwbis9MSxhd2FpdCB2bigpLGRuKCJTZW5kaW5nIGV2ZW50Iik7Y29uc3QgZT17ZXZlbnRfaWQ6RigpLGNvbnRleHRzOmZuLmNvbnRleHRzLHJlbGVhc2U6Zm4ucmVsZWFzZSxlbnZpcm9ubWVudDpmbi5lbnZpcm9ubWVudCxkaXN0OmZuLmRpc3QscGxhdGZvcm06Im5vZGUiLGxldmVsOiJlcnJvciIsZXhjZXB0aW9uOnt2YWx1ZXM6W3t0eXBlOiJBcHBsaWNhdGlvbk5vdFJlc3BvbmRpbmciLHZhbHVlOmBBcHBsaWNhdGlvbiBOb3QgUmVzcG9uZGluZyBmb3IgYXQgbGVhc3QgJHtmbi5hbnJUaHJlc2hvbGR9IG1zYCxzdGFja3RyYWNlOntmcmFtZXM6X24odCl9LG1lY2hhbmlzbTp7dHlwZToiQU5SIn19XX0sdGFnczpmbi5zdGF0aWNUYWdzfTtuJiZmdW5jdGlvbih0LG4pe2lmKEd0KHQsbiksIXQuY29udGV4dHM/LnRyYWNlKXtjb25zdHt0cmFjZUlkOmUscGFyZW50U3BhbklkOnIscHJvcGFnYXRpb25TcGFuSWQ6b309bi5wcm9wYWdhdGlvbkNvbnRleHQ7dC5jb250ZXh0cz17dHJhY2U6e3RyYWNlX2lkOmUsc3Bhbl9pZDpvfHxxKCkscGFyZW50X3NwYW5faWQ6cn0sLi4udC5jb250ZXh0c319fShlLG4pLGZ1bmN0aW9uKHQpe2lmKDA9PT1PYmplY3Qua2V5cyhsbikubGVuZ3RoKXJldHVybjtjb25zdCBuPWZuLmFwcFJvb3RQYXRoP3t9OmxuO2lmKGZuLmFwcFJvb3RQYXRoKWZvcihjb25zdFt0LGVdb2YgT2JqZWN0LmVudHJpZXMobG4pKW5bT3QodCxmbi5hcHBSb290UGF0aCldPWU7Y29uc3QgZT1uZXcgTWFwO2Zvcihjb25zdCByIG9mIHQuZXhjZXB0aW9uPy52YWx1ZXN8fFtdKWZvcihjb25zdCB0IG9mIHIuc3RhY2t0cmFjZT8uZnJhbWVzfHxbXSl7Y29uc3Qgcj10LmFic19wYXRofHx0LmZpbGVuYW1lO3ImJm5bcl0mJmUuc2V0KHIsbltyXSl9aWYoZS5zaXplPjApe2NvbnN0IG49W107Zm9yKGNvbnN0W3Qscl1vZiBlLmVudHJpZXMoKSluLnB1c2goe3R5cGU6InNvdXJjZW1hcCIsY29kZV9maWxlOnQsZGVidWdfaWQ6cn0pO3QuZGVidWdfbWV0YT17aW1hZ2VzOm59fX0oZSk7Y29uc3Qgcj16dChlLGZuLmRzbixmbi5zZGtNZXRhZGF0YSxmbi50dW5uZWwpO2RuKEpTT04uc3RyaW5naWZ5KHIpKSxhd2FpdCBibi5zZW5kKHIpLGF3YWl0IGJuLmZsdXNoKDJlMykscG4+PWZuLm1heEFuckV2ZW50cyYmc2V0VGltZW91dCgoKT0+e3Byb2Nlc3MuZXhpdCgwKX0sNWUzKX1sZXQgU247aWYoZG4oIlN0YXJ0ZWQiKSxmbi5jYXB0dXJlU3RhY2tUcmFjZSl7ZG4oIkNvbm5lY3RpbmcgdG8gZGVidWdnZXIiKTtjb25zdCBuPW5ldyB0O24uY29ubmVjdFRvTWFpblRocmVhZCgpLGRuKCJDb25uZWN0ZWQgdG8gZGVidWdnZXIiKTtjb25zdCBlPW5ldyBNYXA7bi5vbigiRGVidWdnZXIuc2NyaXB0UGFyc2VkIix0PT57ZS5zZXQodC5wYXJhbXMuc2NyaXB0SWQsdC5wYXJhbXMudXJsKX0pLG4ub24oIkRlYnVnZ2VyLnBhdXNlZCIsdD0+e2lmKCJvdGhlciI9PT10LnBhcmFtcy5yZWFzb24pdHJ5e2RuKCJEZWJ1Z2dlciBwYXVzZWQiKTtjb25zdCBpPVsuLi50LnBhcmFtcy5jYWxsRnJhbWVzXSxzPWZuLmFwcFJvb3RQYXRoP2Z1bmN0aW9uKHQ9KHByb2Nlc3MuYXJndlsxXT9xdChwcm9jZXNzLmFyZ3ZbMV0pOnByb2Nlc3MuY3dkKCkpLG49IlxcIj09PW8pe2NvbnN0IGU9bj9hbih0KTp0O3JldHVybiB0PT57aWYoIXQpcmV0dXJuO2NvbnN0IG89bj9hbih0KTp0O2xldHtkaXI6aSxiYXNlOnMsZXh0OmN9PXIucGFyc2Uobyk7Ii5qcyIhPT1jJiYiLm1qcyIhPT1jJiYiLmNqcyIhPT1jfHwocz1zLnNsaWNlKDAsLTEqYy5sZW5ndGgpKTtjb25zdCB1PWRlY29kZVVSSUNvbXBvbmVudChzKTtpfHwoaT0iLiIpO2NvbnN0IGE9aS5sYXN0SW5kZXhPZigiL25vZGVfbW9kdWxlcyIpO2lmKGE+LTEpcmV0dXJuYCR7aS5zbGljZShhKzE0KS5yZXBsYWNlKC9cLy9nLCIuIil9OiR7dX1gO2lmKGkuc3RhcnRzV2l0aChlKSl7Y29uc3QgdD1pLnNsaWNlKGUubGVuZ3RoKzEpLnJlcGxhY2UoL1wvL2csIi4iKTtyZXR1cm4gdD9gJHt0fToke3V9YDp1fXJldHVybiB1fX0oZm4uYXBwUm9vdFBhdGgpOigpPT57fSxjPWkubWFwKHQ9PmZ1bmN0aW9uKHQsbixlKXtjb25zdCByPW4/bi5yZXBsYWNlKC9eZmlsZTpcL1wvLywiIik6dm9pZCAwLG89dC5sb2NhdGlvbi5jb2x1bW5OdW1iZXI/dC5sb2NhdGlvbi5jb2x1bW5OdW1iZXIrMTp2b2lkIDAsaT10LmxvY2F0aW9uLmxpbmVOdW1iZXI/dC5sb2NhdGlvbi5saW5lTnVtYmVyKzE6dm9pZCAwO3JldHVybntmaWxlbmFtZTpyLG1vZHVsZTplKHIpLGZ1bmN0aW9uOnQuZnVuY3Rpb25OYW1lfHwiPyIsY29sbm86byxsaW5lbm86aSxpbl9hcHA6cj9RdChyKTp2b2lkIDB9fSh0LGUuZ2V0KHQubG9jYXRpb24uc2NyaXB0SWQpLHMpKSx1PXNldFRpbWVvdXQoKCk9Pnt3bihjKS50aGVuKG51bGwsKCk9PntkbigiU2VuZGluZyBBTlIgZXZlbnQgZmFpbGVkLiIpfSl9LDVlMyk7bi5wb3N0KCJSdW50aW1lLmV2YWx1YXRlIix7ZXhwcmVzc2lvbjoiZ2xvYmFsLl9fU0VOVFJZX0dFVF9TQ09QRVNfXygpOyIsc2lsZW50OiEwLHJldHVybkJ5VmFsdWU6ITB9LCh0LGUpPT57dCYmZG4oYEVycm9yIGV4ZWN1dGluZyBzY3JpcHQ6ICcke3QubWVzc2FnZX0nYCksY2xlYXJUaW1lb3V0KHUpO2NvbnN0IHI9ZT8ucmVzdWx0P2UucmVzdWx0LnZhbHVlOnZvaWQgMDtuLnBvc3QoIkRlYnVnZ2VyLnJlc3VtZSIpLG4ucG9zdCgiRGVidWdnZXIuZGlzYWJsZSIpLHduKGMscikudGhlbihudWxsLCgpPT57ZG4oIlNlbmRpbmcgQU5SIGV2ZW50IGZhaWxlZC4iKX0pfSl9Y2F0Y2godCl7dGhyb3cgbi5wb3N0KCJEZWJ1Z2dlci5yZXN1bWUiKSxuLnBvc3QoIkRlYnVnZ2VyLmRpc2FibGUiKSx0fX0pLFNuPSgpPT57dHJ5e24ucG9zdCgiRGVidWdnZXIuZW5hYmxlIiwoKT0+e24ucG9zdCgiRGVidWdnZXIucGF1c2UiKX0pfWNhdGNoe319fWNvbnN0e3BvbGw6JG59PWZ1bmN0aW9uKHQsbixlLHIpe2NvbnN0IG89dCgpO2xldCBpPSExLHM9ITA7cmV0dXJuIHNldEludGVydmFsKCgpPT57Y29uc3QgdD1vLmdldFRpbWVNcygpOyExPT09aSYmdD5uK2UmJihpPSEwLHMmJnIoKSksdDxuK2UmJihpPSExKX0sMjApLHtwb2xsOigpPT57by5yZXNldCgpfSxlbmFibGVkOnQ9PntzPXR9fX0oZnVuY3Rpb24oKXtsZXQgdD1wcm9jZXNzLmhydGltZSgpO3JldHVybntnZXRUaW1lTXM6KCk9Pntjb25zdFtuLGVdPXByb2Nlc3MuaHJ0aW1lKHQpO3JldHVybiBNYXRoLmZsb29yKDFlMypuK2UvMWU2KX0scmVzZXQ6KCk9Pnt0PXByb2Nlc3MuaHJ0aW1lKCl9fX0sZm4ucG9sbEludGVydmFsLGZuLmFuclRocmVzaG9sZCxmdW5jdGlvbigpe2RuKCJXYXRjaGRvZyB0aW1lb3V0IiksU24/KGRuKCJQYXVzaW5nIGRlYnVnZ2VyIHRvIGNhcHR1cmUgc3RhY2sgdHJhY2UiKSxTbigpKTooZG4oIkNhcHR1cmluZyBldmVudCB3aXRob3V0IGEgc3RhY2sgdHJhY2UiKSx3bigpLnRoZW4obnVsbCwoKT0+e2RuKCJTZW5kaW5nIEFOUiBldmVudCBmYWlsZWQgb24gd2F0Y2hkb2cgdGltZW91dC4iKX0pKX0pO2U/Lm9uKCJtZXNzYWdlIix0PT57dC5zZXNzaW9uJiYoaG49WSh0LnNlc3Npb24pKSx0LmRlYnVnSW1hZ2VzJiYobG49dC5kZWJ1Z0ltYWdlcyksJG4oKX0pOw=="; | ||
| const DEFAULT_INTERVAL = 50; | ||
| const DEFAULT_HANG_THRESHOLD = 5000; | ||
| const DEFAULT_HANG_THRESHOLD = 5e3; | ||
| function log(message, ...args) { | ||
| debug.log(`[ANR] ${message}`, ...args); | ||
| } | ||
| function globalWithScopeFetchFn() { | ||
| return GLOBAL_OBJ; | ||
| } | ||
| /** Fetches merged scope data */ | ||
| function getScopeData() { | ||
| const scope = getCombinedScopeData(getIsolationScope(), getCurrentScope()); | ||
| // We remove attachments because they likely won't serialize well as json | ||
| scope.attachments = []; | ||
| // We can't serialize event processor functions | ||
| scope.eventProcessors = []; | ||
| return scope; | ||
| } | ||
| /** | ||
| * Gets contexts by calling all event processors. This shouldn't be called until all integrations are setup | ||
| */ | ||
| async function getContexts(client) { | ||
| let event = { message: 'ANR' }; | ||
| let event = { message: "ANR" }; | ||
| const eventHint = {}; | ||
| for (const processor of client.getEventProcessors()) { | ||
@@ -47,22 +31,13 @@ if (event === null) break; | ||
| } | ||
| return event?.contexts || {}; | ||
| } | ||
| const INTEGRATION_NAME = 'Anr'; | ||
| // eslint-disable-next-line deprecation/deprecation | ||
| const INTEGRATION_NAME = "Anr"; | ||
| const _anrIntegration = ((options = {}) => { | ||
| if (NODE_VERSION.major < 16 || (NODE_VERSION.major === 16 && NODE_VERSION.minor < 17)) { | ||
| throw new Error('ANR detection requires Node 16.17.0 or later'); | ||
| if (NODE_VERSION.major < 16 || NODE_VERSION.major === 16 && NODE_VERSION.minor < 17) { | ||
| throw new Error("ANR detection requires Node 16.17.0 or later"); | ||
| } | ||
| let worker; | ||
| let client; | ||
| // Hookup the scope fetch function to the global object so that it can be called from the worker thread via the | ||
| // debugger when it pauses | ||
| const gbl = globalWithScopeFetchFn(); | ||
| gbl.__SENTRY_GET_SCOPES__ = getScopeData; | ||
| return { | ||
@@ -74,3 +49,2 @@ name: INTEGRATION_NAME, | ||
| } | ||
| if (client) { | ||
@@ -82,6 +56,5 @@ worker = _startWorker(client, options); | ||
| if (worker) { | ||
| // eslint-disable-next-line @typescript-eslint/no-floating-promises | ||
| worker.then(stop => { | ||
| worker.then((stop) => { | ||
| stop(); | ||
| worker = undefined; | ||
| worker = void 0; | ||
| }); | ||
@@ -92,86 +65,25 @@ } | ||
| client = initClient; | ||
| if (options.captureStackTrace && (await isDebuggerEnabled())) { | ||
| debug.warn('ANR captureStackTrace has been disabled because the debugger was already enabled'); | ||
| if (options.captureStackTrace && await isDebuggerEnabled()) { | ||
| debug.warn("ANR captureStackTrace has been disabled because the debugger was already enabled"); | ||
| options.captureStackTrace = false; | ||
| } | ||
| // setImmediate is used to ensure that all other integrations have had their setup called first. | ||
| // This allows us to call into all integrations to fetch the full context | ||
| setImmediate(() => this.startWorker()); | ||
| }, | ||
| } ; | ||
| }) ; | ||
| // eslint-disable-next-line deprecation/deprecation | ||
| /** | ||
| * Application Not Responding (ANR) integration for Node.js applications. | ||
| * | ||
| * @deprecated The ANR integration has been deprecated. Use `eventLoopBlockIntegration` from `@sentry/node-native` instead. | ||
| * | ||
| * Detects when the Node.js main thread event loop is blocked for more than the configured | ||
| * threshold (5 seconds by default) and reports these as Sentry events. | ||
| * | ||
| * ANR detection uses a worker thread to monitor the event loop in the main app thread. | ||
| * The main app thread sends a heartbeat message to the ANR worker thread every 50ms by default. | ||
| * If the ANR worker does not receive a heartbeat message for the configured threshold duration, | ||
| * it triggers an ANR event. | ||
| * | ||
| * - Node.js 16.17.0 or higher | ||
| * - Only supported in the Node.js runtime (not browsers) | ||
| * - Not supported for Node.js clusters | ||
| * | ||
| * Overhead should be minimal: | ||
| * - Main thread: Only polling the ANR worker over IPC every 50ms | ||
| * - Worker thread: Consumes around 10-20 MB of RAM | ||
| * - When ANR detected: Brief pause in debugger to capture stack trace (negligible compared to the blocking) | ||
| * | ||
| * @example | ||
| * ```javascript | ||
| * Sentry.init({ | ||
| * dsn: "https://examplePublicKey@o0.ingest.sentry.io/0", | ||
| * integrations: [ | ||
| * Sentry.anrIntegration({ | ||
| * anrThreshold: 5000, | ||
| * captureStackTrace: true, | ||
| * pollInterval: 50, | ||
| * }), | ||
| * ], | ||
| * }); | ||
| * ``` | ||
| */ | ||
| const anrIntegration = defineIntegration(_anrIntegration) ; | ||
| /** | ||
| * Starts the ANR worker thread | ||
| * | ||
| * @returns A function to stop the worker | ||
| */ | ||
| async function _startWorker( | ||
| client, | ||
| // eslint-disable-next-line deprecation/deprecation | ||
| integrationOptions, | ||
| ) { | ||
| } | ||
| }; | ||
| }); | ||
| const anrIntegration = defineIntegration(_anrIntegration); | ||
| async function _startWorker(client, integrationOptions) { | ||
| const dsn = client.getDsn(); | ||
| if (!dsn) { | ||
| return () => { | ||
| // | ||
| }; | ||
| } | ||
| const contexts = await getContexts(client); | ||
| // These will not be accurate if sent later from the worker thread | ||
| delete contexts.app?.app_memory; | ||
| delete contexts.device?.free_memory; | ||
| const initOptions = client.getOptions(); | ||
| const sdkMetadata = client.getSdkMetadata() || {}; | ||
| if (sdkMetadata.sdk) { | ||
| sdkMetadata.sdk.integrations = initOptions.integrations.map(i => i.name); | ||
| sdkMetadata.sdk.integrations = initOptions.integrations.map((i) => i.name); | ||
| } | ||
| const options = { | ||
@@ -181,3 +93,3 @@ debug: debug.isEnabled(), | ||
| tunnel: initOptions.tunnel, | ||
| environment: initOptions.environment || 'production', | ||
| environment: initOptions.environment || "production", | ||
| release: initOptions.release, | ||
@@ -192,5 +104,4 @@ dist: initOptions.dist, | ||
| staticTags: integrationOptions.staticTags || {}, | ||
| contexts, | ||
| contexts | ||
| }; | ||
| if (options.captureStackTrace) { | ||
@@ -202,3 +113,2 @@ const inspector = await import('node:inspector'); | ||
| } | ||
| const worker = new Worker(new URL(`data:application/javascript;base64,${base64WorkerScript}`), { | ||
@@ -208,47 +118,32 @@ workerData: options, | ||
| execArgv: [], | ||
| env: { ...process.env, NODE_OPTIONS: undefined }, | ||
| env: { ...process.env, NODE_OPTIONS: void 0 } | ||
| }); | ||
| process.on('exit', () => { | ||
| // eslint-disable-next-line @typescript-eslint/no-floating-promises | ||
| process.on("exit", () => { | ||
| worker.terminate(); | ||
| }); | ||
| const timer = setInterval(() => { | ||
| try { | ||
| const currentSession = getIsolationScope().getSession(); | ||
| // We need to copy the session object and remove the toJSON method so it can be sent to the worker | ||
| // serialized without making it a SerializedSession | ||
| const session = currentSession ? { ...currentSession, toJSON: undefined } : undefined; | ||
| // message the worker to tell it the main event loop is still running | ||
| const session = currentSession ? { ...currentSession, toJSON: void 0 } : void 0; | ||
| worker.postMessage({ session, debugImages: getFilenameToDebugIdMap(initOptions.stackParser) }); | ||
| } catch { | ||
| // | ||
| } | ||
| }, options.pollInterval); | ||
| // Timer should not block exit | ||
| timer.unref(); | ||
| worker.on('message', (msg) => { | ||
| if (msg === 'session-ended') { | ||
| log('ANR event sent from ANR worker. Clearing session in this thread.'); | ||
| getIsolationScope().setSession(undefined); | ||
| worker.on("message", (msg) => { | ||
| if (msg === "session-ended") { | ||
| log("ANR event sent from ANR worker. Clearing session in this thread."); | ||
| getIsolationScope().setSession(void 0); | ||
| } | ||
| }); | ||
| worker.once('error', (err) => { | ||
| worker.once("error", (err) => { | ||
| clearInterval(timer); | ||
| log('ANR worker error', err); | ||
| log("ANR worker error", err); | ||
| }); | ||
| worker.once('exit', (code) => { | ||
| worker.once("exit", (code) => { | ||
| clearInterval(timer); | ||
| log('ANR worker exit', code); | ||
| log("ANR worker exit", code); | ||
| }); | ||
| // Ensure this thread can't block app exit | ||
| worker.unref(); | ||
| return () => { | ||
| // eslint-disable-next-line @typescript-eslint/no-floating-promises | ||
| worker.terminate(); | ||
@@ -258,27 +153,8 @@ clearInterval(timer); | ||
| } | ||
| /** | ||
| * @see {@link disableBlockDetectionForCallback} | ||
| * | ||
| * @deprecated The ANR integration has been deprecated. Use `eventLoopBlockIntegration` from `@sentry/node-native` instead. | ||
| */ | ||
| /** | ||
| * Temporarily disables ANR detection for the duration of a callback function. | ||
| * | ||
| * This utility function allows you to disable ANR detection during operations that | ||
| * are expected to block the event loop, such as intensive computational tasks or | ||
| * synchronous I/O operations. | ||
| * | ||
| * @deprecated The ANR integration has been deprecated. Use `eventLoopBlockIntegration` from `@sentry/node-native` instead. | ||
| */ | ||
| function disableAnrDetectionForCallback(callback) { | ||
| const integration = getClient()?.getIntegrationByName(INTEGRATION_NAME) ; | ||
| const integration = getClient()?.getIntegrationByName(INTEGRATION_NAME); | ||
| if (!integration) { | ||
| return callback(); | ||
| } | ||
| integration.stopWorker(); | ||
| const result = callback(); | ||
@@ -288,3 +164,2 @@ if (isPromise(result)) { | ||
| } | ||
| integration.startWorker(); | ||
@@ -291,0 +166,0 @@ return result; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.js","sources":["../../../../src/integrations/anr/index.ts"],"sourcesContent":["import { types } from 'node:util';\nimport { Worker } from 'node:worker_threads';\nimport type { Contexts, Event, EventHint, Integration, IntegrationFn, ScopeData } from '@sentry/core';\nimport {\n debug,\n defineIntegration,\n getClient,\n getCombinedScopeData,\n getCurrentScope,\n getFilenameToDebugIdMap,\n getIsolationScope,\n GLOBAL_OBJ,\n} from '@sentry/core';\nimport { NODE_VERSION } from '../../nodeVersion';\nimport type { NodeClient } from '../../sdk/client';\nimport { isDebuggerEnabled } from '../../utils/debug';\nimport type { AnrIntegrationOptions, WorkerStartData } from './common';\n\nconst { isPromise } = types;\n\n// This string is a placeholder that gets overwritten with the worker code.\nexport const base64WorkerScript = '###AnrWorkerScript###';\n\nconst DEFAULT_INTERVAL = 50;\nconst DEFAULT_HANG_THRESHOLD = 5000;\n\nfunction log(message: string, ...args: unknown[]): void {\n debug.log(`[ANR] ${message}`, ...args);\n}\n\nfunction globalWithScopeFetchFn(): typeof GLOBAL_OBJ & { __SENTRY_GET_SCOPES__?: () => ScopeData } {\n return GLOBAL_OBJ;\n}\n\n/** Fetches merged scope data */\nfunction getScopeData(): ScopeData {\n const scope = getCombinedScopeData(getIsolationScope(), getCurrentScope());\n\n // We remove attachments because they likely won't serialize well as json\n scope.attachments = [];\n // We can't serialize event processor functions\n scope.eventProcessors = [];\n\n return scope;\n}\n\n/**\n * Gets contexts by calling all event processors. This shouldn't be called until all integrations are setup\n */\nasync function getContexts(client: NodeClient): Promise<Contexts> {\n let event: Event | null = { message: 'ANR' };\n const eventHint: EventHint = {};\n\n for (const processor of client.getEventProcessors()) {\n if (event === null) break;\n event = await processor(event, eventHint);\n }\n\n return event?.contexts || {};\n}\n\nconst INTEGRATION_NAME = 'Anr';\n\ntype AnrInternal = { startWorker: () => void; stopWorker: () => void };\n\n// eslint-disable-next-line deprecation/deprecation\nconst _anrIntegration = ((options: Partial<AnrIntegrationOptions> = {}) => {\n if (NODE_VERSION.major < 16 || (NODE_VERSION.major === 16 && NODE_VERSION.minor < 17)) {\n throw new Error('ANR detection requires Node 16.17.0 or later');\n }\n\n let worker: Promise<() => void> | undefined;\n let client: NodeClient | undefined;\n\n // Hookup the scope fetch function to the global object so that it can be called from the worker thread via the\n // debugger when it pauses\n const gbl = globalWithScopeFetchFn();\n gbl.__SENTRY_GET_SCOPES__ = getScopeData;\n\n return {\n name: INTEGRATION_NAME,\n startWorker: () => {\n if (worker) {\n return;\n }\n\n if (client) {\n worker = _startWorker(client, options);\n }\n },\n stopWorker: () => {\n if (worker) {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n worker.then(stop => {\n stop();\n worker = undefined;\n });\n }\n },\n async setup(initClient: NodeClient) {\n client = initClient;\n\n if (options.captureStackTrace && (await isDebuggerEnabled())) {\n debug.warn('ANR captureStackTrace has been disabled because the debugger was already enabled');\n options.captureStackTrace = false;\n }\n\n // setImmediate is used to ensure that all other integrations have had their setup called first.\n // This allows us to call into all integrations to fetch the full context\n setImmediate(() => this.startWorker());\n },\n } as Integration & AnrInternal;\n}) satisfies IntegrationFn;\n\n// eslint-disable-next-line deprecation/deprecation\ntype AnrReturn = (options?: Partial<AnrIntegrationOptions>) => Integration & AnrInternal;\n\n/**\n * Application Not Responding (ANR) integration for Node.js applications.\n *\n * @deprecated The ANR integration has been deprecated. Use `eventLoopBlockIntegration` from `@sentry/node-native` instead.\n *\n * Detects when the Node.js main thread event loop is blocked for more than the configured\n * threshold (5 seconds by default) and reports these as Sentry events.\n *\n * ANR detection uses a worker thread to monitor the event loop in the main app thread.\n * The main app thread sends a heartbeat message to the ANR worker thread every 50ms by default.\n * If the ANR worker does not receive a heartbeat message for the configured threshold duration,\n * it triggers an ANR event.\n *\n * - Node.js 16.17.0 or higher\n * - Only supported in the Node.js runtime (not browsers)\n * - Not supported for Node.js clusters\n *\n * Overhead should be minimal:\n * - Main thread: Only polling the ANR worker over IPC every 50ms\n * - Worker thread: Consumes around 10-20 MB of RAM\n * - When ANR detected: Brief pause in debugger to capture stack trace (negligible compared to the blocking)\n *\n * @example\n * ```javascript\n * Sentry.init({\n * dsn: \"https://examplePublicKey@o0.ingest.sentry.io/0\",\n * integrations: [\n * Sentry.anrIntegration({\n * anrThreshold: 5000,\n * captureStackTrace: true,\n * pollInterval: 50,\n * }),\n * ],\n * });\n * ```\n */\nexport const anrIntegration = defineIntegration(_anrIntegration) as AnrReturn;\n\n/**\n * Starts the ANR worker thread\n *\n * @returns A function to stop the worker\n */\nasync function _startWorker(\n client: NodeClient,\n // eslint-disable-next-line deprecation/deprecation\n integrationOptions: Partial<AnrIntegrationOptions>,\n): Promise<() => void> {\n const dsn = client.getDsn();\n\n if (!dsn) {\n return () => {\n //\n };\n }\n\n const contexts = await getContexts(client);\n\n // These will not be accurate if sent later from the worker thread\n delete contexts.app?.app_memory;\n delete contexts.device?.free_memory;\n\n const initOptions = client.getOptions();\n\n const sdkMetadata = client.getSdkMetadata() || {};\n if (sdkMetadata.sdk) {\n sdkMetadata.sdk.integrations = initOptions.integrations.map(i => i.name);\n }\n\n const options: WorkerStartData = {\n debug: debug.isEnabled(),\n dsn,\n tunnel: initOptions.tunnel,\n environment: initOptions.environment || 'production',\n release: initOptions.release,\n dist: initOptions.dist,\n sdkMetadata,\n appRootPath: integrationOptions.appRootPath,\n pollInterval: integrationOptions.pollInterval || DEFAULT_INTERVAL,\n anrThreshold: integrationOptions.anrThreshold || DEFAULT_HANG_THRESHOLD,\n captureStackTrace: !!integrationOptions.captureStackTrace,\n maxAnrEvents: integrationOptions.maxAnrEvents || 1,\n staticTags: integrationOptions.staticTags || {},\n contexts,\n };\n\n if (options.captureStackTrace) {\n const inspector = await import('node:inspector');\n if (!inspector.url()) {\n inspector.open(0);\n }\n }\n\n const worker = new Worker(new URL(`data:application/javascript;base64,${base64WorkerScript}`), {\n workerData: options,\n // We don't want any Node args to be passed to the worker\n execArgv: [],\n env: { ...process.env, NODE_OPTIONS: undefined },\n });\n\n process.on('exit', () => {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n worker.terminate();\n });\n\n const timer = setInterval(() => {\n try {\n const currentSession = getIsolationScope().getSession();\n // We need to copy the session object and remove the toJSON method so it can be sent to the worker\n // serialized without making it a SerializedSession\n const session = currentSession ? { ...currentSession, toJSON: undefined } : undefined;\n // message the worker to tell it the main event loop is still running\n worker.postMessage({ session, debugImages: getFilenameToDebugIdMap(initOptions.stackParser) });\n } catch {\n //\n }\n }, options.pollInterval);\n // Timer should not block exit\n timer.unref();\n\n worker.on('message', (msg: string) => {\n if (msg === 'session-ended') {\n log('ANR event sent from ANR worker. Clearing session in this thread.');\n getIsolationScope().setSession(undefined);\n }\n });\n\n worker.once('error', (err: Error) => {\n clearInterval(timer);\n log('ANR worker error', err);\n });\n\n worker.once('exit', (code: number) => {\n clearInterval(timer);\n log('ANR worker exit', code);\n });\n\n // Ensure this thread can't block app exit\n worker.unref();\n\n return () => {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n worker.terminate();\n clearInterval(timer);\n };\n}\n\n/**\n * @see {@link disableBlockDetectionForCallback}\n *\n * @deprecated The ANR integration has been deprecated. Use `eventLoopBlockIntegration` from `@sentry/node-native` instead.\n */\nexport function disableAnrDetectionForCallback<T>(callback: () => T): T;\n/**\n * @see {@link disableBlockDetectionForCallback}\n *\n * @deprecated The ANR integration has been deprecated. Use `eventLoopBlockIntegration` from `@sentry/node-native` instead.\n */\nexport function disableAnrDetectionForCallback<T>(callback: () => Promise<T>): Promise<T>;\n/**\n * Temporarily disables ANR detection for the duration of a callback function.\n *\n * This utility function allows you to disable ANR detection during operations that\n * are expected to block the event loop, such as intensive computational tasks or\n * synchronous I/O operations.\n *\n * @deprecated The ANR integration has been deprecated. Use `eventLoopBlockIntegration` from `@sentry/node-native` instead.\n */\nexport function disableAnrDetectionForCallback<T>(callback: () => T | Promise<T>): T | Promise<T> {\n const integration = getClient()?.getIntegrationByName(INTEGRATION_NAME) as AnrInternal | undefined;\n\n if (!integration) {\n return callback();\n }\n\n integration.stopWorker();\n\n const result = callback();\n if (isPromise(result)) {\n return result.finally(() => integration.startWorker());\n }\n\n integration.startWorker();\n return result;\n}\n"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"index.js","sources":["../../../../src/integrations/anr/index.ts"],"sourcesContent":["import { types } from 'node:util';\nimport { Worker } from 'node:worker_threads';\nimport type { Contexts, Event, EventHint, Integration, IntegrationFn, ScopeData } from '@sentry/core';\nimport {\n debug,\n defineIntegration,\n getClient,\n getCombinedScopeData,\n getCurrentScope,\n getFilenameToDebugIdMap,\n getIsolationScope,\n GLOBAL_OBJ,\n} from '@sentry/core';\nimport { NODE_VERSION } from '../../nodeVersion';\nimport type { NodeClient } from '../../sdk/client';\nimport { isDebuggerEnabled } from '../../utils/debug';\nimport type { AnrIntegrationOptions, WorkerStartData } from './common';\n\nconst { isPromise } = types;\n\n// This string is a placeholder that gets overwritten with the worker code.\nexport const base64WorkerScript = '###AnrWorkerScript###';\n\nconst DEFAULT_INTERVAL = 50;\nconst DEFAULT_HANG_THRESHOLD = 5000;\n\nfunction log(message: string, ...args: unknown[]): void {\n debug.log(`[ANR] ${message}`, ...args);\n}\n\nfunction globalWithScopeFetchFn(): typeof GLOBAL_OBJ & { __SENTRY_GET_SCOPES__?: () => ScopeData } {\n return GLOBAL_OBJ;\n}\n\n/** Fetches merged scope data */\nfunction getScopeData(): ScopeData {\n const scope = getCombinedScopeData(getIsolationScope(), getCurrentScope());\n\n // We remove attachments because they likely won't serialize well as json\n scope.attachments = [];\n // We can't serialize event processor functions\n scope.eventProcessors = [];\n\n return scope;\n}\n\n/**\n * Gets contexts by calling all event processors. This shouldn't be called until all integrations are setup\n */\nasync function getContexts(client: NodeClient): Promise<Contexts> {\n let event: Event | null = { message: 'ANR' };\n const eventHint: EventHint = {};\n\n for (const processor of client.getEventProcessors()) {\n if (event === null) break;\n event = await processor(event, eventHint);\n }\n\n return event?.contexts || {};\n}\n\nconst INTEGRATION_NAME = 'Anr';\n\ntype AnrInternal = { startWorker: () => void; stopWorker: () => void };\n\n// eslint-disable-next-line deprecation/deprecation\nconst _anrIntegration = ((options: Partial<AnrIntegrationOptions> = {}) => {\n if (NODE_VERSION.major < 16 || (NODE_VERSION.major === 16 && NODE_VERSION.minor < 17)) {\n throw new Error('ANR detection requires Node 16.17.0 or later');\n }\n\n let worker: Promise<() => void> | undefined;\n let client: NodeClient | undefined;\n\n // Hookup the scope fetch function to the global object so that it can be called from the worker thread via the\n // debugger when it pauses\n const gbl = globalWithScopeFetchFn();\n gbl.__SENTRY_GET_SCOPES__ = getScopeData;\n\n return {\n name: INTEGRATION_NAME,\n startWorker: () => {\n if (worker) {\n return;\n }\n\n if (client) {\n worker = _startWorker(client, options);\n }\n },\n stopWorker: () => {\n if (worker) {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n worker.then(stop => {\n stop();\n worker = undefined;\n });\n }\n },\n async setup(initClient: NodeClient) {\n client = initClient;\n\n if (options.captureStackTrace && (await isDebuggerEnabled())) {\n debug.warn('ANR captureStackTrace has been disabled because the debugger was already enabled');\n options.captureStackTrace = false;\n }\n\n // setImmediate is used to ensure that all other integrations have had their setup called first.\n // This allows us to call into all integrations to fetch the full context\n setImmediate(() => this.startWorker());\n },\n } as Integration & AnrInternal;\n}) satisfies IntegrationFn;\n\n// eslint-disable-next-line deprecation/deprecation\ntype AnrReturn = (options?: Partial<AnrIntegrationOptions>) => Integration & AnrInternal;\n\n/**\n * Application Not Responding (ANR) integration for Node.js applications.\n *\n * @deprecated The ANR integration has been deprecated. Use `eventLoopBlockIntegration` from `@sentry/node-native` instead.\n *\n * Detects when the Node.js main thread event loop is blocked for more than the configured\n * threshold (5 seconds by default) and reports these as Sentry events.\n *\n * ANR detection uses a worker thread to monitor the event loop in the main app thread.\n * The main app thread sends a heartbeat message to the ANR worker thread every 50ms by default.\n * If the ANR worker does not receive a heartbeat message for the configured threshold duration,\n * it triggers an ANR event.\n *\n * - Node.js 16.17.0 or higher\n * - Only supported in the Node.js runtime (not browsers)\n * - Not supported for Node.js clusters\n *\n * Overhead should be minimal:\n * - Main thread: Only polling the ANR worker over IPC every 50ms\n * - Worker thread: Consumes around 10-20 MB of RAM\n * - When ANR detected: Brief pause in debugger to capture stack trace (negligible compared to the blocking)\n *\n * @example\n * ```javascript\n * Sentry.init({\n * dsn: \"https://examplePublicKey@o0.ingest.sentry.io/0\",\n * integrations: [\n * Sentry.anrIntegration({\n * anrThreshold: 5000,\n * captureStackTrace: true,\n * pollInterval: 50,\n * }),\n * ],\n * });\n * ```\n */\nexport const anrIntegration = defineIntegration(_anrIntegration) as AnrReturn;\n\n/**\n * Starts the ANR worker thread\n *\n * @returns A function to stop the worker\n */\nasync function _startWorker(\n client: NodeClient,\n // eslint-disable-next-line deprecation/deprecation\n integrationOptions: Partial<AnrIntegrationOptions>,\n): Promise<() => void> {\n const dsn = client.getDsn();\n\n if (!dsn) {\n return () => {\n //\n };\n }\n\n const contexts = await getContexts(client);\n\n // These will not be accurate if sent later from the worker thread\n delete contexts.app?.app_memory;\n delete contexts.device?.free_memory;\n\n const initOptions = client.getOptions();\n\n const sdkMetadata = client.getSdkMetadata() || {};\n if (sdkMetadata.sdk) {\n sdkMetadata.sdk.integrations = initOptions.integrations.map(i => i.name);\n }\n\n const options: WorkerStartData = {\n debug: debug.isEnabled(),\n dsn,\n tunnel: initOptions.tunnel,\n environment: initOptions.environment || 'production',\n release: initOptions.release,\n dist: initOptions.dist,\n sdkMetadata,\n appRootPath: integrationOptions.appRootPath,\n pollInterval: integrationOptions.pollInterval || DEFAULT_INTERVAL,\n anrThreshold: integrationOptions.anrThreshold || DEFAULT_HANG_THRESHOLD,\n captureStackTrace: !!integrationOptions.captureStackTrace,\n maxAnrEvents: integrationOptions.maxAnrEvents || 1,\n staticTags: integrationOptions.staticTags || {},\n contexts,\n };\n\n if (options.captureStackTrace) {\n const inspector = await import('node:inspector');\n if (!inspector.url()) {\n inspector.open(0);\n }\n }\n\n const worker = new Worker(new URL(`data:application/javascript;base64,${base64WorkerScript}`), {\n workerData: options,\n // We don't want any Node args to be passed to the worker\n execArgv: [],\n env: { ...process.env, NODE_OPTIONS: undefined },\n });\n\n process.on('exit', () => {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n worker.terminate();\n });\n\n const timer = setInterval(() => {\n try {\n const currentSession = getIsolationScope().getSession();\n // We need to copy the session object and remove the toJSON method so it can be sent to the worker\n // serialized without making it a SerializedSession\n const session = currentSession ? { ...currentSession, toJSON: undefined } : undefined;\n // message the worker to tell it the main event loop is still running\n worker.postMessage({ session, debugImages: getFilenameToDebugIdMap(initOptions.stackParser) });\n } catch {\n //\n }\n }, options.pollInterval);\n // Timer should not block exit\n timer.unref();\n\n worker.on('message', (msg: string) => {\n if (msg === 'session-ended') {\n log('ANR event sent from ANR worker. Clearing session in this thread.');\n getIsolationScope().setSession(undefined);\n }\n });\n\n worker.once('error', (err: Error) => {\n clearInterval(timer);\n log('ANR worker error', err);\n });\n\n worker.once('exit', (code: number) => {\n clearInterval(timer);\n log('ANR worker exit', code);\n });\n\n // Ensure this thread can't block app exit\n worker.unref();\n\n return () => {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n worker.terminate();\n clearInterval(timer);\n };\n}\n\n/**\n * @see {@link disableBlockDetectionForCallback}\n *\n * @deprecated The ANR integration has been deprecated. Use `eventLoopBlockIntegration` from `@sentry/node-native` instead.\n */\nexport function disableAnrDetectionForCallback<T>(callback: () => T): T;\n/**\n * @see {@link disableBlockDetectionForCallback}\n *\n * @deprecated The ANR integration has been deprecated. Use `eventLoopBlockIntegration` from `@sentry/node-native` instead.\n */\nexport function disableAnrDetectionForCallback<T>(callback: () => Promise<T>): Promise<T>;\n/**\n * Temporarily disables ANR detection for the duration of a callback function.\n *\n * This utility function allows you to disable ANR detection during operations that\n * are expected to block the event loop, such as intensive computational tasks or\n * synchronous I/O operations.\n *\n * @deprecated The ANR integration has been deprecated. Use `eventLoopBlockIntegration` from `@sentry/node-native` instead.\n */\nexport function disableAnrDetectionForCallback<T>(callback: () => T | Promise<T>): T | Promise<T> {\n const integration = getClient()?.getIntegrationByName(INTEGRATION_NAME) as AnrInternal | undefined;\n\n if (!integration) {\n return callback();\n }\n\n integration.stopWorker();\n\n const result = callback();\n if (isPromise(result)) {\n return result.finally(() => integration.startWorker());\n }\n\n integration.startWorker();\n return result;\n}\n"],"names":[],"mappings":"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"} |
@@ -1,2 +0,2 @@ | ||
| /*! @sentry/node-core 10.53.1 (cd97408) | https://github.com/getsentry/sentry-javascript */ | ||
| import{Session as t}from"node:inspector";import{workerData as n,parentPort as e}from"node:worker_threads";import{posix as r,sep as o}from"node:path";import*as i from"node:http";import*as s from"node:https";import{Readable as c}from"node:stream";import{createGzip as u}from"node:zlib";import*as a from"node:net";import*as f from"node:tls";const h="undefined"==typeof __SENTRY_DEBUG__||__SENTRY_DEBUG__,p=globalThis,l="10.53.1";function d(){return m(p),p}function m(t){const n=t.__SENTRY__=t.__SENTRY__||{};return n.version=n.version||l,n[l]=n[l]||{}}function g(t,n,e=p){const r=e.__SENTRY__=e.__SENTRY__||{},o=r[l]=r[l]||{};return o[t]||(o[t]=n())}const y={};function b(t){if(!("console"in p))return t();const n=p.console,e={},r=Object.keys(y);r.forEach(t=>{const r=y[t];e[t]=n[t],n[t]=r});try{return t()}finally{r.forEach(t=>{n[t]=e[t]})}}function v(){return w().enabled}function _(t,...n){h&&v()&&b(()=>{p.console[t](`Sentry Logger [${t}]:`,...n)})}function w(){return h?g("loggerSettings",()=>({enabled:!1})):{enabled:!1}}const S={enable:function(){w().enabled=!0},disable:function(){w().enabled=!1},isEnabled:v,log:function(...t){_("log",...t)},warn:function(...t){_("warn",...t)},error:function(...t){_("error",...t)}},$=/captureMessage|captureException/;function E(t){return t[t.length-1]||{}}const x="<anonymous>";const N=Object.prototype.toString;function j(t,n){return N.call(t)===`[object ${n}]`}function C(t){return j(t,"String")}function A(t){return j(t,"Object")}function k(t){return Boolean(t?.then&&"function"==typeof t.then)}function T(t,n){try{return t instanceof n}catch{return!1}}const I=p;function O(t,n){const e=t,r=[];if(!e?.tagName)return"";if(I.HTMLElement&&e instanceof HTMLElement&&e.dataset){if(e.dataset.sentryComponent)return e.dataset.sentryComponent;if(e.dataset.sentryElement)return e.dataset.sentryElement}r.push(e.tagName.toLowerCase());const o=n?.length?n.filter(t=>e.getAttribute(t)).map(t=>[t,e.getAttribute(t)]):null;if(o?.length)o.forEach(t=>{r.push(`[${t[0]}="${t[1]}"]`)});else{e.id&&r.push(`#${e.id}`);const t=e.className;if(t&&C(t)){const n=t.split(/\s+/);for(const t of n)r.push(`.${t}`)}}for(const t of["aria-label","type","name","title","alt"]){const n=e.getAttribute(t);n&&r.push(`[${t}="${n}"]`)}return r.join("")}function R(t){if(function(t){switch(N.call(t)){case"[object Error]":case"[object Exception]":case"[object DOMException]":case"[object WebAssembly.Exception]":return!0;default:return T(t,Error)}}(t))return{message:t.message,name:t.name,stack:t.stack,...D(t)};if(n=t,"undefined"!=typeof Event&&T(n,Event)){const n={type:t.type,target:P(t.target),currentTarget:P(t.currentTarget),...D(t)};return"undefined"!=typeof CustomEvent&&T(t,CustomEvent)&&(n.detail=t.detail),n}return t;var n}function P(t){try{return n=t,"undefined"!=typeof Element&&T(n,Element)?function(t,n={}){if(!t)return"<unknown>";try{let e=t;const r=5,o=[];let i=0,s=0;const c=" > ",u=c.length;let a;const f=Array.isArray(n)?n:n.keyAttrs,h=!Array.isArray(n)&&n.maxStringLength||80;for(;e&&i++<r&&(a=O(e,f),!("html"===a||i>1&&s+o.length*u+a.length>=h));)o.push(a),s+=a.length,e=e.parentNode;return o.reverse().join(c)}catch{return"<unknown>"}}(t):Object.prototype.toString.call(t)}catch{return"<unknown>"}var n}function D(t){return"object"==typeof t&&null!==t?Object.fromEntries(Object.entries(t)):{}}let U,L;function M(t){if(void 0!==U)return U?U(t):t();const n=Symbol.for("__SENTRY_SAFE_RANDOM_ID_WRAPPER__"),e=p;return n in e&&"function"==typeof e[n]?(U=e[n],U(t)):(U=null,t())}function B(){return M(()=>Math.random())}function z(){return M(()=>Date.now())}function W(t,n=0){return"string"!=typeof t||0===n||t.length<=n?t:`${t.slice(0,n)}...`}function F(t=function(){const t=p;return t.crypto||t.msCrypto}()){try{if(t?.randomUUID)return M(()=>t.randomUUID()).replace(/-/g,"")}catch{}return L||(L=[1e7]+1e3+4e3+8e3+1e11),L.replace(/[018]/g,t=>(t^(16*B()&15)>>t/4).toString(16))}function G(){return z()/1e3}let H;function J(){return(H??(H=function(){const{performance:t}=p;if(!t?.now||!t.timeOrigin)return G;const n=t.timeOrigin;return()=>(n+M(()=>t.now()))/1e3}()))()}function Y(t){const n=J(),e={sid:F(),init:!0,timestamp:n,started:n,duration:0,status:"ok",errors:0,ignoreDuration:!1,toJSON:()=>function(t){return{sid:`${t.sid}`,init:t.init,started:new Date(1e3*t.started).toISOString(),timestamp:new Date(1e3*t.timestamp).toISOString(),status:t.status,errors:t.errors,did:"number"==typeof t.did||"string"==typeof t.did?`${t.did}`:void 0,duration:t.duration,abnormal_mechanism:t.abnormal_mechanism,attrs:{release:t.release,environment:t.environment,ip_address:t.ipAddress,user_agent:t.userAgent}}}(e)};return t&&V(e,t),e}function V(t,n={}){if(n.user&&(!t.ipAddress&&n.user.ip_address&&(t.ipAddress=n.user.ip_address),t.did||n.did||(t.did=n.user.id||n.user.email||n.user.username)),t.timestamp=n.timestamp||J(),n.abnormal_mechanism&&(t.abnormal_mechanism=n.abnormal_mechanism),n.ignoreDuration&&(t.ignoreDuration=n.ignoreDuration),n.sid&&(t.sid=32===n.sid.length?n.sid:F()),void 0!==n.init&&(t.init=n.init),!t.did&&n.did&&(t.did=`${n.did}`),"number"==typeof n.started&&(t.started=n.started),t.ignoreDuration)t.duration=void 0;else if("number"==typeof n.duration)t.duration=n.duration;else{const n=t.timestamp-t.started;t.duration=n>=0?n:0}n.release&&(t.release=n.release),n.environment&&(t.environment=n.environment),!t.ipAddress&&n.ipAddress&&(t.ipAddress=n.ipAddress),!t.userAgent&&n.userAgent&&(t.userAgent=n.userAgent),"number"==typeof n.errors&&(t.errors=n.errors),n.status&&(t.status=n.status)}function K(t,n,e=2){if(!n||"object"!=typeof n||e<=0)return n;if(t&&0===Object.keys(n).length)return t;const r={...t};for(const t in n)Object.prototype.hasOwnProperty.call(n,t)&&(r[t]=K(r[t],n[t],e-1));return r}function Z(){return F()}function q(){return F().substring(16)}const Q="_sentrySpan";function X(t,n){n?function(t,n,e){try{Object.defineProperty(t,n,{value:e,writable:!0,configurable:!0})}catch{h&&S.log(`Failed to add non-enumerable property "${String(n)}" to object`,t)}}(t,Q,n):delete t[Q]}function tt(t){return t[Q]}class nt{constructor(){this.t=!1,this.o=[],this.i=[],this.u=[],this.h=[],this.p={},this.l={},this.m={},this.v={},this._={},this.S={},this.N={traceId:Z(),sampleRand:B()}}clone(){const t=new nt;return t.u=[...this.u],t.l={...this.l},t.m={...this.m},t.v={...this.v},t._={...this._},this._.flags&&(t._.flags={values:[...this._.flags.values]}),t.p=this.p,t.j=this.j,t.C=this.C,t.A=this.A,t.k=this.k,t.i=[...this.i],t.h=[...this.h],t.S={...this.S},t.N={...this.N},t.T=this.T,t.I=this.I,t.O=this.O,X(t,tt(this)),t}setClient(t){this.T=t}setLastEventId(t){this.I=t}getClient(){return this.T}lastEventId(){return this.I}addScopeListener(t){this.o.push(t)}addEventProcessor(t){return this.i.push(t),this}setUser(t){return this.p=t||{email:void 0,id:void 0,ip_address:void 0,username:void 0},this.C&&V(this.C,{user:t}),this.R(),this}getUser(){return this.p}setConversationId(t){return this.O=t||void 0,this.R(),this}setTags(t){return this.l={...this.l,...t},this.R(),this}setTag(t,n){return this.setTags({[t]:n})}setAttributes(t){return this.m={...this.m,...t},this.R(),this}setAttribute(t,n){return this.setAttributes({[t]:n})}removeAttribute(t){return t in this.m&&(delete this.m[t],this.R()),this}setExtras(t){return this.v={...this.v,...t},this.R(),this}setExtra(t,n){return this.v={...this.v,[t]:n},this.R(),this}setFingerprint(t){return this.k=t,this.R(),this}setLevel(t){return this.j=t,this.R(),this}setTransactionName(t){return this.A=t,this.R(),this}setContext(t,n){return null===n?delete this._[t]:this._[t]=n,this.R(),this}setSession(t){return t?this.C=t:delete this.C,this.R(),this}getSession(){return this.C}update(t){if(!t)return this;const n="function"==typeof t?t(this):t,e=n instanceof nt?n.getScopeData():A(n)?t:void 0,{tags:r,attributes:o,extra:i,user:s,contexts:c,level:u,fingerprint:a=[],propagationContext:f,conversationId:h}=e||{};return this.l={...this.l,...r},this.m={...this.m,...o},this.v={...this.v,...i},this._={...this._,...c},s&&Object.keys(s).length&&(this.p=s),u&&(this.j=u),a.length&&(this.k=a),f&&(this.N=f),h&&(this.O=h),this}clear(){return this.u=[],this.l={},this.m={},this.v={},this.p={},this._={},this.j=void 0,this.A=void 0,this.k=void 0,this.C=void 0,this.O=void 0,X(this,void 0),this.h=[],this.setPropagationContext({traceId:Z(),sampleRand:B()}),this.R(),this}addBreadcrumb(t,n){const e="number"==typeof n?n:100;if(e<=0)return this;const r={timestamp:G(),...t,message:t.message?W(t.message,2048):t.message};return this.u.push(r),this.u.length>e&&(this.u=this.u.slice(-e),this.T?.recordDroppedEvent("buffer_overflow","log_item")),this.R(),this}getLastBreadcrumb(){return this.u[this.u.length-1]}clearBreadcrumbs(){return this.u=[],this.R(),this}addAttachment(t){return this.h.push(t),this}clearAttachments(){return this.h=[],this}getScopeData(){return{breadcrumbs:this.u,attachments:this.h,contexts:this._,tags:this.l,attributes:this.m,extra:this.v,user:this.p,level:this.j,fingerprint:this.k||[],eventProcessors:this.i,propagationContext:this.N,sdkProcessingMetadata:this.S,transactionName:this.A,span:tt(this),conversationId:this.O}}setSDKProcessingMetadata(t){return this.S=K(this.S,t,2),this}setPropagationContext(t){return this.N=t,this}getPropagationContext(){return this.N}captureException(t,n){const e=n?.event_id||F();if(!this.T)return h&&S.warn("No client configured on scope - will not capture exception!"),e;const r=new Error("Sentry syntheticException");return this.T.captureException(t,{originalException:t,syntheticException:r,...n,event_id:e},this),e}captureMessage(t,n,e){const r=e?.event_id||F();if(!this.T)return h&&S.warn("No client configured on scope - will not capture message!"),r;const o=e?.syntheticException??new Error(t);return this.T.captureMessage(t,n,{originalException:t,syntheticException:o,...e,event_id:r},this),r}captureEvent(t,n){const e=t.event_id||n?.event_id||F();return this.T?(this.T.captureEvent(t,{...n,event_id:e},this),e):(h&&S.warn("No client configured on scope - will not capture event!"),e)}R(){this.t||(this.t=!0,this.o.forEach(t=>{t(this)}),this.t=!1)}}const et=t=>t instanceof Promise&&!t[rt],rt=Symbol("chained PromiseLike"),ot=(t,n)=>{if(!n)return t;let e=!1;for(const r in t){if(r in n)continue;e=!0;const o=t[r];"function"==typeof o?Object.defineProperty(n,r,{value:(...n)=>o.apply(t,n),enumerable:!0,configurable:!0,writable:!0}):n[r]=o}return e&&Object.assign(n,{[rt]:!0}),n};class it{constructor(t,n){let e,r;e=t||new nt,r=n||new nt,this.P=[{scope:e}],this.D=r}withScope(t){const n=this.U();let e;try{e=t(n)}catch(t){throw this.L(),t}return k(e)?((t,n,e)=>{const r=t.then(t=>(n(t),t),t=>{throw e(t),t});return et(r)&&et(t)?r:ot(t,r)})(e,()=>this.L(),()=>this.L()):(this.L(),e)}getClient(){return this.getStackTop().client}getScope(){return this.getStackTop().scope}getIsolationScope(){return this.D}getStackTop(){return this.P[this.P.length-1]}U(){const t=this.getScope().clone();return this.P.push({client:this.getClient(),scope:t}),t}L(){return!(this.P.length<=1)&&!!this.P.pop()}}function st(){const t=m(d());return t.stack=t.stack||new it(g("defaultCurrentScope",()=>new nt),g("defaultIsolationScope",()=>new nt))}function ct(t){return st().withScope(t)}function ut(t,n){const e=st();return e.withScope(()=>(e.getStackTop().scope=t,n(t)))}function at(t){return st().withScope(()=>t(st().getIsolationScope()))}function ft(t){const n=m(t);return n.acs?n.acs:{withIsolationScope:at,withScope:ct,withSetScope:ut,withSetIsolationScope:(t,n)=>at(n),getCurrentScope:()=>st().getScope(),getIsolationScope:()=>st().getIsolationScope()}}function ht(){return ft(d()).getCurrentScope().getClient()}function pt(t){if(t){if("object"==typeof t&&"deref"in t&&"function"==typeof t.deref)try{return t.deref()}catch{return}return t}}function lt(t){const n=t;return{scope:n._sentryScope,isolationScope:pt(n._sentryIsolationScope)}}const dt="sentry-";function mt(t){const n=function(t){if(!t||!C(t)&&!Array.isArray(t))return;if(Array.isArray(t))return t.reduce((t,n)=>{const e=gt(n);return Object.entries(e).forEach(([n,e])=>{t[n]=e}),t},{});return gt(t)}(t);if(!n)return;const e=Object.entries(n).reduce((t,[n,e])=>{if(n.startsWith(dt)){t[n.slice(7)]=e}return t},{});return Object.keys(e).length>0?e:void 0}function gt(t){return t.split(",").map(t=>{const n=t.indexOf("=");if(-1===n)return[];return[t.slice(0,n),t.slice(n+1)].map(t=>{try{return decodeURIComponent(t.trim())}catch{return}})}).reduce((t,[n,e])=>(n&&e&&(t[n]=e),t),{})}const yt=/^o(\d+)\./;function bt(t,n=!1){const{host:e,path:r,pass:o,port:i,projectId:s,protocol:c,publicKey:u}=t;return`${c}://${u}${n&&o?`:${o}`:""}@${e}${i?`:${i}`:""}/${r?`${r}/`:r}${s}`}function vt(t){const n=t.getOptions(),{host:e}=t.getDsn()||{};let r;return n.orgId?r=String(n.orgId):e&&(r=function(t){const n=t.match(yt);return n?.[1]}(e)),r}function _t(t){const{spanId:n,traceId:e,isRemote:r}=t.spanContext(),o=r?n:Et(t).parent_span_id,i=lt(t).scope;return{parent_span_id:o,span_id:r?i?.getPropagationContext().propagationSpanId||q():n,trace_id:e}}function wt(t){return t&&t.length>0?t.map(({context:{spanId:t,traceId:n,traceFlags:e,...r},attributes:o})=>({span_id:t,trace_id:n,sampled:1===e,attributes:o,...r})):void 0}function St(t){return"number"==typeof t?$t(t):Array.isArray(t)?t[0]+t[1]/1e9:t instanceof Date?$t(t.getTime()):J()}function $t(t){return t>9999999999?t/1e3:t}function Et(t){if(function(t){return"function"==typeof t.getSpanJSON}(t))return t.getSpanJSON();const{spanId:n,traceId:e}=t.spanContext();if(function(t){const n=t;return!!(n.attributes&&n.startTime&&n.name&&n.endTime&&n.status)}(t)){const{attributes:r,startTime:o,name:i,endTime:s,status:c,links:u}=t;return{span_id:n,trace_id:e,data:r,description:i,parent_span_id:xt(t),start_timestamp:St(o),timestamp:St(s)||void 0,status:Nt(c),op:r["sentry.op"],origin:r["sentry.origin"],links:wt(u)}}return{span_id:n,trace_id:e,start_timestamp:0,data:{}}}function xt(t){return"parentSpanId"in t?t.parentSpanId:"parentSpanContext"in t?t.parentSpanContext?.spanId:void 0}function Nt(t){if(t&&0!==t.code)return 1===t.code?"ok":t.message||"internal_error"}const jt=function(t){return t._sentryRootSpan||t};function Ct(t){const n=ht();if(!n)return{};const e=jt(t),r=Et(e),o=r.data,i=e.spanContext().traceState,s=i?.get("sentry.sample_rate")??o["sentry.sample_rate"]??o["sentry.previous_trace_sample_rate"];function c(t){return"number"!=typeof s&&"string"!=typeof s||(t.sample_rate=`${s}`),t}const u=e._frozenDsc;if(u)return c(u);const a=i?.get("sentry.dsc"),f=a&&mt(a);if(f)return c(f);const h=function(t,n){const e=n.getOptions(),{publicKey:r}=n.getDsn()||{},o={environment:e.environment||"production",release:e.release,public_key:r,trace_id:t,org_id:vt(n)};return n.emit("createDsc",o),o}(t.spanContext().traceId,n),p=o["sentry.source"]??o["sentry.span.source"],l=r.description;return"url"!==p&&l&&(h.transaction=l),function(){if("boolean"==typeof __SENTRY_TRACING__&&!__SENTRY_TRACING__)return!1;const t=ht()?.getOptions();return!(!t||null==t.tracesSampleRate&&!t.tracesSampler)}()&&(h.sampled=String(function(t){const{traceFlags:n}=t.spanContext();return 1===n}(e)),h.sample_rand=i?.get("sentry.sample_rand")??lt(e).scope?.getPropagationContext().sampleRand.toString()),c(h),n.emit("createDsc",h,e),h}const At=Symbol.for("sentry.skipNormalization"),kt=Symbol.for("sentry.overrideNormalizationDepth");function Tt(t,n=100,e=1/0){try{return It("",t,n,e)}catch(t){return{ERROR:`**non-serializable** (${t})`}}}function It(t,n,e=1/0,r=1/0,o=function(){const t=new WeakSet;function n(n){return!!t.has(n)||(t.add(n),!1)}function e(n){t.delete(n)}return[n,e]}()){const[i,s]=o;if(null==n||["boolean","string"].includes(typeof n)||"number"==typeof n&&Number.isFinite(n))return n;const c=function(t,n){try{if("domain"===t&&n&&"object"==typeof n&&n.M)return"[Domain]";if("domainEmitter"===t)return"[DomainEmitter]";if("undefined"!=typeof global&&n===global)return"[Global]";if("undefined"!=typeof window&&n===window)return"[Window]";if("undefined"!=typeof document&&n===document)return"[Document]";if("object"==typeof(e=n)&&null!==e&&(e.__isVue||e.B||e.__v_isVNode))return function(t){return"__v_isVNode"in t&&t.__v_isVNode?"[VueVNode]":"[VueViewModel]"}(n);if(function(t){return A(t)&&"nativeEvent"in t&&"preventDefault"in t&&"stopPropagation"in t}(n))return"[SyntheticEvent]";if("number"==typeof n&&!Number.isFinite(n))return`[${n}]`;if("function"==typeof n)return`[Function: ${function(t){try{return t&&"function"==typeof t&&t.name||x}catch{return x}}(n)}]`;if("symbol"==typeof n)return`[${String(n)}]`;if("bigint"==typeof n)return`[BigInt: ${String(n)}]`;const r=function(t){const n=Object.getPrototypeOf(t);return n?.constructor?n.constructor.name:"null prototype"}(n);return/^HTML(\w*)Element$/.test(r)?`[HTMLElement: ${r}]`:`[object ${r}]`}catch(t){return`**non-serializable** (${t})`}var e}(t,n);if(!c.startsWith("[object "))return c;if(function(t){return Boolean(t[At])}(n))return n;const u=function(t){const n=t[kt];return"number"==typeof n?n:void 0}(n),a=void 0!==u?u:e;if(0===a)return c.replace("object ","");if(i(n))return"[Circular ~]";const f=n;if(f&&"function"==typeof f.toJSON)try{return It("",f.toJSON(),a-1,r,o)}catch{}const h=Array.isArray(n)?[]:{};let p=0;const l=R(n);for(const t in l){if(!Object.prototype.hasOwnProperty.call(l,t))continue;if(p>=r){h[t]="[MaxProperties ~]";break}const n=l[t];h[t]=It(t,n,a-1,r,o),p++}return s(n),h}function Ot(t,n){const e=n.replace(/\\/g,"/").replace(/[|\\{}()[\]^$+*?.]/g,"\\$&");let r=t;try{r=decodeURI(t)}catch{}return r.replace(/\\/g,"/").replace(/webpack:\/?/g,"").replace(new RegExp(`(file://)?/*${e}/*`,"ig"),"app:///")}function Rt(t,n=[]){return[t,n]}function Pt(t,n){const e=t[1];for(const t of e){if(n(t,t[0].type))return!0}return!1}function Dt(t){const n=m(p);return n.encodePolyfill?n.encodePolyfill(t):(new TextEncoder).encode(t)}function Ut(t){const[n,e]=t;let r=JSON.stringify(n);function o(t){"string"==typeof r?r="string"==typeof t?r+t:[Dt(r),t]:r.push("string"==typeof t?Dt(t):t)}for(const t of e){const[n,e]=t;if(o(`\n${JSON.stringify(n)}\n`),"string"==typeof e||e instanceof Uint8Array)o(e);else{let t;try{t=JSON.stringify(e)}catch{t=JSON.stringify(Tt(e))}o(t)}}return"string"==typeof r?r:function(t){const n=t.reduce((t,n)=>t+n.length,0),e=new Uint8Array(n);let r=0;for(const n of t)e.set(n,r),r+=n.length;return e}(r)}const Lt={sessions:"session",event:"error",client_report:"internal",user_report:"default",profile_chunk:"profile",replay_event:"replay",replay_recording:"replay",check_in:"monitor",raw_security:"security",log:"log_item",trace_metric:"metric"};function Mt(t){return function(t){return t in Lt}(t)?Lt[t]:t}function Bt(t){if(!t?.sdk)return;const{name:n,version:e}=t.sdk;return{name:n,version:e}}function zt(t,n,e,r){const o=Bt(e),i=t.type&&"replay_event"!==t.type?t.type:"event";!function(t,n){if(!n)return t;const e=t.sdk||{};t.sdk={...e,name:e.name||n.name,version:e.version||n.version,integrations:[...t.sdk?.integrations||[],...n.integrations||[]],packages:[...t.sdk?.packages||[],...n.packages||[]],settings:t.sdk?.settings||n.settings?{...t.sdk?.settings,...n.settings}:void 0}}(t,e?.sdk);const s=function(t,n,e,r){const o=t.sdkProcessingMetadata?.dynamicSamplingContext;return{event_id:t.event_id,sent_at:(new Date).toISOString(),...n&&{sdk:n},...!!e&&r&&{dsn:bt(r)},...o&&{trace:o}}}(t,o,r,n);delete t.sdkProcessingMetadata;return Rt(s,[[{type:i},t]])}const Wt="__SENTRY_SUPPRESS_TRACING__";function Ft(t){const n=ft(d());return n.suppressTracing?n.suppressTracing(t):function(...t){const n=ft(d());if(2===t.length){const[e,r]=t;return e?n.withSetScope(e,r):n.withScope(r)}return n.withScope(t[0])}(n=>{n.setSDKProcessingMetadata({[Wt]:!0});const e=t();return n.setSDKProcessingMetadata({[Wt]:void 0}),e})}function Gt(t,n){const{fingerprint:e,span:r,breadcrumbs:o,sdkProcessingMetadata:i}=n;!function(t,n){const{extra:e,tags:r,user:o,contexts:i,level:s,transactionName:c}=n;Object.keys(e).length&&(t.extra={...e,...t.extra});Object.keys(r).length&&(t.tags={...r,...t.tags});Object.keys(o).length&&(t.user={...o,...t.user});Object.keys(i).length&&(t.contexts={...i,...t.contexts});s&&(t.level=s);c&&"transaction"!==t.type&&(t.transaction=c)}(t,n),r&&function(t,n){t.contexts={trace:_t(n),...t.contexts},t.sdkProcessingMetadata={dynamicSamplingContext:Ct(n),...t.sdkProcessingMetadata};const e=jt(n),r=Et(e).description;r&&!t.transaction&&"transaction"===t.type&&(t.transaction=r)}(t,r),function(t,n){t.fingerprint=t.fingerprint?Array.isArray(t.fingerprint)?t.fingerprint:[t.fingerprint]:[],n&&(t.fingerprint=t.fingerprint.concat(n));t.fingerprint.length||delete t.fingerprint}(t,e),function(t,n){const e=[...t.breadcrumbs||[],...n];t.breadcrumbs=e.length?e:void 0}(t,o),function(t,n){t.sdkProcessingMetadata={...t.sdkProcessingMetadata,...n}}(t,i)}class Ht{constructor(t){this.W=0,this.F=[],this.G(t)}then(t,n){return new Ht((e,r)=>{this.F.push([!1,n=>{if(t)try{e(t(n))}catch(t){r(t)}else e(n)},t=>{if(n)try{e(n(t))}catch(t){r(t)}else r(t)}]),this.H()})}catch(t){return this.then(t=>t,t)}finally(t){return new Ht((n,e)=>{let r,o;return this.then(n=>{o=!1,r=n,t&&t()},n=>{o=!0,r=n,t&&t()}).then(()=>{o?e(r):n(r)})})}H(){if(0===this.W)return;const t=this.F.slice();this.F=[],t.forEach(t=>{t[0]||(1===this.W&&t[1](this.J),2===this.W&&t[2](this.J),t[0]=!0)})}G(t){const n=(t,n)=>{0===this.W&&(k(n)?n.then(e,r):(this.W=t,this.J=n,this.H()))},e=t=>{n(1,t)},r=t=>{n(2,t)};try{t(e,r)}catch(t){r(t)}}}const Jt=Symbol.for("SentryBufferFullError");function Yt(t=100){const n=new Set;function e(t){n.delete(t)}return{get $(){return Array.from(n)},add:function(r){if(!(n.size<t))return o=Jt,new Ht((t,n)=>{n(o)});var o;const i=r();return n.add(i),i.then(()=>e(i),()=>e(i)),i},drain:function(t){if(!n.size)return e=!0,new Ht(t=>{t(e)});var e;const r=Promise.allSettled(Array.from(n)).then(()=>!0);if(!t)return r;const o=[r,new Promise(n=>{return"object"==typeof(e=setTimeout(()=>n(!1),t))&&"function"==typeof e.unref&&e.unref(),e;var e})];return Promise.race(o)}}}function Vt(t,{statusCode:n,headers:e},r=z()){const o={...t},i=e?.["x-sentry-rate-limits"],s=e?.["retry-after"];if(i)for(const t of i.trim().split(",")){const[n,e,,,i]=t.split(":",5),s=parseInt(n,10),c=1e3*(isNaN(s)?60:s);if(e)for(const t of e.split(";"))"metric_bucket"===t&&i&&!i.split(";").includes("custom")||(o[t]=r+c);else o.all=r+c}else s?o.all=r+function(t,n=z()){const e=parseInt(`${t}`,10);if(!isNaN(e))return 1e3*e;const r=Date.parse(`${t}`);return isNaN(r)?6e4:r-n}(s,r):429===n&&(o.all=r+6e4);return o}function Kt(t,n,e=Yt(t.bufferSize||64)){let r={};return{send:function(t){const o=[];if(Pt(t,(t,n)=>{const e=Mt(n);(function(t,n,e=z()){return function(t,n){return t[n]||t.all||0}(t,n)>e})(r,e)||o.push(t)}),0===o.length)return Promise.resolve({});const i=Rt(t[0],o),s=t=>{!function(t,n){return Pt(t,(t,e)=>n.includes(e))}(i,["client_report"])?Pt(i,(t,n)=>{}):h&&S.warn(`Dropping client report. Will not send outcomes (reason: ${t}).`)};return e.add(()=>n({body:Ut(i)}).then(t=>413===t.statusCode?(h&&S.error("Sentry responded with status code 413. Envelope was discarded due to exceeding size limits."),s("send_error"),t):(h&&void 0!==t.statusCode&&(t.statusCode<200||t.statusCode>=300)&&S.warn(`Sentry responded with status code ${t.statusCode} to sent event.`),r=Vt(r,t),t),t=>{throw s("network_error"),h&&S.error("Encountered error running transport request:",t),t})).then(t=>t,t=>{if(t===Jt)return h&&S.error("Skipped sending event because buffer is full."),s("queue_overflow"),Promise.resolve({});throw t})},flush:t=>e.drain(t)}}const Zt=/^(\S+:\\|\/?)([\s\S]*?)((?:\.{1,2}|[^/\\]+?|)(\.[^./\\]*|))(?:[/\\]*)$/;function qt(t){const n=function(t){const n=t.length>1024?`<truncated>${t.slice(-1024)}`:t,e=Zt.exec(n);return e?e.slice(1):[]}(t),e=n[0]||"";let r=n[1];return e||r?(r&&(r=r.slice(0,r.length-1)),e+r):"."}function Qt(t,n=!1){return!(n||t&&!t.startsWith("/")&&!t.match(/^[A-Z]:/)&&!t.startsWith(".")&&!t.match(/^[a-zA-Z]([a-zA-Z0-9.\-+])*:\/\//))&&void 0!==t&&!t.includes("node_modules/")}const Xt=Symbol("AgentBaseInternalState");class tn extends i.Agent{constructor(t){super(t),this[Xt]={}}isSecureEndpoint(t){if(t){if("boolean"==typeof t.secureEndpoint)return t.secureEndpoint;if("string"==typeof t.protocol)return"https:"===t.protocol}const{stack:n}=new Error;return"string"==typeof n&&n.split("\n").some(t=>-1!==t.indexOf("(https.js:")||-1!==t.indexOf("node:https:"))}createSocket(t,n,e){const r={...n,secureEndpoint:this.isSecureEndpoint(n)};Promise.resolve().then(()=>this.connect(t,r)).then(o=>{if(o instanceof i.Agent)return o.addRequest(t,r);this[Xt].currentSocket=o,super.createSocket(t,n,e)},e)}createConnection(){const t=this[Xt].currentSocket;if(this[Xt].currentSocket=void 0,!t)throw new Error("No socket was returned in the `connect()` function");return t}get defaultPort(){return this[Xt].defaultPort??("https:"===this.protocol?443:80)}set defaultPort(t){this[Xt]&&(this[Xt].defaultPort=t)}get protocol(){return this[Xt].protocol??(this.isSecureEndpoint()?"https:":"http:")}set protocol(t){this[Xt]&&(this[Xt].protocol=t)}}function nn(...t){S.log("[https-proxy-agent:parse-proxy-response]",...t)}function en(t){return new Promise((n,e)=>{let r=0;const o=[];function i(){const c=t.read();c?function(c){o.push(c),r+=c.length;const u=Buffer.concat(o,r),a=u.indexOf("\r\n\r\n");if(-1===a)return nn("have not received end of HTTP headers yet..."),void i();const f=u.subarray(0,a).toString("ascii").split("\r\n"),h=f.shift();if(!h)return t.destroy(),e(new Error("No header received from proxy CONNECT response"));const p=h.split(" "),l=+(p[1]||0),d=p.slice(2).join(" "),m={};for(const n of f){if(!n)continue;const r=n.indexOf(":");if(-1===r)return t.destroy(),e(new Error(`Invalid header from proxy CONNECT response: "${n}"`));const o=n.slice(0,r).toLowerCase(),i=n.slice(r+1).trimStart(),s=m[o];"string"==typeof s?m[o]=[s,i]:Array.isArray(s)?s.push(i):m[o]=i}nn("got proxy server response: %o %o",h,m),s(),n({connect:{statusCode:l,statusText:d,headers:m},buffered:u})}(c):t.once("readable",i)}function s(){t.removeListener("end",c),t.removeListener("error",u),t.removeListener("readable",i)}function c(){s(),nn("onend"),e(new Error("Proxy connection ended before receiving CONNECT response"))}function u(t){s(),nn("onerror %o",t),e(t)}t.on("error",u),t.on("end",c),i()})}function rn(...t){S.log("[https-proxy-agent]",...t)}class on extends tn{static __initStatic(){this.protocols=["http","https"]}constructor(t,n){super(n),this.options={},this.proxy="string"==typeof t?new URL(t):t,this.proxyHeaders=n?.headers??{},rn("Creating new HttpsProxyAgent instance: %o",this.proxy.href);const e=(this.proxy.hostname||this.proxy.host).replace(/^\[|\]$/g,""),r=this.proxy.port?parseInt(this.proxy.port,10):"https:"===this.proxy.protocol?443:80;this.connectOpts={ALPNProtocols:["http/1.1"],...n?cn(n,"headers"):null,host:e,port:r}}async connect(t,n){const{proxy:e}=this;if(!n.host)throw new TypeError('No "host" provided');let r;if("https:"===e.protocol){rn("Creating `tls.Socket`: %o",this.connectOpts);const t=this.connectOpts.servername||this.connectOpts.host;r=f.connect({...this.connectOpts,servername:t&&a.isIP(t)?void 0:t})}else rn("Creating `net.Socket`: %o",this.connectOpts),r=a.connect(this.connectOpts);const o="function"==typeof this.proxyHeaders?this.proxyHeaders():{...this.proxyHeaders},i=a.isIPv6(n.host)?`[${n.host}]`:n.host;let s=`CONNECT ${i}:${n.port} HTTP/1.1\r\n`;if(e.username||e.password){const t=`${decodeURIComponent(e.username)}:${decodeURIComponent(e.password)}`;o["Proxy-Authorization"]=`Basic ${Buffer.from(t).toString("base64")}`}o.Host=`${i}:${n.port}`,o["Proxy-Connection"]||(o["Proxy-Connection"]=this.keepAlive?"Keep-Alive":"close");for(const t of Object.keys(o))s+=`${t}: ${o[t]}\r\n`;const c=en(r);r.write(`${s}\r\n`);const{connect:u,buffered:h}=await c;if(t.emit("proxyConnect",u),this.emit("proxyConnect",u,t),200===u.statusCode){if(t.once("socket",sn),n.secureEndpoint){rn("Upgrading socket connection to TLS");const t=n.servername||n.host;return f.connect({...cn(n,"host","path","port"),socket:r,servername:a.isIP(t)?void 0:t})}return r}r.destroy();const p=new a.Socket({writable:!1});return p.readable=!0,t.once("socket",t=>{rn("Replaying proxy buffer for failed request"),t.push(h),t.push(null)}),p}}function sn(t){t.resume()}function cn(t,...n){const e={};let r;for(r in t)n.includes(r)||(e[r]=t[r]);return e}on.__initStatic();function un(t){return t.replace(/^[A-Z]:/,"").replace(/\\/g,"/")}const an=n;let fn,hn=0,pn={};function ln(t){an.debug&&console.log(`[ANR Worker] ${t}`)}var dn,mn,gn;const yn=function(t){let n;try{n=new URL(t.url)}catch(n){return b(()=>{console.warn("[@sentry/node]: Invalid dsn or tunnel option, will not send any events. The tunnel option must be a full URL when used.")}),Kt(t,()=>Promise.resolve({}))}const e="https:"===n.protocol,r=function(t,n){const{no_proxy:e}=process.env,r=e?.split(",").some(n=>t.host.endsWith(n)||t.hostname.endsWith(n));return r?void 0:n}(n,t.proxy||(e?process.env.https_proxy:void 0)||process.env.http_proxy),o=e?s:i,a=void 0!==t.keepAlive&&t.keepAlive,f=r?new on(r):new o.Agent({keepAlive:a,maxSockets:30,timeout:2e3}),h=function(t,n,e){const{hostname:r,pathname:o,port:i,protocol:s,search:a}=new URL(t.url);return function(f){return new Promise((h,p)=>{Ft(()=>{let l=function(t){return new c({read(){this.push(t),this.push(null)}})}(f.body);const d={...t.headers};f.body.length>32768&&(d["content-encoding"]="gzip",l=l.pipe(u()));const m=r.startsWith("["),g=n.request({method:"POST",agent:e,headers:d,hostname:m?r.slice(1,-1):r,path:`${o}${a}`,port:i,protocol:s,ca:t.caCerts},t=>{t.on("data",()=>{}),t.on("end",()=>{}),t.setEncoding("utf8");const n=t.headers["retry-after"]??null,e=t.headers["x-sentry-rate-limits"]??null;h({statusCode:t.statusCode,headers:{"retry-after":n,"x-sentry-rate-limits":Array.isArray(e)?e[0]||null:e}})});g.on("error",p),l.pipe(g)})})}}(t,t.httpModule??o,f);return Kt(t,h)}({url:(dn=an.dsn,mn=an.tunnel,gn=an.sdkMetadata.sdk,mn||`${function(t){return`${function(t){const n=t.protocol?`${t.protocol}:`:"",e=t.port?`:${t.port}`:"";return`${n}//${t.host}${e}${t.path?`/${t.path}`:""}/api/`}(t)}${t.projectId}/envelope/`}(dn)}?${function(t,n){const e={sentry_version:"7"};return t.publicKey&&(e.sentry_key=t.publicKey),n&&(e.sentry_client=`${n.name}/${n.version}`),new URLSearchParams(e).toString()}(dn,gn)}`)});async function bn(){if(fn){ln("Sending abnormal session"),V(fn,{status:"abnormal",abnormal_mechanism:"anr_foreground",release:an.release,environment:an.environment});const t=function(t,n,e,r){const o=Bt(e);return Rt({sent_at:(new Date).toISOString(),...o&&{sdk:o},...!!r&&n&&{dsn:bt(n)}},["aggregates"in t?[{type:"sessions"},t]:[{type:"session"},t.toJSON()]])}(fn,an.dsn,an.sdkMetadata,an.tunnel);ln(JSON.stringify(t)),await yn.send(t);try{e?.postMessage("session-ended")}catch{}}}function vn(t){if(!t)return;const n=function(t){if(!t.length)return[];const n=Array.from(t);return/sentryWrapped/.test(E(n).function||"")&&n.pop(),n.reverse(),$.test(E(n).function||"")&&(n.pop(),$.test(E(n).function||"")&&n.pop()),n.slice(0,50).map(t=>({...t,filename:t.filename||E(n).filename,function:t.function||"?"}))}(t);if(an.appRootPath)for(const t of n)t.filename&&(t.filename=Ot(t.filename,an.appRootPath));return n}async function _n(t,n){if(hn>=an.maxAnrEvents)return;hn+=1,await bn(),ln("Sending event");const e={event_id:F(),contexts:an.contexts,release:an.release,environment:an.environment,dist:an.dist,platform:"node",level:"error",exception:{values:[{type:"ApplicationNotResponding",value:`Application Not Responding for at least ${an.anrThreshold} ms`,stacktrace:{frames:vn(t)},mechanism:{type:"ANR"}}]},tags:an.staticTags};n&&function(t,n){if(Gt(t,n),!t.contexts?.trace){const{traceId:e,parentSpanId:r,propagationSpanId:o}=n.propagationContext;t.contexts={trace:{trace_id:e,span_id:o||q(),parent_span_id:r},...t.contexts}}}(e,n),function(t){if(0===Object.keys(pn).length)return;const n=an.appRootPath?{}:pn;if(an.appRootPath)for(const[t,e]of Object.entries(pn))n[Ot(t,an.appRootPath)]=e;const e=new Map;for(const r of t.exception?.values||[])for(const t of r.stacktrace?.frames||[]){const r=t.abs_path||t.filename;r&&n[r]&&e.set(r,n[r])}if(e.size>0){const n=[];for(const[t,r]of e.entries())n.push({type:"sourcemap",code_file:t,debug_id:r});t.debug_meta={images:n}}}(e);const r=zt(e,an.dsn,an.sdkMetadata,an.tunnel);ln(JSON.stringify(r)),await yn.send(r),await yn.flush(2e3),hn>=an.maxAnrEvents&&setTimeout(()=>{process.exit(0)},5e3)}let wn;if(ln("Started"),an.captureStackTrace){ln("Connecting to debugger");const n=new t;n.connectToMainThread(),ln("Connected to debugger");const e=new Map;n.on("Debugger.scriptParsed",t=>{e.set(t.params.scriptId,t.params.url)}),n.on("Debugger.paused",t=>{if("other"===t.params.reason)try{ln("Debugger paused");const i=[...t.params.callFrames],s=an.appRootPath?function(t=(process.argv[1]?qt(process.argv[1]):process.cwd()),n="\\"===o){const e=n?un(t):t;return t=>{if(!t)return;const o=n?un(t):t;let{dir:i,base:s,ext:c}=r.parse(o);".js"!==c&&".mjs"!==c&&".cjs"!==c||(s=s.slice(0,-1*c.length));const u=decodeURIComponent(s);i||(i=".");const a=i.lastIndexOf("/node_modules");if(a>-1)return`${i.slice(a+14).replace(/\//g,".")}:${u}`;if(i.startsWith(e)){const t=i.slice(e.length+1).replace(/\//g,".");return t?`${t}:${u}`:u}return u}}(an.appRootPath):()=>{},c=i.map(t=>function(t,n,e){const r=n?n.replace(/^file:\/\//,""):void 0,o=t.location.columnNumber?t.location.columnNumber+1:void 0,i=t.location.lineNumber?t.location.lineNumber+1:void 0;return{filename:r,module:e(r),function:t.functionName||"?",colno:o,lineno:i,in_app:r?Qt(r):void 0}}(t,e.get(t.location.scriptId),s)),u=setTimeout(()=>{_n(c).then(null,()=>{ln("Sending ANR event failed.")})},5e3);n.post("Runtime.evaluate",{expression:"global.__SENTRY_GET_SCOPES__();",silent:!0,returnByValue:!0},(t,e)=>{t&&ln(`Error executing script: '${t.message}'`),clearTimeout(u);const r=e?.result?e.result.value:void 0;n.post("Debugger.resume"),n.post("Debugger.disable"),_n(c,r).then(null,()=>{ln("Sending ANR event failed.")})})}catch(t){throw n.post("Debugger.resume"),n.post("Debugger.disable"),t}}),wn=()=>{try{n.post("Debugger.enable",()=>{n.post("Debugger.pause")})}catch{}}}const{poll:Sn}=function(t,n,e,r){const o=t();let i=!1,s=!0;return setInterval(()=>{const t=o.getTimeMs();!1===i&&t>n+e&&(i=!0,s&&r()),t<n+e&&(i=!1)},20),{poll:()=>{o.reset()},enabled:t=>{s=t}}}(function(){let t=process.hrtime();return{getTimeMs:()=>{const[n,e]=process.hrtime(t);return Math.floor(1e3*n+e/1e6)},reset:()=>{t=process.hrtime()}}},an.pollInterval,an.anrThreshold,function(){ln("Watchdog timeout"),wn?(ln("Pausing debugger to capture stack trace"),wn()):(ln("Capturing event without a stack trace"),_n().then(null,()=>{ln("Sending ANR event failed on watchdog timeout.")}))});e?.on("message",t=>{t.session&&(fn=Y(t.session)),t.debugImages&&(pn=t.debugImages),Sn()}); | ||
| /*! @sentry/node-core 10.54.0 (f31686b) | https://github.com/getsentry/sentry-javascript */ | ||
| import{Session as t}from"node:inspector";import{workerData as n,parentPort as e}from"node:worker_threads";import{posix as r,sep as o}from"node:path";import*as i from"node:http";import*as s from"node:https";import{Readable as c}from"node:stream";import{createGzip as u}from"node:zlib";import*as a from"node:net";import*as f from"node:tls";const h="undefined"==typeof __SENTRY_DEBUG__||__SENTRY_DEBUG__,p=globalThis,l="10.54.0";function d(){return m(p),p}function m(t){const n=t.__SENTRY__=t.__SENTRY__||{};return n.version=n.version||l,n[l]=n[l]||{}}function g(t,n,e=p){const r=e.__SENTRY__=e.__SENTRY__||{},o=r[l]=r[l]||{};return o[t]||(o[t]=n())}const y={};function b(t){if(!("console"in p))return t();const n=p.console,e={},r=Object.keys(y);r.forEach(t=>{const r=y[t];e[t]=n[t],n[t]=r});try{return t()}finally{r.forEach(t=>{n[t]=e[t]})}}function v(){return w().enabled}function _(t,...n){h&&v()&&b(()=>{p.console[t](`Sentry Logger [${t}]:`,...n)})}function w(){return h?g("loggerSettings",()=>({enabled:!1})):{enabled:!1}}const S={enable:function(){w().enabled=!0},disable:function(){w().enabled=!1},isEnabled:v,log:function(...t){_("log",...t)},warn:function(...t){_("warn",...t)},error:function(...t){_("error",...t)}},$=/captureMessage|captureException/;function E(t){return t[t.length-1]||{}}const x="<anonymous>";const N=Object.prototype.toString;function j(t,n){return N.call(t)===`[object ${n}]`}function C(t){return j(t,"String")}function A(t){return j(t,"Object")}function k(t){return Boolean(t?.then&&"function"==typeof t.then)}function T(t,n){try{return t instanceof n}catch{return!1}}const I=p;function O(t,n){const e=t,r=[];if(!e?.tagName)return"";if(I.HTMLElement&&e instanceof HTMLElement&&e.dataset){if(e.dataset.sentryComponent)return e.dataset.sentryComponent;if(e.dataset.sentryElement)return e.dataset.sentryElement}r.push(e.tagName.toLowerCase());const o=n?.length?n.filter(t=>e.getAttribute(t)).map(t=>[t,e.getAttribute(t)]):null;if(o?.length)o.forEach(t=>{r.push(`[${t[0]}="${t[1]}"]`)});else{e.id&&r.push(`#${e.id}`);const t=e.className;if(t&&C(t)){const n=t.split(/\s+/);for(const t of n)r.push(`.${t}`)}}for(const t of["aria-label","type","name","title","alt"]){const n=e.getAttribute(t);n&&r.push(`[${t}="${n}"]`)}return r.join("")}function R(t){if(function(t){switch(N.call(t)){case"[object Error]":case"[object Exception]":case"[object DOMException]":case"[object WebAssembly.Exception]":return!0;default:return T(t,Error)}}(t))return{message:t.message,name:t.name,stack:t.stack,...D(t)};if(n=t,"undefined"!=typeof Event&&T(n,Event)){const n={type:t.type,target:P(t.target),currentTarget:P(t.currentTarget),...D(t)};return"undefined"!=typeof CustomEvent&&T(t,CustomEvent)&&(n.detail=t.detail),n}return t;var n}function P(t){try{return n=t,"undefined"!=typeof Element&&T(n,Element)?function(t,n={}){if(!t)return"<unknown>";try{let e=t;const r=5,o=[];let i=0,s=0;const c=" > ",u=c.length;let a;const f=Array.isArray(n)?n:n.keyAttrs,h=!Array.isArray(n)&&n.maxStringLength||80;for(;e&&i++<r&&(a=O(e,f),!("html"===a||i>1&&s+o.length*u+a.length>=h));)o.push(a),s+=a.length,e=e.parentNode;return o.reverse().join(c)}catch{return"<unknown>"}}(t):Object.prototype.toString.call(t)}catch{return"<unknown>"}var n}function D(t){return"object"==typeof t&&null!==t?Object.fromEntries(Object.entries(t)):{}}let U,L;function M(t){if(void 0!==U)return U?U(t):t();const n=Symbol.for("__SENTRY_SAFE_RANDOM_ID_WRAPPER__"),e=p;return n in e&&"function"==typeof e[n]?(U=e[n],U(t)):(U=null,t())}function B(){return M(()=>Math.random())}function z(){return M(()=>Date.now())}function W(t,n=0){return"string"!=typeof t||0===n||t.length<=n?t:`${t.slice(0,n)}...`}function F(t=function(){const t=p;return t.crypto||t.msCrypto}()){try{if(t?.randomUUID)return M(()=>t.randomUUID()).replace(/-/g,"")}catch{}return L||(L="10000000100040008000100000000000"),L.replace(/[018]/g,t=>(t^(16*B()&15)>>t/4).toString(16))}function G(){return z()/1e3}let H;function J(){return(H??(H=function(){const{performance:t}=p;if(!t?.now||!t.timeOrigin)return G;const n=t.timeOrigin;return()=>(n+M(()=>t.now()))/1e3}()))()}function Y(t){const n=J(),e={sid:F(),init:!0,timestamp:n,started:n,duration:0,status:"ok",errors:0,ignoreDuration:!1,toJSON:()=>function(t){return{sid:`${t.sid}`,init:t.init,started:new Date(1e3*t.started).toISOString(),timestamp:new Date(1e3*t.timestamp).toISOString(),status:t.status,errors:t.errors,did:"number"==typeof t.did||"string"==typeof t.did?`${t.did}`:void 0,duration:t.duration,abnormal_mechanism:t.abnormal_mechanism,attrs:{release:t.release,environment:t.environment,ip_address:t.ipAddress,user_agent:t.userAgent}}}(e)};return t&&V(e,t),e}function V(t,n={}){if(n.user&&(!t.ipAddress&&n.user.ip_address&&(t.ipAddress=n.user.ip_address),t.did||n.did||(t.did=n.user.id||n.user.email||n.user.username)),t.timestamp=n.timestamp||J(),n.abnormal_mechanism&&(t.abnormal_mechanism=n.abnormal_mechanism),n.ignoreDuration&&(t.ignoreDuration=n.ignoreDuration),n.sid&&(t.sid=32===n.sid.length?n.sid:F()),void 0!==n.init&&(t.init=n.init),!t.did&&n.did&&(t.did=`${n.did}`),"number"==typeof n.started&&(t.started=n.started),t.ignoreDuration)t.duration=void 0;else if("number"==typeof n.duration)t.duration=n.duration;else{const n=t.timestamp-t.started;t.duration=n>=0?n:0}n.release&&(t.release=n.release),n.environment&&(t.environment=n.environment),!t.ipAddress&&n.ipAddress&&(t.ipAddress=n.ipAddress),!t.userAgent&&n.userAgent&&(t.userAgent=n.userAgent),"number"==typeof n.errors&&(t.errors=n.errors),n.status&&(t.status=n.status)}function K(t,n,e=2){if(!n||"object"!=typeof n||e<=0)return n;if(t&&0===Object.keys(n).length)return t;const r={...t};for(const t in n)Object.prototype.hasOwnProperty.call(n,t)&&(r[t]=K(r[t],n[t],e-1));return r}function Z(){return F()}function q(){return F().substring(16)}const Q="_sentrySpan";function X(t,n){n?function(t,n,e){try{Object.defineProperty(t,n,{value:e,writable:!0,configurable:!0})}catch{h&&S.log(`Failed to add non-enumerable property "${String(n)}" to object`,t)}}(t,Q,n):delete t[Q]}function tt(t){return t[Q]}class nt{constructor(){this.t=!1,this.o=[],this.i=[],this.u=[],this.h=[],this.p={},this.l={},this.m={},this.v={},this._={},this.S={},this.N={traceId:Z(),sampleRand:B()}}clone(){const t=new nt;return t.u=[...this.u],t.l={...this.l},t.m={...this.m},t.v={...this.v},t._={...this._},this._.flags&&(t._.flags={values:[...this._.flags.values]}),t.p=this.p,t.j=this.j,t.C=this.C,t.A=this.A,t.k=this.k,t.i=[...this.i],t.h=[...this.h],t.S={...this.S},t.N={...this.N},t.T=this.T,t.I=this.I,t.O=this.O,X(t,tt(this)),t}setClient(t){this.T=t}setLastEventId(t){this.I=t}getClient(){return this.T}lastEventId(){return this.I}addScopeListener(t){this.o.push(t)}addEventProcessor(t){return this.i.push(t),this}setUser(t){return this.p=t||{email:void 0,id:void 0,ip_address:void 0,username:void 0},this.C&&V(this.C,{user:t}),this.R(),this}getUser(){return this.p}setConversationId(t){return this.O=t||void 0,this.R(),this}setTags(t){return this.l={...this.l,...t},this.R(),this}setTag(t,n){return this.setTags({[t]:n})}setAttributes(t){return this.m={...this.m,...t},this.R(),this}setAttribute(t,n){return this.setAttributes({[t]:n})}removeAttribute(t){return t in this.m&&(delete this.m[t],this.R()),this}setExtras(t){return this.v={...this.v,...t},this.R(),this}setExtra(t,n){return this.v={...this.v,[t]:n},this.R(),this}setFingerprint(t){return this.k=t,this.R(),this}setLevel(t){return this.j=t,this.R(),this}setTransactionName(t){return this.A=t,this.R(),this}setContext(t,n){return null===n?delete this._[t]:this._[t]=n,this.R(),this}setSession(t){return t?this.C=t:delete this.C,this.R(),this}getSession(){return this.C}update(t){if(!t)return this;const n="function"==typeof t?t(this):t,e=n instanceof nt?n.getScopeData():A(n)?t:void 0,{tags:r,attributes:o,extra:i,user:s,contexts:c,level:u,fingerprint:a=[],propagationContext:f,conversationId:h}=e||{};return this.l={...this.l,...r},this.m={...this.m,...o},this.v={...this.v,...i},this._={...this._,...c},s&&Object.keys(s).length&&(this.p=s),u&&(this.j=u),a.length&&(this.k=a),f&&(this.N=f),h&&(this.O=h),this}clear(){return this.u=[],this.l={},this.m={},this.v={},this.p={},this._={},this.j=void 0,this.A=void 0,this.k=void 0,this.C=void 0,this.O=void 0,X(this,void 0),this.h=[],this.setPropagationContext({traceId:Z(),sampleRand:B()}),this.R(),this}addBreadcrumb(t,n){const e="number"==typeof n?n:100;if(e<=0)return this;const r={timestamp:G(),...t,message:t.message?W(t.message,2048):t.message};return this.u.push(r),this.u.length>e&&(this.u=this.u.slice(-e),this.T?.recordDroppedEvent("buffer_overflow","log_item")),this.R(),this}getLastBreadcrumb(){return this.u[this.u.length-1]}clearBreadcrumbs(){return this.u=[],this.R(),this}addAttachment(t){return this.h.push(t),this}clearAttachments(){return this.h=[],this}getScopeData(){return{breadcrumbs:this.u,attachments:this.h,contexts:this._,tags:this.l,attributes:this.m,extra:this.v,user:this.p,level:this.j,fingerprint:this.k||[],eventProcessors:this.i,propagationContext:this.N,sdkProcessingMetadata:this.S,transactionName:this.A,span:tt(this),conversationId:this.O}}setSDKProcessingMetadata(t){return this.S=K(this.S,t,2),this}setPropagationContext(t){return this.N=t,this}getPropagationContext(){return this.N}captureException(t,n){const e=n?.event_id||F();if(!this.T)return h&&S.warn("No client configured on scope - will not capture exception!"),e;const r=new Error("Sentry syntheticException");return this.T.captureException(t,{originalException:t,syntheticException:r,...n,event_id:e},this),e}captureMessage(t,n,e){const r=e?.event_id||F();if(!this.T)return h&&S.warn("No client configured on scope - will not capture message!"),r;const o=e?.syntheticException??new Error(t);return this.T.captureMessage(t,n,{originalException:t,syntheticException:o,...e,event_id:r},this),r}captureEvent(t,n){const e=t.event_id||n?.event_id||F();return this.T?(this.T.captureEvent(t,{...n,event_id:e},this),e):(h&&S.warn("No client configured on scope - will not capture event!"),e)}R(){this.t||(this.t=!0,this.o.forEach(t=>{t(this)}),this.t=!1)}}const et=t=>t instanceof Promise&&!t[rt],rt=Symbol("chained PromiseLike"),ot=(t,n)=>{if(!n)return t;let e=!1;for(const r in t){if(r in n)continue;e=!0;const o=t[r];"function"==typeof o?Object.defineProperty(n,r,{value:(...n)=>o.apply(t,n),enumerable:!0,configurable:!0,writable:!0}):n[r]=o}return e&&Object.assign(n,{[rt]:!0}),n};class it{constructor(t,n){let e,r;e=t||new nt,r=n||new nt,this.P=[{scope:e}],this.D=r}withScope(t){const n=this.U();let e;try{e=t(n)}catch(t){throw this.L(),t}return k(e)?((t,n,e)=>{const r=t.then(t=>(n(t),t),t=>{throw e(t),t});return et(r)&&et(t)?r:ot(t,r)})(e,()=>this.L(),()=>this.L()):(this.L(),e)}getClient(){return this.getStackTop().client}getScope(){return this.getStackTop().scope}getIsolationScope(){return this.D}getStackTop(){return this.P[this.P.length-1]}U(){const t=this.getScope().clone();return this.P.push({client:this.getClient(),scope:t}),t}L(){return!(this.P.length<=1)&&!!this.P.pop()}}function st(){const t=m(d());return t.stack=t.stack||new it(g("defaultCurrentScope",()=>new nt),g("defaultIsolationScope",()=>new nt))}function ct(t){return st().withScope(t)}function ut(t,n){const e=st();return e.withScope(()=>(e.getStackTop().scope=t,n(t)))}function at(t){return st().withScope(()=>t(st().getIsolationScope()))}function ft(t){const n=m(t);return n.acs?n.acs:{withIsolationScope:at,withScope:ct,withSetScope:ut,withSetIsolationScope:(t,n)=>at(n),getCurrentScope:()=>st().getScope(),getIsolationScope:()=>st().getIsolationScope()}}function ht(){return ft(d()).getCurrentScope().getClient()}function pt(t){if(t){if("object"==typeof t&&"deref"in t&&"function"==typeof t.deref)try{return t.deref()}catch{return}return t}}function lt(t){const n=t;return{scope:n._sentryScope,isolationScope:pt(n._sentryIsolationScope)}}const dt="sentry-";function mt(t){const n=function(t){if(!t||!C(t)&&!Array.isArray(t))return;if(Array.isArray(t))return t.reduce((t,n)=>{const e=gt(n);return Object.entries(e).forEach(([n,e])=>{t[n]=e}),t},{});return gt(t)}(t);if(!n)return;const e=Object.entries(n).reduce((t,[n,e])=>{if(n.startsWith(dt)){t[n.slice(7)]=e}return t},{});return Object.keys(e).length>0?e:void 0}function gt(t){return t.split(",").map(t=>{const n=t.indexOf("=");if(-1===n)return[];return[t.slice(0,n),t.slice(n+1)].map(t=>{try{return decodeURIComponent(t.trim())}catch{return}})}).reduce((t,[n,e])=>(n&&e&&(t[n]=e),t),{})}const yt=/^o(\d+)\./;function bt(t,n=!1){const{host:e,path:r,pass:o,port:i,projectId:s,protocol:c,publicKey:u}=t;return`${c}://${u}${n&&o?`:${o}`:""}@${e}${i?`:${i}`:""}/${r?`${r}/`:r}${s}`}function vt(t){const n=t.getOptions(),{host:e}=t.getDsn()||{};let r;return n.orgId?r=String(n.orgId):e&&(r=function(t){const n=t.match(yt);return n?.[1]}(e)),r}function _t(t){const{spanId:n,traceId:e,isRemote:r}=t.spanContext(),o=r?n:Et(t).parent_span_id,i=lt(t).scope;return{parent_span_id:o,span_id:r?i?.getPropagationContext().propagationSpanId||q():n,trace_id:e}}function wt(t){return t&&t.length>0?t.map(({context:{spanId:t,traceId:n,traceFlags:e,...r},attributes:o})=>({span_id:t,trace_id:n,sampled:1===e,attributes:o,...r})):void 0}function St(t){return"number"==typeof t?$t(t):Array.isArray(t)?t[0]+t[1]/1e9:t instanceof Date?$t(t.getTime()):J()}function $t(t){return t>9999999999?t/1e3:t}function Et(t){if(function(t){return"function"==typeof t.getSpanJSON}(t))return t.getSpanJSON();const{spanId:n,traceId:e}=t.spanContext();if(function(t){const n=t;return!!(n.attributes&&n.startTime&&n.name&&n.endTime&&n.status)}(t)){const{attributes:r,startTime:o,name:i,endTime:s,status:c,links:u}=t;return{span_id:n,trace_id:e,data:r,description:i,parent_span_id:xt(t),start_timestamp:St(o),timestamp:St(s)||void 0,status:Nt(c),op:r["sentry.op"],origin:r["sentry.origin"],links:wt(u)}}return{span_id:n,trace_id:e,start_timestamp:0,data:{}}}function xt(t){return"parentSpanId"in t?t.parentSpanId:"parentSpanContext"in t?t.parentSpanContext?.spanId:void 0}function Nt(t){if(t&&0!==t.code)return 1===t.code?"ok":t.message||"internal_error"}const jt=function(t){return t._sentryRootSpan||t};function Ct(t){const n=ht();if(!n)return{};const e=jt(t),r=Et(e),o=r.data,i=e.spanContext().traceState,s=i?.get("sentry.sample_rate")??o["sentry.sample_rate"]??o["sentry.previous_trace_sample_rate"];function c(t){return"number"!=typeof s&&"string"!=typeof s||(t.sample_rate=`${s}`),t}const u=e._frozenDsc;if(u)return c(u);const a=i?.get("sentry.dsc"),f=a&&mt(a);if(f)return c(f);const h=function(t,n){const e=n.getOptions(),{publicKey:r}=n.getDsn()||{},o={environment:e.environment||"production",release:e.release,public_key:r,trace_id:t,org_id:vt(n)};return n.emit("createDsc",o),o}(t.spanContext().traceId,n),p=o["sentry.source"]??o["sentry.span.source"],l=r.description;return"url"!==p&&l&&(h.transaction=l),function(){if("boolean"==typeof __SENTRY_TRACING__&&!__SENTRY_TRACING__)return!1;const t=ht()?.getOptions();return!(!t||null==t.tracesSampleRate&&!t.tracesSampler)}()&&(h.sampled=String(function(t){const{traceFlags:n}=t.spanContext();return 1===n}(e)),h.sample_rand=i?.get("sentry.sample_rand")??lt(e).scope?.getPropagationContext().sampleRand.toString()),c(h),n.emit("createDsc",h,e),h}const At=Symbol.for("sentry.skipNormalization"),kt=Symbol.for("sentry.overrideNormalizationDepth");function Tt(t,n=100,e=1/0){try{return It("",t,n,e)}catch(t){return{ERROR:`**non-serializable** (${t})`}}}function It(t,n,e=1/0,r=1/0,o=function(){const t=new WeakSet;function n(n){return!!t.has(n)||(t.add(n),!1)}function e(n){t.delete(n)}return[n,e]}()){const[i,s]=o;if(null==n||["boolean","string"].includes(typeof n)||"number"==typeof n&&Number.isFinite(n))return n;const c=function(t,n){try{if("domain"===t&&n&&"object"==typeof n&&n.M)return"[Domain]";if("domainEmitter"===t)return"[DomainEmitter]";if("undefined"!=typeof global&&n===global)return"[Global]";if("undefined"!=typeof window&&n===window)return"[Window]";if("undefined"!=typeof document&&n===document)return"[Document]";if("object"==typeof(e=n)&&null!==e&&(e.__isVue||e.B||e.__v_isVNode))return function(t){return"__v_isVNode"in t&&t.__v_isVNode?"[VueVNode]":"[VueViewModel]"}(n);if(function(t){return A(t)&&"nativeEvent"in t&&"preventDefault"in t&&"stopPropagation"in t}(n))return"[SyntheticEvent]";if("number"==typeof n&&!Number.isFinite(n))return`[${n}]`;if("function"==typeof n)return`[Function: ${function(t){try{return t&&"function"==typeof t&&t.name||x}catch{return x}}(n)}]`;if("symbol"==typeof n)return`[${String(n)}]`;if("bigint"==typeof n)return`[BigInt: ${String(n)}]`;const r=function(t){const n=Object.getPrototypeOf(t);return n?.constructor?n.constructor.name:"null prototype"}(n);return/^HTML(\w*)Element$/.test(r)?`[HTMLElement: ${r}]`:`[object ${r}]`}catch(t){return`**non-serializable** (${t})`}var e}(t,n);if(!c.startsWith("[object "))return c;if(function(t){return Boolean(t[At])}(n))return n;const u=function(t){const n=t[kt];return"number"==typeof n?n:void 0}(n),a=void 0!==u?u:e;if(0===a)return c.replace("object ","");if(i(n))return"[Circular ~]";const f=n;if(f&&"function"==typeof f.toJSON)try{return It("",f.toJSON(),a-1,r,o)}catch{}const h=Array.isArray(n)?[]:{};let p=0;const l=R(n);for(const t in l){if(!Object.prototype.hasOwnProperty.call(l,t))continue;if(p>=r){h[t]="[MaxProperties ~]";break}const n=l[t];h[t]=It(t,n,a-1,r,o),p++}return s(n),h}function Ot(t,n){const e=n.replace(/\\/g,"/").replace(/[|\\{}()[\]^$+*?.]/g,"\\$&");let r=t;try{r=decodeURI(t)}catch{}return r.replace(/\\/g,"/").replace(/webpack:\/?/g,"").replace(new RegExp(`(file://)?/*${e}/*`,"ig"),"app:///")}function Rt(t,n=[]){return[t,n]}function Pt(t,n){const e=t[1];for(const t of e){if(n(t,t[0].type))return!0}return!1}function Dt(t){const n=m(p);return n.encodePolyfill?n.encodePolyfill(t):(new TextEncoder).encode(t)}function Ut(t){const[n,e]=t;let r=JSON.stringify(n);function o(t){"string"==typeof r?r="string"==typeof t?r+t:[Dt(r),t]:r.push("string"==typeof t?Dt(t):t)}for(const t of e){const[n,e]=t;if(o(`\n${JSON.stringify(n)}\n`),"string"==typeof e||e instanceof Uint8Array)o(e);else{let t;try{t=JSON.stringify(e)}catch{t=JSON.stringify(Tt(e))}o(t)}}return"string"==typeof r?r:function(t){const n=t.reduce((t,n)=>t+n.length,0),e=new Uint8Array(n);let r=0;for(const n of t)e.set(n,r),r+=n.length;return e}(r)}const Lt={sessions:"session",event:"error",client_report:"internal",user_report:"default",profile_chunk:"profile",replay_event:"replay",replay_recording:"replay",check_in:"monitor",raw_security:"security",log:"log_item",trace_metric:"metric"};function Mt(t){return function(t){return t in Lt}(t)?Lt[t]:t}function Bt(t){if(!t?.sdk)return;const{name:n,version:e}=t.sdk;return{name:n,version:e}}function zt(t,n,e,r){const o=Bt(e),i=t.type&&"replay_event"!==t.type?t.type:"event";!function(t,n){if(!n)return t;const e=t.sdk||{};t.sdk={...e,name:e.name||n.name,version:e.version||n.version,integrations:[...t.sdk?.integrations||[],...n.integrations||[]],packages:[...t.sdk?.packages||[],...n.packages||[]],settings:t.sdk?.settings||n.settings?{...t.sdk?.settings,...n.settings}:void 0}}(t,e?.sdk);const s=function(t,n,e,r){const o=t.sdkProcessingMetadata?.dynamicSamplingContext;return{event_id:t.event_id,sent_at:(new Date).toISOString(),...n&&{sdk:n},...!!e&&r&&{dsn:bt(r)},...o&&{trace:o}}}(t,o,r,n);delete t.sdkProcessingMetadata;return Rt(s,[[{type:i},t]])}const Wt="__SENTRY_SUPPRESS_TRACING__";function Ft(t){const n=ft(d());return n.suppressTracing?n.suppressTracing(t):function(...t){const n=ft(d());if(2===t.length){const[e,r]=t;return e?n.withSetScope(e,r):n.withScope(r)}return n.withScope(t[0])}(n=>{n.setSDKProcessingMetadata({[Wt]:!0});const e=t();return n.setSDKProcessingMetadata({[Wt]:void 0}),e})}function Gt(t,n){const{fingerprint:e,span:r,breadcrumbs:o,sdkProcessingMetadata:i}=n;!function(t,n){const{extra:e,tags:r,user:o,contexts:i,level:s,transactionName:c}=n;Object.keys(e).length&&(t.extra={...e,...t.extra});Object.keys(r).length&&(t.tags={...r,...t.tags});Object.keys(o).length&&(t.user={...o,...t.user});Object.keys(i).length&&(t.contexts={...i,...t.contexts});s&&(t.level=s);c&&"transaction"!==t.type&&(t.transaction=c)}(t,n),r&&function(t,n){t.contexts={trace:_t(n),...t.contexts},t.sdkProcessingMetadata={dynamicSamplingContext:Ct(n),...t.sdkProcessingMetadata};const e=jt(n),r=Et(e).description;r&&!t.transaction&&"transaction"===t.type&&(t.transaction=r)}(t,r),function(t,n){t.fingerprint=t.fingerprint?Array.isArray(t.fingerprint)?t.fingerprint:[t.fingerprint]:[],n&&(t.fingerprint=t.fingerprint.concat(n));t.fingerprint.length||delete t.fingerprint}(t,e),function(t,n){const e=[...t.breadcrumbs||[],...n];t.breadcrumbs=e.length?e:void 0}(t,o),function(t,n){t.sdkProcessingMetadata={...t.sdkProcessingMetadata,...n}}(t,i)}class Ht{constructor(t){this.W=0,this.F=[],this.G(t)}then(t,n){return new Ht((e,r)=>{this.F.push([!1,n=>{if(t)try{e(t(n))}catch(t){r(t)}else e(n)},t=>{if(n)try{e(n(t))}catch(t){r(t)}else r(t)}]),this.H()})}catch(t){return this.then(t=>t,t)}finally(t){return new Ht((n,e)=>{let r,o;return this.then(n=>{o=!1,r=n,t&&t()},n=>{o=!0,r=n,t&&t()}).then(()=>{o?e(r):n(r)})})}H(){if(0===this.W)return;const t=this.F.slice();this.F=[],t.forEach(t=>{t[0]||(1===this.W&&t[1](this.J),2===this.W&&t[2](this.J),t[0]=!0)})}G(t){const n=(t,n)=>{0===this.W&&(k(n)?n.then(e,r):(this.W=t,this.J=n,this.H()))},e=t=>{n(1,t)},r=t=>{n(2,t)};try{t(e,r)}catch(t){r(t)}}}const Jt=Symbol.for("SentryBufferFullError");function Yt(t=100){const n=new Set;function e(t){n.delete(t)}return{get $(){return Array.from(n)},add:function(r){if(!(n.size<t))return o=Jt,new Ht((t,n)=>{n(o)});var o;const i=r();return n.add(i),i.then(()=>e(i),()=>e(i)),i},drain:function(t){if(!n.size)return e=!0,new Ht(t=>{t(e)});var e;const r=Promise.allSettled(Array.from(n)).then(()=>!0);if(!t)return r;const o=[r,new Promise(n=>{return"object"==typeof(e=setTimeout(()=>n(!1),t))&&"function"==typeof e.unref&&e.unref(),e;var e})];return Promise.race(o)}}}function Vt(t,{statusCode:n,headers:e},r=z()){const o={...t},i=e?.["x-sentry-rate-limits"],s=e?.["retry-after"];if(i)for(const t of i.trim().split(",")){const[n,e,,,i]=t.split(":",5),s=parseInt(n,10),c=1e3*(isNaN(s)?60:s);if(e)for(const t of e.split(";"))"metric_bucket"===t&&i&&!i.split(";").includes("custom")||(o[t]=r+c);else o.all=r+c}else s?o.all=r+function(t,n=z()){const e=parseInt(`${t}`,10);if(!isNaN(e))return 1e3*e;const r=Date.parse(`${t}`);return isNaN(r)?6e4:r-n}(s,r):429===n&&(o.all=r+6e4);return o}function Kt(t,n,e=Yt(t.bufferSize||64)){let r={};return{send:function(t){const o=[];if(Pt(t,(t,n)=>{const e=Mt(n);(function(t,n,e=z()){return function(t,n){return t[n]||t.all||0}(t,n)>e})(r,e)||o.push(t)}),0===o.length)return Promise.resolve({});const i=Rt(t[0],o),s=t=>{!function(t,n){return Pt(t,(t,e)=>n.includes(e))}(i,["client_report"])?Pt(i,(t,n)=>{}):h&&S.warn(`Dropping client report. Will not send outcomes (reason: ${t}).`)};return e.add(()=>n({body:Ut(i)}).then(t=>413===t.statusCode?(h&&S.error("Sentry responded with status code 413. Envelope was discarded due to exceeding size limits."),s("send_error"),t):(h&&void 0!==t.statusCode&&(t.statusCode<200||t.statusCode>=300)&&S.warn(`Sentry responded with status code ${t.statusCode} to sent event.`),r=Vt(r,t),t),t=>{throw s("network_error"),h&&S.error("Encountered error running transport request:",t),t})).then(t=>t,t=>{if(t===Jt)return h&&S.error("Skipped sending event because buffer is full."),s("queue_overflow"),Promise.resolve({});throw t})},flush:t=>e.drain(t)}}const Zt=/^(\S+:\\|\/?)([\s\S]*?)((?:\.{1,2}|[^/\\]+?|)(\.[^./\\]*|))(?:[/\\]*)$/;function qt(t){const n=function(t){const n=t.length>1024?`<truncated>${t.slice(-1024)}`:t,e=Zt.exec(n);return e?e.slice(1):[]}(t),e=n[0]||"";let r=n[1];return e||r?(r&&(r=r.slice(0,r.length-1)),e+r):"."}function Qt(t,n=!1){return!(n||t&&!t.startsWith("/")&&!t.match(/^[A-Z]:/)&&!t.startsWith(".")&&!t.match(/^[a-zA-Z]([a-zA-Z0-9.\-+])*:\/\//))&&void 0!==t&&!t.includes("node_modules/")}var Xt;const tn=Symbol("AgentBaseInternalState");class nn extends(Xt=i.Agent,Xt){constructor(t){super(t),this[tn]={}}isSecureEndpoint(t){if(t){if("boolean"==typeof t.secureEndpoint)return t.secureEndpoint;if("string"==typeof t.protocol)return"https:"===t.protocol}const{stack:n}=new Error;return"string"==typeof n&&n.split("\n").some(t=>-1!==t.indexOf("(https.js:")||-1!==t.indexOf("node:https:"))}createSocket(t,n,e){const r={...n,secureEndpoint:this.isSecureEndpoint(n)};Promise.resolve().then(()=>this.connect(t,r)).then(o=>{if(o instanceof i.Agent)return o.addRequest(t,r);this[tn].currentSocket=o,super.createSocket(t,n,e)},e)}createConnection(){const t=this[tn].currentSocket;if(this[tn].currentSocket=void 0,!t)throw new Error("No socket was returned in the `connect()` function");return t}get defaultPort(){return this[tn].defaultPort??("https:"===this.protocol?443:80)}set defaultPort(t){this[tn]&&(this[tn].defaultPort=t)}get protocol(){return this[tn].protocol??(this.isSecureEndpoint()?"https:":"http:")}set protocol(t){this[tn]&&(this[tn].protocol=t)}}function en(...t){S.log("[https-proxy-agent:parse-proxy-response]",...t)}function rn(t){return new Promise((n,e)=>{let r=0;const o=[];function i(){const c=t.read();c?function(c){o.push(c),r+=c.length;const u=Buffer.concat(o,r),a=u.indexOf("\r\n\r\n");if(-1===a)return en("have not received end of HTTP headers yet..."),void i();const f=u.subarray(0,a).toString("ascii").split("\r\n"),h=f.shift();if(!h)return t.destroy(),e(new Error("No header received from proxy CONNECT response"));const p=h.split(" "),l=+(p[1]||0),d=p.slice(2).join(" "),m={};for(const n of f){if(!n)continue;const r=n.indexOf(":");if(-1===r)return t.destroy(),e(new Error(`Invalid header from proxy CONNECT response: "${n}"`));const o=n.slice(0,r).toLowerCase(),i=n.slice(r+1).trimStart(),s=m[o];"string"==typeof s?m[o]=[s,i]:Array.isArray(s)?s.push(i):m[o]=i}en("got proxy server response: %o %o",h,m),s(),n({connect:{statusCode:l,statusText:d,headers:m},buffered:u})}(c):t.once("readable",i)}function s(){t.removeListener("end",c),t.removeListener("error",u),t.removeListener("readable",i)}function c(){s(),en("onend"),e(new Error("Proxy connection ended before receiving CONNECT response"))}function u(t){s(),en("onerror %o",t),e(t)}t.on("error",u),t.on("end",c),i()})}function on(...t){S.log("[https-proxy-agent]",...t)}class sn extends nn{constructor(t,n){super(n),this.options={},this.proxy="string"==typeof t?new URL(t):t,this.proxyHeaders=n?.headers??{},on("Creating new HttpsProxyAgent instance: %o",this.proxy.href);const e=(this.proxy.hostname||this.proxy.host).replace(/^\[|\]$/g,""),r=this.proxy.port?parseInt(this.proxy.port,10):"https:"===this.proxy.protocol?443:80;this.connectOpts={ALPNProtocols:["http/1.1"],...n?un(n,"headers"):null,host:e,port:r}}async connect(t,n){const{proxy:e}=this;if(!n.host)throw new TypeError('No "host" provided');let r;if("https:"===e.protocol){on("Creating `tls.Socket`: %o",this.connectOpts);const t=this.connectOpts.servername||this.connectOpts.host;r=f.connect({...this.connectOpts,servername:t&&a.isIP(t)?void 0:t})}else on("Creating `net.Socket`: %o",this.connectOpts),r=a.connect(this.connectOpts);const o="function"==typeof this.proxyHeaders?this.proxyHeaders():{...this.proxyHeaders},i=a.isIPv6(n.host)?`[${n.host}]`:n.host;let s=`CONNECT ${i}:${n.port} HTTP/1.1\r\n`;if(e.username||e.password){const t=`${decodeURIComponent(e.username)}:${decodeURIComponent(e.password)}`;o["Proxy-Authorization"]=`Basic ${Buffer.from(t).toString("base64")}`}o.Host=`${i}:${n.port}`,o["Proxy-Connection"]||(o["Proxy-Connection"]=this.keepAlive?"Keep-Alive":"close");for(const t of Object.keys(o))s+=`${t}: ${o[t]}\r\n`;const c=rn(r);r.write(`${s}\r\n`);const{connect:u,buffered:h}=await c;if(t.emit("proxyConnect",u),this.emit("proxyConnect",u,t),200===u.statusCode){if(t.once("socket",cn),n.secureEndpoint){on("Upgrading socket connection to TLS");const t=n.servername||n.host;return f.connect({...un(n,"host","path","port"),socket:r,servername:a.isIP(t)?void 0:t})}return r}r.destroy();const p=new a.Socket({writable:!1});return p.readable=!0,t.once("socket",t=>{on("Replaying proxy buffer for failed request"),t.push(h),t.push(null)}),p}}function cn(t){t.resume()}function un(t,...n){const e={};let r;for(r in t)n.includes(r)||(e[r]=t[r]);return e}sn.protocols=["http","https"];function an(t){return t.replace(/^[A-Z]:/,"").replace(/\\/g,"/")}const fn=n;let hn,pn=0,ln={};function dn(t){fn.debug&&console.log(`[ANR Worker] ${t}`)}var mn,gn,yn;const bn=function(t){let n;try{n=new URL(t.url)}catch(n){return b(()=>{console.warn("[@sentry/node]: Invalid dsn or tunnel option, will not send any events. The tunnel option must be a full URL when used.")}),Kt(t,()=>Promise.resolve({}))}const e="https:"===n.protocol,r=function(t,n){const{no_proxy:e}=process.env,r=e?.split(",").some(n=>t.host.endsWith(n)||t.hostname.endsWith(n));return r?void 0:n}(n,t.proxy||(e?process.env.https_proxy:void 0)||process.env.http_proxy),o=e?s:i,a=void 0!==t.keepAlive&&t.keepAlive,f=r?new sn(r):new o.Agent({keepAlive:a,maxSockets:30,timeout:2e3}),h=function(t,n,e){const{hostname:r,pathname:o,port:i,protocol:s,search:a}=new URL(t.url);return function(f){return new Promise((h,p)=>{Ft(()=>{let l=function(t){return new c({read(){this.push(t),this.push(null)}})}(f.body);const d={...t.headers};f.body.length>32768&&(d["content-encoding"]="gzip",l=l.pipe(u()));const m=r.startsWith("["),g=n.request({method:"POST",agent:e,headers:d,hostname:m?r.slice(1,-1):r,path:`${o}${a}`,port:i,protocol:s,ca:t.caCerts},t=>{t.on("data",()=>{}),t.on("end",()=>{}),t.setEncoding("utf8");const n=t.headers["retry-after"]??null,e=t.headers["x-sentry-rate-limits"]??null;h({statusCode:t.statusCode,headers:{"retry-after":n,"x-sentry-rate-limits":Array.isArray(e)?e[0]||null:e}})});g.on("error",p),l.pipe(g)})})}}(t,t.httpModule??o,f);return Kt(t,h)}({url:(mn=fn.dsn,gn=fn.tunnel,yn=fn.sdkMetadata.sdk,gn||`${function(t){return`${function(t){const n=t.protocol?`${t.protocol}:`:"",e=t.port?`:${t.port}`:"";return`${n}//${t.host}${e}${t.path?`/${t.path}`:""}/api/`}(t)}${t.projectId}/envelope/`}(mn)}?${function(t,n){const e={sentry_version:"7"};return t.publicKey&&(e.sentry_key=t.publicKey),n&&(e.sentry_client=`${n.name}/${n.version}`),new URLSearchParams(e).toString()}(mn,yn)}`)});async function vn(){if(hn){dn("Sending abnormal session"),V(hn,{status:"abnormal",abnormal_mechanism:"anr_foreground",release:fn.release,environment:fn.environment});const t=function(t,n,e,r){const o=Bt(e);return Rt({sent_at:(new Date).toISOString(),...o&&{sdk:o},...!!r&&n&&{dsn:bt(n)}},["aggregates"in t?[{type:"sessions"},t]:[{type:"session"},t.toJSON()]])}(hn,fn.dsn,fn.sdkMetadata,fn.tunnel);dn(JSON.stringify(t)),await bn.send(t);try{e?.postMessage("session-ended")}catch{}}}function _n(t){if(!t)return;const n=function(t){if(!t.length)return[];const n=Array.from(t);return/sentryWrapped/.test(E(n).function||"")&&n.pop(),n.reverse(),$.test(E(n).function||"")&&(n.pop(),$.test(E(n).function||"")&&n.pop()),n.slice(0,50).map(t=>({...t,filename:t.filename||E(n).filename,function:t.function||"?"}))}(t);if(fn.appRootPath)for(const t of n)t.filename&&(t.filename=Ot(t.filename,fn.appRootPath));return n}async function wn(t,n){if(pn>=fn.maxAnrEvents)return;pn+=1,await vn(),dn("Sending event");const e={event_id:F(),contexts:fn.contexts,release:fn.release,environment:fn.environment,dist:fn.dist,platform:"node",level:"error",exception:{values:[{type:"ApplicationNotResponding",value:`Application Not Responding for at least ${fn.anrThreshold} ms`,stacktrace:{frames:_n(t)},mechanism:{type:"ANR"}}]},tags:fn.staticTags};n&&function(t,n){if(Gt(t,n),!t.contexts?.trace){const{traceId:e,parentSpanId:r,propagationSpanId:o}=n.propagationContext;t.contexts={trace:{trace_id:e,span_id:o||q(),parent_span_id:r},...t.contexts}}}(e,n),function(t){if(0===Object.keys(ln).length)return;const n=fn.appRootPath?{}:ln;if(fn.appRootPath)for(const[t,e]of Object.entries(ln))n[Ot(t,fn.appRootPath)]=e;const e=new Map;for(const r of t.exception?.values||[])for(const t of r.stacktrace?.frames||[]){const r=t.abs_path||t.filename;r&&n[r]&&e.set(r,n[r])}if(e.size>0){const n=[];for(const[t,r]of e.entries())n.push({type:"sourcemap",code_file:t,debug_id:r});t.debug_meta={images:n}}}(e);const r=zt(e,fn.dsn,fn.sdkMetadata,fn.tunnel);dn(JSON.stringify(r)),await bn.send(r),await bn.flush(2e3),pn>=fn.maxAnrEvents&&setTimeout(()=>{process.exit(0)},5e3)}let Sn;if(dn("Started"),fn.captureStackTrace){dn("Connecting to debugger");const n=new t;n.connectToMainThread(),dn("Connected to debugger");const e=new Map;n.on("Debugger.scriptParsed",t=>{e.set(t.params.scriptId,t.params.url)}),n.on("Debugger.paused",t=>{if("other"===t.params.reason)try{dn("Debugger paused");const i=[...t.params.callFrames],s=fn.appRootPath?function(t=(process.argv[1]?qt(process.argv[1]):process.cwd()),n="\\"===o){const e=n?an(t):t;return t=>{if(!t)return;const o=n?an(t):t;let{dir:i,base:s,ext:c}=r.parse(o);".js"!==c&&".mjs"!==c&&".cjs"!==c||(s=s.slice(0,-1*c.length));const u=decodeURIComponent(s);i||(i=".");const a=i.lastIndexOf("/node_modules");if(a>-1)return`${i.slice(a+14).replace(/\//g,".")}:${u}`;if(i.startsWith(e)){const t=i.slice(e.length+1).replace(/\//g,".");return t?`${t}:${u}`:u}return u}}(fn.appRootPath):()=>{},c=i.map(t=>function(t,n,e){const r=n?n.replace(/^file:\/\//,""):void 0,o=t.location.columnNumber?t.location.columnNumber+1:void 0,i=t.location.lineNumber?t.location.lineNumber+1:void 0;return{filename:r,module:e(r),function:t.functionName||"?",colno:o,lineno:i,in_app:r?Qt(r):void 0}}(t,e.get(t.location.scriptId),s)),u=setTimeout(()=>{wn(c).then(null,()=>{dn("Sending ANR event failed.")})},5e3);n.post("Runtime.evaluate",{expression:"global.__SENTRY_GET_SCOPES__();",silent:!0,returnByValue:!0},(t,e)=>{t&&dn(`Error executing script: '${t.message}'`),clearTimeout(u);const r=e?.result?e.result.value:void 0;n.post("Debugger.resume"),n.post("Debugger.disable"),wn(c,r).then(null,()=>{dn("Sending ANR event failed.")})})}catch(t){throw n.post("Debugger.resume"),n.post("Debugger.disable"),t}}),Sn=()=>{try{n.post("Debugger.enable",()=>{n.post("Debugger.pause")})}catch{}}}const{poll:$n}=function(t,n,e,r){const o=t();let i=!1,s=!0;return setInterval(()=>{const t=o.getTimeMs();!1===i&&t>n+e&&(i=!0,s&&r()),t<n+e&&(i=!1)},20),{poll:()=>{o.reset()},enabled:t=>{s=t}}}(function(){let t=process.hrtime();return{getTimeMs:()=>{const[n,e]=process.hrtime(t);return Math.floor(1e3*n+e/1e6)},reset:()=>{t=process.hrtime()}}},fn.pollInterval,fn.anrThreshold,function(){dn("Watchdog timeout"),Sn?(dn("Pausing debugger to capture stack trace"),Sn()):(dn("Capturing event without a stack trace"),wn().then(null,()=>{dn("Sending ANR event failed on watchdog timeout.")}))});e?.on("message",t=>{t.session&&(hn=Y(t.session)),t.debugImages&&(ln=t.debugImages),$n()}); |
| import * as diagnosticsChannel from 'node:diagnostics_channel'; | ||
| import { defineIntegration, addBreadcrumb, captureException } from '@sentry/core'; | ||
| const INTEGRATION_NAME = 'ChildProcess'; | ||
| /** | ||
| * Capture breadcrumbs and events for child processes and worker threads. | ||
| */ | ||
| const INTEGRATION_NAME = "ChildProcess"; | ||
| const childProcessIntegration = defineIntegration((options = {}) => { | ||
@@ -13,84 +9,69 @@ return { | ||
| setup() { | ||
| diagnosticsChannel.channel('child_process').subscribe((event) => { | ||
| if (event && typeof event === 'object' && 'process' in event) { | ||
| captureChildProcessEvents(event.process , options); | ||
| diagnosticsChannel.channel("child_process").subscribe((event) => { | ||
| if (event && typeof event === "object" && "process" in event) { | ||
| captureChildProcessEvents(event.process, options); | ||
| } | ||
| }); | ||
| diagnosticsChannel.channel('worker_threads').subscribe((event) => { | ||
| if (event && typeof event === 'object' && 'worker' in event) { | ||
| captureWorkerThreadEvents(event.worker , options); | ||
| diagnosticsChannel.channel("worker_threads").subscribe((event) => { | ||
| if (event && typeof event === "object" && "worker" in event) { | ||
| captureWorkerThreadEvents(event.worker, options); | ||
| } | ||
| }); | ||
| }, | ||
| } | ||
| }; | ||
| }); | ||
| function captureChildProcessEvents(child, options) { | ||
| let hasExited = false; | ||
| let data; | ||
| child | ||
| .on('spawn', () => { | ||
| // This is Sentry getting macOS OS context | ||
| if (child.spawnfile === '/usr/bin/sw_vers') { | ||
| hasExited = true; | ||
| return; | ||
| } | ||
| data = { spawnfile: child.spawnfile }; | ||
| if (options.includeChildProcessArgs) { | ||
| data.spawnargs = child.spawnargs; | ||
| } | ||
| }) | ||
| .on('exit', code => { | ||
| if (!hasExited) { | ||
| hasExited = true; | ||
| // Only log for non-zero exit codes | ||
| if (code !== null && code !== 0) { | ||
| addBreadcrumb({ | ||
| category: 'child_process', | ||
| message: `Child process exited with code '${code}'`, | ||
| level: code === 0 ? 'info' : 'warning', | ||
| data, | ||
| }); | ||
| } | ||
| } | ||
| }) | ||
| .on('error', error => { | ||
| if (!hasExited) { | ||
| hasExited = true; | ||
| child.on("spawn", () => { | ||
| if (child.spawnfile === "/usr/bin/sw_vers") { | ||
| hasExited = true; | ||
| return; | ||
| } | ||
| data = { spawnfile: child.spawnfile }; | ||
| if (options.includeChildProcessArgs) { | ||
| data.spawnargs = child.spawnargs; | ||
| } | ||
| }).on("exit", (code) => { | ||
| if (!hasExited) { | ||
| hasExited = true; | ||
| if (code !== null && code !== 0) { | ||
| addBreadcrumb({ | ||
| category: 'child_process', | ||
| message: `Child process errored with '${error.message}'`, | ||
| level: 'error', | ||
| data, | ||
| category: "child_process", | ||
| message: `Child process exited with code '${code}'`, | ||
| level: code === 0 ? "info" : "warning", | ||
| data | ||
| }); | ||
| } | ||
| }); | ||
| } | ||
| }).on("error", (error) => { | ||
| if (!hasExited) { | ||
| hasExited = true; | ||
| addBreadcrumb({ | ||
| category: "child_process", | ||
| message: `Child process errored with '${error.message}'`, | ||
| level: "error", | ||
| data | ||
| }); | ||
| } | ||
| }); | ||
| } | ||
| function captureWorkerThreadEvents(worker, options) { | ||
| let threadId; | ||
| worker | ||
| .on('online', () => { | ||
| threadId = worker.threadId; | ||
| }) | ||
| .on('error', error => { | ||
| if (options.captureWorkerErrors !== false) { | ||
| captureException(error, { | ||
| mechanism: { type: 'auto.child_process.worker_thread', handled: false, data: { threadId: String(threadId) } }, | ||
| }); | ||
| } else { | ||
| addBreadcrumb({ | ||
| category: 'worker_thread', | ||
| message: `Worker thread errored with '${error.message}'`, | ||
| level: 'error', | ||
| data: { threadId }, | ||
| }); | ||
| } | ||
| }); | ||
| worker.on("online", () => { | ||
| threadId = worker.threadId; | ||
| }).on("error", (error) => { | ||
| if (options.captureWorkerErrors !== false) { | ||
| captureException(error, { | ||
| mechanism: { type: "auto.child_process.worker_thread", handled: false, data: { threadId: String(threadId) } } | ||
| }); | ||
| } else { | ||
| addBreadcrumb({ | ||
| category: "worker_thread", | ||
| message: `Worker thread errored with '${error.message}'`, | ||
| level: "error", | ||
| data: { threadId } | ||
| }); | ||
| } | ||
| }); | ||
| } | ||
@@ -97,0 +78,0 @@ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"childProcess.js","sources":["../../../src/integrations/childProcess.ts"],"sourcesContent":["import type { ChildProcess } from 'node:child_process';\nimport * as diagnosticsChannel from 'node:diagnostics_channel';\nimport type { Worker } from 'node:worker_threads';\nimport { addBreadcrumb, captureException, defineIntegration } from '@sentry/core';\n\ninterface Options {\n /**\n * Whether to include child process arguments in breadcrumbs data.\n *\n * @default false\n */\n includeChildProcessArgs?: boolean;\n\n /**\n * Whether to capture errors from worker threads.\n *\n * @default true\n */\n captureWorkerErrors?: boolean;\n}\n\nconst INTEGRATION_NAME = 'ChildProcess';\n\n/**\n * Capture breadcrumbs and events for child processes and worker threads.\n */\nexport const childProcessIntegration = defineIntegration((options: Options = {}) => {\n return {\n name: INTEGRATION_NAME,\n setup() {\n diagnosticsChannel.channel('child_process').subscribe((event: unknown) => {\n if (event && typeof event === 'object' && 'process' in event) {\n captureChildProcessEvents(event.process as ChildProcess, options);\n }\n });\n\n diagnosticsChannel.channel('worker_threads').subscribe((event: unknown) => {\n if (event && typeof event === 'object' && 'worker' in event) {\n captureWorkerThreadEvents(event.worker as Worker, options);\n }\n });\n },\n };\n});\n\nfunction captureChildProcessEvents(child: ChildProcess, options: Options): void {\n let hasExited = false;\n let data: Record<string, unknown> | undefined;\n\n child\n .on('spawn', () => {\n // This is Sentry getting macOS OS context\n if (child.spawnfile === '/usr/bin/sw_vers') {\n hasExited = true;\n return;\n }\n\n data = { spawnfile: child.spawnfile };\n if (options.includeChildProcessArgs) {\n data.spawnargs = child.spawnargs;\n }\n })\n .on('exit', code => {\n if (!hasExited) {\n hasExited = true;\n\n // Only log for non-zero exit codes\n if (code !== null && code !== 0) {\n addBreadcrumb({\n category: 'child_process',\n message: `Child process exited with code '${code}'`,\n level: code === 0 ? 'info' : 'warning',\n data,\n });\n }\n }\n })\n .on('error', error => {\n if (!hasExited) {\n hasExited = true;\n\n addBreadcrumb({\n category: 'child_process',\n message: `Child process errored with '${error.message}'`,\n level: 'error',\n data,\n });\n }\n });\n}\n\nfunction captureWorkerThreadEvents(worker: Worker, options: Options): void {\n let threadId: number | undefined;\n\n worker\n .on('online', () => {\n threadId = worker.threadId;\n })\n .on('error', error => {\n if (options.captureWorkerErrors !== false) {\n captureException(error, {\n mechanism: { type: 'auto.child_process.worker_thread', handled: false, data: { threadId: String(threadId) } },\n });\n } else {\n addBreadcrumb({\n category: 'worker_thread',\n message: `Worker thread errored with '${error.message}'`,\n level: 'error',\n data: { threadId },\n });\n }\n });\n}\n"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"childProcess.js","sources":["../../../src/integrations/childProcess.ts"],"sourcesContent":["import type { ChildProcess } from 'node:child_process';\nimport * as diagnosticsChannel from 'node:diagnostics_channel';\nimport type { Worker } from 'node:worker_threads';\nimport { addBreadcrumb, captureException, defineIntegration } from '@sentry/core';\n\ninterface Options {\n /**\n * Whether to include child process arguments in breadcrumbs data.\n *\n * @default false\n */\n includeChildProcessArgs?: boolean;\n\n /**\n * Whether to capture errors from worker threads.\n *\n * @default true\n */\n captureWorkerErrors?: boolean;\n}\n\nconst INTEGRATION_NAME = 'ChildProcess';\n\n/**\n * Capture breadcrumbs and events for child processes and worker threads.\n */\nexport const childProcessIntegration = defineIntegration((options: Options = {}) => {\n return {\n name: INTEGRATION_NAME,\n setup() {\n diagnosticsChannel.channel('child_process').subscribe((event: unknown) => {\n if (event && typeof event === 'object' && 'process' in event) {\n captureChildProcessEvents(event.process as ChildProcess, options);\n }\n });\n\n diagnosticsChannel.channel('worker_threads').subscribe((event: unknown) => {\n if (event && typeof event === 'object' && 'worker' in event) {\n captureWorkerThreadEvents(event.worker as Worker, options);\n }\n });\n },\n };\n});\n\nfunction captureChildProcessEvents(child: ChildProcess, options: Options): void {\n let hasExited = false;\n let data: Record<string, unknown> | undefined;\n\n child\n .on('spawn', () => {\n // This is Sentry getting macOS OS context\n if (child.spawnfile === '/usr/bin/sw_vers') {\n hasExited = true;\n return;\n }\n\n data = { spawnfile: child.spawnfile };\n if (options.includeChildProcessArgs) {\n data.spawnargs = child.spawnargs;\n }\n })\n .on('exit', code => {\n if (!hasExited) {\n hasExited = true;\n\n // Only log for non-zero exit codes\n if (code !== null && code !== 0) {\n addBreadcrumb({\n category: 'child_process',\n message: `Child process exited with code '${code}'`,\n level: code === 0 ? 'info' : 'warning',\n data,\n });\n }\n }\n })\n .on('error', error => {\n if (!hasExited) {\n hasExited = true;\n\n addBreadcrumb({\n category: 'child_process',\n message: `Child process errored with '${error.message}'`,\n level: 'error',\n data,\n });\n }\n });\n}\n\nfunction captureWorkerThreadEvents(worker: Worker, options: Options): void {\n let threadId: number | undefined;\n\n worker\n .on('online', () => {\n threadId = worker.threadId;\n })\n .on('error', error => {\n if (options.captureWorkerErrors !== false) {\n captureException(error, {\n mechanism: { type: 'auto.child_process.worker_thread', handled: false, data: { threadId: String(threadId) } },\n });\n } else {\n addBreadcrumb({\n category: 'worker_thread',\n message: `Worker thread errored with '${error.message}'`,\n level: 'error',\n data: { threadId },\n });\n }\n });\n}\n"],"names":[],"mappings":"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"} |
| import { defineIntegration, maybeInstrument, consoleIntegration as consoleIntegration$1, GLOBAL_OBJ, CONSOLE_LEVELS, originalConsoleMethods, markFunctionWrapped, fill, triggerHandlers } from '@sentry/core'; | ||
| /** | ||
| * Node-specific console integration that captures breadcrumbs and handles | ||
| * the AWS Lambda runtime replacing console methods after our patch. | ||
| * | ||
| * In Lambda, console methods are patched via `Object.defineProperty` so that | ||
| * external replacements (by the Lambda runtime) are absorbed as the delegate | ||
| * while our wrapper stays in place. Outside Lambda, this delegates entirely | ||
| * to the core `consoleIntegration` which uses the simpler `fill`-based patch. | ||
| */ | ||
| const consoleIntegration = defineIntegration((options = {}) => { | ||
| return { | ||
| name: 'Console', | ||
| name: "Console", | ||
| setup(client) { | ||
| if (process.env.LAMBDA_TASK_ROOT) { | ||
| maybeInstrument('console', instrumentConsoleLambda); | ||
| maybeInstrument("console", instrumentConsoleLambda); | ||
| } | ||
| // Delegate breadcrumb handling to the core console integration. | ||
| const core = consoleIntegration$1({ | ||
| ...options, | ||
| filter: [ | ||
| ...(options.filter || []), | ||
| ...options.filter || [], | ||
| // Deprecation on Node 26 for module.require(), which is used by IITM | ||
| '[DEP0205] DeprecationWarning', | ||
| ], | ||
| "[DEP0205] DeprecationWarning" | ||
| ] | ||
| }); | ||
| core.setup?.(client); | ||
| }, | ||
| } | ||
| }; | ||
| }); | ||
| /** | ||
| * NOTE: This currently ignores the filter option. | ||
| * We can revisit this later. | ||
| */ | ||
| function instrumentConsoleLambda() { | ||
@@ -43,3 +27,2 @@ const consoleObj = GLOBAL_OBJ?.console; | ||
| } | ||
| CONSOLE_LEVELS.forEach((level) => { | ||
@@ -51,16 +34,10 @@ if (level in consoleObj) { | ||
| } | ||
| function patchWithDefineProperty(consoleObj, level) { | ||
| const nativeMethod = consoleObj[level] ; | ||
| const nativeMethod = consoleObj[level]; | ||
| originalConsoleMethods[level] = nativeMethod; | ||
| let delegate = nativeMethod; | ||
| let savedDelegate; | ||
| let isExecuting = false; | ||
| const wrapper = function (...args) { | ||
| const wrapper = function(...args) { | ||
| if (isExecuting) { | ||
| // Re-entrant call: a third party captured `wrapper` via the getter and calls it from inside their replacement. We must | ||
| // use `nativeMethod` (not `delegate`) to break the cycle, and we intentionally skip `triggerHandlers` to avoid duplicate | ||
| // breadcrumbs. The outer invocation already triggered the handlers for this console call. | ||
| nativeMethod.apply(consoleObj, args); | ||
@@ -71,3 +48,3 @@ return; | ||
| try { | ||
| triggerHandlers('console', { args, level } ); | ||
| triggerHandlers("console", { args, level }); | ||
| delegate.apply(consoleObj, args); | ||
@@ -78,12 +55,7 @@ } finally { | ||
| }; | ||
| markFunctionWrapped(wrapper , nativeMethod ); | ||
| // consoleSandbox reads originalConsoleMethods[level] to temporarily bypass instrumentation. We replace it with a distinct reference (.bind creates a | ||
| // new function identity) so the setter can tell apart "consoleSandbox bypass" from "external code restoring a native method captured before Sentry init." | ||
| markFunctionWrapped(wrapper, nativeMethod); | ||
| const sandboxBypass = nativeMethod.bind(consoleObj); | ||
| originalConsoleMethods[level] = sandboxBypass; | ||
| try { | ||
| let current = wrapper; | ||
| Object.defineProperty(consoleObj, level, { | ||
@@ -97,13 +69,11 @@ configurable: true, | ||
| if (newValue === wrapper) { | ||
| // consoleSandbox restoring the wrapper: recover the saved delegate. | ||
| if (savedDelegate !== undefined) { | ||
| if (savedDelegate !== void 0) { | ||
| delegate = savedDelegate; | ||
| savedDelegate = undefined; | ||
| savedDelegate = void 0; | ||
| } | ||
| current = wrapper; | ||
| } else if (newValue === sandboxBypass) { | ||
| // consoleSandbox entering bypass: save delegate, let getter return sandboxBypass directly so calls skip the wrapper entirely. | ||
| savedDelegate = delegate; | ||
| current = sandboxBypass; | ||
| } else if (typeof newValue === 'function' && !(newValue ).__sentry_original__) { | ||
| } else if (typeof newValue === "function" && !newValue.__sentry_original__) { | ||
| delegate = newValue; | ||
@@ -114,11 +84,9 @@ current = wrapper; | ||
| } | ||
| }, | ||
| } | ||
| }); | ||
| } catch { | ||
| // Fall back to fill-based patching if defineProperty fails | ||
| fill(consoleObj, level, function (originalConsoleMethod) { | ||
| fill(consoleObj, level, function(originalConsoleMethod) { | ||
| originalConsoleMethods[level] = originalConsoleMethod; | ||
| return function ( ...args) { | ||
| triggerHandlers('console', { args, level } ); | ||
| return function(...args) { | ||
| triggerHandlers("console", { args, level }); | ||
| originalConsoleMethods[level]?.apply(this, args); | ||
@@ -125,0 +93,0 @@ }; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"console.js","sources":["../../../src/integrations/console.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type { ConsoleLevel, HandlerDataConsole, WrappedFunction } from '@sentry/core';\nimport {\n CONSOLE_LEVELS,\n GLOBAL_OBJ,\n consoleIntegration as coreConsoleIntegration,\n defineIntegration,\n fill,\n markFunctionWrapped,\n maybeInstrument,\n originalConsoleMethods,\n triggerHandlers,\n} from '@sentry/core';\n\ninterface ConsoleIntegrationOptions {\n levels: ConsoleLevel[];\n /**\n * Filter out console messages that match the given strings or regular expressions.\n * These will neither be passed to the handler, and they will also not be logged to the user, unless they have debug enabled.\n */\n filter?: (string | RegExp)[];\n}\n\n/**\n * Node-specific console integration that captures breadcrumbs and handles\n * the AWS Lambda runtime replacing console methods after our patch.\n *\n * In Lambda, console methods are patched via `Object.defineProperty` so that\n * external replacements (by the Lambda runtime) are absorbed as the delegate\n * while our wrapper stays in place. Outside Lambda, this delegates entirely\n * to the core `consoleIntegration` which uses the simpler `fill`-based patch.\n */\nexport const consoleIntegration = defineIntegration((options: Partial<ConsoleIntegrationOptions> = {}) => {\n return {\n name: 'Console',\n setup(client) {\n if (process.env.LAMBDA_TASK_ROOT) {\n maybeInstrument('console', instrumentConsoleLambda);\n }\n\n // Delegate breadcrumb handling to the core console integration.\n const core = coreConsoleIntegration({\n ...options,\n filter: [\n ...(options.filter || []),\n // Deprecation on Node 26 for module.require(), which is used by IITM\n '[DEP0205] DeprecationWarning',\n ],\n });\n core.setup?.(client);\n },\n };\n});\n\n/**\n * NOTE: This currently ignores the filter option.\n * We can revisit this later.\n */\nfunction instrumentConsoleLambda(): void {\n const consoleObj = GLOBAL_OBJ?.console;\n if (!consoleObj) {\n return;\n }\n\n CONSOLE_LEVELS.forEach((level: ConsoleLevel) => {\n if (level in consoleObj) {\n patchWithDefineProperty(consoleObj, level);\n }\n });\n}\n\nfunction patchWithDefineProperty(consoleObj: Console, level: ConsoleLevel): void {\n const nativeMethod = consoleObj[level] as (...args: unknown[]) => void;\n originalConsoleMethods[level] = nativeMethod;\n\n let delegate: Function = nativeMethod;\n let savedDelegate: Function | undefined;\n let isExecuting = false;\n\n const wrapper = function (...args: any[]): void {\n if (isExecuting) {\n // Re-entrant call: a third party captured `wrapper` via the getter and calls it from inside their replacement. We must\n // use `nativeMethod` (not `delegate`) to break the cycle, and we intentionally skip `triggerHandlers` to avoid duplicate\n // breadcrumbs. The outer invocation already triggered the handlers for this console call.\n nativeMethod.apply(consoleObj, args);\n return;\n }\n isExecuting = true;\n try {\n triggerHandlers('console', { args, level } as HandlerDataConsole);\n delegate.apply(consoleObj, args);\n } finally {\n isExecuting = false;\n }\n };\n markFunctionWrapped(wrapper as unknown as WrappedFunction, nativeMethod as unknown as WrappedFunction);\n\n // consoleSandbox reads originalConsoleMethods[level] to temporarily bypass instrumentation. We replace it with a distinct reference (.bind creates a\n // new function identity) so the setter can tell apart \"consoleSandbox bypass\" from \"external code restoring a native method captured before Sentry init.\"\n const sandboxBypass = nativeMethod.bind(consoleObj);\n originalConsoleMethods[level] = sandboxBypass;\n\n try {\n let current: any = wrapper;\n\n Object.defineProperty(consoleObj, level, {\n configurable: true,\n enumerable: true,\n get() {\n return current;\n },\n set(newValue) {\n if (newValue === wrapper) {\n // consoleSandbox restoring the wrapper: recover the saved delegate.\n if (savedDelegate !== undefined) {\n delegate = savedDelegate;\n savedDelegate = undefined;\n }\n current = wrapper;\n } else if (newValue === sandboxBypass) {\n // consoleSandbox entering bypass: save delegate, let getter return sandboxBypass directly so calls skip the wrapper entirely.\n savedDelegate = delegate;\n current = sandboxBypass;\n } else if (typeof newValue === 'function' && !(newValue as WrappedFunction).__sentry_original__) {\n delegate = newValue;\n current = wrapper;\n } else {\n current = newValue;\n }\n },\n });\n } catch {\n // Fall back to fill-based patching if defineProperty fails\n fill(consoleObj, level, function (originalConsoleMethod: () => any): Function {\n originalConsoleMethods[level] = originalConsoleMethod;\n\n return function (this: Console, ...args: any[]): void {\n triggerHandlers('console', { args, level } as HandlerDataConsole);\n originalConsoleMethods[level]?.apply(this, args);\n };\n });\n }\n}\n"],"names":["coreConsoleIntegration"],"mappings":"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"} | ||
| {"version":3,"file":"console.js","sources":["../../../src/integrations/console.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type { ConsoleLevel, HandlerDataConsole, WrappedFunction } from '@sentry/core';\nimport {\n CONSOLE_LEVELS,\n GLOBAL_OBJ,\n consoleIntegration as coreConsoleIntegration,\n defineIntegration,\n fill,\n markFunctionWrapped,\n maybeInstrument,\n originalConsoleMethods,\n triggerHandlers,\n} from '@sentry/core';\n\ninterface ConsoleIntegrationOptions {\n levels: ConsoleLevel[];\n /**\n * Filter out console messages that match the given strings or regular expressions.\n * These will neither be passed to the handler, and they will also not be logged to the user, unless they have debug enabled.\n */\n filter?: (string | RegExp)[];\n}\n\n/**\n * Node-specific console integration that captures breadcrumbs and handles\n * the AWS Lambda runtime replacing console methods after our patch.\n *\n * In Lambda, console methods are patched via `Object.defineProperty` so that\n * external replacements (by the Lambda runtime) are absorbed as the delegate\n * while our wrapper stays in place. Outside Lambda, this delegates entirely\n * to the core `consoleIntegration` which uses the simpler `fill`-based patch.\n */\nexport const consoleIntegration = defineIntegration((options: Partial<ConsoleIntegrationOptions> = {}) => {\n return {\n name: 'Console',\n setup(client) {\n if (process.env.LAMBDA_TASK_ROOT) {\n maybeInstrument('console', instrumentConsoleLambda);\n }\n\n // Delegate breadcrumb handling to the core console integration.\n const core = coreConsoleIntegration({\n ...options,\n filter: [\n ...(options.filter || []),\n // Deprecation on Node 26 for module.require(), which is used by IITM\n '[DEP0205] DeprecationWarning',\n ],\n });\n core.setup?.(client);\n },\n };\n});\n\n/**\n * NOTE: This currently ignores the filter option.\n * We can revisit this later.\n */\nfunction instrumentConsoleLambda(): void {\n const consoleObj = GLOBAL_OBJ?.console;\n if (!consoleObj) {\n return;\n }\n\n CONSOLE_LEVELS.forEach((level: ConsoleLevel) => {\n if (level in consoleObj) {\n patchWithDefineProperty(consoleObj, level);\n }\n });\n}\n\nfunction patchWithDefineProperty(consoleObj: Console, level: ConsoleLevel): void {\n const nativeMethod = consoleObj[level] as (...args: unknown[]) => void;\n originalConsoleMethods[level] = nativeMethod;\n\n let delegate: Function = nativeMethod;\n let savedDelegate: Function | undefined;\n let isExecuting = false;\n\n const wrapper = function (...args: any[]): void {\n if (isExecuting) {\n // Re-entrant call: a third party captured `wrapper` via the getter and calls it from inside their replacement. We must\n // use `nativeMethod` (not `delegate`) to break the cycle, and we intentionally skip `triggerHandlers` to avoid duplicate\n // breadcrumbs. The outer invocation already triggered the handlers for this console call.\n nativeMethod.apply(consoleObj, args);\n return;\n }\n isExecuting = true;\n try {\n triggerHandlers('console', { args, level } as HandlerDataConsole);\n delegate.apply(consoleObj, args);\n } finally {\n isExecuting = false;\n }\n };\n markFunctionWrapped(wrapper as unknown as WrappedFunction, nativeMethod as unknown as WrappedFunction);\n\n // consoleSandbox reads originalConsoleMethods[level] to temporarily bypass instrumentation. We replace it with a distinct reference (.bind creates a\n // new function identity) so the setter can tell apart \"consoleSandbox bypass\" from \"external code restoring a native method captured before Sentry init.\"\n const sandboxBypass = nativeMethod.bind(consoleObj);\n originalConsoleMethods[level] = sandboxBypass;\n\n try {\n let current: any = wrapper;\n\n Object.defineProperty(consoleObj, level, {\n configurable: true,\n enumerable: true,\n get() {\n return current;\n },\n set(newValue) {\n if (newValue === wrapper) {\n // consoleSandbox restoring the wrapper: recover the saved delegate.\n if (savedDelegate !== undefined) {\n delegate = savedDelegate;\n savedDelegate = undefined;\n }\n current = wrapper;\n } else if (newValue === sandboxBypass) {\n // consoleSandbox entering bypass: save delegate, let getter return sandboxBypass directly so calls skip the wrapper entirely.\n savedDelegate = delegate;\n current = sandboxBypass;\n } else if (typeof newValue === 'function' && !(newValue as WrappedFunction).__sentry_original__) {\n delegate = newValue;\n current = wrapper;\n } else {\n current = newValue;\n }\n },\n });\n } catch {\n // Fall back to fill-based patching if defineProperty fails\n fill(consoleObj, level, function (originalConsoleMethod: () => any): Function {\n originalConsoleMethods[level] = originalConsoleMethod;\n\n return function (this: Console, ...args: any[]): void {\n triggerHandlers('console', { args, level } as HandlerDataConsole);\n originalConsoleMethods[level]?.apply(this, args);\n };\n });\n }\n}\n"],"names":["coreConsoleIntegration"],"mappings":"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"} |
@@ -8,13 +8,5 @@ import { execFile } from 'node:child_process'; | ||
| /* eslint-disable max-lines */ | ||
| const readFileAsync = promisify(readFile); | ||
| const readDirAsync = promisify(readdir); | ||
| // Process enhanced with methods from Node 18, 20, 22 as @types/node | ||
| // is on `14.18.0` to match minimum version requirements of the SDK | ||
| const INTEGRATION_NAME = 'Context'; | ||
| const INTEGRATION_NAME = "Context"; | ||
| const _nodeContextIntegration = ((options = {}) => { | ||
@@ -27,16 +19,12 @@ const _options = { | ||
| cloudResource: true, | ||
| ...options, | ||
| ...options | ||
| }; | ||
| // Compute contexts eagerly (shared between tx and span paths) | ||
| const appContext = _options.app ? getAppContext() : undefined; | ||
| const deviceContext = _options.device ? getDeviceContext(_options.device) : undefined; | ||
| const cultureContext = _options.culture ? getCultureContext() : undefined; | ||
| const cloudResourceContext = _options.cloudResource ? getCloudResourceContext() : undefined; | ||
| const osContextPromise = _options.os ? getOsContext() : undefined; | ||
| // Map static context data to span attributes | ||
| const appContext = _options.app ? getAppContext() : void 0; | ||
| const deviceContext = _options.device ? getDeviceContext(_options.device) : void 0; | ||
| const cultureContext = _options.culture ? getCultureContext() : void 0; | ||
| const cloudResourceContext = _options.cloudResource ? getCloudResourceContext() : void 0; | ||
| const osContextPromise = _options.os ? getOsContext() : void 0; | ||
| const cachedSpanAttributes = { | ||
| 'process.runtime.engine.name': 'v8', | ||
| 'process.runtime.engine.version': process.versions.v8, | ||
| "process.runtime.engine.name": "v8", | ||
| "process.runtime.engine.version": process.versions.v8, | ||
| ...contextsToSpanAttributes({ | ||
@@ -46,15 +34,9 @@ app: appContext, | ||
| culture: cultureContext, | ||
| cloud_resource: cloudResourceContext, | ||
| }), | ||
| cloud_resource: cloudResourceContext | ||
| }) | ||
| }; | ||
| if (osContextPromise) { | ||
| osContextPromise | ||
| .then(osCtx => Object.assign(cachedSpanAttributes, contextsToSpanAttributes({ os: osCtx }))) | ||
| .catch(() => { | ||
| // Ignore - os attributes will be undefined | ||
| }); | ||
| osContextPromise.then((osCtx) => Object.assign(cachedSpanAttributes, contextsToSpanAttributes({ os: osCtx }))).catch(() => { | ||
| }); | ||
| } | ||
| // Build contexts for event processing (reuses same data, awaits async OS context) | ||
| const contextsPromise = (async () => { | ||
@@ -79,7 +61,4 @@ const contexts = {}; | ||
| })(); | ||
| async function addContext(event) { | ||
| const updatedContext = _updateContext(await contextsPromise); | ||
| // TODO(v11): conditional with `sendDefaultPii` here? | ||
| event.contexts = { | ||
@@ -91,8 +70,6 @@ ...event.contexts, | ||
| culture: { ...updatedContext.culture, ...event.contexts?.culture }, | ||
| cloud_resource: { ...updatedContext.cloud_resource, ...event.contexts?.cloud_resource }, | ||
| cloud_resource: { ...updatedContext.cloud_resource, ...event.contexts?.cloud_resource } | ||
| }; | ||
| return event; | ||
| } | ||
| return { | ||
@@ -106,23 +83,12 @@ name: INTEGRATION_NAME, | ||
| safeSetSpanJSONAttributes(span, getDynamicSpanAttributes(appContext, deviceContext)); | ||
| }, | ||
| } | ||
| }; | ||
| }) ; | ||
| /** | ||
| * Capture context about the environment and the device that the client is running on, to events. | ||
| */ | ||
| }); | ||
| const nodeContextIntegration = defineIntegration(_nodeContextIntegration); | ||
| /** | ||
| * Updates the context with dynamic values that can change | ||
| */ | ||
| function _updateContext(contexts) { | ||
| // Only update properties if they exist | ||
| if (contexts.app?.app_memory) { | ||
| contexts.app.app_memory = process.memoryUsage().rss; | ||
| } | ||
| if (contexts.app?.free_memory && typeof (process ).availableMemory === 'function') { | ||
| const freeMemory = (process ).availableMemory?.(); | ||
| if (contexts.app?.free_memory && typeof process.availableMemory === "function") { | ||
| const freeMemory = process.availableMemory?.(); | ||
| if (freeMemory != null) { | ||
@@ -132,67 +98,57 @@ contexts.app.free_memory = freeMemory; | ||
| } | ||
| if (contexts.device?.free_memory) { | ||
| contexts.device.free_memory = os.freemem(); | ||
| } | ||
| return contexts; | ||
| } | ||
| function contextsToSpanAttributes(contexts) { | ||
| const attrs = {}; | ||
| const { app, device, os: osCtx, culture, cloud_resource } = contexts; | ||
| if (app) { | ||
| if (app.app_start_time) { | ||
| attrs['app.start_time'] = app.app_start_time; | ||
| attrs["app.start_time"] = app.app_start_time; | ||
| } | ||
| } | ||
| if (device) { | ||
| if (device.arch) { | ||
| attrs['device.archs'] = [device.arch]; | ||
| attrs["device.archs"] = [device.arch]; | ||
| } | ||
| if (device.boot_time) { | ||
| attrs['device.boot_time'] = device.boot_time; | ||
| attrs["device.boot_time"] = device.boot_time; | ||
| } | ||
| if (device.memory_size != null) { | ||
| attrs['device.memory_size'] = device.memory_size; | ||
| attrs["device.memory_size"] = device.memory_size; | ||
| } | ||
| if (device.processor_count != null) { | ||
| attrs['device.processor_count'] = device.processor_count; | ||
| attrs["device.processor_count"] = device.processor_count; | ||
| } | ||
| if (device.cpu_description) { | ||
| attrs['device.cpu_description'] = device.cpu_description; | ||
| attrs["device.cpu_description"] = device.cpu_description; | ||
| } | ||
| if (device.processor_frequency != null) { | ||
| attrs['device.processor_frequency'] = device.processor_frequency; | ||
| attrs["device.processor_frequency"] = device.processor_frequency; | ||
| } | ||
| } | ||
| if (osCtx) { | ||
| if (osCtx.name) { | ||
| attrs['os.name'] = osCtx.name; | ||
| attrs["os.name"] = osCtx.name; | ||
| } | ||
| if (osCtx.version) { | ||
| attrs['os.version'] = osCtx.version; | ||
| attrs["os.version"] = osCtx.version; | ||
| } | ||
| if (osCtx.kernel_version) { | ||
| attrs['os.kernel_version'] = osCtx.kernel_version; | ||
| attrs["os.kernel_version"] = osCtx.kernel_version; | ||
| } | ||
| if (osCtx.build) { | ||
| attrs['os.build'] = osCtx.build; | ||
| attrs["os.build"] = osCtx.build; | ||
| } | ||
| } | ||
| if (culture) { | ||
| if (culture.locale) { | ||
| attrs['culture.locale'] = culture.locale; | ||
| attrs["culture.locale"] = culture.locale; | ||
| } | ||
| if (culture.timezone) { | ||
| attrs['culture.timezone'] = culture.timezone; | ||
| attrs["culture.timezone"] = culture.timezone; | ||
| } | ||
| } | ||
| // CloudResourceContext already uses dot-notation keys matching span attribute conventions | ||
| if (cloud_resource) { | ||
@@ -205,50 +161,26 @@ for (const [key, value] of Object.entries(cloud_resource)) { | ||
| } | ||
| return attrs; | ||
| } | ||
| function getDynamicSpanAttributes( | ||
| appContext, | ||
| deviceContext, | ||
| ) { | ||
| function getDynamicSpanAttributes(appContext, deviceContext) { | ||
| const attrs = {}; | ||
| if (appContext) { | ||
| attrs['app.memory'] = process.memoryUsage().rss; | ||
| if (typeof (process ).availableMemory === 'function') { | ||
| const freeMemory = (process ).availableMemory?.(); | ||
| attrs["app.memory"] = process.memoryUsage().rss; | ||
| if (typeof process.availableMemory === "function") { | ||
| const freeMemory = process.availableMemory?.(); | ||
| if (freeMemory != null) { | ||
| attrs['app.free_memory'] = freeMemory; | ||
| attrs["app.free_memory"] = freeMemory; | ||
| } | ||
| } | ||
| } | ||
| // Only include if memory tracking was initially enabled (indicated by free_memory being set) | ||
| if (deviceContext?.free_memory != null) { | ||
| attrs['device.free_memory'] = os.freemem(); | ||
| attrs["device.free_memory"] = os.freemem(); | ||
| } | ||
| return attrs; | ||
| } | ||
| /** | ||
| * Returns the operating system context. | ||
| * | ||
| * Based on the current platform, this uses a different strategy to provide the | ||
| * most accurate OS information. Since this might involve spawning subprocesses | ||
| * or accessing the file system, this should only be executed lazily and cached. | ||
| * | ||
| * - On macOS (Darwin), this will execute the `sw_vers` utility. The context | ||
| * has a `name`, `version`, `build` and `kernel_version` set. | ||
| * - On Linux, this will try to load a distribution release from `/etc` and set | ||
| * the `name`, `version` and `kernel_version` fields. | ||
| * - On all other platforms, only a `name` and `version` will be returned. Note | ||
| * that `version` might actually be the kernel version. | ||
| */ | ||
| async function getOsContext() { | ||
| const platformId = os.platform(); | ||
| switch (platformId) { | ||
| case 'darwin': | ||
| case "darwin": | ||
| return getDarwinInfo(); | ||
| case 'linux': | ||
| case "linux": | ||
| return getLinuxInfo(); | ||
@@ -258,47 +190,30 @@ default: | ||
| name: PLATFORM_NAMES[platformId] || platformId, | ||
| version: os.release(), | ||
| version: os.release() | ||
| }; | ||
| } | ||
| } | ||
| function getCultureContext() { | ||
| try { | ||
| if (typeof process.versions.icu !== 'string') { | ||
| // Node was built without ICU support | ||
| if (typeof process.versions.icu !== "string") { | ||
| return; | ||
| } | ||
| // Check that node was built with full Intl support. Its possible it was built without support for non-English | ||
| // locales which will make resolvedOptions inaccurate | ||
| // | ||
| // https://nodejs.org/api/intl.html#detecting-internationalization-support | ||
| const january = new Date(9e8); | ||
| const spanish = new Intl.DateTimeFormat('es', { month: 'long' }); | ||
| if (spanish.format(january) === 'enero') { | ||
| const january = /* @__PURE__ */ new Date(9e8); | ||
| const spanish = new Intl.DateTimeFormat("es", { month: "long" }); | ||
| if (spanish.format(january) === "enero") { | ||
| const options = Intl.DateTimeFormat().resolvedOptions(); | ||
| return { | ||
| locale: options.locale, | ||
| timezone: options.timeZone, | ||
| timezone: options.timeZone | ||
| }; | ||
| } | ||
| } catch { | ||
| // | ||
| } | ||
| return; | ||
| } | ||
| /** | ||
| * Get app context information from process | ||
| */ | ||
| function getAppContext() { | ||
| const app_memory = process.memoryUsage().rss; | ||
| // oxlint-disable-next-line sdk/no-unsafe-random-apis | ||
| const app_start_time = new Date(Date.now() - process.uptime() * 1000).toISOString(); | ||
| // https://nodejs.org/api/process.html#processavailablememory | ||
| const app_start_time = new Date(Date.now() - process.uptime() * 1e3).toISOString(); | ||
| const appContext = { app_start_time, app_memory }; | ||
| if (typeof (process ).availableMemory === 'function') { | ||
| const freeMemory = (process ).availableMemory?.(); | ||
| if (typeof process.availableMemory === "function") { | ||
| const freeMemory = process.availableMemory?.(); | ||
| if (freeMemory != null) { | ||
@@ -308,13 +223,6 @@ appContext.free_memory = freeMemory; | ||
| } | ||
| return appContext; | ||
| } | ||
| /** | ||
| * Gets device information from os | ||
| */ | ||
| function getDeviceContext(deviceOpt) { | ||
| const device = {}; | ||
| // Sometimes os.uptime() throws due to lacking permissions: https://github.com/getsentry/sentry-javascript/issues/8202 | ||
| let uptime; | ||
@@ -324,15 +232,7 @@ try { | ||
| } catch { | ||
| // noop | ||
| } | ||
| // os.uptime or its return value seem to be undefined in certain environments (e.g. Azure functions). | ||
| // Hence, we only set boot time, if we get a valid uptime value. | ||
| // @see https://github.com/getsentry/sentry-javascript/issues/5856 | ||
| if (typeof uptime === 'number') { | ||
| // oxlint-disable-next-line sdk/no-unsafe-random-apis | ||
| device.boot_time = new Date(Date.now() - uptime * 1000).toISOString(); | ||
| if (typeof uptime === "number") { | ||
| device.boot_time = new Date(Date.now() - uptime * 1e3).toISOString(); | ||
| } | ||
| device.arch = os.arch(); | ||
| if (deviceOpt === true || deviceOpt.memory) { | ||
@@ -342,5 +242,4 @@ device.memory_size = os.totalmem(); | ||
| } | ||
| if (deviceOpt === true || deviceOpt.cpu) { | ||
| const cpuInfo = os.cpus() ; | ||
| const cpuInfo = os.cpus(); | ||
| const firstCpu = cpuInfo?.[0]; | ||
@@ -353,79 +252,50 @@ if (firstCpu) { | ||
| } | ||
| return device; | ||
| } | ||
| /** Mapping of Node's platform names to actual OS names. */ | ||
| const PLATFORM_NAMES = { | ||
| aix: 'IBM AIX', | ||
| freebsd: 'FreeBSD', | ||
| openbsd: 'OpenBSD', | ||
| sunos: 'SunOS', | ||
| win32: 'Windows', | ||
| ohos: 'OpenHarmony', | ||
| android: 'Android', | ||
| aix: "IBM AIX", | ||
| freebsd: "FreeBSD", | ||
| openbsd: "OpenBSD", | ||
| sunos: "SunOS", | ||
| win32: "Windows", | ||
| ohos: "OpenHarmony", | ||
| android: "Android" | ||
| }; | ||
| /** Linux version file to check for a distribution. */ | ||
| /** Mapping of linux release files located in /etc to distributions. */ | ||
| const LINUX_DISTROS = [ | ||
| { name: 'fedora-release', distros: ['Fedora'] }, | ||
| { name: 'redhat-release', distros: ['Red Hat Linux', 'Centos'] }, | ||
| { name: 'redhat_version', distros: ['Red Hat Linux'] }, | ||
| { name: 'SuSE-release', distros: ['SUSE Linux'] }, | ||
| { name: 'lsb-release', distros: ['Ubuntu Linux', 'Arch Linux'] }, | ||
| { name: 'debian_version', distros: ['Debian'] }, | ||
| { name: 'debian_release', distros: ['Debian'] }, | ||
| { name: 'arch-release', distros: ['Arch Linux'] }, | ||
| { name: 'gentoo-release', distros: ['Gentoo Linux'] }, | ||
| { name: 'novell-release', distros: ['SUSE Linux'] }, | ||
| { name: 'alpine-release', distros: ['Alpine Linux'] }, | ||
| { name: "fedora-release", distros: ["Fedora"] }, | ||
| { name: "redhat-release", distros: ["Red Hat Linux", "Centos"] }, | ||
| { name: "redhat_version", distros: ["Red Hat Linux"] }, | ||
| { name: "SuSE-release", distros: ["SUSE Linux"] }, | ||
| { name: "lsb-release", distros: ["Ubuntu Linux", "Arch Linux"] }, | ||
| { name: "debian_version", distros: ["Debian"] }, | ||
| { name: "debian_release", distros: ["Debian"] }, | ||
| { name: "arch-release", distros: ["Arch Linux"] }, | ||
| { name: "gentoo-release", distros: ["Gentoo Linux"] }, | ||
| { name: "novell-release", distros: ["SUSE Linux"] }, | ||
| { name: "alpine-release", distros: ["Alpine Linux"] } | ||
| ]; | ||
| /** Functions to extract the OS version from Linux release files. */ | ||
| const LINUX_VERSIONS | ||
| = { | ||
| alpine: content => content, | ||
| arch: content => matchFirst(/distrib_release=(.*)/, content), | ||
| centos: content => matchFirst(/release ([^ ]+)/, content), | ||
| debian: content => content, | ||
| fedora: content => matchFirst(/release (..)/, content), | ||
| mint: content => matchFirst(/distrib_release=(.*)/, content), | ||
| red: content => matchFirst(/release ([^ ]+)/, content), | ||
| suse: content => matchFirst(/VERSION = (.*)\n/, content), | ||
| ubuntu: content => matchFirst(/distrib_release=(.*)/, content), | ||
| const LINUX_VERSIONS = { | ||
| alpine: (content) => content, | ||
| arch: (content) => matchFirst(/distrib_release=(.*)/, content), | ||
| centos: (content) => matchFirst(/release ([^ ]+)/, content), | ||
| debian: (content) => content, | ||
| fedora: (content) => matchFirst(/release (..)/, content), | ||
| mint: (content) => matchFirst(/distrib_release=(.*)/, content), | ||
| red: (content) => matchFirst(/release ([^ ]+)/, content), | ||
| suse: (content) => matchFirst(/VERSION = (.*)\n/, content), | ||
| ubuntu: (content) => matchFirst(/distrib_release=(.*)/, content) | ||
| }; | ||
| /** | ||
| * Executes a regular expression with one capture group. | ||
| * | ||
| * @param regex A regular expression to execute. | ||
| * @param text Content to execute the RegEx on. | ||
| * @returns The captured string if matched; otherwise undefined. | ||
| */ | ||
| function matchFirst(regex, text) { | ||
| const match = regex.exec(text); | ||
| return match ? match[1] : undefined; | ||
| return match ? match[1] : void 0; | ||
| } | ||
| /** Loads the macOS operating system context. */ | ||
| async function getDarwinInfo() { | ||
| // Default values that will be used in case no operating system information | ||
| // can be loaded. The default version is computed via heuristics from the | ||
| // kernel version, but the build ID is missing. | ||
| const darwinInfo = { | ||
| kernel_version: os.release(), | ||
| name: 'Mac OS X', | ||
| version: `10.${Number(os.release().split('.')[0]) - 4}`, | ||
| name: "Mac OS X", | ||
| version: `10.${Number(os.release().split(".")[0]) - 4}` | ||
| }; | ||
| try { | ||
| // We try to load the actual macOS version by executing the `sw_vers` tool. | ||
| // This tool should be available on every standard macOS installation. In | ||
| // case this fails, we stick with the values computed above. | ||
| const output = await new Promise((resolve, reject) => { | ||
| execFile('/usr/bin/sw_vers', (error, stdout) => { | ||
| execFile("/usr/bin/sw_vers", (error, stdout) => { | ||
| if (error) { | ||
@@ -438,3 +308,2 @@ reject(error); | ||
| }); | ||
| darwinInfo.name = matchFirst(/^ProductName:\s+(.*)$/m, output); | ||
@@ -444,129 +313,82 @@ darwinInfo.version = matchFirst(/^ProductVersion:\s+(.*)$/m, output); | ||
| } catch { | ||
| // ignore | ||
| } | ||
| return darwinInfo; | ||
| } | ||
| /** Returns a distribution identifier to look up version callbacks. */ | ||
| function getLinuxDistroId(name) { | ||
| return (name.split(' ') )[0].toLowerCase(); | ||
| return name.split(" ")[0].toLowerCase(); | ||
| } | ||
| /** Loads the Linux operating system context. */ | ||
| async function getLinuxInfo() { | ||
| // By default, we cannot assume anything about the distribution or Linux | ||
| // version. `os.release()` returns the kernel version and we assume a generic | ||
| // "Linux" name, which will be replaced down below. | ||
| const linuxInfo = { | ||
| kernel_version: os.release(), | ||
| name: 'Linux', | ||
| name: "Linux" | ||
| }; | ||
| try { | ||
| // We start guessing the distribution by listing files in the /etc | ||
| // directory. This is were most Linux distributions (except Knoppix) store | ||
| // release files with certain distribution-dependent meta data. We search | ||
| // for exactly one known file defined in `LINUX_DISTROS` and exit if none | ||
| // are found. In case there are more than one file, we just stick with the | ||
| // first one. | ||
| const etcFiles = await readDirAsync('/etc'); | ||
| const distroFile = LINUX_DISTROS.find(file => etcFiles.includes(file.name)); | ||
| const etcFiles = await readDirAsync("/etc"); | ||
| const distroFile = LINUX_DISTROS.find((file) => etcFiles.includes(file.name)); | ||
| if (!distroFile) { | ||
| return linuxInfo; | ||
| } | ||
| // Once that file is known, load its contents. To make searching in those | ||
| // files easier, we lowercase the file contents. Since these files are | ||
| // usually quite small, this should not allocate too much memory and we only | ||
| // hold on to it for a very short amount of time. | ||
| const distroPath = join('/etc', distroFile.name); | ||
| const contents = (await readFileAsync(distroPath, { encoding: 'utf-8' })).toLowerCase(); | ||
| // Some Linux distributions store their release information in the same file | ||
| // (e.g. RHEL and Centos). In those cases, we scan the file for an | ||
| // identifier, that basically consists of the first word of the linux | ||
| // distribution name (e.g. "red" for Red Hat). In case there is no match, we | ||
| // just assume the first distribution in our list. | ||
| const distroPath = join("/etc", distroFile.name); | ||
| const contents = (await readFileAsync(distroPath, { encoding: "utf-8" })).toLowerCase(); | ||
| const { distros } = distroFile; | ||
| linuxInfo.name = distros.find(d => contents.indexOf(getLinuxDistroId(d)) >= 0) || distros[0]; | ||
| // Based on the found distribution, we can now compute the actual version | ||
| // number. This is different for every distribution, so several strategies | ||
| // are computed in `LINUX_VERSIONS`. | ||
| linuxInfo.name = distros.find((d) => contents.indexOf(getLinuxDistroId(d)) >= 0) || distros[0]; | ||
| const id = getLinuxDistroId(linuxInfo.name); | ||
| linuxInfo.version = LINUX_VERSIONS[id]?.(contents); | ||
| } catch { | ||
| // ignore | ||
| } | ||
| return linuxInfo; | ||
| } | ||
| /** | ||
| * Grabs some information about hosting provider based on best effort. | ||
| */ | ||
| function getCloudResourceContext() { | ||
| if (process.env.VERCEL) { | ||
| // https://vercel.com/docs/concepts/projects/environment-variables/system-environment-variables#system-environment-variables | ||
| return { | ||
| 'cloud.provider': 'vercel', | ||
| 'cloud.region': process.env.VERCEL_REGION, | ||
| "cloud.provider": "vercel", | ||
| "cloud.region": process.env.VERCEL_REGION | ||
| }; | ||
| } else if (process.env.AWS_REGION) { | ||
| // https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html | ||
| return { | ||
| 'cloud.provider': 'aws', | ||
| 'cloud.region': process.env.AWS_REGION, | ||
| 'cloud.platform': process.env.AWS_EXECUTION_ENV, | ||
| "cloud.provider": "aws", | ||
| "cloud.region": process.env.AWS_REGION, | ||
| "cloud.platform": process.env.AWS_EXECUTION_ENV | ||
| }; | ||
| } else if (process.env.GCP_PROJECT) { | ||
| // https://cloud.google.com/composer/docs/how-to/managing/environment-variables#reserved_variables | ||
| return { | ||
| 'cloud.provider': 'gcp', | ||
| "cloud.provider": "gcp" | ||
| }; | ||
| } else if (process.env.ALIYUN_REGION_ID) { | ||
| // TODO: find where I found these environment variables - at least gc.github.com returns something | ||
| return { | ||
| 'cloud.provider': 'alibaba_cloud', | ||
| 'cloud.region': process.env.ALIYUN_REGION_ID, | ||
| "cloud.provider": "alibaba_cloud", | ||
| "cloud.region": process.env.ALIYUN_REGION_ID | ||
| }; | ||
| } else if (process.env.WEBSITE_SITE_NAME && process.env.REGION_NAME) { | ||
| // https://learn.microsoft.com/en-us/azure/app-service/reference-app-settings?tabs=kudu%2Cdotnet#app-environment | ||
| return { | ||
| 'cloud.provider': 'azure', | ||
| 'cloud.region': process.env.REGION_NAME, | ||
| "cloud.provider": "azure", | ||
| "cloud.region": process.env.REGION_NAME | ||
| }; | ||
| } else if (process.env.IBM_CLOUD_REGION) { | ||
| // TODO: find where I found these environment variables - at least gc.github.com returns something | ||
| return { | ||
| 'cloud.provider': 'ibm_cloud', | ||
| 'cloud.region': process.env.IBM_CLOUD_REGION, | ||
| "cloud.provider": "ibm_cloud", | ||
| "cloud.region": process.env.IBM_CLOUD_REGION | ||
| }; | ||
| } else if (process.env.TENCENTCLOUD_REGION) { | ||
| // https://www.tencentcloud.com/document/product/583/32748 | ||
| return { | ||
| 'cloud.provider': 'tencent_cloud', | ||
| 'cloud.region': process.env.TENCENTCLOUD_REGION, | ||
| 'cloud.account.id': process.env.TENCENTCLOUD_APPID, | ||
| 'cloud.availability_zone': process.env.TENCENTCLOUD_ZONE, | ||
| "cloud.provider": "tencent_cloud", | ||
| "cloud.region": process.env.TENCENTCLOUD_REGION, | ||
| "cloud.account.id": process.env.TENCENTCLOUD_APPID, | ||
| "cloud.availability_zone": process.env.TENCENTCLOUD_ZONE | ||
| }; | ||
| } else if (process.env.NETLIFY) { | ||
| // https://docs.netlify.com/configure-builds/environment-variables/#read-only-variables | ||
| return { | ||
| 'cloud.provider': 'netlify', | ||
| "cloud.provider": "netlify" | ||
| }; | ||
| } else if (process.env.FLY_REGION) { | ||
| // https://fly.io/docs/reference/runtime-environment/ | ||
| return { | ||
| 'cloud.provider': 'fly.io', | ||
| 'cloud.region': process.env.FLY_REGION, | ||
| "cloud.provider": "fly.io", | ||
| "cloud.region": process.env.FLY_REGION | ||
| }; | ||
| } else if (process.env.DYNO) { | ||
| // https://devcenter.heroku.com/articles/dynos#local-environment-variables | ||
| return { | ||
| 'cloud.provider': 'heroku', | ||
| "cloud.provider": "heroku" | ||
| }; | ||
| } else { | ||
| return undefined; | ||
| return void 0; | ||
| } | ||
@@ -573,0 +395,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"context.js","sources":["../../../src/integrations/context.ts"],"sourcesContent":["/* eslint-disable max-lines */\n\nimport { execFile } from 'node:child_process';\nimport { readdir, readFile } from 'node:fs';\nimport * as os from 'node:os';\nimport { join } from 'node:path';\nimport { promisify } from 'node:util';\nimport type {\n AppContext,\n CloudResourceContext,\n Contexts,\n CultureContext,\n DeviceContext,\n Event,\n IntegrationFn,\n OsContext,\n} from '@sentry/core';\nimport { defineIntegration, safeSetSpanJSONAttributes } from '@sentry/core';\n\nexport const readFileAsync = promisify(readFile);\nexport const readDirAsync = promisify(readdir);\n\n// Process enhanced with methods from Node 18, 20, 22 as @types/node\n// is on `14.18.0` to match minimum version requirements of the SDK\ninterface ProcessWithCurrentValues extends NodeJS.Process {\n availableMemory?(): number;\n}\n\nconst INTEGRATION_NAME = 'Context';\n\ninterface DeviceContextOptions {\n cpu?: boolean;\n memory?: boolean;\n}\n\ninterface ContextOptions {\n app?: boolean;\n os?: boolean;\n device?: DeviceContextOptions | boolean;\n culture?: boolean;\n cloudResource?: boolean;\n}\n\nconst _nodeContextIntegration = ((options: ContextOptions = {}) => {\n const _options = {\n app: true,\n os: true,\n device: true,\n culture: true,\n cloudResource: true,\n ...options,\n };\n\n // Compute contexts eagerly (shared between tx and span paths)\n const appContext = _options.app ? getAppContext() : undefined;\n const deviceContext = _options.device ? getDeviceContext(_options.device) : undefined;\n const cultureContext = _options.culture ? getCultureContext() : undefined;\n const cloudResourceContext = _options.cloudResource ? getCloudResourceContext() : undefined;\n const osContextPromise = _options.os ? getOsContext() : undefined;\n\n // Map static context data to span attributes\n const cachedSpanAttributes: Record<string, unknown> = {\n 'process.runtime.engine.name': 'v8',\n 'process.runtime.engine.version': process.versions.v8,\n ...contextsToSpanAttributes({\n app: appContext,\n device: deviceContext,\n culture: cultureContext,\n cloud_resource: cloudResourceContext,\n }),\n };\n\n if (osContextPromise) {\n osContextPromise\n .then(osCtx => Object.assign(cachedSpanAttributes, contextsToSpanAttributes({ os: osCtx })))\n .catch(() => {\n // Ignore - os attributes will be undefined\n });\n }\n\n // Build contexts for event processing (reuses same data, awaits async OS context)\n const contextsPromise: Promise<Contexts> = (async () => {\n const contexts: Contexts = {};\n if (osContextPromise) {\n contexts.os = await osContextPromise;\n }\n if (appContext) {\n contexts.app = appContext;\n }\n if (deviceContext) {\n contexts.device = deviceContext;\n }\n if (cultureContext) {\n contexts.culture = cultureContext;\n }\n if (cloudResourceContext) {\n contexts.cloud_resource = cloudResourceContext;\n }\n return contexts;\n })();\n\n async function addContext(event: Event): Promise<Event> {\n const updatedContext = _updateContext(await contextsPromise);\n\n // TODO(v11): conditional with `sendDefaultPii` here?\n event.contexts = {\n ...event.contexts,\n app: { ...updatedContext.app, ...event.contexts?.app },\n os: { ...updatedContext.os, ...event.contexts?.os },\n device: { ...updatedContext.device, ...event.contexts?.device },\n culture: { ...updatedContext.culture, ...event.contexts?.culture },\n cloud_resource: { ...updatedContext.cloud_resource, ...event.contexts?.cloud_resource },\n };\n\n return event;\n }\n\n return {\n name: INTEGRATION_NAME,\n processEvent(event) {\n return addContext(event);\n },\n processSegmentSpan(span) {\n safeSetSpanJSONAttributes(span, cachedSpanAttributes);\n safeSetSpanJSONAttributes(span, getDynamicSpanAttributes(appContext, deviceContext));\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Capture context about the environment and the device that the client is running on, to events.\n */\nexport const nodeContextIntegration = defineIntegration(_nodeContextIntegration);\n\n/**\n * Updates the context with dynamic values that can change\n */\nfunction _updateContext(contexts: Contexts): Contexts {\n // Only update properties if they exist\n\n if (contexts.app?.app_memory) {\n contexts.app.app_memory = process.memoryUsage().rss;\n }\n\n if (contexts.app?.free_memory && typeof (process as ProcessWithCurrentValues).availableMemory === 'function') {\n const freeMemory = (process as ProcessWithCurrentValues).availableMemory?.();\n if (freeMemory != null) {\n contexts.app.free_memory = freeMemory;\n }\n }\n\n if (contexts.device?.free_memory) {\n contexts.device.free_memory = os.freemem();\n }\n\n return contexts;\n}\n\nexport function contextsToSpanAttributes(contexts: Contexts): Record<string, unknown> {\n const attrs: Record<string, unknown> = {};\n\n const { app, device, os: osCtx, culture, cloud_resource } = contexts;\n\n if (app) {\n if (app.app_start_time) {\n attrs['app.start_time'] = app.app_start_time;\n }\n }\n\n if (device) {\n if (device.arch) {\n attrs['device.archs'] = [device.arch];\n }\n if (device.boot_time) {\n attrs['device.boot_time'] = device.boot_time;\n }\n if (device.memory_size != null) {\n attrs['device.memory_size'] = device.memory_size;\n }\n if (device.processor_count != null) {\n attrs['device.processor_count'] = device.processor_count;\n }\n if (device.cpu_description) {\n attrs['device.cpu_description'] = device.cpu_description;\n }\n if (device.processor_frequency != null) {\n attrs['device.processor_frequency'] = device.processor_frequency;\n }\n }\n\n if (osCtx) {\n if (osCtx.name) {\n attrs['os.name'] = osCtx.name;\n }\n if (osCtx.version) {\n attrs['os.version'] = osCtx.version;\n }\n if (osCtx.kernel_version) {\n attrs['os.kernel_version'] = osCtx.kernel_version;\n }\n if (osCtx.build) {\n attrs['os.build'] = osCtx.build;\n }\n }\n\n if (culture) {\n if (culture.locale) {\n attrs['culture.locale'] = culture.locale;\n }\n if (culture.timezone) {\n attrs['culture.timezone'] = culture.timezone;\n }\n }\n\n // CloudResourceContext already uses dot-notation keys matching span attribute conventions\n if (cloud_resource) {\n for (const [key, value] of Object.entries(cloud_resource)) {\n if (value != null) {\n attrs[key] = value;\n }\n }\n }\n\n return attrs;\n}\n\nexport function getDynamicSpanAttributes(\n appContext: AppContext | undefined,\n deviceContext: DeviceContext | undefined,\n): Record<string, unknown> {\n const attrs: Record<string, unknown> = {};\n\n if (appContext) {\n attrs['app.memory'] = process.memoryUsage().rss;\n if (typeof (process as ProcessWithCurrentValues).availableMemory === 'function') {\n const freeMemory = (process as ProcessWithCurrentValues).availableMemory?.();\n if (freeMemory != null) {\n attrs['app.free_memory'] = freeMemory;\n }\n }\n }\n\n // Only include if memory tracking was initially enabled (indicated by free_memory being set)\n if (deviceContext?.free_memory != null) {\n attrs['device.free_memory'] = os.freemem();\n }\n\n return attrs;\n}\n\n/**\n * Returns the operating system context.\n *\n * Based on the current platform, this uses a different strategy to provide the\n * most accurate OS information. Since this might involve spawning subprocesses\n * or accessing the file system, this should only be executed lazily and cached.\n *\n * - On macOS (Darwin), this will execute the `sw_vers` utility. The context\n * has a `name`, `version`, `build` and `kernel_version` set.\n * - On Linux, this will try to load a distribution release from `/etc` and set\n * the `name`, `version` and `kernel_version` fields.\n * - On all other platforms, only a `name` and `version` will be returned. Note\n * that `version` might actually be the kernel version.\n */\nasync function getOsContext(): Promise<OsContext> {\n const platformId = os.platform();\n switch (platformId) {\n case 'darwin':\n return getDarwinInfo();\n case 'linux':\n return getLinuxInfo();\n default:\n return {\n name: PLATFORM_NAMES[platformId] || platformId,\n version: os.release(),\n };\n }\n}\n\nfunction getCultureContext(): CultureContext | undefined {\n try {\n if (typeof process.versions.icu !== 'string') {\n // Node was built without ICU support\n return;\n }\n\n // Check that node was built with full Intl support. Its possible it was built without support for non-English\n // locales which will make resolvedOptions inaccurate\n //\n // https://nodejs.org/api/intl.html#detecting-internationalization-support\n const january = new Date(9e8);\n const spanish = new Intl.DateTimeFormat('es', { month: 'long' });\n if (spanish.format(january) === 'enero') {\n const options = Intl.DateTimeFormat().resolvedOptions();\n\n return {\n locale: options.locale,\n timezone: options.timeZone,\n };\n }\n } catch {\n //\n }\n\n return;\n}\n\n/**\n * Get app context information from process\n */\nexport function getAppContext(): AppContext {\n const app_memory = process.memoryUsage().rss;\n // oxlint-disable-next-line sdk/no-unsafe-random-apis\n const app_start_time = new Date(Date.now() - process.uptime() * 1000).toISOString();\n // https://nodejs.org/api/process.html#processavailablememory\n const appContext: AppContext = { app_start_time, app_memory };\n\n if (typeof (process as ProcessWithCurrentValues).availableMemory === 'function') {\n const freeMemory = (process as ProcessWithCurrentValues).availableMemory?.();\n if (freeMemory != null) {\n appContext.free_memory = freeMemory;\n }\n }\n\n return appContext;\n}\n\n/**\n * Gets device information from os\n */\nexport function getDeviceContext(deviceOpt: DeviceContextOptions | true): DeviceContext {\n const device: DeviceContext = {};\n\n // Sometimes os.uptime() throws due to lacking permissions: https://github.com/getsentry/sentry-javascript/issues/8202\n let uptime;\n try {\n uptime = os.uptime();\n } catch {\n // noop\n }\n\n // os.uptime or its return value seem to be undefined in certain environments (e.g. Azure functions).\n // Hence, we only set boot time, if we get a valid uptime value.\n // @see https://github.com/getsentry/sentry-javascript/issues/5856\n if (typeof uptime === 'number') {\n // oxlint-disable-next-line sdk/no-unsafe-random-apis\n device.boot_time = new Date(Date.now() - uptime * 1000).toISOString();\n }\n\n device.arch = os.arch();\n\n if (deviceOpt === true || deviceOpt.memory) {\n device.memory_size = os.totalmem();\n device.free_memory = os.freemem();\n }\n\n if (deviceOpt === true || deviceOpt.cpu) {\n const cpuInfo = os.cpus() as os.CpuInfo[] | undefined;\n const firstCpu = cpuInfo?.[0];\n if (firstCpu) {\n device.processor_count = cpuInfo.length;\n device.cpu_description = firstCpu.model;\n device.processor_frequency = firstCpu.speed;\n }\n }\n\n return device;\n}\n\n/** Mapping of Node's platform names to actual OS names. */\nconst PLATFORM_NAMES: { [platform: string]: string } = {\n aix: 'IBM AIX',\n freebsd: 'FreeBSD',\n openbsd: 'OpenBSD',\n sunos: 'SunOS',\n win32: 'Windows',\n ohos: 'OpenHarmony',\n android: 'Android',\n};\n\n/** Linux version file to check for a distribution. */\ninterface DistroFile {\n /** The file name, located in `/etc`. */\n name: string;\n /** Potential distributions to check. */\n distros: [string, ...string[]];\n}\n\n/** Mapping of linux release files located in /etc to distributions. */\nconst LINUX_DISTROS: DistroFile[] = [\n { name: 'fedora-release', distros: ['Fedora'] },\n { name: 'redhat-release', distros: ['Red Hat Linux', 'Centos'] },\n { name: 'redhat_version', distros: ['Red Hat Linux'] },\n { name: 'SuSE-release', distros: ['SUSE Linux'] },\n { name: 'lsb-release', distros: ['Ubuntu Linux', 'Arch Linux'] },\n { name: 'debian_version', distros: ['Debian'] },\n { name: 'debian_release', distros: ['Debian'] },\n { name: 'arch-release', distros: ['Arch Linux'] },\n { name: 'gentoo-release', distros: ['Gentoo Linux'] },\n { name: 'novell-release', distros: ['SUSE Linux'] },\n { name: 'alpine-release', distros: ['Alpine Linux'] },\n];\n\n/** Functions to extract the OS version from Linux release files. */\nconst LINUX_VERSIONS: {\n [identifier: string]: (content: string) => string | undefined;\n} = {\n alpine: content => content,\n arch: content => matchFirst(/distrib_release=(.*)/, content),\n centos: content => matchFirst(/release ([^ ]+)/, content),\n debian: content => content,\n fedora: content => matchFirst(/release (..)/, content),\n mint: content => matchFirst(/distrib_release=(.*)/, content),\n red: content => matchFirst(/release ([^ ]+)/, content),\n suse: content => matchFirst(/VERSION = (.*)\\n/, content),\n ubuntu: content => matchFirst(/distrib_release=(.*)/, content),\n};\n\n/**\n * Executes a regular expression with one capture group.\n *\n * @param regex A regular expression to execute.\n * @param text Content to execute the RegEx on.\n * @returns The captured string if matched; otherwise undefined.\n */\nfunction matchFirst(regex: RegExp, text: string): string | undefined {\n const match = regex.exec(text);\n return match ? match[1] : undefined;\n}\n\n/** Loads the macOS operating system context. */\nasync function getDarwinInfo(): Promise<OsContext> {\n // Default values that will be used in case no operating system information\n // can be loaded. The default version is computed via heuristics from the\n // kernel version, but the build ID is missing.\n const darwinInfo: OsContext = {\n kernel_version: os.release(),\n name: 'Mac OS X',\n version: `10.${Number(os.release().split('.')[0]) - 4}`,\n };\n\n try {\n // We try to load the actual macOS version by executing the `sw_vers` tool.\n // This tool should be available on every standard macOS installation. In\n // case this fails, we stick with the values computed above.\n\n const output = await new Promise<string>((resolve, reject) => {\n execFile('/usr/bin/sw_vers', (error: Error | null, stdout: string) => {\n if (error) {\n reject(error);\n return;\n }\n resolve(stdout);\n });\n });\n\n darwinInfo.name = matchFirst(/^ProductName:\\s+(.*)$/m, output);\n darwinInfo.version = matchFirst(/^ProductVersion:\\s+(.*)$/m, output);\n darwinInfo.build = matchFirst(/^BuildVersion:\\s+(.*)$/m, output);\n } catch {\n // ignore\n }\n\n return darwinInfo;\n}\n\n/** Returns a distribution identifier to look up version callbacks. */\nfunction getLinuxDistroId(name: string): string {\n return (name.split(' ') as [string])[0].toLowerCase();\n}\n\n/** Loads the Linux operating system context. */\nasync function getLinuxInfo(): Promise<OsContext> {\n // By default, we cannot assume anything about the distribution or Linux\n // version. `os.release()` returns the kernel version and we assume a generic\n // \"Linux\" name, which will be replaced down below.\n const linuxInfo: OsContext = {\n kernel_version: os.release(),\n name: 'Linux',\n };\n\n try {\n // We start guessing the distribution by listing files in the /etc\n // directory. This is were most Linux distributions (except Knoppix) store\n // release files with certain distribution-dependent meta data. We search\n // for exactly one known file defined in `LINUX_DISTROS` and exit if none\n // are found. In case there are more than one file, we just stick with the\n // first one.\n const etcFiles = await readDirAsync('/etc');\n const distroFile = LINUX_DISTROS.find(file => etcFiles.includes(file.name));\n if (!distroFile) {\n return linuxInfo;\n }\n\n // Once that file is known, load its contents. To make searching in those\n // files easier, we lowercase the file contents. Since these files are\n // usually quite small, this should not allocate too much memory and we only\n // hold on to it for a very short amount of time.\n const distroPath = join('/etc', distroFile.name);\n const contents = (await readFileAsync(distroPath, { encoding: 'utf-8' })).toLowerCase();\n\n // Some Linux distributions store their release information in the same file\n // (e.g. RHEL and Centos). In those cases, we scan the file for an\n // identifier, that basically consists of the first word of the linux\n // distribution name (e.g. \"red\" for Red Hat). In case there is no match, we\n // just assume the first distribution in our list.\n const { distros } = distroFile;\n linuxInfo.name = distros.find(d => contents.indexOf(getLinuxDistroId(d)) >= 0) || distros[0];\n\n // Based on the found distribution, we can now compute the actual version\n // number. This is different for every distribution, so several strategies\n // are computed in `LINUX_VERSIONS`.\n const id = getLinuxDistroId(linuxInfo.name);\n linuxInfo.version = LINUX_VERSIONS[id]?.(contents);\n } catch {\n // ignore\n }\n\n return linuxInfo;\n}\n\n/**\n * Grabs some information about hosting provider based on best effort.\n */\nfunction getCloudResourceContext(): CloudResourceContext | undefined {\n if (process.env.VERCEL) {\n // https://vercel.com/docs/concepts/projects/environment-variables/system-environment-variables#system-environment-variables\n return {\n 'cloud.provider': 'vercel',\n 'cloud.region': process.env.VERCEL_REGION,\n };\n } else if (process.env.AWS_REGION) {\n // https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html\n return {\n 'cloud.provider': 'aws',\n 'cloud.region': process.env.AWS_REGION,\n 'cloud.platform': process.env.AWS_EXECUTION_ENV,\n };\n } else if (process.env.GCP_PROJECT) {\n // https://cloud.google.com/composer/docs/how-to/managing/environment-variables#reserved_variables\n return {\n 'cloud.provider': 'gcp',\n };\n } else if (process.env.ALIYUN_REGION_ID) {\n // TODO: find where I found these environment variables - at least gc.github.com returns something\n return {\n 'cloud.provider': 'alibaba_cloud',\n 'cloud.region': process.env.ALIYUN_REGION_ID,\n };\n } else if (process.env.WEBSITE_SITE_NAME && process.env.REGION_NAME) {\n // https://learn.microsoft.com/en-us/azure/app-service/reference-app-settings?tabs=kudu%2Cdotnet#app-environment\n return {\n 'cloud.provider': 'azure',\n 'cloud.region': process.env.REGION_NAME,\n };\n } else if (process.env.IBM_CLOUD_REGION) {\n // TODO: find where I found these environment variables - at least gc.github.com returns something\n return {\n 'cloud.provider': 'ibm_cloud',\n 'cloud.region': process.env.IBM_CLOUD_REGION,\n };\n } else if (process.env.TENCENTCLOUD_REGION) {\n // https://www.tencentcloud.com/document/product/583/32748\n return {\n 'cloud.provider': 'tencent_cloud',\n 'cloud.region': process.env.TENCENTCLOUD_REGION,\n 'cloud.account.id': process.env.TENCENTCLOUD_APPID,\n 'cloud.availability_zone': process.env.TENCENTCLOUD_ZONE,\n };\n } else if (process.env.NETLIFY) {\n // https://docs.netlify.com/configure-builds/environment-variables/#read-only-variables\n return {\n 'cloud.provider': 'netlify',\n };\n } else if (process.env.FLY_REGION) {\n // https://fly.io/docs/reference/runtime-environment/\n return {\n 'cloud.provider': 'fly.io',\n 'cloud.region': process.env.FLY_REGION,\n };\n } else if (process.env.DYNO) {\n // https://devcenter.heroku.com/articles/dynos#local-environment-variables\n return {\n 'cloud.provider': 'heroku',\n };\n } else {\n return undefined;\n }\n}\n"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"context.js","sources":["../../../src/integrations/context.ts"],"sourcesContent":["/* eslint-disable max-lines */\n\nimport { execFile } from 'node:child_process';\nimport { readdir, readFile } from 'node:fs';\nimport * as os from 'node:os';\nimport { join } from 'node:path';\nimport { promisify } from 'node:util';\nimport type {\n AppContext,\n CloudResourceContext,\n Contexts,\n CultureContext,\n DeviceContext,\n Event,\n IntegrationFn,\n OsContext,\n} from '@sentry/core';\nimport { defineIntegration, safeSetSpanJSONAttributes } from '@sentry/core';\n\nexport const readFileAsync = promisify(readFile);\nexport const readDirAsync = promisify(readdir);\n\n// Process enhanced with methods from Node 18, 20, 22 as @types/node\n// is on `14.18.0` to match minimum version requirements of the SDK\ninterface ProcessWithCurrentValues extends NodeJS.Process {\n availableMemory?(): number;\n}\n\nconst INTEGRATION_NAME = 'Context';\n\ninterface DeviceContextOptions {\n cpu?: boolean;\n memory?: boolean;\n}\n\ninterface ContextOptions {\n app?: boolean;\n os?: boolean;\n device?: DeviceContextOptions | boolean;\n culture?: boolean;\n cloudResource?: boolean;\n}\n\nconst _nodeContextIntegration = ((options: ContextOptions = {}) => {\n const _options = {\n app: true,\n os: true,\n device: true,\n culture: true,\n cloudResource: true,\n ...options,\n };\n\n // Compute contexts eagerly (shared between tx and span paths)\n const appContext = _options.app ? getAppContext() : undefined;\n const deviceContext = _options.device ? getDeviceContext(_options.device) : undefined;\n const cultureContext = _options.culture ? getCultureContext() : undefined;\n const cloudResourceContext = _options.cloudResource ? getCloudResourceContext() : undefined;\n const osContextPromise = _options.os ? getOsContext() : undefined;\n\n // Map static context data to span attributes\n const cachedSpanAttributes: Record<string, unknown> = {\n 'process.runtime.engine.name': 'v8',\n 'process.runtime.engine.version': process.versions.v8,\n ...contextsToSpanAttributes({\n app: appContext,\n device: deviceContext,\n culture: cultureContext,\n cloud_resource: cloudResourceContext,\n }),\n };\n\n if (osContextPromise) {\n osContextPromise\n .then(osCtx => Object.assign(cachedSpanAttributes, contextsToSpanAttributes({ os: osCtx })))\n .catch(() => {\n // Ignore - os attributes will be undefined\n });\n }\n\n // Build contexts for event processing (reuses same data, awaits async OS context)\n const contextsPromise: Promise<Contexts> = (async () => {\n const contexts: Contexts = {};\n if (osContextPromise) {\n contexts.os = await osContextPromise;\n }\n if (appContext) {\n contexts.app = appContext;\n }\n if (deviceContext) {\n contexts.device = deviceContext;\n }\n if (cultureContext) {\n contexts.culture = cultureContext;\n }\n if (cloudResourceContext) {\n contexts.cloud_resource = cloudResourceContext;\n }\n return contexts;\n })();\n\n async function addContext(event: Event): Promise<Event> {\n const updatedContext = _updateContext(await contextsPromise);\n\n // TODO(v11): conditional with `sendDefaultPii` here?\n event.contexts = {\n ...event.contexts,\n app: { ...updatedContext.app, ...event.contexts?.app },\n os: { ...updatedContext.os, ...event.contexts?.os },\n device: { ...updatedContext.device, ...event.contexts?.device },\n culture: { ...updatedContext.culture, ...event.contexts?.culture },\n cloud_resource: { ...updatedContext.cloud_resource, ...event.contexts?.cloud_resource },\n };\n\n return event;\n }\n\n return {\n name: INTEGRATION_NAME,\n processEvent(event) {\n return addContext(event);\n },\n processSegmentSpan(span) {\n safeSetSpanJSONAttributes(span, cachedSpanAttributes);\n safeSetSpanJSONAttributes(span, getDynamicSpanAttributes(appContext, deviceContext));\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Capture context about the environment and the device that the client is running on, to events.\n */\nexport const nodeContextIntegration = defineIntegration(_nodeContextIntegration);\n\n/**\n * Updates the context with dynamic values that can change\n */\nfunction _updateContext(contexts: Contexts): Contexts {\n // Only update properties if they exist\n\n if (contexts.app?.app_memory) {\n contexts.app.app_memory = process.memoryUsage().rss;\n }\n\n if (contexts.app?.free_memory && typeof (process as ProcessWithCurrentValues).availableMemory === 'function') {\n const freeMemory = (process as ProcessWithCurrentValues).availableMemory?.();\n if (freeMemory != null) {\n contexts.app.free_memory = freeMemory;\n }\n }\n\n if (contexts.device?.free_memory) {\n contexts.device.free_memory = os.freemem();\n }\n\n return contexts;\n}\n\nexport function contextsToSpanAttributes(contexts: Contexts): Record<string, unknown> {\n const attrs: Record<string, unknown> = {};\n\n const { app, device, os: osCtx, culture, cloud_resource } = contexts;\n\n if (app) {\n if (app.app_start_time) {\n attrs['app.start_time'] = app.app_start_time;\n }\n }\n\n if (device) {\n if (device.arch) {\n attrs['device.archs'] = [device.arch];\n }\n if (device.boot_time) {\n attrs['device.boot_time'] = device.boot_time;\n }\n if (device.memory_size != null) {\n attrs['device.memory_size'] = device.memory_size;\n }\n if (device.processor_count != null) {\n attrs['device.processor_count'] = device.processor_count;\n }\n if (device.cpu_description) {\n attrs['device.cpu_description'] = device.cpu_description;\n }\n if (device.processor_frequency != null) {\n attrs['device.processor_frequency'] = device.processor_frequency;\n }\n }\n\n if (osCtx) {\n if (osCtx.name) {\n attrs['os.name'] = osCtx.name;\n }\n if (osCtx.version) {\n attrs['os.version'] = osCtx.version;\n }\n if (osCtx.kernel_version) {\n attrs['os.kernel_version'] = osCtx.kernel_version;\n }\n if (osCtx.build) {\n attrs['os.build'] = osCtx.build;\n }\n }\n\n if (culture) {\n if (culture.locale) {\n attrs['culture.locale'] = culture.locale;\n }\n if (culture.timezone) {\n attrs['culture.timezone'] = culture.timezone;\n }\n }\n\n // CloudResourceContext already uses dot-notation keys matching span attribute conventions\n if (cloud_resource) {\n for (const [key, value] of Object.entries(cloud_resource)) {\n if (value != null) {\n attrs[key] = value;\n }\n }\n }\n\n return attrs;\n}\n\nexport function getDynamicSpanAttributes(\n appContext: AppContext | undefined,\n deviceContext: DeviceContext | undefined,\n): Record<string, unknown> {\n const attrs: Record<string, unknown> = {};\n\n if (appContext) {\n attrs['app.memory'] = process.memoryUsage().rss;\n if (typeof (process as ProcessWithCurrentValues).availableMemory === 'function') {\n const freeMemory = (process as ProcessWithCurrentValues).availableMemory?.();\n if (freeMemory != null) {\n attrs['app.free_memory'] = freeMemory;\n }\n }\n }\n\n // Only include if memory tracking was initially enabled (indicated by free_memory being set)\n if (deviceContext?.free_memory != null) {\n attrs['device.free_memory'] = os.freemem();\n }\n\n return attrs;\n}\n\n/**\n * Returns the operating system context.\n *\n * Based on the current platform, this uses a different strategy to provide the\n * most accurate OS information. Since this might involve spawning subprocesses\n * or accessing the file system, this should only be executed lazily and cached.\n *\n * - On macOS (Darwin), this will execute the `sw_vers` utility. The context\n * has a `name`, `version`, `build` and `kernel_version` set.\n * - On Linux, this will try to load a distribution release from `/etc` and set\n * the `name`, `version` and `kernel_version` fields.\n * - On all other platforms, only a `name` and `version` will be returned. Note\n * that `version` might actually be the kernel version.\n */\nasync function getOsContext(): Promise<OsContext> {\n const platformId = os.platform();\n switch (platformId) {\n case 'darwin':\n return getDarwinInfo();\n case 'linux':\n return getLinuxInfo();\n default:\n return {\n name: PLATFORM_NAMES[platformId] || platformId,\n version: os.release(),\n };\n }\n}\n\nfunction getCultureContext(): CultureContext | undefined {\n try {\n if (typeof process.versions.icu !== 'string') {\n // Node was built without ICU support\n return;\n }\n\n // Check that node was built with full Intl support. Its possible it was built without support for non-English\n // locales which will make resolvedOptions inaccurate\n //\n // https://nodejs.org/api/intl.html#detecting-internationalization-support\n const january = new Date(9e8);\n const spanish = new Intl.DateTimeFormat('es', { month: 'long' });\n if (spanish.format(january) === 'enero') {\n const options = Intl.DateTimeFormat().resolvedOptions();\n\n return {\n locale: options.locale,\n timezone: options.timeZone,\n };\n }\n } catch {\n //\n }\n\n return;\n}\n\n/**\n * Get app context information from process\n */\nexport function getAppContext(): AppContext {\n const app_memory = process.memoryUsage().rss;\n // oxlint-disable-next-line sdk/no-unsafe-random-apis\n const app_start_time = new Date(Date.now() - process.uptime() * 1000).toISOString();\n // https://nodejs.org/api/process.html#processavailablememory\n const appContext: AppContext = { app_start_time, app_memory };\n\n if (typeof (process as ProcessWithCurrentValues).availableMemory === 'function') {\n const freeMemory = (process as ProcessWithCurrentValues).availableMemory?.();\n if (freeMemory != null) {\n appContext.free_memory = freeMemory;\n }\n }\n\n return appContext;\n}\n\n/**\n * Gets device information from os\n */\nexport function getDeviceContext(deviceOpt: DeviceContextOptions | true): DeviceContext {\n const device: DeviceContext = {};\n\n // Sometimes os.uptime() throws due to lacking permissions: https://github.com/getsentry/sentry-javascript/issues/8202\n let uptime;\n try {\n uptime = os.uptime();\n } catch {\n // noop\n }\n\n // os.uptime or its return value seem to be undefined in certain environments (e.g. Azure functions).\n // Hence, we only set boot time, if we get a valid uptime value.\n // @see https://github.com/getsentry/sentry-javascript/issues/5856\n if (typeof uptime === 'number') {\n // oxlint-disable-next-line sdk/no-unsafe-random-apis\n device.boot_time = new Date(Date.now() - uptime * 1000).toISOString();\n }\n\n device.arch = os.arch();\n\n if (deviceOpt === true || deviceOpt.memory) {\n device.memory_size = os.totalmem();\n device.free_memory = os.freemem();\n }\n\n if (deviceOpt === true || deviceOpt.cpu) {\n const cpuInfo = os.cpus() as os.CpuInfo[] | undefined;\n const firstCpu = cpuInfo?.[0];\n if (firstCpu) {\n device.processor_count = cpuInfo.length;\n device.cpu_description = firstCpu.model;\n device.processor_frequency = firstCpu.speed;\n }\n }\n\n return device;\n}\n\n/** Mapping of Node's platform names to actual OS names. */\nconst PLATFORM_NAMES: { [platform: string]: string } = {\n aix: 'IBM AIX',\n freebsd: 'FreeBSD',\n openbsd: 'OpenBSD',\n sunos: 'SunOS',\n win32: 'Windows',\n ohos: 'OpenHarmony',\n android: 'Android',\n};\n\n/** Linux version file to check for a distribution. */\ninterface DistroFile {\n /** The file name, located in `/etc`. */\n name: string;\n /** Potential distributions to check. */\n distros: [string, ...string[]];\n}\n\n/** Mapping of linux release files located in /etc to distributions. */\nconst LINUX_DISTROS: DistroFile[] = [\n { name: 'fedora-release', distros: ['Fedora'] },\n { name: 'redhat-release', distros: ['Red Hat Linux', 'Centos'] },\n { name: 'redhat_version', distros: ['Red Hat Linux'] },\n { name: 'SuSE-release', distros: ['SUSE Linux'] },\n { name: 'lsb-release', distros: ['Ubuntu Linux', 'Arch Linux'] },\n { name: 'debian_version', distros: ['Debian'] },\n { name: 'debian_release', distros: ['Debian'] },\n { name: 'arch-release', distros: ['Arch Linux'] },\n { name: 'gentoo-release', distros: ['Gentoo Linux'] },\n { name: 'novell-release', distros: ['SUSE Linux'] },\n { name: 'alpine-release', distros: ['Alpine Linux'] },\n];\n\n/** Functions to extract the OS version from Linux release files. */\nconst LINUX_VERSIONS: {\n [identifier: string]: (content: string) => string | undefined;\n} = {\n alpine: content => content,\n arch: content => matchFirst(/distrib_release=(.*)/, content),\n centos: content => matchFirst(/release ([^ ]+)/, content),\n debian: content => content,\n fedora: content => matchFirst(/release (..)/, content),\n mint: content => matchFirst(/distrib_release=(.*)/, content),\n red: content => matchFirst(/release ([^ ]+)/, content),\n suse: content => matchFirst(/VERSION = (.*)\\n/, content),\n ubuntu: content => matchFirst(/distrib_release=(.*)/, content),\n};\n\n/**\n * Executes a regular expression with one capture group.\n *\n * @param regex A regular expression to execute.\n * @param text Content to execute the RegEx on.\n * @returns The captured string if matched; otherwise undefined.\n */\nfunction matchFirst(regex: RegExp, text: string): string | undefined {\n const match = regex.exec(text);\n return match ? match[1] : undefined;\n}\n\n/** Loads the macOS operating system context. */\nasync function getDarwinInfo(): Promise<OsContext> {\n // Default values that will be used in case no operating system information\n // can be loaded. The default version is computed via heuristics from the\n // kernel version, but the build ID is missing.\n const darwinInfo: OsContext = {\n kernel_version: os.release(),\n name: 'Mac OS X',\n version: `10.${Number(os.release().split('.')[0]) - 4}`,\n };\n\n try {\n // We try to load the actual macOS version by executing the `sw_vers` tool.\n // This tool should be available on every standard macOS installation. In\n // case this fails, we stick with the values computed above.\n\n const output = await new Promise<string>((resolve, reject) => {\n execFile('/usr/bin/sw_vers', (error: Error | null, stdout: string) => {\n if (error) {\n reject(error);\n return;\n }\n resolve(stdout);\n });\n });\n\n darwinInfo.name = matchFirst(/^ProductName:\\s+(.*)$/m, output);\n darwinInfo.version = matchFirst(/^ProductVersion:\\s+(.*)$/m, output);\n darwinInfo.build = matchFirst(/^BuildVersion:\\s+(.*)$/m, output);\n } catch {\n // ignore\n }\n\n return darwinInfo;\n}\n\n/** Returns a distribution identifier to look up version callbacks. */\nfunction getLinuxDistroId(name: string): string {\n return (name.split(' ') as [string])[0].toLowerCase();\n}\n\n/** Loads the Linux operating system context. */\nasync function getLinuxInfo(): Promise<OsContext> {\n // By default, we cannot assume anything about the distribution or Linux\n // version. `os.release()` returns the kernel version and we assume a generic\n // \"Linux\" name, which will be replaced down below.\n const linuxInfo: OsContext = {\n kernel_version: os.release(),\n name: 'Linux',\n };\n\n try {\n // We start guessing the distribution by listing files in the /etc\n // directory. This is were most Linux distributions (except Knoppix) store\n // release files with certain distribution-dependent meta data. We search\n // for exactly one known file defined in `LINUX_DISTROS` and exit if none\n // are found. In case there are more than one file, we just stick with the\n // first one.\n const etcFiles = await readDirAsync('/etc');\n const distroFile = LINUX_DISTROS.find(file => etcFiles.includes(file.name));\n if (!distroFile) {\n return linuxInfo;\n }\n\n // Once that file is known, load its contents. To make searching in those\n // files easier, we lowercase the file contents. Since these files are\n // usually quite small, this should not allocate too much memory and we only\n // hold on to it for a very short amount of time.\n const distroPath = join('/etc', distroFile.name);\n const contents = (await readFileAsync(distroPath, { encoding: 'utf-8' })).toLowerCase();\n\n // Some Linux distributions store their release information in the same file\n // (e.g. RHEL and Centos). In those cases, we scan the file for an\n // identifier, that basically consists of the first word of the linux\n // distribution name (e.g. \"red\" for Red Hat). In case there is no match, we\n // just assume the first distribution in our list.\n const { distros } = distroFile;\n linuxInfo.name = distros.find(d => contents.indexOf(getLinuxDistroId(d)) >= 0) || distros[0];\n\n // Based on the found distribution, we can now compute the actual version\n // number. This is different for every distribution, so several strategies\n // are computed in `LINUX_VERSIONS`.\n const id = getLinuxDistroId(linuxInfo.name);\n linuxInfo.version = LINUX_VERSIONS[id]?.(contents);\n } catch {\n // ignore\n }\n\n return linuxInfo;\n}\n\n/**\n * Grabs some information about hosting provider based on best effort.\n */\nfunction getCloudResourceContext(): CloudResourceContext | undefined {\n if (process.env.VERCEL) {\n // https://vercel.com/docs/concepts/projects/environment-variables/system-environment-variables#system-environment-variables\n return {\n 'cloud.provider': 'vercel',\n 'cloud.region': process.env.VERCEL_REGION,\n };\n } else if (process.env.AWS_REGION) {\n // https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html\n return {\n 'cloud.provider': 'aws',\n 'cloud.region': process.env.AWS_REGION,\n 'cloud.platform': process.env.AWS_EXECUTION_ENV,\n };\n } else if (process.env.GCP_PROJECT) {\n // https://cloud.google.com/composer/docs/how-to/managing/environment-variables#reserved_variables\n return {\n 'cloud.provider': 'gcp',\n };\n } else if (process.env.ALIYUN_REGION_ID) {\n // TODO: find where I found these environment variables - at least gc.github.com returns something\n return {\n 'cloud.provider': 'alibaba_cloud',\n 'cloud.region': process.env.ALIYUN_REGION_ID,\n };\n } else if (process.env.WEBSITE_SITE_NAME && process.env.REGION_NAME) {\n // https://learn.microsoft.com/en-us/azure/app-service/reference-app-settings?tabs=kudu%2Cdotnet#app-environment\n return {\n 'cloud.provider': 'azure',\n 'cloud.region': process.env.REGION_NAME,\n };\n } else if (process.env.IBM_CLOUD_REGION) {\n // TODO: find where I found these environment variables - at least gc.github.com returns something\n return {\n 'cloud.provider': 'ibm_cloud',\n 'cloud.region': process.env.IBM_CLOUD_REGION,\n };\n } else if (process.env.TENCENTCLOUD_REGION) {\n // https://www.tencentcloud.com/document/product/583/32748\n return {\n 'cloud.provider': 'tencent_cloud',\n 'cloud.region': process.env.TENCENTCLOUD_REGION,\n 'cloud.account.id': process.env.TENCENTCLOUD_APPID,\n 'cloud.availability_zone': process.env.TENCENTCLOUD_ZONE,\n };\n } else if (process.env.NETLIFY) {\n // https://docs.netlify.com/configure-builds/environment-variables/#read-only-variables\n return {\n 'cloud.provider': 'netlify',\n };\n } else if (process.env.FLY_REGION) {\n // https://fly.io/docs/reference/runtime-environment/\n return {\n 'cloud.provider': 'fly.io',\n 'cloud.region': process.env.FLY_REGION,\n };\n } else if (process.env.DYNO) {\n // https://devcenter.heroku.com/articles/dynos#local-environment-variables\n return {\n 'cloud.provider': 'heroku',\n };\n } else {\n return undefined;\n }\n}\n"],"names":[],"mappings":"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"} |
@@ -9,68 +9,36 @@ import { createReadStream } from 'node:fs'; | ||
| const DEFAULT_LINES_OF_CONTEXT = 7; | ||
| const INTEGRATION_NAME = 'ContextLines'; | ||
| // Determines the upper bound of lineno/colno that we will attempt to read. Large colno values are likely to be | ||
| // minified code while large lineno values are likely to be bundled code. | ||
| // Exported for testing purposes. | ||
| const MAX_CONTEXTLINES_COLNO = 1000; | ||
| const MAX_CONTEXTLINES_LINENO = 10000; | ||
| /** | ||
| * Get or init map value | ||
| */ | ||
| const INTEGRATION_NAME = "ContextLines"; | ||
| const MAX_CONTEXTLINES_COLNO = 1e3; | ||
| const MAX_CONTEXTLINES_LINENO = 1e4; | ||
| function emplace(map, key, contents) { | ||
| const value = map.get(key); | ||
| if (value === undefined) { | ||
| if (value === void 0) { | ||
| map.set(key, contents); | ||
| return contents; | ||
| } | ||
| return value; | ||
| } | ||
| /** | ||
| * Determines if context lines should be skipped for a file. | ||
| * - .min.(mjs|cjs|js) files are and not useful since they dont point to the original source | ||
| * - node: prefixed modules are part of the runtime and cannot be resolved to a file | ||
| * - data: skip json, wasm and inline js https://nodejs.org/api/esm.html#data-imports | ||
| */ | ||
| function shouldSkipContextLinesForFile(path) { | ||
| // Test the most common prefix and extension first. These are the ones we | ||
| // are most likely to see in user applications and are the ones we can break out of first. | ||
| if (path.startsWith('node:')) return true; | ||
| if (path.endsWith('.min.js')) return true; | ||
| if (path.endsWith('.min.cjs')) return true; | ||
| if (path.endsWith('.min.mjs')) return true; | ||
| if (path.startsWith('data:')) return true; | ||
| if (path.startsWith("node:")) return true; | ||
| if (path.endsWith(".min.js")) return true; | ||
| if (path.endsWith(".min.cjs")) return true; | ||
| if (path.endsWith(".min.mjs")) return true; | ||
| if (path.startsWith("data:")) return true; | ||
| return false; | ||
| } | ||
| /** | ||
| * Determines if we should skip contextlines based off the max lineno and colno values. | ||
| */ | ||
| function shouldSkipContextLinesForFrame(frame) { | ||
| if (frame.lineno !== undefined && frame.lineno > MAX_CONTEXTLINES_LINENO) return true; | ||
| if (frame.colno !== undefined && frame.colno > MAX_CONTEXTLINES_COLNO) return true; | ||
| if (frame.lineno !== void 0 && frame.lineno > MAX_CONTEXTLINES_LINENO) return true; | ||
| if (frame.colno !== void 0 && frame.colno > MAX_CONTEXTLINES_COLNO) return true; | ||
| return false; | ||
| } | ||
| /** | ||
| * Checks if we have all the contents that we need in the cache. | ||
| */ | ||
| function rangeExistsInContentCache(file, range) { | ||
| const contents = LRU_FILE_CONTENTS_CACHE.get(file); | ||
| if (contents === undefined) return false; | ||
| if (contents === void 0) return false; | ||
| for (let i = range[0]; i <= range[1]; i++) { | ||
| if (contents[i] === undefined) { | ||
| if (contents[i] === void 0) { | ||
| return false; | ||
| } | ||
| } | ||
| return true; | ||
| } | ||
| /** | ||
| * Creates contiguous ranges of lines to read from a file. In the case where context lines overlap, | ||
| * the ranges are merged to create a single range. | ||
| */ | ||
| function makeLineReaderRanges(lines, linecontext) { | ||
@@ -80,13 +48,9 @@ if (!lines.length) { | ||
| } | ||
| let i = 0; | ||
| const line = lines[0]; | ||
| if (typeof line !== 'number') { | ||
| if (typeof line !== "number") { | ||
| return []; | ||
| } | ||
| let current = makeContextRange(line, linecontext); | ||
| const out = []; | ||
| // eslint-disable-next-line no-constant-condition | ||
| while (true) { | ||
@@ -97,6 +61,4 @@ if (i === lines.length - 1) { | ||
| } | ||
| // If the next line falls into the current range, extend the current range to lineno + linecontext. | ||
| const next = lines[i + 1]; | ||
| if (typeof next !== 'number') { | ||
| if (typeof next !== "number") { | ||
| break; | ||
@@ -110,25 +72,12 @@ } | ||
| } | ||
| i++; | ||
| } | ||
| return out; | ||
| } | ||
| /** | ||
| * Extracts lines from a file and stores them in a cache. | ||
| */ | ||
| function getContextLinesFromFile(path, ranges, output) { | ||
| return new Promise((resolve, _reject) => { | ||
| // It is important *not* to have any async code between createInterface and the 'line' event listener | ||
| // as it will cause the 'line' event to | ||
| // be emitted before the listener is attached. | ||
| const stream = createReadStream(path); | ||
| const lineReaded = createInterface({ | ||
| input: stream, | ||
| input: stream | ||
| }); | ||
| // We need to explicitly destroy the stream to prevent memory leaks, | ||
| // removing the listeners on the readline interface is not enough. | ||
| // See: https://github.com/nodejs/node/issues/9002 and https://github.com/getsentry/sentry-javascript/issues/14892 | ||
| function destroyStreamAndResolve() { | ||
@@ -138,9 +87,6 @@ stream.destroy(); | ||
| } | ||
| // Init at zero and increment at the start of the loop because lines are 1 indexed. | ||
| let lineNumber = 0; | ||
| let currentRangeIndex = 0; | ||
| const range = ranges[currentRangeIndex]; | ||
| if (range === undefined) { | ||
| // We should never reach this point, but if we do, we should resolve the promise to prevent it from hanging. | ||
| if (range === void 0) { | ||
| destroyStreamAndResolve(); | ||
@@ -151,7 +97,3 @@ return; | ||
| let rangeEnd = range[1]; | ||
| // We use this inside Promise.all, so we need to resolve the promise even if there is an error | ||
| // to prevent Promise.all from short circuiting the rest. | ||
| function onStreamError(e) { | ||
| // Mark file path as failed to read and prevent multiple read attempts. | ||
| LRU_FILE_CONTENTS_FS_READ_FAILED.set(path, 1); | ||
@@ -163,19 +105,11 @@ DEBUG_BUILD && debug.error(`Failed to read file: ${path}. Error: ${e}`); | ||
| } | ||
| // We need to handle the error event to prevent the process from crashing in < Node 16 | ||
| // https://github.com/nodejs/node/pull/31603 | ||
| stream.on('error', onStreamError); | ||
| lineReaded.on('error', onStreamError); | ||
| lineReaded.on('close', destroyStreamAndResolve); | ||
| lineReaded.on('line', line => { | ||
| stream.on("error", onStreamError); | ||
| lineReaded.on("error", onStreamError); | ||
| lineReaded.on("close", destroyStreamAndResolve); | ||
| lineReaded.on("line", (line) => { | ||
| lineNumber++; | ||
| if (lineNumber < rangeStart) return; | ||
| // !Warning: This mutates the cache by storing the snipped line into the cache. | ||
| output[lineNumber] = snipLine(line, 0); | ||
| if (lineNumber >= rangeEnd) { | ||
| if (currentRangeIndex === ranges.length - 1) { | ||
| // We need to close the file stream and remove listeners, else the reader will continue to run our listener; | ||
| lineReaded.close(); | ||
@@ -186,5 +120,4 @@ lineReaded.removeAllListeners(); | ||
| currentRangeIndex++; | ||
| const range = ranges[currentRangeIndex]; | ||
| if (range === undefined) { | ||
| // This should never happen as it means we have a bug in the context. | ||
| const range2 = ranges[currentRangeIndex]; | ||
| if (range2 === void 0) { | ||
| lineReaded.close(); | ||
@@ -194,4 +127,4 @@ lineReaded.removeAllListeners(); | ||
| } | ||
| rangeStart = range[0]; | ||
| rangeEnd = range[1]; | ||
| rangeStart = range2[0]; | ||
| rangeEnd = range2[1]; | ||
| } | ||
@@ -201,15 +134,4 @@ }); | ||
| } | ||
| /** | ||
| * Adds surrounding (context) lines of the line that an exception occurred on to the event. | ||
| * This is done by reading the file line by line and extracting the lines. The extracted lines are stored in | ||
| * a cache to prevent multiple reads of the same file. Failures to read a file are similarly cached to prevent multiple | ||
| * failing reads from happening. | ||
| */ | ||
| /* eslint-disable complexity */ | ||
| async function addSourceContext(event, contextLines) { | ||
| // keep a lookup map of which files we've already enqueued to read, | ||
| // so we don't enqueue the same file multiple times which would cause multiple i/o reads | ||
| const filesToLines = {}; | ||
| if (contextLines > 0 && event.exception?.values) { | ||
@@ -220,22 +142,10 @@ for (const exception of event.exception.values) { | ||
| } | ||
| // Maps preserve insertion order, so we iterate in reverse, starting at the | ||
| // outermost frame and closer to where the exception has occurred (poor mans priority) | ||
| for (let i = exception.stacktrace.frames.length - 1; i >= 0; i--) { | ||
| const frame = exception.stacktrace.frames[i]; | ||
| const filename = frame?.filename; | ||
| if ( | ||
| !frame || | ||
| typeof filename !== 'string' || | ||
| typeof frame.lineno !== 'number' || | ||
| shouldSkipContextLinesForFile(filename) || | ||
| shouldSkipContextLinesForFrame(frame) | ||
| ) { | ||
| if (!frame || typeof filename !== "string" || typeof frame.lineno !== "number" || shouldSkipContextLinesForFile(filename) || shouldSkipContextLinesForFrame(frame)) { | ||
| continue; | ||
| } | ||
| const filesToLinesOutput = filesToLines[filename]; | ||
| if (!filesToLinesOutput) filesToLines[filename] = []; | ||
| // @ts-expect-error this is defined above | ||
| filesToLines[filename].push(frame.lineno); | ||
@@ -245,3 +155,2 @@ } | ||
| } | ||
| const files = Object.keys(filesToLines); | ||
@@ -251,10 +160,7 @@ if (files.length == 0) { | ||
| } | ||
| const readlinePromises = []; | ||
| for (const file of files) { | ||
| // If we failed to read this before, dont try reading it again. | ||
| if (LRU_FILE_CONTENTS_FS_READ_FAILED.get(file)) { | ||
| continue; | ||
| } | ||
| const filesToLineRanges = filesToLines[file]; | ||
@@ -264,22 +170,13 @@ if (!filesToLineRanges) { | ||
| } | ||
| // Sort ranges so that they are sorted by line increasing order and match how the file is read. | ||
| filesToLineRanges.sort((a, b) => a - b); | ||
| // Check if the contents are already in the cache and if we can avoid reading the file again. | ||
| const ranges = makeLineReaderRanges(filesToLineRanges, contextLines); | ||
| if (ranges.every(r => rangeExistsInContentCache(file, r))) { | ||
| if (ranges.every((r) => rangeExistsInContentCache(file, r))) { | ||
| continue; | ||
| } | ||
| const cache = emplace(LRU_FILE_CONTENTS_CACHE, file, {}); | ||
| readlinePromises.push(getContextLinesFromFile(file, ranges, cache)); | ||
| } | ||
| // The promise rejections are caught in order to prevent them from short circuiting Promise.all | ||
| await Promise.all(readlinePromises).catch(() => { | ||
| DEBUG_BUILD && debug.log('Failed to read one or more source files and resolve context lines'); | ||
| DEBUG_BUILD && debug.log("Failed to read one or more source files and resolve context lines"); | ||
| }); | ||
| // Perform the same loop as above, but this time we can assume all files are in the cache | ||
| // and attempt to add source context to frames. | ||
| if (contextLines > 0 && event.exception?.values) { | ||
@@ -292,21 +189,11 @@ for (const exception of event.exception.values) { | ||
| } | ||
| return event; | ||
| } | ||
| /* eslint-enable complexity */ | ||
| /** Adds context lines to frames */ | ||
| function addSourceContextToFrames( | ||
| frames, | ||
| contextLines, | ||
| cache, | ||
| ) { | ||
| function addSourceContextToFrames(frames, contextLines, cache) { | ||
| for (const frame of frames) { | ||
| // Only add context if we have a filename and it hasn't already been added | ||
| if (frame.filename && frame.context_line === undefined && typeof frame.lineno === 'number') { | ||
| if (frame.filename && frame.context_line === void 0 && typeof frame.lineno === "number") { | ||
| const contents = cache.get(frame.filename); | ||
| if (contents === undefined) { | ||
| if (contents === void 0) { | ||
| continue; | ||
| } | ||
| addContextToFrame(frame.lineno, frame, contextLines, contents); | ||
@@ -316,7 +203,2 @@ } | ||
| } | ||
| /** | ||
| * Clears the context lines from a frame, used to reset a frame to its original state | ||
| * if we fail to resolve all context lines for it. | ||
| */ | ||
| function clearLineContext(frame) { | ||
@@ -327,25 +209,11 @@ delete frame.pre_context; | ||
| } | ||
| /** | ||
| * Resolves context lines before and after the given line number and appends them to the frame; | ||
| */ | ||
| function addContextToFrame( | ||
| lineno, | ||
| frame, | ||
| contextLines, | ||
| contents, | ||
| ) { | ||
| // When there is no line number in the frame, attaching context is nonsensical and will even break grouping. | ||
| // We already check for lineno before calling this, but since StackFrame lineno ism optional, we check it again. | ||
| if (frame.lineno === undefined || contents === undefined) { | ||
| DEBUG_BUILD && debug.error('Cannot resolve context for frame with no lineno or file contents'); | ||
| function addContextToFrame(lineno, frame, contextLines, contents) { | ||
| if (frame.lineno === void 0 || contents === void 0) { | ||
| DEBUG_BUILD && debug.error("Cannot resolve context for frame with no lineno or file contents"); | ||
| return; | ||
| } | ||
| frame.pre_context = []; | ||
| for (let i = makeRangeStart(lineno, contextLines); i < lineno; i++) { | ||
| // We always expect the start context as line numbers cannot be negative. If we dont find a line, then | ||
| // something went wrong somewhere. Clear the context and return without adding any linecontext. | ||
| const line = contents[i]; | ||
| if (line === undefined) { | ||
| if (line === void 0) { | ||
| clearLineContext(frame); | ||
@@ -355,9 +223,5 @@ DEBUG_BUILD && debug.error(`Could not find line ${i} in file ${frame.filename}`); | ||
| } | ||
| frame.pre_context.push(line); | ||
| } | ||
| // We should always have the context line. If we dont, something went wrong, so we clear the context and return | ||
| // without adding any linecontext. | ||
| if (contents[lineno] === undefined) { | ||
| if (contents[lineno] === void 0) { | ||
| clearLineContext(frame); | ||
@@ -367,12 +231,8 @@ DEBUG_BUILD && debug.error(`Could not find line ${lineno} in file ${frame.filename}`); | ||
| } | ||
| frame.context_line = contents[lineno]; | ||
| const end = makeRangeEnd(lineno, contextLines); | ||
| frame.post_context = []; | ||
| for (let i = lineno + 1; i <= end; i++) { | ||
| // Since we dont track when the file ends, we cant clear the context if we dont find a line as it could | ||
| // just be that we reached the end of the file. | ||
| const line = contents[i]; | ||
| if (line === undefined) { | ||
| if (line === void 0) { | ||
| break; | ||
@@ -383,23 +243,13 @@ } | ||
| } | ||
| // Helper functions for generating line context ranges. They take a line number and the number of lines of context to | ||
| // include before and after the line and generate an inclusive range of indices. | ||
| // Compute inclusive end context range | ||
| function makeRangeStart(line, linecontext) { | ||
| return Math.max(1, line - linecontext); | ||
| } | ||
| // Compute inclusive start context range | ||
| function makeRangeEnd(line, linecontext) { | ||
| return line + linecontext; | ||
| } | ||
| // Determine start and end indices for context range (inclusive); | ||
| function makeContextRange(line, linecontext) { | ||
| return [makeRangeStart(line, linecontext), makeRangeEnd(line, linecontext)]; | ||
| } | ||
| /** Exported only for tests, as a type-safe variant. */ | ||
| const _contextLinesIntegration = ((options = {}) => { | ||
| const contextLines = options.frameContextLines !== undefined ? options.frameContextLines : DEFAULT_LINES_OF_CONTEXT; | ||
| const contextLines = options.frameContextLines !== void 0 ? options.frameContextLines : DEFAULT_LINES_OF_CONTEXT; | ||
| return { | ||
@@ -409,9 +259,5 @@ name: INTEGRATION_NAME, | ||
| return addSourceContext(event, contextLines); | ||
| }, | ||
| } | ||
| }; | ||
| }) ; | ||
| /** | ||
| * Capture the lines before and after the frame's context. | ||
| */ | ||
| }); | ||
| const contextLinesIntegration = defineIntegration(_contextLinesIntegration); | ||
@@ -418,0 +264,0 @@ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"contextlines.js","sources":["../../../src/integrations/contextlines.ts"],"sourcesContent":["import { createReadStream } from 'node:fs';\nimport { createInterface } from 'node:readline';\nimport type { Event, IntegrationFn, StackFrame } from '@sentry/core';\nimport { debug, defineIntegration, LRUMap, snipLine } from '@sentry/core';\nimport { DEBUG_BUILD } from '../debug-build';\n\nconst LRU_FILE_CONTENTS_CACHE = new LRUMap<string, Record<number, string>>(10);\nconst LRU_FILE_CONTENTS_FS_READ_FAILED = new LRUMap<string, 1>(20);\nconst DEFAULT_LINES_OF_CONTEXT = 7;\nconst INTEGRATION_NAME = 'ContextLines';\n// Determines the upper bound of lineno/colno that we will attempt to read. Large colno values are likely to be\n// minified code while large lineno values are likely to be bundled code.\n// Exported for testing purposes.\nexport const MAX_CONTEXTLINES_COLNO: number = 1000;\nexport const MAX_CONTEXTLINES_LINENO: number = 10000;\n\ninterface ContextLinesOptions {\n /**\n * Sets the number of context lines for each frame when loading a file.\n * Defaults to 7.\n *\n * Set to 0 to disable loading and inclusion of source files.\n **/\n frameContextLines?: number;\n}\n\n/**\n * Exported for testing purposes.\n */\nexport function resetFileContentCache(): void {\n LRU_FILE_CONTENTS_CACHE.clear();\n}\n\n/**\n * Get or init map value\n */\nfunction emplace<T extends LRUMap<K, V>, K extends string, V>(map: T, key: K, contents: V): V {\n const value = map.get(key);\n\n if (value === undefined) {\n map.set(key, contents);\n return contents;\n }\n\n return value;\n}\n\n/**\n * Determines if context lines should be skipped for a file.\n * - .min.(mjs|cjs|js) files are and not useful since they dont point to the original source\n * - node: prefixed modules are part of the runtime and cannot be resolved to a file\n * - data: skip json, wasm and inline js https://nodejs.org/api/esm.html#data-imports\n */\nfunction shouldSkipContextLinesForFile(path: string): boolean {\n // Test the most common prefix and extension first. These are the ones we\n // are most likely to see in user applications and are the ones we can break out of first.\n if (path.startsWith('node:')) return true;\n if (path.endsWith('.min.js')) return true;\n if (path.endsWith('.min.cjs')) return true;\n if (path.endsWith('.min.mjs')) return true;\n if (path.startsWith('data:')) return true;\n return false;\n}\n\n/**\n * Determines if we should skip contextlines based off the max lineno and colno values.\n */\nfunction shouldSkipContextLinesForFrame(frame: StackFrame): boolean {\n if (frame.lineno !== undefined && frame.lineno > MAX_CONTEXTLINES_LINENO) return true;\n if (frame.colno !== undefined && frame.colno > MAX_CONTEXTLINES_COLNO) return true;\n return false;\n}\n/**\n * Checks if we have all the contents that we need in the cache.\n */\nfunction rangeExistsInContentCache(file: string, range: ReadlineRange): boolean {\n const contents = LRU_FILE_CONTENTS_CACHE.get(file);\n if (contents === undefined) return false;\n\n for (let i = range[0]; i <= range[1]; i++) {\n if (contents[i] === undefined) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Creates contiguous ranges of lines to read from a file. In the case where context lines overlap,\n * the ranges are merged to create a single range.\n */\nfunction makeLineReaderRanges(lines: number[], linecontext: number): ReadlineRange[] {\n if (!lines.length) {\n return [];\n }\n\n let i = 0;\n const line = lines[0];\n\n if (typeof line !== 'number') {\n return [];\n }\n\n let current = makeContextRange(line, linecontext);\n const out: ReadlineRange[] = [];\n // eslint-disable-next-line no-constant-condition\n while (true) {\n if (i === lines.length - 1) {\n out.push(current);\n break;\n }\n\n // If the next line falls into the current range, extend the current range to lineno + linecontext.\n const next = lines[i + 1];\n if (typeof next !== 'number') {\n break;\n }\n if (next <= current[1]) {\n current[1] = next + linecontext;\n } else {\n out.push(current);\n current = makeContextRange(next, linecontext);\n }\n\n i++;\n }\n\n return out;\n}\n\n/**\n * Extracts lines from a file and stores them in a cache.\n */\nfunction getContextLinesFromFile(path: string, ranges: ReadlineRange[], output: Record<number, string>): Promise<void> {\n return new Promise((resolve, _reject) => {\n // It is important *not* to have any async code between createInterface and the 'line' event listener\n // as it will cause the 'line' event to\n // be emitted before the listener is attached.\n const stream = createReadStream(path);\n const lineReaded = createInterface({\n input: stream,\n });\n\n // We need to explicitly destroy the stream to prevent memory leaks,\n // removing the listeners on the readline interface is not enough.\n // See: https://github.com/nodejs/node/issues/9002 and https://github.com/getsentry/sentry-javascript/issues/14892\n function destroyStreamAndResolve(): void {\n stream.destroy();\n resolve();\n }\n\n // Init at zero and increment at the start of the loop because lines are 1 indexed.\n let lineNumber = 0;\n let currentRangeIndex = 0;\n const range = ranges[currentRangeIndex];\n if (range === undefined) {\n // We should never reach this point, but if we do, we should resolve the promise to prevent it from hanging.\n destroyStreamAndResolve();\n return;\n }\n let rangeStart = range[0];\n let rangeEnd = range[1];\n\n // We use this inside Promise.all, so we need to resolve the promise even if there is an error\n // to prevent Promise.all from short circuiting the rest.\n function onStreamError(e: Error): void {\n // Mark file path as failed to read and prevent multiple read attempts.\n LRU_FILE_CONTENTS_FS_READ_FAILED.set(path, 1);\n DEBUG_BUILD && debug.error(`Failed to read file: ${path}. Error: ${e}`);\n lineReaded.close();\n lineReaded.removeAllListeners();\n destroyStreamAndResolve();\n }\n\n // We need to handle the error event to prevent the process from crashing in < Node 16\n // https://github.com/nodejs/node/pull/31603\n stream.on('error', onStreamError);\n lineReaded.on('error', onStreamError);\n lineReaded.on('close', destroyStreamAndResolve);\n\n lineReaded.on('line', line => {\n lineNumber++;\n if (lineNumber < rangeStart) return;\n\n // !Warning: This mutates the cache by storing the snipped line into the cache.\n output[lineNumber] = snipLine(line, 0);\n\n if (lineNumber >= rangeEnd) {\n if (currentRangeIndex === ranges.length - 1) {\n // We need to close the file stream and remove listeners, else the reader will continue to run our listener;\n lineReaded.close();\n lineReaded.removeAllListeners();\n return;\n }\n currentRangeIndex++;\n const range = ranges[currentRangeIndex];\n if (range === undefined) {\n // This should never happen as it means we have a bug in the context.\n lineReaded.close();\n lineReaded.removeAllListeners();\n return;\n }\n rangeStart = range[0];\n rangeEnd = range[1];\n }\n });\n });\n}\n\n/**\n * Adds surrounding (context) lines of the line that an exception occurred on to the event.\n * This is done by reading the file line by line and extracting the lines. The extracted lines are stored in\n * a cache to prevent multiple reads of the same file. Failures to read a file are similarly cached to prevent multiple\n * failing reads from happening.\n */\n/* eslint-disable complexity */\nasync function addSourceContext(event: Event, contextLines: number): Promise<Event> {\n // keep a lookup map of which files we've already enqueued to read,\n // so we don't enqueue the same file multiple times which would cause multiple i/o reads\n const filesToLines: Record<string, number[]> = {};\n\n if (contextLines > 0 && event.exception?.values) {\n for (const exception of event.exception.values) {\n if (!exception.stacktrace?.frames?.length) {\n continue;\n }\n\n // Maps preserve insertion order, so we iterate in reverse, starting at the\n // outermost frame and closer to where the exception has occurred (poor mans priority)\n for (let i = exception.stacktrace.frames.length - 1; i >= 0; i--) {\n const frame: StackFrame | undefined = exception.stacktrace.frames[i];\n const filename = frame?.filename;\n\n if (\n !frame ||\n typeof filename !== 'string' ||\n typeof frame.lineno !== 'number' ||\n shouldSkipContextLinesForFile(filename) ||\n shouldSkipContextLinesForFrame(frame)\n ) {\n continue;\n }\n\n const filesToLinesOutput = filesToLines[filename];\n if (!filesToLinesOutput) filesToLines[filename] = [];\n // @ts-expect-error this is defined above\n filesToLines[filename].push(frame.lineno);\n }\n }\n }\n\n const files = Object.keys(filesToLines);\n if (files.length == 0) {\n return event;\n }\n\n const readlinePromises: Promise<void>[] = [];\n for (const file of files) {\n // If we failed to read this before, dont try reading it again.\n if (LRU_FILE_CONTENTS_FS_READ_FAILED.get(file)) {\n continue;\n }\n\n const filesToLineRanges = filesToLines[file];\n if (!filesToLineRanges) {\n continue;\n }\n\n // Sort ranges so that they are sorted by line increasing order and match how the file is read.\n filesToLineRanges.sort((a, b) => a - b);\n // Check if the contents are already in the cache and if we can avoid reading the file again.\n const ranges = makeLineReaderRanges(filesToLineRanges, contextLines);\n if (ranges.every(r => rangeExistsInContentCache(file, r))) {\n continue;\n }\n\n const cache = emplace(LRU_FILE_CONTENTS_CACHE, file, {});\n readlinePromises.push(getContextLinesFromFile(file, ranges, cache));\n }\n\n // The promise rejections are caught in order to prevent them from short circuiting Promise.all\n await Promise.all(readlinePromises).catch(() => {\n DEBUG_BUILD && debug.log('Failed to read one or more source files and resolve context lines');\n });\n\n // Perform the same loop as above, but this time we can assume all files are in the cache\n // and attempt to add source context to frames.\n if (contextLines > 0 && event.exception?.values) {\n for (const exception of event.exception.values) {\n if (exception.stacktrace?.frames && exception.stacktrace.frames.length > 0) {\n addSourceContextToFrames(exception.stacktrace.frames, contextLines, LRU_FILE_CONTENTS_CACHE);\n }\n }\n }\n\n return event;\n}\n/* eslint-enable complexity */\n\n/** Adds context lines to frames */\nfunction addSourceContextToFrames(\n frames: StackFrame[],\n contextLines: number,\n cache: LRUMap<string, Record<number, string>>,\n): void {\n for (const frame of frames) {\n // Only add context if we have a filename and it hasn't already been added\n if (frame.filename && frame.context_line === undefined && typeof frame.lineno === 'number') {\n const contents = cache.get(frame.filename);\n if (contents === undefined) {\n continue;\n }\n\n addContextToFrame(frame.lineno, frame, contextLines, contents);\n }\n }\n}\n\n/**\n * Clears the context lines from a frame, used to reset a frame to its original state\n * if we fail to resolve all context lines for it.\n */\nfunction clearLineContext(frame: StackFrame): void {\n delete frame.pre_context;\n delete frame.context_line;\n delete frame.post_context;\n}\n\n/**\n * Resolves context lines before and after the given line number and appends them to the frame;\n */\nexport function addContextToFrame(\n lineno: number,\n frame: StackFrame,\n contextLines: number,\n contents: Record<number, string> | undefined,\n): void {\n // When there is no line number in the frame, attaching context is nonsensical and will even break grouping.\n // We already check for lineno before calling this, but since StackFrame lineno ism optional, we check it again.\n if (frame.lineno === undefined || contents === undefined) {\n DEBUG_BUILD && debug.error('Cannot resolve context for frame with no lineno or file contents');\n return;\n }\n\n frame.pre_context = [];\n for (let i = makeRangeStart(lineno, contextLines); i < lineno; i++) {\n // We always expect the start context as line numbers cannot be negative. If we dont find a line, then\n // something went wrong somewhere. Clear the context and return without adding any linecontext.\n const line = contents[i];\n if (line === undefined) {\n clearLineContext(frame);\n DEBUG_BUILD && debug.error(`Could not find line ${i} in file ${frame.filename}`);\n return;\n }\n\n frame.pre_context.push(line);\n }\n\n // We should always have the context line. If we dont, something went wrong, so we clear the context and return\n // without adding any linecontext.\n if (contents[lineno] === undefined) {\n clearLineContext(frame);\n DEBUG_BUILD && debug.error(`Could not find line ${lineno} in file ${frame.filename}`);\n return;\n }\n\n frame.context_line = contents[lineno];\n\n const end = makeRangeEnd(lineno, contextLines);\n frame.post_context = [];\n for (let i = lineno + 1; i <= end; i++) {\n // Since we dont track when the file ends, we cant clear the context if we dont find a line as it could\n // just be that we reached the end of the file.\n const line = contents[i];\n if (line === undefined) {\n break;\n }\n frame.post_context.push(line);\n }\n}\n\n// Helper functions for generating line context ranges. They take a line number and the number of lines of context to\n// include before and after the line and generate an inclusive range of indices.\ntype ReadlineRange = [start: number, end: number];\n// Compute inclusive end context range\nfunction makeRangeStart(line: number, linecontext: number): number {\n return Math.max(1, line - linecontext);\n}\n// Compute inclusive start context range\nfunction makeRangeEnd(line: number, linecontext: number): number {\n return line + linecontext;\n}\n// Determine start and end indices for context range (inclusive);\nfunction makeContextRange(line: number, linecontext: number): [start: number, end: number] {\n return [makeRangeStart(line, linecontext), makeRangeEnd(line, linecontext)];\n}\n\n/** Exported only for tests, as a type-safe variant. */\nexport const _contextLinesIntegration = ((options: ContextLinesOptions = {}) => {\n const contextLines = options.frameContextLines !== undefined ? options.frameContextLines : DEFAULT_LINES_OF_CONTEXT;\n\n return {\n name: INTEGRATION_NAME,\n processEvent(event) {\n return addSourceContext(event, contextLines);\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Capture the lines before and after the frame's context.\n */\nexport const contextLinesIntegration = defineIntegration(_contextLinesIntegration);\n"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"contextlines.js","sources":["../../../src/integrations/contextlines.ts"],"sourcesContent":["import { createReadStream } from 'node:fs';\nimport { createInterface } from 'node:readline';\nimport type { Event, IntegrationFn, StackFrame } from '@sentry/core';\nimport { debug, defineIntegration, LRUMap, snipLine } from '@sentry/core';\nimport { DEBUG_BUILD } from '../debug-build';\n\nconst LRU_FILE_CONTENTS_CACHE = new LRUMap<string, Record<number, string>>(10);\nconst LRU_FILE_CONTENTS_FS_READ_FAILED = new LRUMap<string, 1>(20);\nconst DEFAULT_LINES_OF_CONTEXT = 7;\nconst INTEGRATION_NAME = 'ContextLines';\n// Determines the upper bound of lineno/colno that we will attempt to read. Large colno values are likely to be\n// minified code while large lineno values are likely to be bundled code.\n// Exported for testing purposes.\nexport const MAX_CONTEXTLINES_COLNO: number = 1000;\nexport const MAX_CONTEXTLINES_LINENO: number = 10000;\n\ninterface ContextLinesOptions {\n /**\n * Sets the number of context lines for each frame when loading a file.\n * Defaults to 7.\n *\n * Set to 0 to disable loading and inclusion of source files.\n **/\n frameContextLines?: number;\n}\n\n/**\n * Exported for testing purposes.\n */\nexport function resetFileContentCache(): void {\n LRU_FILE_CONTENTS_CACHE.clear();\n}\n\n/**\n * Get or init map value\n */\nfunction emplace<T extends LRUMap<K, V>, K extends string, V>(map: T, key: K, contents: V): V {\n const value = map.get(key);\n\n if (value === undefined) {\n map.set(key, contents);\n return contents;\n }\n\n return value;\n}\n\n/**\n * Determines if context lines should be skipped for a file.\n * - .min.(mjs|cjs|js) files are and not useful since they dont point to the original source\n * - node: prefixed modules are part of the runtime and cannot be resolved to a file\n * - data: skip json, wasm and inline js https://nodejs.org/api/esm.html#data-imports\n */\nfunction shouldSkipContextLinesForFile(path: string): boolean {\n // Test the most common prefix and extension first. These are the ones we\n // are most likely to see in user applications and are the ones we can break out of first.\n if (path.startsWith('node:')) return true;\n if (path.endsWith('.min.js')) return true;\n if (path.endsWith('.min.cjs')) return true;\n if (path.endsWith('.min.mjs')) return true;\n if (path.startsWith('data:')) return true;\n return false;\n}\n\n/**\n * Determines if we should skip contextlines based off the max lineno and colno values.\n */\nfunction shouldSkipContextLinesForFrame(frame: StackFrame): boolean {\n if (frame.lineno !== undefined && frame.lineno > MAX_CONTEXTLINES_LINENO) return true;\n if (frame.colno !== undefined && frame.colno > MAX_CONTEXTLINES_COLNO) return true;\n return false;\n}\n/**\n * Checks if we have all the contents that we need in the cache.\n */\nfunction rangeExistsInContentCache(file: string, range: ReadlineRange): boolean {\n const contents = LRU_FILE_CONTENTS_CACHE.get(file);\n if (contents === undefined) return false;\n\n for (let i = range[0]; i <= range[1]; i++) {\n if (contents[i] === undefined) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Creates contiguous ranges of lines to read from a file. In the case where context lines overlap,\n * the ranges are merged to create a single range.\n */\nfunction makeLineReaderRanges(lines: number[], linecontext: number): ReadlineRange[] {\n if (!lines.length) {\n return [];\n }\n\n let i = 0;\n const line = lines[0];\n\n if (typeof line !== 'number') {\n return [];\n }\n\n let current = makeContextRange(line, linecontext);\n const out: ReadlineRange[] = [];\n // eslint-disable-next-line no-constant-condition\n while (true) {\n if (i === lines.length - 1) {\n out.push(current);\n break;\n }\n\n // If the next line falls into the current range, extend the current range to lineno + linecontext.\n const next = lines[i + 1];\n if (typeof next !== 'number') {\n break;\n }\n if (next <= current[1]) {\n current[1] = next + linecontext;\n } else {\n out.push(current);\n current = makeContextRange(next, linecontext);\n }\n\n i++;\n }\n\n return out;\n}\n\n/**\n * Extracts lines from a file and stores them in a cache.\n */\nfunction getContextLinesFromFile(path: string, ranges: ReadlineRange[], output: Record<number, string>): Promise<void> {\n return new Promise((resolve, _reject) => {\n // It is important *not* to have any async code between createInterface and the 'line' event listener\n // as it will cause the 'line' event to\n // be emitted before the listener is attached.\n const stream = createReadStream(path);\n const lineReaded = createInterface({\n input: stream,\n });\n\n // We need to explicitly destroy the stream to prevent memory leaks,\n // removing the listeners on the readline interface is not enough.\n // See: https://github.com/nodejs/node/issues/9002 and https://github.com/getsentry/sentry-javascript/issues/14892\n function destroyStreamAndResolve(): void {\n stream.destroy();\n resolve();\n }\n\n // Init at zero and increment at the start of the loop because lines are 1 indexed.\n let lineNumber = 0;\n let currentRangeIndex = 0;\n const range = ranges[currentRangeIndex];\n if (range === undefined) {\n // We should never reach this point, but if we do, we should resolve the promise to prevent it from hanging.\n destroyStreamAndResolve();\n return;\n }\n let rangeStart = range[0];\n let rangeEnd = range[1];\n\n // We use this inside Promise.all, so we need to resolve the promise even if there is an error\n // to prevent Promise.all from short circuiting the rest.\n function onStreamError(e: Error): void {\n // Mark file path as failed to read and prevent multiple read attempts.\n LRU_FILE_CONTENTS_FS_READ_FAILED.set(path, 1);\n DEBUG_BUILD && debug.error(`Failed to read file: ${path}. Error: ${e}`);\n lineReaded.close();\n lineReaded.removeAllListeners();\n destroyStreamAndResolve();\n }\n\n // We need to handle the error event to prevent the process from crashing in < Node 16\n // https://github.com/nodejs/node/pull/31603\n stream.on('error', onStreamError);\n lineReaded.on('error', onStreamError);\n lineReaded.on('close', destroyStreamAndResolve);\n\n lineReaded.on('line', line => {\n lineNumber++;\n if (lineNumber < rangeStart) return;\n\n // !Warning: This mutates the cache by storing the snipped line into the cache.\n output[lineNumber] = snipLine(line, 0);\n\n if (lineNumber >= rangeEnd) {\n if (currentRangeIndex === ranges.length - 1) {\n // We need to close the file stream and remove listeners, else the reader will continue to run our listener;\n lineReaded.close();\n lineReaded.removeAllListeners();\n return;\n }\n currentRangeIndex++;\n const range = ranges[currentRangeIndex];\n if (range === undefined) {\n // This should never happen as it means we have a bug in the context.\n lineReaded.close();\n lineReaded.removeAllListeners();\n return;\n }\n rangeStart = range[0];\n rangeEnd = range[1];\n }\n });\n });\n}\n\n/**\n * Adds surrounding (context) lines of the line that an exception occurred on to the event.\n * This is done by reading the file line by line and extracting the lines. The extracted lines are stored in\n * a cache to prevent multiple reads of the same file. Failures to read a file are similarly cached to prevent multiple\n * failing reads from happening.\n */\n/* eslint-disable complexity */\nasync function addSourceContext(event: Event, contextLines: number): Promise<Event> {\n // keep a lookup map of which files we've already enqueued to read,\n // so we don't enqueue the same file multiple times which would cause multiple i/o reads\n const filesToLines: Record<string, number[]> = {};\n\n if (contextLines > 0 && event.exception?.values) {\n for (const exception of event.exception.values) {\n if (!exception.stacktrace?.frames?.length) {\n continue;\n }\n\n // Maps preserve insertion order, so we iterate in reverse, starting at the\n // outermost frame and closer to where the exception has occurred (poor mans priority)\n for (let i = exception.stacktrace.frames.length - 1; i >= 0; i--) {\n const frame: StackFrame | undefined = exception.stacktrace.frames[i];\n const filename = frame?.filename;\n\n if (\n !frame ||\n typeof filename !== 'string' ||\n typeof frame.lineno !== 'number' ||\n shouldSkipContextLinesForFile(filename) ||\n shouldSkipContextLinesForFrame(frame)\n ) {\n continue;\n }\n\n const filesToLinesOutput = filesToLines[filename];\n if (!filesToLinesOutput) filesToLines[filename] = [];\n // @ts-expect-error this is defined above\n filesToLines[filename].push(frame.lineno);\n }\n }\n }\n\n const files = Object.keys(filesToLines);\n if (files.length == 0) {\n return event;\n }\n\n const readlinePromises: Promise<void>[] = [];\n for (const file of files) {\n // If we failed to read this before, dont try reading it again.\n if (LRU_FILE_CONTENTS_FS_READ_FAILED.get(file)) {\n continue;\n }\n\n const filesToLineRanges = filesToLines[file];\n if (!filesToLineRanges) {\n continue;\n }\n\n // Sort ranges so that they are sorted by line increasing order and match how the file is read.\n filesToLineRanges.sort((a, b) => a - b);\n // Check if the contents are already in the cache and if we can avoid reading the file again.\n const ranges = makeLineReaderRanges(filesToLineRanges, contextLines);\n if (ranges.every(r => rangeExistsInContentCache(file, r))) {\n continue;\n }\n\n const cache = emplace(LRU_FILE_CONTENTS_CACHE, file, {});\n readlinePromises.push(getContextLinesFromFile(file, ranges, cache));\n }\n\n // The promise rejections are caught in order to prevent them from short circuiting Promise.all\n await Promise.all(readlinePromises).catch(() => {\n DEBUG_BUILD && debug.log('Failed to read one or more source files and resolve context lines');\n });\n\n // Perform the same loop as above, but this time we can assume all files are in the cache\n // and attempt to add source context to frames.\n if (contextLines > 0 && event.exception?.values) {\n for (const exception of event.exception.values) {\n if (exception.stacktrace?.frames && exception.stacktrace.frames.length > 0) {\n addSourceContextToFrames(exception.stacktrace.frames, contextLines, LRU_FILE_CONTENTS_CACHE);\n }\n }\n }\n\n return event;\n}\n/* eslint-enable complexity */\n\n/** Adds context lines to frames */\nfunction addSourceContextToFrames(\n frames: StackFrame[],\n contextLines: number,\n cache: LRUMap<string, Record<number, string>>,\n): void {\n for (const frame of frames) {\n // Only add context if we have a filename and it hasn't already been added\n if (frame.filename && frame.context_line === undefined && typeof frame.lineno === 'number') {\n const contents = cache.get(frame.filename);\n if (contents === undefined) {\n continue;\n }\n\n addContextToFrame(frame.lineno, frame, contextLines, contents);\n }\n }\n}\n\n/**\n * Clears the context lines from a frame, used to reset a frame to its original state\n * if we fail to resolve all context lines for it.\n */\nfunction clearLineContext(frame: StackFrame): void {\n delete frame.pre_context;\n delete frame.context_line;\n delete frame.post_context;\n}\n\n/**\n * Resolves context lines before and after the given line number and appends them to the frame;\n */\nexport function addContextToFrame(\n lineno: number,\n frame: StackFrame,\n contextLines: number,\n contents: Record<number, string> | undefined,\n): void {\n // When there is no line number in the frame, attaching context is nonsensical and will even break grouping.\n // We already check for lineno before calling this, but since StackFrame lineno ism optional, we check it again.\n if (frame.lineno === undefined || contents === undefined) {\n DEBUG_BUILD && debug.error('Cannot resolve context for frame with no lineno or file contents');\n return;\n }\n\n frame.pre_context = [];\n for (let i = makeRangeStart(lineno, contextLines); i < lineno; i++) {\n // We always expect the start context as line numbers cannot be negative. If we dont find a line, then\n // something went wrong somewhere. Clear the context and return without adding any linecontext.\n const line = contents[i];\n if (line === undefined) {\n clearLineContext(frame);\n DEBUG_BUILD && debug.error(`Could not find line ${i} in file ${frame.filename}`);\n return;\n }\n\n frame.pre_context.push(line);\n }\n\n // We should always have the context line. If we dont, something went wrong, so we clear the context and return\n // without adding any linecontext.\n if (contents[lineno] === undefined) {\n clearLineContext(frame);\n DEBUG_BUILD && debug.error(`Could not find line ${lineno} in file ${frame.filename}`);\n return;\n }\n\n frame.context_line = contents[lineno];\n\n const end = makeRangeEnd(lineno, contextLines);\n frame.post_context = [];\n for (let i = lineno + 1; i <= end; i++) {\n // Since we dont track when the file ends, we cant clear the context if we dont find a line as it could\n // just be that we reached the end of the file.\n const line = contents[i];\n if (line === undefined) {\n break;\n }\n frame.post_context.push(line);\n }\n}\n\n// Helper functions for generating line context ranges. They take a line number and the number of lines of context to\n// include before and after the line and generate an inclusive range of indices.\ntype ReadlineRange = [start: number, end: number];\n// Compute inclusive end context range\nfunction makeRangeStart(line: number, linecontext: number): number {\n return Math.max(1, line - linecontext);\n}\n// Compute inclusive start context range\nfunction makeRangeEnd(line: number, linecontext: number): number {\n return line + linecontext;\n}\n// Determine start and end indices for context range (inclusive);\nfunction makeContextRange(line: number, linecontext: number): [start: number, end: number] {\n return [makeRangeStart(line, linecontext), makeRangeEnd(line, linecontext)];\n}\n\n/** Exported only for tests, as a type-safe variant. */\nexport const _contextLinesIntegration = ((options: ContextLinesOptions = {}) => {\n const contextLines = options.frameContextLines !== undefined ? options.frameContextLines : DEFAULT_LINES_OF_CONTEXT;\n\n return {\n name: INTEGRATION_NAME,\n processEvent(event) {\n return addSourceContext(event, contextLines);\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Capture the lines before and after the frame's context.\n */\nexport const contextLinesIntegration = defineIntegration(_contextLinesIntegration);\n"],"names":["range"],"mappings":"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"} |
@@ -1,4 +0,4 @@ | ||
| const INSTRUMENTATION_NAME = '@sentry/instrumentation-http'; | ||
| const INSTRUMENTATION_NAME = "@sentry/instrumentation-http"; | ||
| export { INSTRUMENTATION_NAME }; | ||
| //# sourceMappingURL=constants.js.map |
| import { subscribe } from 'node:diagnostics_channel'; | ||
| import { createContextKey, context, propagation } from '@opentelemetry/api'; | ||
| import { debug, addNonEnumerableProperty, getClient, getIsolationScope, httpRequestToRequestData, stripUrlQueryAndFragment, withIsolationScope, generateSpanId, _INTERNAL_safeMathRandom, generateTraceId, getCurrentScope } from '@sentry/core'; | ||
| import { debug, getHttpServerSubscriptions, HTTP_ON_SERVER_REQUEST, getClient, addNonEnumerableProperty } from '@sentry/core'; | ||
| export { recordRequestSession } from '@sentry/core'; | ||
| import { DEBUG_BUILD } from '../../debug-build.js'; | ||
| import { patchRequestToCaptureBody } from '../../utils/captureRequestBody.js'; | ||
| const HTTP_SERVER_INSTRUMENTED_KEY = createContextKey('sentry_http_server_instrumented'); | ||
| const INTEGRATION_NAME = 'Http.Server'; | ||
| const clientToRequestSessionAggregatesMap = new Map | ||
| (); | ||
| // We keep track of emit functions we wrapped, to avoid double wrapping | ||
| // We do this instead of putting a non-enumerable property on the function, because | ||
| // sometimes the property seems to be migrated to forks of the emit function, which we do not want to happen | ||
| // This was the case in the nestjs-distributed-tracing E2E test | ||
| const wrappedEmitFns = new WeakSet(); | ||
| /** | ||
| * Add a callback to the request object that will be called when the request is started. | ||
| * The callback will receive the next function to continue processing the request. | ||
| */ | ||
| const HTTP_SERVER_INSTRUMENTED_KEY = createContextKey("sentry_http_server_instrumented"); | ||
| const INTEGRATION_NAME = "Http.Server"; | ||
| function addStartSpanCallback(request, callback) { | ||
| addNonEnumerableProperty(request, '_startSpanCallback', new WeakRef(callback)); | ||
| addNonEnumerableProperty(request, "_startSpanCallback", new WeakRef(callback)); | ||
| } | ||
| const _httpServerIntegration = ((options = {}) => { | ||
| const _options = { | ||
| sessions: options.sessions ?? true, | ||
| sessionFlushingDelayMS: options.sessionFlushingDelayMS ?? 60000, | ||
| maxRequestBodySize: options.maxRequestBodySize ?? 'medium', | ||
| sessionFlushingDelayMS: options.sessionFlushingDelayMS ?? 6e4, | ||
| maxRequestBodySize: options.maxRequestBodySize ?? "medium", | ||
| // Server spans are created by `httpServerSpansIntegration` via the | ||
| // `httpServerRequest` client event + `_startSpanCallback`, not by the | ||
| // core subscription helper. Explicitly opt out so the helper does not | ||
| // double-create spans when the client has tracing enabled. | ||
| spans: false, | ||
| // Cast: core uses HttpIncomingMessage; node consumers pass | ||
| // RequestOptions-typed callbacks. | ||
| // The two are structurally compatible for the fields the callback reads | ||
| // (url, method, headers). | ||
| ignoreRequestBody: options.ignoreRequestBody, | ||
| /** | ||
| * Hook called by core's `instrumentServer` to wrap the upstream | ||
| * `emit('request')` call. | ||
| * | ||
| * We use it to extract OTel context from request headers and re-enter | ||
| * the OTel context before the framework sees the request, so subsequent | ||
| * spans (eg from `httpServerSpansIntegration`) attach to the right trace. | ||
| */ | ||
| wrapServerEmitRequest(request, response, normalizedRequest, next) { | ||
| const client = getClient(); | ||
| if (!client) return next(); | ||
| if (context.active().getValue(HTTP_SERVER_INSTRUMENTED_KEY)) { | ||
| return next(); | ||
| } | ||
| const ctx = propagation.extract(context.active(), normalizedRequest.headers).setValue(HTTP_SERVER_INSTRUMENTED_KEY, true); | ||
| context.with(ctx, () => { | ||
| client.emit("httpServerRequest", request, response, normalizedRequest); | ||
| const callback = request._startSpanCallback?.deref(); | ||
| if (callback) { | ||
| callback(() => { | ||
| next(); | ||
| return true; | ||
| }); | ||
| } else { | ||
| next(); | ||
| } | ||
| }); | ||
| } | ||
| }; | ||
| return { | ||
| name: INTEGRATION_NAME, | ||
| setupOnce() { | ||
| const onHttpServerRequestStart = ((_data) => { | ||
| const data = _data ; | ||
| instrumentServer(data.server, _options); | ||
| }) ; | ||
| subscribe('http.server.request.start', onHttpServerRequestStart); | ||
| const { [HTTP_ON_SERVER_REQUEST]: onHttpServerRequestStart } = getHttpServerSubscriptions(_options); | ||
| subscribe(HTTP_ON_SERVER_REQUEST, onHttpServerRequestStart); | ||
| }, | ||
| afterAllSetup(client) { | ||
| if (DEBUG_BUILD && client.getIntegrationByName('Http')) { | ||
| if (DEBUG_BUILD && client.getIntegrationByName("Http")) { | ||
| debug.warn( | ||
| 'It seems that you have manually added `httpServerIntegration` while `httpIntegration` is also present. Make sure to remove `httpServerIntegration` when adding `httpIntegration`.', | ||
| "It seems that you have manually added `httpServerIntegration` while `httpIntegration` is also present. Make sure to remove `httpServerIntegration` when adding `httpIntegration`." | ||
| ); | ||
| } | ||
| }, | ||
| } | ||
| }; | ||
| }) ; | ||
| }); | ||
| const httpServerIntegration = _httpServerIntegration; | ||
| /** | ||
| * This integration handles request isolation, trace continuation and other core Sentry functionality around incoming http requests | ||
| * handled via the node `http` module. | ||
| * | ||
| * This version uses OpenTelemetry for context propagation and span management. | ||
| * | ||
| * @see {@link ../../light/integrations/httpServerIntegration.ts} for the lightweight version without OpenTelemetry | ||
| */ | ||
| const httpServerIntegration = _httpServerIntegration | ||
| ; | ||
| /** | ||
| * Instrument a server to capture incoming requests. | ||
| * | ||
| */ | ||
| function instrumentServer( | ||
| server, | ||
| { | ||
| ignoreRequestBody, | ||
| maxRequestBodySize, | ||
| sessions, | ||
| sessionFlushingDelayMS, | ||
| } | ||
| , | ||
| ) { | ||
| // eslint-disable-next-line @typescript-eslint/unbound-method | ||
| const originalEmit = server.emit; | ||
| if (wrappedEmitFns.has(originalEmit)) { | ||
| return; | ||
| } | ||
| const newEmit = new Proxy(originalEmit, { | ||
| apply(target, thisArg, args) { | ||
| // Only traces request events | ||
| if (args[0] !== 'request') { | ||
| return target.apply(thisArg, args); | ||
| } | ||
| const client = getClient(); | ||
| // Make sure we do not double execute our wrapper code, for edge cases... | ||
| // Without this check, if we double-wrap emit, for whatever reason, you'd get two http.server spans (one the children of the other) | ||
| if (context.active().getValue(HTTP_SERVER_INSTRUMENTED_KEY) || !client) { | ||
| return target.apply(thisArg, args); | ||
| } | ||
| DEBUG_BUILD && debug.log(INTEGRATION_NAME, 'Handling incoming request'); | ||
| const isolationScope = getIsolationScope().clone(); | ||
| const request = args[1] ; | ||
| const response = args[2] ; | ||
| const normalizedRequest = httpRequestToRequestData(request); | ||
| // request.ip is non-standard but some frameworks set this | ||
| const ipAddress = (request ).ip || request.socket?.remoteAddress; | ||
| const url = request.url || '/'; | ||
| if (maxRequestBodySize !== 'none' && !ignoreRequestBody?.(url, request)) { | ||
| patchRequestToCaptureBody(request, isolationScope, maxRequestBodySize, INTEGRATION_NAME); | ||
| } | ||
| // Update the isolation scope, isolate this request | ||
| isolationScope.setSDKProcessingMetadata({ normalizedRequest, ipAddress }); | ||
| // attempt to update the scope's `transactionName` based on the request URL | ||
| // Ideally, framework instrumentations coming after the HttpInstrumentation | ||
| // update the transactionName once we get a parameterized route. | ||
| const httpMethod = (request.method || 'GET').toUpperCase(); | ||
| const httpTargetWithoutQueryFragment = stripUrlQueryAndFragment(url); | ||
| const bestEffortTransactionName = `${httpMethod} ${httpTargetWithoutQueryFragment}`; | ||
| isolationScope.setTransactionName(bestEffortTransactionName); | ||
| if (sessions && client) { | ||
| recordRequestSession(client, { | ||
| requestIsolationScope: isolationScope, | ||
| response, | ||
| sessionFlushingDelayMS: sessionFlushingDelayMS ?? 60000, | ||
| }); | ||
| } | ||
| return withIsolationScope(isolationScope, () => { | ||
| const newPropagationContext = { | ||
| traceId: generateTraceId(), | ||
| sampleRand: _INTERNAL_safeMathRandom(), | ||
| propagationSpanId: generateSpanId(), | ||
| }; | ||
| // - Set a fresh propagation context so each request gets a unique traceId. | ||
| // When there are incoming trace headers, propagation.extract() below sets a remote | ||
| // span on the OTel context which takes precedence in getTraceContextForScope(). | ||
| // - We can write directly to the current scope here because it is forked implicitly via | ||
| // `context.with` in `withIsolationScope` (See `SentryContextManager`). | ||
| // - explicitly making a deep copy to avoid mutation of original PC on the other scope | ||
| getCurrentScope().setPropagationContext({ ...newPropagationContext }); | ||
| isolationScope.setPropagationContext({ ...newPropagationContext }); | ||
| const ctx = propagation | ||
| .extract(context.active(), normalizedRequest.headers) | ||
| .setValue(HTTP_SERVER_INSTRUMENTED_KEY, true); | ||
| return context.with(ctx, () => { | ||
| // This is used (optionally) by the httpServerSpansIntegration to attach _startSpanCallback to the request object | ||
| client.emit('httpServerRequest', request, response, normalizedRequest); | ||
| const callback = (request )._startSpanCallback?.deref(); | ||
| if (callback) { | ||
| return callback(() => target.apply(thisArg, args)); | ||
| } | ||
| return target.apply(thisArg, args); | ||
| }); | ||
| }); | ||
| }, | ||
| }); | ||
| wrappedEmitFns.add(newEmit); | ||
| server.emit = newEmit; | ||
| } | ||
| /** | ||
| * Starts a session and tracks it in the context of a given isolation scope. | ||
| * When the passed response is finished, the session is put into a task and is | ||
| * aggregated with other sessions that may happen in a certain time window | ||
| * (sessionFlushingDelayMs). | ||
| * | ||
| * The sessions are always aggregated by the client that is on the current scope | ||
| * at the time of ending the response (if there is one). | ||
| */ | ||
| // Exported for unit tests | ||
| function recordRequestSession( | ||
| client, | ||
| { | ||
| requestIsolationScope, | ||
| response, | ||
| sessionFlushingDelayMS, | ||
| } | ||
| , | ||
| ) { | ||
| requestIsolationScope.setSDKProcessingMetadata({ | ||
| requestSession: { status: 'ok' }, | ||
| }); | ||
| response.once('close', () => { | ||
| const requestSession = requestIsolationScope.getScopeData().sdkProcessingMetadata.requestSession; | ||
| if (client && requestSession) { | ||
| DEBUG_BUILD && debug.log(`Recorded request session with status: ${requestSession.status}`); | ||
| const roundedDate = new Date(); | ||
| roundedDate.setSeconds(0, 0); | ||
| const dateBucketKey = roundedDate.toISOString(); | ||
| const existingClientAggregate = clientToRequestSessionAggregatesMap.get(client); | ||
| const bucket = existingClientAggregate?.[dateBucketKey] || { exited: 0, crashed: 0, errored: 0 }; | ||
| bucket[({ ok: 'exited', crashed: 'crashed', errored: 'errored' } )[requestSession.status]]++; | ||
| if (existingClientAggregate) { | ||
| existingClientAggregate[dateBucketKey] = bucket; | ||
| } else { | ||
| DEBUG_BUILD && debug.log('Opened new request session aggregate.'); | ||
| const newClientAggregate = { [dateBucketKey]: bucket }; | ||
| clientToRequestSessionAggregatesMap.set(client, newClientAggregate); | ||
| const flushPendingClientAggregates = () => { | ||
| clearTimeout(timeout); | ||
| unregisterClientFlushHook(); | ||
| clientToRequestSessionAggregatesMap.delete(client); | ||
| const aggregatePayload = Object.entries(newClientAggregate).map( | ||
| ([timestamp, value]) => ({ | ||
| started: timestamp, | ||
| exited: value.exited, | ||
| errored: value.errored, | ||
| crashed: value.crashed, | ||
| }), | ||
| ); | ||
| client.sendSession({ aggregates: aggregatePayload }); | ||
| }; | ||
| const unregisterClientFlushHook = client.on('flush', () => { | ||
| DEBUG_BUILD && debug.log('Sending request session aggregate due to client flush'); | ||
| flushPendingClientAggregates(); | ||
| }); | ||
| const timeout = setTimeout(() => { | ||
| DEBUG_BUILD && debug.log('Sending request session aggregate due to flushing schedule'); | ||
| flushPendingClientAggregates(); | ||
| }, sessionFlushingDelayMS).unref(); | ||
| } | ||
| } | ||
| }); | ||
| } | ||
| export { addStartSpanCallback, httpServerIntegration, recordRequestSession }; | ||
| export { addStartSpanCallback, httpServerIntegration }; | ||
| //# sourceMappingURL=httpServerIntegration.js.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"httpServerIntegration.js","sources":["../../../../src/integrations/http/httpServerIntegration.ts"],"sourcesContent":["import type { ChannelListener } from 'node:diagnostics_channel';\nimport { subscribe } from 'node:diagnostics_channel';\nimport type { EventEmitter } from 'node:events';\nimport type { IncomingMessage, RequestOptions, Server, ServerResponse } from 'node:http';\nimport type { Socket } from 'node:net';\nimport { context, createContextKey, propagation } from '@opentelemetry/api';\nimport type { AggregationCounts, Client, HttpIncomingMessage, Integration, IntegrationFn, Scope } from '@sentry/core';\nimport {\n _INTERNAL_safeMathRandom,\n addNonEnumerableProperty,\n debug,\n generateSpanId,\n generateTraceId,\n getClient,\n getCurrentScope,\n getIsolationScope,\n httpRequestToRequestData,\n stripUrlQueryAndFragment,\n withIsolationScope,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport type { NodeClient } from '../../sdk/client';\nimport { patchRequestToCaptureBody } from '../../utils/captureRequestBody';\n\ntype ServerEmit = typeof Server.prototype.emit;\n\n// Inlining this type to not depend on newer TS types\ninterface WeakRefImpl<T> {\n deref(): T | undefined;\n}\n\ntype StartSpanCallback = (next: () => boolean) => boolean;\ntype RequestWithOptionalStartSpanCallback = HttpIncomingMessage & {\n _startSpanCallback?: WeakRefImpl<StartSpanCallback>;\n};\n\nconst HTTP_SERVER_INSTRUMENTED_KEY = createContextKey('sentry_http_server_instrumented');\nconst INTEGRATION_NAME = 'Http.Server';\n\nconst clientToRequestSessionAggregatesMap = new Map<\n Client,\n { [timestampRoundedToSeconds: string]: { exited: number; crashed: number; errored: number } }\n>();\n\n// We keep track of emit functions we wrapped, to avoid double wrapping\n// We do this instead of putting a non-enumerable property on the function, because\n// sometimes the property seems to be migrated to forks of the emit function, which we do not want to happen\n// This was the case in the nestjs-distributed-tracing E2E test\nconst wrappedEmitFns = new WeakSet<ServerEmit>();\n\nexport interface HttpServerIntegrationOptions {\n /**\n * Whether the integration should create [Sessions](https://docs.sentry.io/product/releases/health/#sessions) for incoming requests to track the health and crash-free rate of your releases in Sentry.\n * Read more about Release Health: https://docs.sentry.io/product/releases/health/\n *\n * Defaults to `true`.\n */\n sessions?: boolean;\n\n /**\n * Number of milliseconds until sessions tracked with `trackIncomingRequestsAsSessions` will be flushed as a session aggregate.\n *\n * Defaults to `60000` (60s).\n */\n sessionFlushingDelayMS?: number;\n\n /**\n * Do not capture the request body for incoming HTTP requests to URLs where the given callback returns `true`.\n * This can be useful for long running requests where the body is not needed and we want to avoid capturing it.\n *\n * @param url Contains the entire URL, including query string (if any), protocol, host, etc. of the incoming request.\n * @param request Contains the {@type RequestOptions} object used to make the incoming request.\n */\n ignoreRequestBody?: (url: string, request: RequestOptions) => boolean;\n\n /**\n * Controls the maximum size of incoming HTTP request bodies attached to events.\n *\n * Available options:\n * - 'none': No request bodies will be attached\n * - 'small': Request bodies up to 1,000 bytes will be attached\n * - 'medium': Request bodies up to 10,000 bytes will be attached (default)\n * - 'always': Request bodies will always be attached\n *\n * Note that even with 'always' setting, bodies exceeding 1MB will never be attached\n * for performance and security reasons.\n *\n * @default 'medium'\n */\n maxRequestBodySize?: 'none' | 'small' | 'medium' | 'always';\n}\n\n/**\n * Add a callback to the request object that will be called when the request is started.\n * The callback will receive the next function to continue processing the request.\n */\nexport function addStartSpanCallback(request: RequestWithOptionalStartSpanCallback, callback: StartSpanCallback): void {\n addNonEnumerableProperty(request, '_startSpanCallback', new WeakRef(callback));\n}\n\nconst _httpServerIntegration = ((options: HttpServerIntegrationOptions = {}) => {\n const _options = {\n sessions: options.sessions ?? true,\n sessionFlushingDelayMS: options.sessionFlushingDelayMS ?? 60_000,\n maxRequestBodySize: options.maxRequestBodySize ?? 'medium',\n ignoreRequestBody: options.ignoreRequestBody,\n };\n\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n const onHttpServerRequestStart = ((_data: unknown) => {\n const data = _data as { server: Server };\n\n instrumentServer(data.server, _options);\n }) satisfies ChannelListener;\n\n subscribe('http.server.request.start', onHttpServerRequestStart);\n },\n afterAllSetup(client) {\n if (DEBUG_BUILD && client.getIntegrationByName('Http')) {\n debug.warn(\n 'It seems that you have manually added `httpServerIntegration` while `httpIntegration` is also present. Make sure to remove `httpServerIntegration` when adding `httpIntegration`.',\n );\n }\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * This integration handles request isolation, trace continuation and other core Sentry functionality around incoming http requests\n * handled via the node `http` module.\n *\n * This version uses OpenTelemetry for context propagation and span management.\n *\n * @see {@link ../../light/integrations/httpServerIntegration.ts} for the lightweight version without OpenTelemetry\n */\nexport const httpServerIntegration = _httpServerIntegration as (\n options?: HttpServerIntegrationOptions,\n) => Integration & {\n name: 'HttpServer';\n setupOnce: () => void;\n};\n\n/**\n * Instrument a server to capture incoming requests.\n *\n */\nfunction instrumentServer(\n server: Server,\n {\n ignoreRequestBody,\n maxRequestBodySize,\n sessions,\n sessionFlushingDelayMS,\n }: {\n ignoreRequestBody?: (url: string, request: IncomingMessage) => boolean;\n maxRequestBodySize: 'small' | 'medium' | 'always' | 'none';\n sessions: boolean;\n sessionFlushingDelayMS: number;\n },\n): void {\n // eslint-disable-next-line @typescript-eslint/unbound-method\n const originalEmit: ServerEmit = server.emit;\n\n if (wrappedEmitFns.has(originalEmit)) {\n return;\n }\n\n const newEmit = new Proxy(originalEmit, {\n apply(target, thisArg, args: [event: string, ...args: unknown[]]) {\n // Only traces request events\n if (args[0] !== 'request') {\n return target.apply(thisArg, args);\n }\n\n const client = getClient<NodeClient>();\n\n // Make sure we do not double execute our wrapper code, for edge cases...\n // Without this check, if we double-wrap emit, for whatever reason, you'd get two http.server spans (one the children of the other)\n if (context.active().getValue(HTTP_SERVER_INSTRUMENTED_KEY) || !client) {\n return target.apply(thisArg, args);\n }\n\n DEBUG_BUILD && debug.log(INTEGRATION_NAME, 'Handling incoming request');\n\n const isolationScope = getIsolationScope().clone();\n const request = args[1] as IncomingMessage;\n const response = args[2] as ServerResponse & { socket: Socket };\n\n const normalizedRequest = httpRequestToRequestData(request);\n\n // request.ip is non-standard but some frameworks set this\n const ipAddress = (request as { ip?: string }).ip || request.socket?.remoteAddress;\n\n const url = request.url || '/';\n if (maxRequestBodySize !== 'none' && !ignoreRequestBody?.(url, request)) {\n patchRequestToCaptureBody(request, isolationScope, maxRequestBodySize, INTEGRATION_NAME);\n }\n\n // Update the isolation scope, isolate this request\n isolationScope.setSDKProcessingMetadata({ normalizedRequest, ipAddress });\n\n // attempt to update the scope's `transactionName` based on the request URL\n // Ideally, framework instrumentations coming after the HttpInstrumentation\n // update the transactionName once we get a parameterized route.\n const httpMethod = (request.method || 'GET').toUpperCase();\n const httpTargetWithoutQueryFragment = stripUrlQueryAndFragment(url);\n\n const bestEffortTransactionName = `${httpMethod} ${httpTargetWithoutQueryFragment}`;\n\n isolationScope.setTransactionName(bestEffortTransactionName);\n\n if (sessions && client) {\n recordRequestSession(client, {\n requestIsolationScope: isolationScope,\n response,\n sessionFlushingDelayMS: sessionFlushingDelayMS ?? 60_000,\n });\n }\n\n return withIsolationScope(isolationScope, () => {\n const newPropagationContext = {\n traceId: generateTraceId(),\n sampleRand: _INTERNAL_safeMathRandom(),\n propagationSpanId: generateSpanId(),\n };\n // - Set a fresh propagation context so each request gets a unique traceId.\n // When there are incoming trace headers, propagation.extract() below sets a remote\n // span on the OTel context which takes precedence in getTraceContextForScope().\n // - We can write directly to the current scope here because it is forked implicitly via\n // `context.with` in `withIsolationScope` (See `SentryContextManager`).\n // - explicitly making a deep copy to avoid mutation of original PC on the other scope\n getCurrentScope().setPropagationContext({ ...newPropagationContext });\n isolationScope.setPropagationContext({ ...newPropagationContext });\n\n const ctx = propagation\n .extract(context.active(), normalizedRequest.headers)\n .setValue(HTTP_SERVER_INSTRUMENTED_KEY, true);\n\n return context.with(ctx, () => {\n // This is used (optionally) by the httpServerSpansIntegration to attach _startSpanCallback to the request object\n client.emit('httpServerRequest', request, response, normalizedRequest);\n\n const callback = (request as RequestWithOptionalStartSpanCallback)._startSpanCallback?.deref();\n if (callback) {\n return callback(() => target.apply(thisArg, args));\n }\n return target.apply(thisArg, args);\n });\n });\n },\n });\n\n wrappedEmitFns.add(newEmit);\n server.emit = newEmit;\n}\n\n/**\n * Starts a session and tracks it in the context of a given isolation scope.\n * When the passed response is finished, the session is put into a task and is\n * aggregated with other sessions that may happen in a certain time window\n * (sessionFlushingDelayMs).\n *\n * The sessions are always aggregated by the client that is on the current scope\n * at the time of ending the response (if there is one).\n */\n// Exported for unit tests\nexport function recordRequestSession(\n client: Client,\n {\n requestIsolationScope,\n response,\n sessionFlushingDelayMS,\n }: {\n requestIsolationScope: Scope;\n response: EventEmitter;\n sessionFlushingDelayMS?: number;\n },\n): void {\n requestIsolationScope.setSDKProcessingMetadata({\n requestSession: { status: 'ok' },\n });\n response.once('close', () => {\n const requestSession = requestIsolationScope.getScopeData().sdkProcessingMetadata.requestSession;\n\n if (client && requestSession) {\n DEBUG_BUILD && debug.log(`Recorded request session with status: ${requestSession.status}`);\n\n const roundedDate = new Date();\n roundedDate.setSeconds(0, 0);\n const dateBucketKey = roundedDate.toISOString();\n\n const existingClientAggregate = clientToRequestSessionAggregatesMap.get(client);\n const bucket = existingClientAggregate?.[dateBucketKey] || { exited: 0, crashed: 0, errored: 0 };\n bucket[({ ok: 'exited', crashed: 'crashed', errored: 'errored' } as const)[requestSession.status]]++;\n\n if (existingClientAggregate) {\n existingClientAggregate[dateBucketKey] = bucket;\n } else {\n DEBUG_BUILD && debug.log('Opened new request session aggregate.');\n const newClientAggregate = { [dateBucketKey]: bucket };\n clientToRequestSessionAggregatesMap.set(client, newClientAggregate);\n\n const flushPendingClientAggregates = (): void => {\n clearTimeout(timeout);\n unregisterClientFlushHook();\n clientToRequestSessionAggregatesMap.delete(client);\n\n const aggregatePayload: AggregationCounts[] = Object.entries(newClientAggregate).map(\n ([timestamp, value]) => ({\n started: timestamp,\n exited: value.exited,\n errored: value.errored,\n crashed: value.crashed,\n }),\n );\n client.sendSession({ aggregates: aggregatePayload });\n };\n\n const unregisterClientFlushHook = client.on('flush', () => {\n DEBUG_BUILD && debug.log('Sending request session aggregate due to client flush');\n flushPendingClientAggregates();\n });\n const timeout = setTimeout(() => {\n DEBUG_BUILD && debug.log('Sending request session aggregate due to flushing schedule');\n flushPendingClientAggregates();\n }, sessionFlushingDelayMS).unref();\n }\n }\n });\n}\n"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"httpServerIntegration.js","sources":["../../../../src/integrations/http/httpServerIntegration.ts"],"sourcesContent":["import { subscribe } from 'node:diagnostics_channel';\nimport type { RequestOptions } from 'node:http';\nimport { context, createContextKey, propagation } from '@opentelemetry/api';\nimport type { HttpIncomingMessage, HttpServerResponse, Integration, IntegrationFn } from '@sentry/core';\nimport {\n addNonEnumerableProperty,\n debug,\n getClient,\n getHttpServerSubscriptions,\n HTTP_ON_SERVER_REQUEST,\n recordRequestSession,\n} from '@sentry/core';\nimport type { RequestEventData } from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\n\n// Re-export so existing test imports continue to work; the implementation now lives in core.\nexport { recordRequestSession };\n\nconst HTTP_SERVER_INSTRUMENTED_KEY = createContextKey('sentry_http_server_instrumented');\nconst INTEGRATION_NAME = 'Http.Server';\n\n// Inlining this type to not depend on newer TS types\ninterface WeakRefImpl<T> {\n deref(): T | undefined;\n}\n\ntype StartSpanCallback = (next: () => boolean) => boolean;\ntype RequestWithOptionalStartSpanCallback = HttpIncomingMessage & {\n _startSpanCallback?: WeakRefImpl<StartSpanCallback>;\n};\n\nexport interface HttpServerIntegrationOptions {\n /**\n * Whether the integration should create [Sessions](https://docs.sentry.io/product/releases/health/#sessions) for incoming requests to track the health and crash-free rate of your releases in Sentry.\n * Read more about Release Health: https://docs.sentry.io/product/releases/health/\n *\n * Defaults to `true`.\n */\n sessions?: boolean;\n\n /**\n * Number of milliseconds until sessions tracked with `trackIncomingRequestsAsSessions` will be flushed as a session aggregate.\n *\n * Defaults to `60000` (60s).\n */\n sessionFlushingDelayMS?: number;\n\n /**\n * Do not capture the request body for incoming HTTP requests to URLs where the given callback returns `true`.\n * This can be useful for long running requests where the body is not needed and we want to avoid capturing it.\n *\n * @param url Contains the entire URL, including query string (if any), protocol, host, etc. of the incoming request.\n * @param request Contains the {@type RequestOptions} object used to make the incoming request.\n */\n ignoreRequestBody?: (url: string, request: RequestOptions) => boolean;\n\n /**\n * Controls the maximum size of incoming HTTP request bodies attached to events.\n *\n * Available options:\n * - 'none': No request bodies will be attached\n * - 'small': Request bodies up to 1,000 bytes will be attached\n * - 'medium': Request bodies up to 10,000 bytes will be attached (default)\n * - 'always': Request bodies will always be attached\n *\n * Note that even with 'always' setting, bodies exceeding 1MB will never be attached\n * for performance and security reasons.\n *\n * @default 'medium'\n */\n maxRequestBodySize?: 'none' | 'small' | 'medium' | 'always';\n}\n\n/**\n * Add a callback to the request object that will be called when the request is started.\n * The callback will receive the next function to continue processing the request.\n */\nexport function addStartSpanCallback(request: RequestWithOptionalStartSpanCallback, callback: StartSpanCallback): void {\n addNonEnumerableProperty(request, '_startSpanCallback', new WeakRef(callback));\n}\n\nconst _httpServerIntegration = ((options: HttpServerIntegrationOptions = {}) => {\n const _options = {\n sessions: options.sessions ?? true,\n sessionFlushingDelayMS: options.sessionFlushingDelayMS ?? 60_000,\n maxRequestBodySize: options.maxRequestBodySize ?? 'medium',\n // Server spans are created by `httpServerSpansIntegration` via the\n // `httpServerRequest` client event + `_startSpanCallback`, not by the\n // core subscription helper. Explicitly opt out so the helper does not\n // double-create spans when the client has tracing enabled.\n spans: false as const,\n // Cast: core uses HttpIncomingMessage; node consumers pass\n // RequestOptions-typed callbacks.\n // The two are structurally compatible for the fields the callback reads\n // (url, method, headers).\n ignoreRequestBody: options.ignoreRequestBody as\n | ((url: string, request: HttpIncomingMessage) => boolean)\n | undefined,\n\n /**\n * Hook called by core's `instrumentServer` to wrap the upstream\n * `emit('request')` call.\n *\n * We use it to extract OTel context from request headers and re-enter\n * the OTel context before the framework sees the request, so subsequent\n * spans (eg from `httpServerSpansIntegration`) attach to the right trace.\n */\n wrapServerEmitRequest(\n request: HttpIncomingMessage,\n response: HttpServerResponse,\n normalizedRequest: RequestEventData,\n next: () => void,\n ): void {\n const client = getClient();\n if (!client) return next();\n\n // Guard against double-wrapping: if our wrapper somehow runs inside an\n // already-instrumented context, just continue without re-extracting\n // and re-emitting.\n if (context.active().getValue(HTTP_SERVER_INSTRUMENTED_KEY)) {\n return next();\n }\n\n const ctx = propagation\n .extract(context.active(), normalizedRequest.headers)\n .setValue(HTTP_SERVER_INSTRUMENTED_KEY, true);\n\n context.with(ctx, () => {\n // httpServerSpansIntegration listens for this and may attach\n // `_startSpanCallback` to the request to wrap span creation around\n // the emit.\n client.emit('httpServerRequest', request, response, normalizedRequest);\n\n const callback = (request as RequestWithOptionalStartSpanCallback)._startSpanCallback?.deref();\n if (callback) {\n callback(() => {\n next();\n return true;\n });\n } else {\n next();\n }\n });\n },\n };\n\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n const { [HTTP_ON_SERVER_REQUEST]: onHttpServerRequestStart } = getHttpServerSubscriptions(_options);\n subscribe(HTTP_ON_SERVER_REQUEST, onHttpServerRequestStart);\n },\n afterAllSetup(client) {\n if (DEBUG_BUILD && client.getIntegrationByName('Http')) {\n debug.warn(\n 'It seems that you have manually added `httpServerIntegration` while `httpIntegration` is also present. Make sure to remove `httpServerIntegration` when adding `httpIntegration`.',\n );\n }\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * This integration handles request isolation, trace continuation and other core Sentry functionality around incoming http requests\n * handled via the node `http` module.\n *\n * This version uses OpenTelemetry for context propagation and span management.\n *\n * @see {@link ../../light/integrations/httpServerIntegration.ts} for the lightweight version without OpenTelemetry\n */\nexport const httpServerIntegration = _httpServerIntegration as (\n options?: HttpServerIntegrationOptions,\n) => Integration & {\n name: 'HttpServer';\n setupOnce: () => void;\n};\n"],"names":[],"mappings":";;;;;;AAkBA,MAAM,4BAAA,GAA+B,iBAAiB,iCAAiC,CAAA;AACvF,MAAM,gBAAA,GAAmB,aAAA;AA0DlB,SAAS,oBAAA,CAAqB,SAA+C,QAAA,EAAmC;AACrH,EAAA,wBAAA,CAAyB,OAAA,EAAS,oBAAA,EAAsB,IAAI,OAAA,CAAQ,QAAQ,CAAC,CAAA;AAC/E;AAEA,MAAM,sBAAA,IAA0B,CAAC,OAAA,GAAwC,EAAC,KAAM;AAC9E,EAAA,MAAM,QAAA,GAAW;AAAA,IACf,QAAA,EAAU,QAAQ,QAAA,IAAY,IAAA;AAAA,IAC9B,sBAAA,EAAwB,QAAQ,sBAAA,IAA0B,GAAA;AAAA,IAC1D,kBAAA,EAAoB,QAAQ,kBAAA,IAAsB,QAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAKlD,KAAA,EAAO,KAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAKP,mBAAmB,OAAA,CAAQ,iBAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAY3B,qBAAA,CACE,OAAA,EACA,QAAA,EACA,iBAAA,EACA,IAAA,EACM;AACN,MAAA,MAAM,SAAS,SAAA,EAAU;AACzB,MAAA,IAAI,CAAC,MAAA,EAAQ,OAAO,IAAA,EAAK;AAKzB,MAAA,IAAI,OAAA,CAAQ,MAAA,EAAO,CAAE,QAAA,CAAS,4BAA4B,CAAA,EAAG;AAC3D,QAAA,OAAO,IAAA,EAAK;AAAA,MACd;AAEA,MAAA,MAAM,GAAA,GAAM,WAAA,CACT,OAAA,CAAQ,OAAA,CAAQ,MAAA,EAAO,EAAG,iBAAA,CAAkB,OAAO,CAAA,CACnD,QAAA,CAAS,4BAAA,EAA8B,IAAI,CAAA;AAE9C,MAAA,OAAA,CAAQ,IAAA,CAAK,KAAK,MAAM;AAItB,QAAA,MAAA,CAAO,IAAA,CAAK,mBAAA,EAAqB,OAAA,EAAS,QAAA,EAAU,iBAAiB,CAAA;AAErE,QAAA,MAAM,QAAA,GAAY,OAAA,CAAiD,kBAAA,EAAoB,KAAA,EAAM;AAC7F,QAAA,IAAI,QAAA,EAAU;AACZ,UAAA,QAAA,CAAS,MAAM;AACb,YAAA,IAAA,EAAK;AACL,YAAA,OAAO,IAAA;AAAA,UACT,CAAC,CAAA;AAAA,QACH,CAAA,MAAO;AACL,UAAA,IAAA,EAAK;AAAA,QACP;AAAA,MACF,CAAC,CAAA;AAAA,IACH;AAAA,GACF;AAEA,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,SAAA,GAAY;AACV,MAAA,MAAM,EAAE,CAAC,sBAAsB,GAAG,wBAAA,EAAyB,GAAI,2BAA2B,QAAQ,CAAA;AAClG,MAAA,SAAA,CAAU,wBAAwB,wBAAwB,CAAA;AAAA,IAC5D,CAAA;AAAA,IACA,cAAc,MAAA,EAAQ;AACpB,MAAA,IAAI,WAAA,IAAe,MAAA,CAAO,oBAAA,CAAqB,MAAM,CAAA,EAAG;AACtD,QAAA,KAAA,CAAM,IAAA;AAAA,UACJ;AAAA,SACF;AAAA,MACF;AAAA,IACF;AAAA,GACF;AACF,CAAA,CAAA;AAUO,MAAM,qBAAA,GAAwB;;;;"} |
@@ -9,6 +9,3 @@ import { errorMonitor } from 'node:events'; | ||
| const INTEGRATION_NAME = 'Http.ServerSpans'; | ||
| // Tree-shakable guard to remove all code related to tracing | ||
| const INTEGRATION_NAME = "Http.ServerSpans"; | ||
| const _httpServerSpansIntegration = ((options = {}) => { | ||
@@ -21,51 +18,36 @@ const ignoreStaticAssets = options.ignoreStaticAssets ?? true; | ||
| [301, 303], | ||
| [305, 399], | ||
| [305, 399] | ||
| ]; | ||
| const { onSpanCreated } = options; | ||
| // eslint-disable-next-line deprecation/deprecation | ||
| const { requestHook, responseHook, applyCustomAttributesOnSpan } = options.instrumentation ?? {}; | ||
| return { | ||
| name: INTEGRATION_NAME, | ||
| setup(client) { | ||
| // If no tracing, we can just skip everything here | ||
| if (typeof __SENTRY_TRACING__ !== 'undefined' && !__SENTRY_TRACING__) { | ||
| if (typeof __SENTRY_TRACING__ !== "undefined" && !__SENTRY_TRACING__) { | ||
| return; | ||
| } | ||
| client.on('httpServerRequest', (_request, _response, normalizedRequest) => { | ||
| // Type-casting this here because we do not want to put the node types into core | ||
| const request = _request ; | ||
| const response = _response ; | ||
| client.on("httpServerRequest", (_request, _response, normalizedRequest) => { | ||
| const request = _request; | ||
| const response = _response; | ||
| const startSpan = (next) => { | ||
| if ( | ||
| shouldIgnoreSpansForIncomingRequest(request, { | ||
| ignoreStaticAssets, | ||
| ignoreIncomingRequests, | ||
| }) | ||
| ) { | ||
| DEBUG_BUILD && debug.log(INTEGRATION_NAME, 'Skipping span creation for incoming request', request.url); | ||
| if (shouldIgnoreSpansForIncomingRequest(request, { | ||
| ignoreStaticAssets, | ||
| ignoreIncomingRequests | ||
| })) { | ||
| DEBUG_BUILD && debug.log(INTEGRATION_NAME, "Skipping span creation for incoming request", request.url); | ||
| return next(); | ||
| } | ||
| const fullUrl = normalizedRequest.url || request.url || '/'; | ||
| const fullUrl = normalizedRequest.url || request.url || "/"; | ||
| const urlObj = parseStringToURLObject(fullUrl); | ||
| const headers = request.headers; | ||
| const userAgent = headers['user-agent']; | ||
| const ips = headers['x-forwarded-for']; | ||
| const userAgent = headers["user-agent"]; | ||
| const ips = headers["x-forwarded-for"]; | ||
| const httpVersion = request.httpVersion; | ||
| const host = headers.host ; | ||
| const hostname = host?.replace(/^(.*)(:[0-9]{1,5})/, '$1') || 'localhost'; | ||
| const host = headers.host; | ||
| const hostname = host?.replace(/^(.*)(:[0-9]{1,5})/, "$1") || "localhost"; | ||
| const tracer = client.tracer; | ||
| const scheme = fullUrl.startsWith('https') ? 'https' : 'http'; | ||
| const method = normalizedRequest.method || request.method?.toUpperCase() || 'GET'; | ||
| const scheme = fullUrl.startsWith("https") ? "https" : "http"; | ||
| const method = normalizedRequest.method || request.method?.toUpperCase() || "GET"; | ||
| const httpTargetWithoutQueryFragment = urlObj ? urlObj.pathname : stripUrlQueryAndFragment(fullUrl); | ||
| const bestEffortTransactionName = `${method} ${httpTargetWithoutQueryFragment}`; | ||
| // We use the plain tracer.startSpan here so we can pass the span kind | ||
| const span = tracer.startSpan(bestEffortTransactionName, { | ||
@@ -75,25 +57,23 @@ kind: SpanKind.SERVER, | ||
| // Sentry specific attributes | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'http.server', | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.otel.http', | ||
| 'sentry.http.prefetch': isKnownPrefetchRequest(request) || undefined, | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_OP]: "http.server", | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.http.otel.http", | ||
| "sentry.http.prefetch": isKnownPrefetchRequest(request) || void 0, | ||
| // Old Semantic Conventions attributes - added for compatibility with what `@opentelemetry/instrumentation-http` output before | ||
| 'http.url': fullUrl, | ||
| 'http.method': normalizedRequest.method, | ||
| 'http.target': urlObj ? `${urlObj.pathname}${urlObj.search}` : httpTargetWithoutQueryFragment, | ||
| 'http.host': host, | ||
| 'net.host.name': hostname, | ||
| 'http.client_ip': typeof ips === 'string' ? ips.split(',')[0] : undefined, | ||
| 'http.user_agent': userAgent, | ||
| 'http.scheme': scheme, | ||
| 'http.flavor': httpVersion, | ||
| 'net.transport': httpVersion?.toUpperCase() === 'QUIC' ? 'ip_udp' : 'ip_tcp', | ||
| "http.url": fullUrl, | ||
| "http.method": normalizedRequest.method, | ||
| "http.target": urlObj ? `${urlObj.pathname}${urlObj.search}` : httpTargetWithoutQueryFragment, | ||
| "http.host": host, | ||
| "net.host.name": hostname, | ||
| "http.client_ip": typeof ips === "string" ? ips.split(",")[0] : void 0, | ||
| "http.user_agent": userAgent, | ||
| "http.scheme": scheme, | ||
| "http.flavor": httpVersion, | ||
| "net.transport": httpVersion?.toUpperCase() === "QUIC" ? "ip_udp" : "ip_tcp", | ||
| ...getRequestContentLengthAttribute(request), | ||
| ...httpHeadersToSpanAttributes( | ||
| normalizedRequest.headers || {}, | ||
| client.getOptions().sendDefaultPii ?? false, | ||
| ), | ||
| }, | ||
| client.getOptions().sendDefaultPii ?? false | ||
| ) | ||
| } | ||
| }); | ||
| // TODO v11: Remove the following three hooks, only onSpanCreated should remain | ||
| requestHook?.(span, request); | ||
@@ -103,14 +83,9 @@ responseHook?.(span, response); | ||
| onSpanCreated?.(span, request, response); | ||
| const rpcMetadata = { | ||
| type: RPCType.HTTP, | ||
| span, | ||
| span | ||
| }; | ||
| return context.with(setRPCMetadata(trace.setSpan(context.active(), span), rpcMetadata), () => { | ||
| context.bind(context.active(), request); | ||
| context.bind(context.active(), response); | ||
| // Ensure we only end the span once | ||
| // E.g. error can be emitted before close is emitted | ||
| let isEnded = false; | ||
@@ -121,5 +96,3 @@ function endSpan(status) { | ||
| } | ||
| isEnded = true; | ||
| const newAttributes = getIncomingRequestAttributesOnResponse(request, response); | ||
@@ -129,11 +102,8 @@ span.setAttributes(newAttributes); | ||
| span.end(); | ||
| // Update the transaction name if the route has changed | ||
| const route = newAttributes['http.route']; | ||
| const route = newAttributes["http.route"]; | ||
| if (route) { | ||
| getIsolationScope().setTransactionName(`${request.method?.toUpperCase() || 'GET'} ${route}`); | ||
| getIsolationScope().setTransactionName(`${request.method?.toUpperCase() || "GET"} ${route}`); | ||
| } | ||
| } | ||
| response.on('close', () => { | ||
| response.on("close", () => { | ||
| endSpan(getSpanStatusFromHttpCode(response.statusCode)); | ||
@@ -143,10 +113,7 @@ }); | ||
| const httpStatus = getSpanStatusFromHttpCode(response.statusCode); | ||
| // Ensure we def. have an error status here | ||
| endSpan(httpStatus.code === SPAN_STATUS_ERROR ? httpStatus : { code: SPAN_STATUS_ERROR }); | ||
| }); | ||
| return next(); | ||
| }); | ||
| }; | ||
| addStartSpanCallback(request, startSpan); | ||
@@ -156,9 +123,8 @@ }); | ||
| processEvent(event) { | ||
| // Drop transaction if it has a status code that should be ignored | ||
| if (event.type === 'transaction') { | ||
| const statusCode = event.contexts?.trace?.data?.['http.response.status_code']; | ||
| if (typeof statusCode === 'number') { | ||
| if (event.type === "transaction") { | ||
| const statusCode = event.contexts?.trace?.data?.["http.response.status_code"]; | ||
| if (typeof statusCode === "number") { | ||
| const shouldDrop = shouldFilterStatusCode(statusCode, ignoreStatusCodes); | ||
| if (shouldDrop) { | ||
| DEBUG_BUILD && debug.log('Dropping transaction due to status code', statusCode); | ||
| DEBUG_BUILD && debug.log("Dropping transaction due to status code", statusCode); | ||
| return null; | ||
@@ -168,3 +134,2 @@ } | ||
| } | ||
| return event; | ||
@@ -176,86 +141,49 @@ }, | ||
| } | ||
| if (client.getIntegrationByName('Http')) { | ||
| if (client.getIntegrationByName("Http")) { | ||
| debug.warn( | ||
| 'It seems that you have manually added `httpServerSpansIntegration` while `httpIntegration` is also present. Make sure to remove `httpIntegration` when adding `httpServerSpansIntegration`.', | ||
| "It seems that you have manually added `httpServerSpansIntegration` while `httpIntegration` is also present. Make sure to remove `httpIntegration` when adding `httpServerSpansIntegration`." | ||
| ); | ||
| } | ||
| if (!client.getIntegrationByName('Http.Server')) { | ||
| if (!client.getIntegrationByName("Http.Server")) { | ||
| debug.error( | ||
| 'It seems that you have manually added `httpServerSpansIntegration` without adding `httpServerIntegration`. This is a requiement for spans to be created - please add the `httpServerIntegration` integration.', | ||
| "It seems that you have manually added `httpServerSpansIntegration` without adding `httpServerIntegration`. This is a requiement for spans to be created - please add the `httpServerIntegration` integration." | ||
| ); | ||
| } | ||
| }, | ||
| } | ||
| }; | ||
| }) ; | ||
| /** | ||
| * This integration emits spans for incoming requests handled via the node `http` module. | ||
| * It requires the `httpServerIntegration` to be present. | ||
| */ | ||
| const httpServerSpansIntegration = _httpServerSpansIntegration | ||
| ; | ||
| }); | ||
| const httpServerSpansIntegration = _httpServerSpansIntegration; | ||
| function isKnownPrefetchRequest(req) { | ||
| // Currently only handles Next.js prefetch requests but may check other frameworks in the future. | ||
| return req.headers['next-router-prefetch'] === '1'; | ||
| return req.headers["next-router-prefetch"] === "1"; | ||
| } | ||
| /** | ||
| * Check if a request is for a common static asset that should be ignored by default. | ||
| * | ||
| * Only exported for tests. | ||
| */ | ||
| function isStaticAssetRequest(urlPath) { | ||
| const path = stripUrlQueryAndFragment(urlPath); | ||
| // Common static file extensions | ||
| if (path.match(/\.(ico|png|jpg|jpeg|gif|svg|css|js|woff|woff2|ttf|eot|webp|avif)$/)) { | ||
| return true; | ||
| } | ||
| // Common metadata files | ||
| if (path.match(/^\/(robots\.txt|sitemap\.xml|manifest\.json|browserconfig\.xml)$/)) { | ||
| return true; | ||
| } | ||
| return false; | ||
| } | ||
| function shouldIgnoreSpansForIncomingRequest( | ||
| request, | ||
| { | ||
| ignoreStaticAssets, | ||
| ignoreIncomingRequests, | ||
| } | ||
| , | ||
| ) { | ||
| function shouldIgnoreSpansForIncomingRequest(request, { | ||
| ignoreStaticAssets, | ||
| ignoreIncomingRequests | ||
| }) { | ||
| if (isTracingSuppressed(context.active())) { | ||
| return true; | ||
| } | ||
| // request.url is the only property that holds any information about the url | ||
| // it only consists of the URL path and query string (if any) | ||
| const urlPath = request.url; | ||
| const method = request.method?.toUpperCase(); | ||
| // We do not capture OPTIONS/HEAD requests as spans | ||
| if (method === 'OPTIONS' || method === 'HEAD' || !urlPath) { | ||
| if (method === "OPTIONS" || method === "HEAD" || !urlPath) { | ||
| return true; | ||
| } | ||
| // Default static asset filtering | ||
| if (ignoreStaticAssets && method === 'GET' && isStaticAssetRequest(urlPath)) { | ||
| if (ignoreStaticAssets && method === "GET" && isStaticAssetRequest(urlPath)) { | ||
| return true; | ||
| } | ||
| if (ignoreIncomingRequests?.(urlPath, request)) { | ||
| return true; | ||
| } | ||
| return false; | ||
| } | ||
| function getRequestContentLengthAttribute(request) { | ||
@@ -266,39 +194,26 @@ const length = getContentLength(request.headers); | ||
| } | ||
| if (isCompressed(request.headers)) { | ||
| return { | ||
| ['http.request_content_length']: length, | ||
| ["http.request_content_length"]: length | ||
| }; | ||
| } else { | ||
| return { | ||
| ['http.request_content_length_uncompressed']: length, | ||
| ["http.request_content_length_uncompressed"]: length | ||
| }; | ||
| } | ||
| } | ||
| function getContentLength(headers) { | ||
| const contentLengthHeader = headers['content-length']; | ||
| if (contentLengthHeader === undefined) return null; | ||
| const contentLengthHeader = headers["content-length"]; | ||
| if (contentLengthHeader === void 0) return null; | ||
| const contentLength = parseInt(contentLengthHeader, 10); | ||
| if (isNaN(contentLength)) return null; | ||
| return contentLength; | ||
| } | ||
| function isCompressed(headers) { | ||
| const encoding = headers['content-encoding']; | ||
| return !!encoding && encoding !== 'identity'; | ||
| const encoding = headers["content-encoding"]; | ||
| return !!encoding && encoding !== "identity"; | ||
| } | ||
| function getIncomingRequestAttributesOnResponse( | ||
| request, | ||
| response, | ||
| ) { | ||
| // take socket from the request, | ||
| // since it may be detached from the response object in keep-alive mode | ||
| function getIncomingRequestAttributesOnResponse(request, response) { | ||
| const { socket } = request; | ||
| const { statusCode, statusMessage } = response; | ||
| const newAttributes = { | ||
@@ -308,37 +223,25 @@ [ATTR_HTTP_RESPONSE_STATUS_CODE]: statusCode, | ||
| [SEMATTRS_HTTP_STATUS_CODE]: statusCode, | ||
| 'http.status_text': statusMessage?.toUpperCase(), | ||
| "http.status_text": statusMessage?.toUpperCase() | ||
| }; | ||
| const rpcMetadata = getRPCMetadata(context.active()); | ||
| if (socket) { | ||
| const { localAddress, localPort, remoteAddress, remotePort } = socket; | ||
| // eslint-disable-next-line deprecation/deprecation | ||
| newAttributes[SEMATTRS_NET_HOST_IP] = localAddress; | ||
| // eslint-disable-next-line deprecation/deprecation | ||
| newAttributes[SEMATTRS_NET_HOST_PORT] = localPort; | ||
| // eslint-disable-next-line deprecation/deprecation | ||
| newAttributes[SEMATTRS_NET_PEER_IP] = remoteAddress; | ||
| newAttributes['net.peer.port'] = remotePort; | ||
| newAttributes["net.peer.port"] = remotePort; | ||
| } | ||
| // eslint-disable-next-line deprecation/deprecation | ||
| newAttributes[SEMATTRS_HTTP_STATUS_CODE] = statusCode; | ||
| newAttributes['http.status_text'] = (statusMessage || '').toUpperCase(); | ||
| if (rpcMetadata?.type === RPCType.HTTP && rpcMetadata.route !== undefined) { | ||
| newAttributes["http.status_text"] = (statusMessage || "").toUpperCase(); | ||
| if (rpcMetadata?.type === RPCType.HTTP && rpcMetadata.route !== void 0) { | ||
| const routeName = rpcMetadata.route; | ||
| newAttributes[ATTR_HTTP_ROUTE] = routeName; | ||
| } | ||
| return newAttributes; | ||
| } | ||
| /** | ||
| * If the given status code should be filtered for the given list of status codes/ranges. | ||
| */ | ||
| function shouldFilterStatusCode(statusCode, dropForStatusCodes) { | ||
| return dropForStatusCodes.some(code => { | ||
| if (typeof code === 'number') { | ||
| return dropForStatusCodes.some((code) => { | ||
| if (typeof code === "number") { | ||
| return code === statusCode; | ||
| } | ||
| const [min, max] = code; | ||
@@ -345,0 +248,0 @@ return statusCode >= min && statusCode <= max; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"httpServerSpansIntegration.js","sources":["../../../../src/integrations/http/httpServerSpansIntegration.ts"],"sourcesContent":["import { errorMonitor } from 'node:events';\nimport type { IncomingHttpHeaders } from 'node:http';\nimport { context, SpanKind, trace } from '@opentelemetry/api';\nimport type { RPCMetadata } from '@opentelemetry/core';\nimport { getRPCMetadata, isTracingSuppressed, RPCType, setRPCMetadata } from '@opentelemetry/core';\nimport {\n ATTR_HTTP_RESPONSE_STATUS_CODE,\n ATTR_HTTP_ROUTE,\n SEMATTRS_HTTP_STATUS_CODE,\n SEMATTRS_NET_HOST_IP,\n SEMATTRS_NET_HOST_PORT,\n SEMATTRS_NET_PEER_IP,\n} from '@opentelemetry/semantic-conventions';\nimport type {\n Event,\n HttpClientRequest,\n HttpIncomingMessage,\n HttpServerResponse,\n Integration,\n IntegrationFn,\n Span,\n SpanAttributes,\n SpanStatus,\n} from '@sentry/core';\nimport {\n debug,\n getIsolationScope,\n getSpanStatusFromHttpCode,\n httpHeadersToSpanAttributes,\n parseStringToURLObject,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_STATUS_ERROR,\n stripUrlQueryAndFragment,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport type { NodeClient } from '../../sdk/client';\nimport { addStartSpanCallback } from './httpServerIntegration';\n\nconst INTEGRATION_NAME = 'Http.ServerSpans';\n\n// Tree-shakable guard to remove all code related to tracing\ndeclare const __SENTRY_TRACING__: boolean;\n\nexport interface HttpServerSpansIntegrationOptions {\n /**\n * Do not capture spans for incoming HTTP requests to URLs where the given callback returns `true`.\n * Spans will be non recording if tracing is disabled.\n *\n * The `urlPath` param consists of the URL path and query string (if any) of the incoming request.\n * For example: `'/users/details?id=123'`\n *\n * The `request` param contains the original {@type IncomingMessage} object of the incoming request.\n * You can use it to filter on additional properties like method, headers, etc.\n */\n ignoreIncomingRequests?: (urlPath: string, request: HttpIncomingMessage) => boolean;\n\n /**\n * Whether to automatically ignore common static asset requests like favicon.ico, robots.txt, etc.\n * This helps reduce noise in your transactions.\n *\n * @default `true`\n */\n ignoreStaticAssets?: boolean;\n\n /**\n * Do not capture spans for incoming HTTP requests with the given status codes.\n * By default, spans with some 3xx and 4xx status codes are ignored (see @default).\n * Expects an array of status codes or a range of status codes, e.g. [[300,399], 404] would ignore 3xx and 404 status codes.\n *\n * @default `[[401, 404], [301, 303], [305, 399]]`\n */\n ignoreStatusCodes?: (number | [number, number])[];\n\n /**\n * @deprecated This is deprecated in favor of `incomingRequestSpanHook`.\n */\n instrumentation?: {\n requestHook?: (span: Span, req: HttpClientRequest | HttpIncomingMessage) => void;\n responseHook?: (span: Span, response: HttpIncomingMessage | HttpServerResponse) => void;\n applyCustomAttributesOnSpan?: (\n span: Span,\n request: HttpClientRequest | HttpIncomingMessage,\n response: HttpIncomingMessage | HttpServerResponse,\n ) => void;\n };\n\n /**\n * A hook that can be used to mutate the span for incoming requests.\n * This is triggered after the span is created, but before it is recorded.\n */\n onSpanCreated?: (span: Span, request: HttpIncomingMessage, response: HttpServerResponse) => void;\n}\n\nconst _httpServerSpansIntegration = ((options: HttpServerSpansIntegrationOptions = {}) => {\n const ignoreStaticAssets = options.ignoreStaticAssets ?? true;\n const ignoreIncomingRequests = options.ignoreIncomingRequests;\n const ignoreStatusCodes = options.ignoreStatusCodes ?? [\n [401, 404],\n // 300 and 304 are possibly valid status codes we do not want to filter\n [301, 303],\n [305, 399],\n ];\n\n const { onSpanCreated } = options;\n // eslint-disable-next-line deprecation/deprecation\n const { requestHook, responseHook, applyCustomAttributesOnSpan } = options.instrumentation ?? {};\n\n return {\n name: INTEGRATION_NAME,\n setup(client: NodeClient) {\n // If no tracing, we can just skip everything here\n if (typeof __SENTRY_TRACING__ !== 'undefined' && !__SENTRY_TRACING__) {\n return;\n }\n\n client.on('httpServerRequest', (_request, _response, normalizedRequest) => {\n // Type-casting this here because we do not want to put the node types into core\n const request = _request as HttpIncomingMessage;\n const response = _response as HttpServerResponse;\n\n const startSpan = (next: () => boolean): boolean => {\n if (\n shouldIgnoreSpansForIncomingRequest(request, {\n ignoreStaticAssets,\n ignoreIncomingRequests,\n })\n ) {\n DEBUG_BUILD && debug.log(INTEGRATION_NAME, 'Skipping span creation for incoming request', request.url);\n return next();\n }\n\n const fullUrl = normalizedRequest.url || request.url || '/';\n const urlObj = parseStringToURLObject(fullUrl);\n\n const headers = request.headers;\n const userAgent = headers['user-agent'];\n const ips = headers['x-forwarded-for'];\n const httpVersion = request.httpVersion;\n const host = headers.host as string | undefined;\n const hostname = host?.replace(/^(.*)(:[0-9]{1,5})/, '$1') || 'localhost';\n\n const tracer = client.tracer;\n const scheme = fullUrl.startsWith('https') ? 'https' : 'http';\n\n const method = normalizedRequest.method || request.method?.toUpperCase() || 'GET';\n const httpTargetWithoutQueryFragment = urlObj ? urlObj.pathname : stripUrlQueryAndFragment(fullUrl);\n const bestEffortTransactionName = `${method} ${httpTargetWithoutQueryFragment}`;\n\n // We use the plain tracer.startSpan here so we can pass the span kind\n const span = tracer.startSpan(bestEffortTransactionName, {\n kind: SpanKind.SERVER,\n attributes: {\n // Sentry specific attributes\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'http.server',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.otel.http',\n 'sentry.http.prefetch': isKnownPrefetchRequest(request) || undefined,\n // Old Semantic Conventions attributes - added for compatibility with what `@opentelemetry/instrumentation-http` output before\n 'http.url': fullUrl,\n 'http.method': normalizedRequest.method,\n 'http.target': urlObj ? `${urlObj.pathname}${urlObj.search}` : httpTargetWithoutQueryFragment,\n 'http.host': host,\n 'net.host.name': hostname,\n 'http.client_ip': typeof ips === 'string' ? ips.split(',')[0] : undefined,\n 'http.user_agent': userAgent,\n 'http.scheme': scheme,\n 'http.flavor': httpVersion,\n 'net.transport': httpVersion?.toUpperCase() === 'QUIC' ? 'ip_udp' : 'ip_tcp',\n ...getRequestContentLengthAttribute(request),\n ...httpHeadersToSpanAttributes(\n normalizedRequest.headers || {},\n client.getOptions().sendDefaultPii ?? false,\n ),\n },\n });\n\n // TODO v11: Remove the following three hooks, only onSpanCreated should remain\n requestHook?.(span, request);\n responseHook?.(span, response);\n applyCustomAttributesOnSpan?.(span, request, response);\n onSpanCreated?.(span, request, response);\n\n const rpcMetadata: RPCMetadata = {\n type: RPCType.HTTP,\n span,\n };\n\n return context.with(setRPCMetadata(trace.setSpan(context.active(), span), rpcMetadata), () => {\n context.bind(context.active(), request);\n context.bind(context.active(), response);\n\n // Ensure we only end the span once\n // E.g. error can be emitted before close is emitted\n let isEnded = false;\n function endSpan(status: SpanStatus): void {\n if (isEnded) {\n return;\n }\n\n isEnded = true;\n\n const newAttributes = getIncomingRequestAttributesOnResponse(request, response);\n span.setAttributes(newAttributes);\n span.setStatus(status);\n span.end();\n\n // Update the transaction name if the route has changed\n const route = newAttributes['http.route'];\n if (route) {\n getIsolationScope().setTransactionName(`${request.method?.toUpperCase() || 'GET'} ${route}`);\n }\n }\n\n response.on('close', () => {\n endSpan(getSpanStatusFromHttpCode(response.statusCode));\n });\n response.on(errorMonitor, () => {\n const httpStatus = getSpanStatusFromHttpCode(response.statusCode);\n // Ensure we def. have an error status here\n endSpan(httpStatus.code === SPAN_STATUS_ERROR ? httpStatus : { code: SPAN_STATUS_ERROR });\n });\n\n return next();\n });\n };\n\n addStartSpanCallback(request, startSpan);\n });\n },\n processEvent(event) {\n // Drop transaction if it has a status code that should be ignored\n if (event.type === 'transaction') {\n const statusCode = event.contexts?.trace?.data?.['http.response.status_code'];\n if (typeof statusCode === 'number') {\n const shouldDrop = shouldFilterStatusCode(statusCode, ignoreStatusCodes);\n if (shouldDrop) {\n DEBUG_BUILD && debug.log('Dropping transaction due to status code', statusCode);\n return null;\n }\n }\n }\n\n return event;\n },\n afterAllSetup(client) {\n if (!DEBUG_BUILD) {\n return;\n }\n\n if (client.getIntegrationByName('Http')) {\n debug.warn(\n 'It seems that you have manually added `httpServerSpansIntegration` while `httpIntegration` is also present. Make sure to remove `httpIntegration` when adding `httpServerSpansIntegration`.',\n );\n }\n\n if (!client.getIntegrationByName('Http.Server')) {\n debug.error(\n 'It seems that you have manually added `httpServerSpansIntegration` without adding `httpServerIntegration`. This is a requiement for spans to be created - please add the `httpServerIntegration` integration.',\n );\n }\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * This integration emits spans for incoming requests handled via the node `http` module.\n * It requires the `httpServerIntegration` to be present.\n */\nexport const httpServerSpansIntegration = _httpServerSpansIntegration as (\n options?: HttpServerSpansIntegrationOptions,\n) => Integration & {\n name: 'HttpServerSpans';\n setup: (client: NodeClient) => void;\n processEvent: (event: Event) => Event | null;\n};\n\nfunction isKnownPrefetchRequest(req: HttpIncomingMessage): boolean {\n // Currently only handles Next.js prefetch requests but may check other frameworks in the future.\n return req.headers['next-router-prefetch'] === '1';\n}\n\n/**\n * Check if a request is for a common static asset that should be ignored by default.\n *\n * Only exported for tests.\n */\nexport function isStaticAssetRequest(urlPath: string): boolean {\n const path = stripUrlQueryAndFragment(urlPath);\n // Common static file extensions\n if (path.match(/\\.(ico|png|jpg|jpeg|gif|svg|css|js|woff|woff2|ttf|eot|webp|avif)$/)) {\n return true;\n }\n\n // Common metadata files\n if (path.match(/^\\/(robots\\.txt|sitemap\\.xml|manifest\\.json|browserconfig\\.xml)$/)) {\n return true;\n }\n\n return false;\n}\n\nfunction shouldIgnoreSpansForIncomingRequest(\n request: HttpIncomingMessage,\n {\n ignoreStaticAssets,\n ignoreIncomingRequests,\n }: {\n ignoreStaticAssets?: boolean;\n ignoreIncomingRequests?: (urlPath: string, request: HttpIncomingMessage) => boolean;\n },\n): boolean {\n if (isTracingSuppressed(context.active())) {\n return true;\n }\n\n // request.url is the only property that holds any information about the url\n // it only consists of the URL path and query string (if any)\n const urlPath = request.url;\n\n const method = request.method?.toUpperCase();\n // We do not capture OPTIONS/HEAD requests as spans\n if (method === 'OPTIONS' || method === 'HEAD' || !urlPath) {\n return true;\n }\n\n // Default static asset filtering\n if (ignoreStaticAssets && method === 'GET' && isStaticAssetRequest(urlPath)) {\n return true;\n }\n\n if (ignoreIncomingRequests?.(urlPath, request)) {\n return true;\n }\n\n return false;\n}\n\nfunction getRequestContentLengthAttribute(request: HttpIncomingMessage): SpanAttributes {\n const length = getContentLength(request.headers);\n if (length == null) {\n return {};\n }\n\n if (isCompressed(request.headers)) {\n return {\n ['http.request_content_length']: length,\n };\n } else {\n return {\n ['http.request_content_length_uncompressed']: length,\n };\n }\n}\n\nfunction getContentLength(headers: IncomingHttpHeaders): number | null {\n const contentLengthHeader = headers['content-length'];\n if (contentLengthHeader === undefined) return null;\n\n const contentLength = parseInt(contentLengthHeader, 10);\n if (isNaN(contentLength)) return null;\n\n return contentLength;\n}\n\nfunction isCompressed(headers: IncomingHttpHeaders): boolean {\n const encoding = headers['content-encoding'];\n\n return !!encoding && encoding !== 'identity';\n}\n\nfunction getIncomingRequestAttributesOnResponse(\n request: HttpIncomingMessage,\n response: HttpServerResponse,\n): SpanAttributes {\n // take socket from the request,\n // since it may be detached from the response object in keep-alive mode\n const { socket } = request;\n const { statusCode, statusMessage } = response;\n\n const newAttributes: SpanAttributes = {\n [ATTR_HTTP_RESPONSE_STATUS_CODE]: statusCode,\n // eslint-disable-next-line deprecation/deprecation\n [SEMATTRS_HTTP_STATUS_CODE]: statusCode,\n 'http.status_text': statusMessage?.toUpperCase(),\n };\n\n const rpcMetadata = getRPCMetadata(context.active());\n if (socket) {\n const { localAddress, localPort, remoteAddress, remotePort } = socket;\n // eslint-disable-next-line deprecation/deprecation\n newAttributes[SEMATTRS_NET_HOST_IP] = localAddress;\n // eslint-disable-next-line deprecation/deprecation\n newAttributes[SEMATTRS_NET_HOST_PORT] = localPort;\n // eslint-disable-next-line deprecation/deprecation\n newAttributes[SEMATTRS_NET_PEER_IP] = remoteAddress;\n newAttributes['net.peer.port'] = remotePort;\n }\n // eslint-disable-next-line deprecation/deprecation\n newAttributes[SEMATTRS_HTTP_STATUS_CODE] = statusCode;\n newAttributes['http.status_text'] = (statusMessage || '').toUpperCase();\n\n if (rpcMetadata?.type === RPCType.HTTP && rpcMetadata.route !== undefined) {\n const routeName = rpcMetadata.route;\n newAttributes[ATTR_HTTP_ROUTE] = routeName;\n }\n\n return newAttributes;\n}\n\n/**\n * If the given status code should be filtered for the given list of status codes/ranges.\n */\nfunction shouldFilterStatusCode(statusCode: number, dropForStatusCodes: (number | [number, number])[]): boolean {\n return dropForStatusCodes.some(code => {\n if (typeof code === 'number') {\n return code === statusCode;\n }\n\n const [min, max] = code;\n return statusCode >= min && statusCode <= max;\n });\n}\n"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"httpServerSpansIntegration.js","sources":["../../../../src/integrations/http/httpServerSpansIntegration.ts"],"sourcesContent":["import { errorMonitor } from 'node:events';\nimport type { IncomingHttpHeaders } from 'node:http';\nimport { context, SpanKind, trace } from '@opentelemetry/api';\nimport type { RPCMetadata } from '@opentelemetry/core';\nimport { getRPCMetadata, isTracingSuppressed, RPCType, setRPCMetadata } from '@opentelemetry/core';\nimport {\n ATTR_HTTP_RESPONSE_STATUS_CODE,\n ATTR_HTTP_ROUTE,\n SEMATTRS_HTTP_STATUS_CODE,\n SEMATTRS_NET_HOST_IP,\n SEMATTRS_NET_HOST_PORT,\n SEMATTRS_NET_PEER_IP,\n} from '@opentelemetry/semantic-conventions';\nimport type {\n Event,\n HttpClientRequest,\n HttpIncomingMessage,\n HttpServerResponse,\n Integration,\n IntegrationFn,\n Span,\n SpanAttributes,\n SpanStatus,\n} from '@sentry/core';\nimport {\n debug,\n getIsolationScope,\n getSpanStatusFromHttpCode,\n httpHeadersToSpanAttributes,\n parseStringToURLObject,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_STATUS_ERROR,\n stripUrlQueryAndFragment,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport type { NodeClient } from '../../sdk/client';\nimport { addStartSpanCallback } from './httpServerIntegration';\n\nconst INTEGRATION_NAME = 'Http.ServerSpans';\n\n// Tree-shakable guard to remove all code related to tracing\ndeclare const __SENTRY_TRACING__: boolean;\n\nexport interface HttpServerSpansIntegrationOptions {\n /**\n * Do not capture spans for incoming HTTP requests to URLs where the given callback returns `true`.\n * Spans will be non recording if tracing is disabled.\n *\n * The `urlPath` param consists of the URL path and query string (if any) of the incoming request.\n * For example: `'/users/details?id=123'`\n *\n * The `request` param contains the original {@type IncomingMessage} object of the incoming request.\n * You can use it to filter on additional properties like method, headers, etc.\n */\n ignoreIncomingRequests?: (urlPath: string, request: HttpIncomingMessage) => boolean;\n\n /**\n * Whether to automatically ignore common static asset requests like favicon.ico, robots.txt, etc.\n * This helps reduce noise in your transactions.\n *\n * @default `true`\n */\n ignoreStaticAssets?: boolean;\n\n /**\n * Do not capture spans for incoming HTTP requests with the given status codes.\n * By default, spans with some 3xx and 4xx status codes are ignored (see @default).\n * Expects an array of status codes or a range of status codes, e.g. [[300,399], 404] would ignore 3xx and 404 status codes.\n *\n * @default `[[401, 404], [301, 303], [305, 399]]`\n */\n ignoreStatusCodes?: (number | [number, number])[];\n\n /**\n * @deprecated This is deprecated in favor of `incomingRequestSpanHook`.\n */\n instrumentation?: {\n requestHook?: (span: Span, req: HttpClientRequest | HttpIncomingMessage) => void;\n responseHook?: (span: Span, response: HttpIncomingMessage | HttpServerResponse) => void;\n applyCustomAttributesOnSpan?: (\n span: Span,\n request: HttpClientRequest | HttpIncomingMessage,\n response: HttpIncomingMessage | HttpServerResponse,\n ) => void;\n };\n\n /**\n * A hook that can be used to mutate the span for incoming requests.\n * This is triggered after the span is created, but before it is recorded.\n */\n onSpanCreated?: (span: Span, request: HttpIncomingMessage, response: HttpServerResponse) => void;\n}\n\nconst _httpServerSpansIntegration = ((options: HttpServerSpansIntegrationOptions = {}) => {\n const ignoreStaticAssets = options.ignoreStaticAssets ?? true;\n const ignoreIncomingRequests = options.ignoreIncomingRequests;\n const ignoreStatusCodes = options.ignoreStatusCodes ?? [\n [401, 404],\n // 300 and 304 are possibly valid status codes we do not want to filter\n [301, 303],\n [305, 399],\n ];\n\n const { onSpanCreated } = options;\n // eslint-disable-next-line deprecation/deprecation\n const { requestHook, responseHook, applyCustomAttributesOnSpan } = options.instrumentation ?? {};\n\n return {\n name: INTEGRATION_NAME,\n setup(client: NodeClient) {\n // If no tracing, we can just skip everything here\n if (typeof __SENTRY_TRACING__ !== 'undefined' && !__SENTRY_TRACING__) {\n return;\n }\n\n client.on('httpServerRequest', (_request, _response, normalizedRequest) => {\n // Type-casting this here because we do not want to put the node types into core\n const request = _request as HttpIncomingMessage;\n const response = _response as HttpServerResponse;\n\n const startSpan = (next: () => boolean): boolean => {\n if (\n shouldIgnoreSpansForIncomingRequest(request, {\n ignoreStaticAssets,\n ignoreIncomingRequests,\n })\n ) {\n DEBUG_BUILD && debug.log(INTEGRATION_NAME, 'Skipping span creation for incoming request', request.url);\n return next();\n }\n\n const fullUrl = normalizedRequest.url || request.url || '/';\n const urlObj = parseStringToURLObject(fullUrl);\n\n const headers = request.headers;\n const userAgent = headers['user-agent'];\n const ips = headers['x-forwarded-for'];\n const httpVersion = request.httpVersion;\n const host = headers.host as string | undefined;\n const hostname = host?.replace(/^(.*)(:[0-9]{1,5})/, '$1') || 'localhost';\n\n const tracer = client.tracer;\n const scheme = fullUrl.startsWith('https') ? 'https' : 'http';\n\n const method = normalizedRequest.method || request.method?.toUpperCase() || 'GET';\n const httpTargetWithoutQueryFragment = urlObj ? urlObj.pathname : stripUrlQueryAndFragment(fullUrl);\n const bestEffortTransactionName = `${method} ${httpTargetWithoutQueryFragment}`;\n\n // We use the plain tracer.startSpan here so we can pass the span kind\n const span = tracer.startSpan(bestEffortTransactionName, {\n kind: SpanKind.SERVER,\n attributes: {\n // Sentry specific attributes\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'http.server',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.otel.http',\n 'sentry.http.prefetch': isKnownPrefetchRequest(request) || undefined,\n // Old Semantic Conventions attributes - added for compatibility with what `@opentelemetry/instrumentation-http` output before\n 'http.url': fullUrl,\n 'http.method': normalizedRequest.method,\n 'http.target': urlObj ? `${urlObj.pathname}${urlObj.search}` : httpTargetWithoutQueryFragment,\n 'http.host': host,\n 'net.host.name': hostname,\n 'http.client_ip': typeof ips === 'string' ? ips.split(',')[0] : undefined,\n 'http.user_agent': userAgent,\n 'http.scheme': scheme,\n 'http.flavor': httpVersion,\n 'net.transport': httpVersion?.toUpperCase() === 'QUIC' ? 'ip_udp' : 'ip_tcp',\n ...getRequestContentLengthAttribute(request),\n ...httpHeadersToSpanAttributes(\n normalizedRequest.headers || {},\n client.getOptions().sendDefaultPii ?? false,\n ),\n },\n });\n\n // TODO v11: Remove the following three hooks, only onSpanCreated should remain\n requestHook?.(span, request);\n responseHook?.(span, response);\n applyCustomAttributesOnSpan?.(span, request, response);\n onSpanCreated?.(span, request, response);\n\n const rpcMetadata: RPCMetadata = {\n type: RPCType.HTTP,\n span,\n };\n\n return context.with(setRPCMetadata(trace.setSpan(context.active(), span), rpcMetadata), () => {\n context.bind(context.active(), request);\n context.bind(context.active(), response);\n\n // Ensure we only end the span once\n // E.g. error can be emitted before close is emitted\n let isEnded = false;\n function endSpan(status: SpanStatus): void {\n if (isEnded) {\n return;\n }\n\n isEnded = true;\n\n const newAttributes = getIncomingRequestAttributesOnResponse(request, response);\n span.setAttributes(newAttributes);\n span.setStatus(status);\n span.end();\n\n // Update the transaction name if the route has changed\n const route = newAttributes['http.route'];\n if (route) {\n getIsolationScope().setTransactionName(`${request.method?.toUpperCase() || 'GET'} ${route}`);\n }\n }\n\n response.on('close', () => {\n endSpan(getSpanStatusFromHttpCode(response.statusCode));\n });\n response.on(errorMonitor, () => {\n const httpStatus = getSpanStatusFromHttpCode(response.statusCode);\n // Ensure we def. have an error status here\n endSpan(httpStatus.code === SPAN_STATUS_ERROR ? httpStatus : { code: SPAN_STATUS_ERROR });\n });\n\n return next();\n });\n };\n\n addStartSpanCallback(request, startSpan);\n });\n },\n processEvent(event) {\n // Drop transaction if it has a status code that should be ignored\n if (event.type === 'transaction') {\n const statusCode = event.contexts?.trace?.data?.['http.response.status_code'];\n if (typeof statusCode === 'number') {\n const shouldDrop = shouldFilterStatusCode(statusCode, ignoreStatusCodes);\n if (shouldDrop) {\n DEBUG_BUILD && debug.log('Dropping transaction due to status code', statusCode);\n return null;\n }\n }\n }\n\n return event;\n },\n afterAllSetup(client) {\n if (!DEBUG_BUILD) {\n return;\n }\n\n if (client.getIntegrationByName('Http')) {\n debug.warn(\n 'It seems that you have manually added `httpServerSpansIntegration` while `httpIntegration` is also present. Make sure to remove `httpIntegration` when adding `httpServerSpansIntegration`.',\n );\n }\n\n if (!client.getIntegrationByName('Http.Server')) {\n debug.error(\n 'It seems that you have manually added `httpServerSpansIntegration` without adding `httpServerIntegration`. This is a requiement for spans to be created - please add the `httpServerIntegration` integration.',\n );\n }\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * This integration emits spans for incoming requests handled via the node `http` module.\n * It requires the `httpServerIntegration` to be present.\n */\nexport const httpServerSpansIntegration = _httpServerSpansIntegration as (\n options?: HttpServerSpansIntegrationOptions,\n) => Integration & {\n name: 'HttpServerSpans';\n setup: (client: NodeClient) => void;\n processEvent: (event: Event) => Event | null;\n};\n\nfunction isKnownPrefetchRequest(req: HttpIncomingMessage): boolean {\n // Currently only handles Next.js prefetch requests but may check other frameworks in the future.\n return req.headers['next-router-prefetch'] === '1';\n}\n\n/**\n * Check if a request is for a common static asset that should be ignored by default.\n *\n * Only exported for tests.\n */\nexport function isStaticAssetRequest(urlPath: string): boolean {\n const path = stripUrlQueryAndFragment(urlPath);\n // Common static file extensions\n if (path.match(/\\.(ico|png|jpg|jpeg|gif|svg|css|js|woff|woff2|ttf|eot|webp|avif)$/)) {\n return true;\n }\n\n // Common metadata files\n if (path.match(/^\\/(robots\\.txt|sitemap\\.xml|manifest\\.json|browserconfig\\.xml)$/)) {\n return true;\n }\n\n return false;\n}\n\nfunction shouldIgnoreSpansForIncomingRequest(\n request: HttpIncomingMessage,\n {\n ignoreStaticAssets,\n ignoreIncomingRequests,\n }: {\n ignoreStaticAssets?: boolean;\n ignoreIncomingRequests?: (urlPath: string, request: HttpIncomingMessage) => boolean;\n },\n): boolean {\n if (isTracingSuppressed(context.active())) {\n return true;\n }\n\n // request.url is the only property that holds any information about the url\n // it only consists of the URL path and query string (if any)\n const urlPath = request.url;\n\n const method = request.method?.toUpperCase();\n // We do not capture OPTIONS/HEAD requests as spans\n if (method === 'OPTIONS' || method === 'HEAD' || !urlPath) {\n return true;\n }\n\n // Default static asset filtering\n if (ignoreStaticAssets && method === 'GET' && isStaticAssetRequest(urlPath)) {\n return true;\n }\n\n if (ignoreIncomingRequests?.(urlPath, request)) {\n return true;\n }\n\n return false;\n}\n\nfunction getRequestContentLengthAttribute(request: HttpIncomingMessage): SpanAttributes {\n const length = getContentLength(request.headers);\n if (length == null) {\n return {};\n }\n\n if (isCompressed(request.headers)) {\n return {\n ['http.request_content_length']: length,\n };\n } else {\n return {\n ['http.request_content_length_uncompressed']: length,\n };\n }\n}\n\nfunction getContentLength(headers: IncomingHttpHeaders): number | null {\n const contentLengthHeader = headers['content-length'];\n if (contentLengthHeader === undefined) return null;\n\n const contentLength = parseInt(contentLengthHeader, 10);\n if (isNaN(contentLength)) return null;\n\n return contentLength;\n}\n\nfunction isCompressed(headers: IncomingHttpHeaders): boolean {\n const encoding = headers['content-encoding'];\n\n return !!encoding && encoding !== 'identity';\n}\n\nfunction getIncomingRequestAttributesOnResponse(\n request: HttpIncomingMessage,\n response: HttpServerResponse,\n): SpanAttributes {\n // take socket from the request,\n // since it may be detached from the response object in keep-alive mode\n const { socket } = request;\n const { statusCode, statusMessage } = response;\n\n const newAttributes: SpanAttributes = {\n [ATTR_HTTP_RESPONSE_STATUS_CODE]: statusCode,\n // eslint-disable-next-line deprecation/deprecation\n [SEMATTRS_HTTP_STATUS_CODE]: statusCode,\n 'http.status_text': statusMessage?.toUpperCase(),\n };\n\n const rpcMetadata = getRPCMetadata(context.active());\n if (socket) {\n const { localAddress, localPort, remoteAddress, remotePort } = socket;\n // eslint-disable-next-line deprecation/deprecation\n newAttributes[SEMATTRS_NET_HOST_IP] = localAddress;\n // eslint-disable-next-line deprecation/deprecation\n newAttributes[SEMATTRS_NET_HOST_PORT] = localPort;\n // eslint-disable-next-line deprecation/deprecation\n newAttributes[SEMATTRS_NET_PEER_IP] = remoteAddress;\n newAttributes['net.peer.port'] = remotePort;\n }\n // eslint-disable-next-line deprecation/deprecation\n newAttributes[SEMATTRS_HTTP_STATUS_CODE] = statusCode;\n newAttributes['http.status_text'] = (statusMessage || '').toUpperCase();\n\n if (rpcMetadata?.type === RPCType.HTTP && rpcMetadata.route !== undefined) {\n const routeName = rpcMetadata.route;\n newAttributes[ATTR_HTTP_ROUTE] = routeName;\n }\n\n return newAttributes;\n}\n\n/**\n * If the given status code should be filtered for the given list of status codes/ranges.\n */\nfunction shouldFilterStatusCode(statusCode: number, dropForStatusCodes: (number | [number, number])[]): boolean {\n return dropForStatusCodes.some(code => {\n if (typeof code === 'number') {\n return code === statusCode;\n }\n\n const [min, max] = code;\n return statusCode >= min && statusCode <= max;\n });\n}\n"],"names":[],"mappings":"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"} |
@@ -7,15 +7,9 @@ import { defineIntegration } from '@sentry/core'; | ||
| const INTEGRATION_NAME = 'Http'; | ||
| const INTEGRATION_NAME = "Http"; | ||
| const instrumentSentryHttp = generateInstrumentOnce( | ||
| `${INTEGRATION_NAME}.sentry`, | ||
| options => { | ||
| (options) => { | ||
| return new SentryHttpInstrumentation(options); | ||
| }, | ||
| } | ||
| ); | ||
| /** | ||
| * The http integration instruments Node's internal http and https modules. | ||
| * It creates breadcrumbs for outgoing HTTP requests which will be attached to the currently active span. | ||
| */ | ||
| const httpIntegration = defineIntegration((options = {}) => { | ||
@@ -26,26 +20,19 @@ const serverOptions = { | ||
| ignoreRequestBody: options.ignoreIncomingRequestBody, | ||
| maxRequestBodySize: options.maxIncomingRequestBodySize, | ||
| maxRequestBodySize: options.maxIncomingRequestBodySize | ||
| }; | ||
| const serverSpansOptions = { | ||
| ignoreIncomingRequests: options.ignoreIncomingRequests, | ||
| ignoreStaticAssets: options.ignoreStaticAssets, | ||
| ignoreStatusCodes: options.dropSpansForIncomingRequestStatusCodes, | ||
| ignoreStatusCodes: options.dropSpansForIncomingRequestStatusCodes | ||
| }; | ||
| const httpInstrumentationOptions = { | ||
| breadcrumbs: options.breadcrumbs, | ||
| propagateTraceInOutgoingRequests: options.tracePropagation ?? true, | ||
| ignoreOutgoingRequests: options.ignoreOutgoingRequests, | ||
| ignoreOutgoingRequests: options.ignoreOutgoingRequests | ||
| }; | ||
| const server = httpServerIntegration(serverOptions); | ||
| const serverSpans = httpServerSpansIntegration(serverSpansOptions); | ||
| // In node-core, for now we disable incoming requests spans by default | ||
| // we may revisit this in a future release | ||
| const spans = options.spans ?? false; | ||
| const disableIncomingRequestSpans = options.disableIncomingRequestSpans ?? false; | ||
| const enabledServerSpans = spans && !disableIncomingRequestSpans; | ||
| return { | ||
@@ -60,11 +47,7 @@ name: INTEGRATION_NAME, | ||
| server.setupOnce(); | ||
| instrumentSentryHttp(httpInstrumentationOptions); | ||
| }, | ||
| processEvent(event) { | ||
| // Note: We always run this, even if spans are disabled | ||
| // The reason being that e.g. the remix integration disables span creation here but still wants to use the ignore status codes option | ||
| return serverSpans.processEvent(event); | ||
| }, | ||
| } | ||
| }; | ||
@@ -71,0 +54,0 @@ }); |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.js","sources":["../../../../src/integrations/http/index.ts"],"sourcesContent":["import type { RequestOptions } from 'node:http';\nimport type { HttpIncomingMessage } from '@sentry/core';\nimport { defineIntegration } from '@sentry/core';\nimport { generateInstrumentOnce } from '../../otel/instrument';\nimport type { NodeClient } from '../../sdk/client';\nimport type { HttpServerIntegrationOptions } from './httpServerIntegration';\nimport { httpServerIntegration } from './httpServerIntegration';\nimport type { HttpServerSpansIntegrationOptions } from './httpServerSpansIntegration';\nimport { httpServerSpansIntegration } from './httpServerSpansIntegration';\nimport type { SentryHttpInstrumentationOptions } from './SentryHttpInstrumentation';\nimport { SentryHttpInstrumentation } from './SentryHttpInstrumentation';\n\nconst INTEGRATION_NAME = 'Http';\n\ninterface HttpOptions {\n /**\n * Whether breadcrumbs should be recorded for outgoing requests.\n * Defaults to true\n */\n breadcrumbs?: boolean;\n\n /**\n * Whether to create spans for requests or not.\n * As of now, creates spans for incoming requests, but not outgoing requests.\n *\n * @default `true`\n */\n spans?: boolean;\n\n /**\n * Whether the integration should create [Sessions](https://docs.sentry.io/product/releases/health/#sessions) for incoming requests to track the health and crash-free rate of your releases in Sentry.\n * Read more about Release Health: https://docs.sentry.io/product/releases/health/\n *\n * Defaults to `true`.\n */\n trackIncomingRequestsAsSessions?: boolean;\n\n /**\n * Number of milliseconds until sessions tracked with `trackIncomingRequestsAsSessions` will be flushed as a session aggregate.\n *\n * Defaults to `60000` (60s).\n */\n sessionFlushingDelayMS?: number;\n\n /**\n * Whether to inject trace propagation headers (sentry-trace, baggage, traceparent) into outgoing HTTP requests.\n *\n * When set to `false`, Sentry will not inject any trace propagation headers, but will still create breadcrumbs\n * (if `breadcrumbs` is enabled).\n *\n * @default `true`\n */\n tracePropagation?: boolean;\n\n /**\n * Do not capture spans or breadcrumbs for outgoing HTTP requests to URLs where the given callback returns `true`.\n * This controls both span & breadcrumb creation - spans will be non recording if tracing is disabled.\n *\n * The `url` param contains the entire URL, including query string (if any), protocol, host, etc. of the outgoing request.\n * For example: `'https://someService.com/users/details?id=123'`\n *\n * The `request` param contains the original {@type RequestOptions} object used to make the outgoing request.\n * You can use it to filter on additional properties like method, headers, etc.\n */\n ignoreOutgoingRequests?: (url: string, request: RequestOptions) => boolean;\n\n /**\n * Do not capture spans for incoming HTTP requests to URLs where the given callback returns `true`.\n * Spans will be non recording if tracing is disabled.\n *\n * The `urlPath` param consists of the URL path and query string (if any) of the incoming request.\n * For example: `'/users/details?id=123'`\n *\n * The `request` param contains the original {@type IncomingMessage} object of the incoming request.\n * You can use it to filter on additional properties like method, headers, etc.\n */\n ignoreIncomingRequests?: (urlPath: string, request: HttpIncomingMessage) => boolean;\n\n /**\n * Do not capture spans for incoming HTTP requests with the given status codes.\n * By default, spans with some 3xx and 4xx status codes are ignored (see @default).\n * Expects an array of status codes or a range of status codes, e.g. [[300,399], 404] would ignore 3xx and 404 status codes.\n *\n * @default `[[401, 404], [301, 303], [305, 399]]`\n */\n dropSpansForIncomingRequestStatusCodes?: (number | [number, number])[];\n\n /**\n * Do not capture the request body for incoming HTTP requests to URLs where the given callback returns `true`.\n * This can be useful for long running requests where the body is not needed and we want to avoid capturing it.\n *\n * @param url Contains the entire URL, including query string (if any), protocol, host, etc. of the incoming request.\n * @param request Contains the {@type RequestOptions} object used to make the incoming request.\n */\n ignoreIncomingRequestBody?: (url: string, request: RequestOptions) => boolean;\n\n /**\n * Whether to automatically ignore common static asset requests like favicon.ico, robots.txt, etc.\n * This helps reduce noise in your transactions.\n *\n * @default `true`\n */\n ignoreStaticAssets?: boolean;\n\n /**\n * Controls the maximum size of incoming HTTP request bodies attached to events.\n *\n * Available options:\n * - 'none': No request bodies will be attached\n * - 'small': Request bodies up to 1,000 bytes will be attached\n * - 'medium': Request bodies up to 10,000 bytes will be attached (default)\n * - 'always': Request bodies will always be attached\n *\n * Note that even with 'always' setting, bodies exceeding 1MB will never be attached\n * for performance and security reasons.\n *\n * @default 'medium'\n */\n maxIncomingRequestBodySize?: 'none' | 'small' | 'medium' | 'always';\n\n /**\n * If true, do not generate spans for incoming requests at all.\n * This is used by Remix to avoid generating spans for incoming requests, as it generates its own spans.\n */\n disableIncomingRequestSpans?: boolean;\n}\n\nexport const instrumentSentryHttp = generateInstrumentOnce<SentryHttpInstrumentationOptions>(\n `${INTEGRATION_NAME}.sentry`,\n options => {\n return new SentryHttpInstrumentation(options);\n },\n);\n\n/**\n * The http integration instruments Node's internal http and https modules.\n * It creates breadcrumbs for outgoing HTTP requests which will be attached to the currently active span.\n */\nexport const httpIntegration = defineIntegration((options: HttpOptions = {}) => {\n const serverOptions: HttpServerIntegrationOptions = {\n sessions: options.trackIncomingRequestsAsSessions,\n sessionFlushingDelayMS: options.sessionFlushingDelayMS,\n ignoreRequestBody: options.ignoreIncomingRequestBody,\n maxRequestBodySize: options.maxIncomingRequestBodySize,\n };\n\n const serverSpansOptions: HttpServerSpansIntegrationOptions = {\n ignoreIncomingRequests: options.ignoreIncomingRequests,\n ignoreStaticAssets: options.ignoreStaticAssets,\n ignoreStatusCodes: options.dropSpansForIncomingRequestStatusCodes,\n };\n\n const httpInstrumentationOptions: SentryHttpInstrumentationOptions = {\n breadcrumbs: options.breadcrumbs,\n propagateTraceInOutgoingRequests: options.tracePropagation ?? true,\n ignoreOutgoingRequests: options.ignoreOutgoingRequests,\n };\n\n const server = httpServerIntegration(serverOptions);\n const serverSpans = httpServerSpansIntegration(serverSpansOptions);\n\n // In node-core, for now we disable incoming requests spans by default\n // we may revisit this in a future release\n const spans = options.spans ?? false;\n const disableIncomingRequestSpans = options.disableIncomingRequestSpans ?? false;\n const enabledServerSpans = spans && !disableIncomingRequestSpans;\n\n return {\n name: INTEGRATION_NAME,\n setup(client: NodeClient) {\n if (enabledServerSpans) {\n serverSpans.setup(client);\n }\n },\n setupOnce() {\n server.setupOnce();\n\n instrumentSentryHttp(httpInstrumentationOptions);\n },\n\n processEvent(event) {\n // Note: We always run this, even if spans are disabled\n // The reason being that e.g. the remix integration disables span creation here but still wants to use the ignore status codes option\n return serverSpans.processEvent(event);\n },\n };\n});\n"],"names":[],"mappings":";;;;;;AAYA,MAAM,gBAAA,GAAmB,MAAM;;AAmHxB,MAAM,oBAAA,GAAuB,sBAAsB;AAC1D,EAAE,CAAC,EAAA,gBAAA,CAAA,OAAA,CAAA;AACA,EAAA,OAAA,IAAA;AACA,IAAA,OAAA,IAAA,yBAAA,CAAA,OAAA,CAAA;AACA,EAAA,CAAA;AACA;;AAEA;AACA;AACA;AACA;AACA,MAAA,eAAA,GAAA,iBAAA,CAAA,CAAA,OAAA,GAAA,EAAA,KAAA;AACA,EAAA,MAAA,aAAA,GAAA;AACA,IAAA,QAAA,EAAA,OAAA,CAAA,+BAAA;AACA,IAAA,sBAAA,EAAA,OAAA,CAAA,sBAAA;AACA,IAAA,iBAAA,EAAA,OAAA,CAAA,yBAAA;AACA,IAAA,kBAAA,EAAA,OAAA,CAAA,0BAAA;AACA,GAAA;;AAEA,EAAA,MAAA,kBAAA,GAAA;AACA,IAAA,sBAAA,EAAA,OAAA,CAAA,sBAAA;AACA,IAAA,kBAAA,EAAA,OAAA,CAAA,kBAAA;AACA,IAAA,iBAAA,EAAA,OAAA,CAAA,sCAAA;AACA,GAAA;;AAEA,EAAA,MAAA,0BAAA,GAAA;AACA,IAAA,WAAA,EAAA,OAAA,CAAA,WAAA;AACA,IAAA,gCAAA,EAAA,OAAA,CAAA,gBAAA,IAAA,IAAA;AACA,IAAA,sBAAA,EAAA,OAAA,CAAA,sBAAA;AACA,GAAA;;AAEA,EAAA,MAAA,MAAA,GAAA,qBAAA,CAAA,aAAA,CAAA;AACA,EAAA,MAAA,WAAA,GAAA,0BAAA,CAAA,kBAAA,CAAA;;AAEA;AACA;AACA,EAAA,MAAA,KAAA,GAAA,OAAA,CAAA,KAAA,IAAA,KAAA;AACA,EAAA,MAAA,2BAAA,GAAA,OAAA,CAAA,2BAAA,IAAA,KAAA;AACA,EAAA,MAAA,kBAAA,GAAA,KAAA,IAAA,CAAA,2BAAA;;AAEA,EAAA,OAAA;AACA,IAAA,IAAA,EAAA,gBAAA;AACA,IAAA,KAAA,CAAA,MAAA,EAAA;AACA,MAAA,IAAA,kBAAA,EAAA;AACA,QAAA,WAAA,CAAA,KAAA,CAAA,MAAA,CAAA;AACA,MAAA;AACA,IAAA,CAAA;AACA,IAAA,SAAA,GAAA;AACA,MAAA,MAAA,CAAA,SAAA,EAAA;;AAEA,MAAA,oBAAA,CAAA,0BAAA,CAAA;AACA,IAAA,CAAA;;AAEA,IAAA,YAAA,CAAA,KAAA,EAAA;AACA;AACA;AACA,MAAA,OAAA,WAAA,CAAA,YAAA,CAAA,KAAA,CAAA;AACA,IAAA,CAAA;AACA,GAAA;AACA,CAAA;;;;"} | ||
| {"version":3,"file":"index.js","sources":["../../../../src/integrations/http/index.ts"],"sourcesContent":["import type { RequestOptions } from 'node:http';\nimport type { HttpIncomingMessage } from '@sentry/core';\nimport { defineIntegration } from '@sentry/core';\nimport { generateInstrumentOnce } from '../../otel/instrument';\nimport type { NodeClient } from '../../sdk/client';\nimport type { HttpServerIntegrationOptions } from './httpServerIntegration';\nimport { httpServerIntegration } from './httpServerIntegration';\nimport type { HttpServerSpansIntegrationOptions } from './httpServerSpansIntegration';\nimport { httpServerSpansIntegration } from './httpServerSpansIntegration';\nimport type { SentryHttpInstrumentationOptions } from './SentryHttpInstrumentation';\nimport { SentryHttpInstrumentation } from './SentryHttpInstrumentation';\n\nconst INTEGRATION_NAME = 'Http';\n\ninterface HttpOptions {\n /**\n * Whether breadcrumbs should be recorded for outgoing requests.\n * Defaults to true\n */\n breadcrumbs?: boolean;\n\n /**\n * Whether to create spans for requests or not.\n * As of now, creates spans for incoming requests, but not outgoing requests.\n *\n * @default `true`\n */\n spans?: boolean;\n\n /**\n * Whether the integration should create [Sessions](https://docs.sentry.io/product/releases/health/#sessions) for incoming requests to track the health and crash-free rate of your releases in Sentry.\n * Read more about Release Health: https://docs.sentry.io/product/releases/health/\n *\n * Defaults to `true`.\n */\n trackIncomingRequestsAsSessions?: boolean;\n\n /**\n * Number of milliseconds until sessions tracked with `trackIncomingRequestsAsSessions` will be flushed as a session aggregate.\n *\n * Defaults to `60000` (60s).\n */\n sessionFlushingDelayMS?: number;\n\n /**\n * Whether to inject trace propagation headers (sentry-trace, baggage, traceparent) into outgoing HTTP requests.\n *\n * When set to `false`, Sentry will not inject any trace propagation headers, but will still create breadcrumbs\n * (if `breadcrumbs` is enabled).\n *\n * @default `true`\n */\n tracePropagation?: boolean;\n\n /**\n * Do not capture spans or breadcrumbs for outgoing HTTP requests to URLs where the given callback returns `true`.\n * This controls both span & breadcrumb creation - spans will be non recording if tracing is disabled.\n *\n * The `url` param contains the entire URL, including query string (if any), protocol, host, etc. of the outgoing request.\n * For example: `'https://someService.com/users/details?id=123'`\n *\n * The `request` param contains the original {@type RequestOptions} object used to make the outgoing request.\n * You can use it to filter on additional properties like method, headers, etc.\n */\n ignoreOutgoingRequests?: (url: string, request: RequestOptions) => boolean;\n\n /**\n * Do not capture spans for incoming HTTP requests to URLs where the given callback returns `true`.\n * Spans will be non recording if tracing is disabled.\n *\n * The `urlPath` param consists of the URL path and query string (if any) of the incoming request.\n * For example: `'/users/details?id=123'`\n *\n * The `request` param contains the original {@type IncomingMessage} object of the incoming request.\n * You can use it to filter on additional properties like method, headers, etc.\n */\n ignoreIncomingRequests?: (urlPath: string, request: HttpIncomingMessage) => boolean;\n\n /**\n * Do not capture spans for incoming HTTP requests with the given status codes.\n * By default, spans with some 3xx and 4xx status codes are ignored (see @default).\n * Expects an array of status codes or a range of status codes, e.g. [[300,399], 404] would ignore 3xx and 404 status codes.\n *\n * @default `[[401, 404], [301, 303], [305, 399]]`\n */\n dropSpansForIncomingRequestStatusCodes?: (number | [number, number])[];\n\n /**\n * Do not capture the request body for incoming HTTP requests to URLs where the given callback returns `true`.\n * This can be useful for long running requests where the body is not needed and we want to avoid capturing it.\n *\n * @param url Contains the entire URL, including query string (if any), protocol, host, etc. of the incoming request.\n * @param request Contains the {@type RequestOptions} object used to make the incoming request.\n */\n ignoreIncomingRequestBody?: (url: string, request: RequestOptions) => boolean;\n\n /**\n * Whether to automatically ignore common static asset requests like favicon.ico, robots.txt, etc.\n * This helps reduce noise in your transactions.\n *\n * @default `true`\n */\n ignoreStaticAssets?: boolean;\n\n /**\n * Controls the maximum size of incoming HTTP request bodies attached to events.\n *\n * Available options:\n * - 'none': No request bodies will be attached\n * - 'small': Request bodies up to 1,000 bytes will be attached\n * - 'medium': Request bodies up to 10,000 bytes will be attached (default)\n * - 'always': Request bodies will always be attached\n *\n * Note that even with 'always' setting, bodies exceeding 1MB will never be attached\n * for performance and security reasons.\n *\n * @default 'medium'\n */\n maxIncomingRequestBodySize?: 'none' | 'small' | 'medium' | 'always';\n\n /**\n * If true, do not generate spans for incoming requests at all.\n * This is used by Remix to avoid generating spans for incoming requests, as it generates its own spans.\n */\n disableIncomingRequestSpans?: boolean;\n}\n\nexport const instrumentSentryHttp = generateInstrumentOnce<SentryHttpInstrumentationOptions>(\n `${INTEGRATION_NAME}.sentry`,\n options => {\n return new SentryHttpInstrumentation(options);\n },\n);\n\n/**\n * The http integration instruments Node's internal http and https modules.\n * It creates breadcrumbs for outgoing HTTP requests which will be attached to the currently active span.\n */\nexport const httpIntegration = defineIntegration((options: HttpOptions = {}) => {\n const serverOptions: HttpServerIntegrationOptions = {\n sessions: options.trackIncomingRequestsAsSessions,\n sessionFlushingDelayMS: options.sessionFlushingDelayMS,\n ignoreRequestBody: options.ignoreIncomingRequestBody,\n maxRequestBodySize: options.maxIncomingRequestBodySize,\n };\n\n const serverSpansOptions: HttpServerSpansIntegrationOptions = {\n ignoreIncomingRequests: options.ignoreIncomingRequests,\n ignoreStaticAssets: options.ignoreStaticAssets,\n ignoreStatusCodes: options.dropSpansForIncomingRequestStatusCodes,\n };\n\n const httpInstrumentationOptions: SentryHttpInstrumentationOptions = {\n breadcrumbs: options.breadcrumbs,\n propagateTraceInOutgoingRequests: options.tracePropagation ?? true,\n ignoreOutgoingRequests: options.ignoreOutgoingRequests,\n };\n\n const server = httpServerIntegration(serverOptions);\n const serverSpans = httpServerSpansIntegration(serverSpansOptions);\n\n // In node-core, for now we disable incoming requests spans by default\n // we may revisit this in a future release\n const spans = options.spans ?? false;\n const disableIncomingRequestSpans = options.disableIncomingRequestSpans ?? false;\n const enabledServerSpans = spans && !disableIncomingRequestSpans;\n\n return {\n name: INTEGRATION_NAME,\n setup(client: NodeClient) {\n if (enabledServerSpans) {\n serverSpans.setup(client);\n }\n },\n setupOnce() {\n server.setupOnce();\n\n instrumentSentryHttp(httpInstrumentationOptions);\n },\n\n processEvent(event) {\n // Note: We always run this, even if spans are disabled\n // The reason being that e.g. the remix integration disables span creation here but still wants to use the ignore status codes option\n return serverSpans.processEvent(event);\n },\n };\n});\n"],"names":[],"mappings":";;;;;;AAYA,MAAM,gBAAA,GAAmB,MAAA;AAmHlB,MAAM,oBAAA,GAAuB,sBAAA;AAAA,EAClC,GAAG,gBAAgB,CAAA,OAAA,CAAA;AAAA,EACnB,CAAA,OAAA,KAAW;AACT,IAAA,OAAO,IAAI,0BAA0B,OAAO,CAAA;AAAA,EAC9C;AACF;AAMO,MAAM,eAAA,GAAkB,iBAAA,CAAkB,CAAC,OAAA,GAAuB,EAAC,KAAM;AAC9E,EAAA,MAAM,aAAA,GAA8C;AAAA,IAClD,UAAU,OAAA,CAAQ,+BAAA;AAAA,IAClB,wBAAwB,OAAA,CAAQ,sBAAA;AAAA,IAChC,mBAAmB,OAAA,CAAQ,yBAAA;AAAA,IAC3B,oBAAoB,OAAA,CAAQ;AAAA,GAC9B;AAEA,EAAA,MAAM,kBAAA,GAAwD;AAAA,IAC5D,wBAAwB,OAAA,CAAQ,sBAAA;AAAA,IAChC,oBAAoB,OAAA,CAAQ,kBAAA;AAAA,IAC5B,mBAAmB,OAAA,CAAQ;AAAA,GAC7B;AAEA,EAAA,MAAM,0BAAA,GAA+D;AAAA,IACnE,aAAa,OAAA,CAAQ,WAAA;AAAA,IACrB,gCAAA,EAAkC,QAAQ,gBAAA,IAAoB,IAAA;AAAA,IAC9D,wBAAwB,OAAA,CAAQ;AAAA,GAClC;AAEA,EAAA,MAAM,MAAA,GAAS,sBAAsB,aAAa,CAAA;AAClD,EAAA,MAAM,WAAA,GAAc,2BAA2B,kBAAkB,CAAA;AAIjE,EAAA,MAAM,KAAA,GAAQ,QAAQ,KAAA,IAAS,KAAA;AAC/B,EAAA,MAAM,2BAAA,GAA8B,QAAQ,2BAAA,IAA+B,KAAA;AAC3E,EAAA,MAAM,kBAAA,GAAqB,SAAS,CAAC,2BAAA;AAErC,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,MAAM,MAAA,EAAoB;AACxB,MAAA,IAAI,kBAAA,EAAoB;AACtB,QAAA,WAAA,CAAY,MAAM,MAAM,CAAA;AAAA,MAC1B;AAAA,IACF,CAAA;AAAA,IACA,SAAA,GAAY;AACV,MAAA,MAAA,CAAO,SAAA,EAAU;AAEjB,MAAA,oBAAA,CAAqB,0BAA0B,CAAA;AAAA,IACjD,CAAA;AAAA,IAEA,aAAa,KAAA,EAAO;AAGlB,MAAA,OAAO,WAAA,CAAY,aAAa,KAAK,CAAA;AAAA,IACvC;AAAA,GACF;AACF,CAAC;;;;"} |
@@ -12,30 +12,9 @@ import { subscribe } from 'node:diagnostics_channel'; | ||
| const FULLY_SUPPORTS_HTTP_DIAGNOSTICS_CHANNEL = | ||
| (NODE_VERSION.major === 22 && NODE_VERSION.minor >= 12) || | ||
| (NODE_VERSION.major === 23 && NODE_VERSION.minor >= 2) || | ||
| NODE_VERSION.major >= 24; | ||
| /** | ||
| * This custom HTTP instrumentation handles outgoing HTTP requests. | ||
| * | ||
| * It provides: | ||
| * - Breadcrumbs for all outgoing requests | ||
| * - Trace propagation headers (when enabled) | ||
| * - Span creation for outgoing requests (when createSpansForOutgoingRequests is enabled) | ||
| * | ||
| * Span creation requires Node 22+ and uses diagnostic channels to avoid monkey-patching. | ||
| * By default, this is only enabled in the node SDK, not in node-core or other runtime SDKs. | ||
| * | ||
| * Important note: Contrary to other OTEL instrumentation, this one cannot be unwrapped. | ||
| * | ||
| * This is heavily inspired & adapted from: | ||
| * https://github.com/open-telemetry/opentelemetry-js/blob/f8ab5592ddea5cba0a3b33bf8d74f27872c0367f/experimental/packages/opentelemetry-instrumentation-http/src/http.ts | ||
| */ | ||
| const FULLY_SUPPORTS_HTTP_DIAGNOSTICS_CHANNEL = NODE_VERSION.major === 22 && NODE_VERSION.minor >= 12 || NODE_VERSION.major === 23 && NODE_VERSION.minor >= 2 || NODE_VERSION.major >= 24; | ||
| class SentryHttpInstrumentation extends InstrumentationBase { | ||
| constructor(config = {}) { | ||
| constructor(config = {}) { | ||
| super(INSTRUMENTATION_NAME, SDK_VERSION, config); | ||
| } | ||
| /** @inheritdoc */ | ||
| init() { | ||
| init() { | ||
| const { outgoingRequestApplyCustomAttributes: applyCustomAttributesOnSpan, ...options } = this.getConfig(); | ||
@@ -48,31 +27,20 @@ const patchOptions = { | ||
| ignoreOutgoingRequests(url, request) { | ||
| return ( | ||
| isTracingSuppressed(context.active()) || | ||
| !!options.ignoreOutgoingRequests?.(url, getRequestOptions(request )) | ||
| ); | ||
| return isTracingSuppressed(context.active()) || !!options.ignoreOutgoingRequests?.(url, getRequestOptions(request)); | ||
| }, | ||
| outgoingRequestHook(span, request) { | ||
| options.outgoingRequestHook?.(span, request); | ||
| // We monkey-patch `req.once('response'), which is used to trigger | ||
| // the callback of the request, so that it runs in the active context | ||
| // eslint-disable-next-line @typescript-eslint/unbound-method, deprecation/deprecation | ||
| const originalOnce = request.once; | ||
| const newOnce = new Proxy(originalOnce, { | ||
| apply(target, thisArg, args) { | ||
| const [event] = args; | ||
| if (event !== 'response') { | ||
| if (event !== "response") { | ||
| return target.apply(thisArg, args); | ||
| } | ||
| const parentContext = context.active(); | ||
| const requestContext = trace.setSpan(parentContext, span); | ||
| return context.with(requestContext, () => { | ||
| return target.apply(thisArg, args); | ||
| }); | ||
| }, | ||
| } | ||
| }); | ||
| // eslint-disable-next-line deprecation/deprecation | ||
| request.once = newOnce; | ||
@@ -87,37 +55,18 @@ }, | ||
| http, | ||
| https, | ||
| https | ||
| }; | ||
| // only generate the subscriber function if we'll actually use it | ||
| const { [HTTP_ON_CLIENT_REQUEST]: onHttpClientRequestCreated } = FULLY_SUPPORTS_HTTP_DIAGNOSTICS_CHANNEL | ||
| ? getHttpClientSubscriptions(patchOptions) | ||
| : {}; | ||
| // guard because we cover both http and https with the same subscribers | ||
| const { [HTTP_ON_CLIENT_REQUEST]: onHttpClientRequestCreated } = FULLY_SUPPORTS_HTTP_DIAGNOSTICS_CHANNEL ? getHttpClientSubscriptions(patchOptions) : {}; | ||
| let hasRegisteredHandlers = false; | ||
| const sub = onHttpClientRequestCreated | ||
| ? (moduleExports) => { | ||
| if (!hasRegisteredHandlers && onHttpClientRequestCreated) { | ||
| hasRegisteredHandlers = true; | ||
| subscribe(HTTP_ON_CLIENT_REQUEST, onHttpClientRequestCreated); | ||
| } | ||
| return moduleExports; | ||
| } | ||
| : undefined; | ||
| const sub = onHttpClientRequestCreated ? (moduleExports) => { | ||
| if (!hasRegisteredHandlers && onHttpClientRequestCreated) { | ||
| hasRegisteredHandlers = true; | ||
| subscribe(HTTP_ON_CLIENT_REQUEST, onHttpClientRequestCreated); | ||
| } | ||
| return moduleExports; | ||
| } : void 0; | ||
| const wrapHttp = sub ?? ((moduleExports) => patchHttpModuleClient(moduleExports, patchOptions)); | ||
| const wrapHttps = sub ?? ((moduleExports) => patchHttpModuleClient(moduleExports, patchOptions)); | ||
| /** | ||
| * You may be wondering why we register these diagnostics-channel listeners | ||
| * in such a convoluted way (as InstrumentationNodeModuleDefinition...)˝, | ||
| * instead of simply subscribing to the events once in here. | ||
| * The reason for this is timing semantics: These functions are called once the http or https module is loaded. | ||
| * If we'd subscribe before that, there seem to be conflicts with the OTEL native instrumentation in some scenarios, | ||
| * especially the "import-on-top" pattern of setting up ESM applications. | ||
| */ | ||
| return [ | ||
| new InstrumentationNodeModuleDefinition('http', ['*'], wrapHttp), | ||
| new InstrumentationNodeModuleDefinition('https', ['*'], wrapHttps), | ||
| new InstrumentationNodeModuleDefinition("http", ["*"], wrapHttp), | ||
| new InstrumentationNodeModuleDefinition("https", ["*"], wrapHttps) | ||
| ]; | ||
@@ -124,0 +73,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"SentryHttpInstrumentation.js","sources":["../../../../src/integrations/http/SentryHttpInstrumentation.ts"],"sourcesContent":["import { subscribe } from 'node:diagnostics_channel';\nimport { context, trace } from '@opentelemetry/api';\nimport { isTracingSuppressed } from '@opentelemetry/core';\nimport type { InstrumentationConfig } from '@opentelemetry/instrumentation';\nimport { InstrumentationBase, InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation';\nimport type { ClientRequest, IncomingMessage, ServerResponse } from 'node:http';\nimport type {\n HttpClientRequest,\n HttpIncomingMessage,\n HttpInstrumentationOptions,\n HttpModuleExport,\n Span,\n} from '@sentry/core';\nimport { getHttpClientSubscriptions, patchHttpModuleClient, SDK_VERSION, getRequestOptions } from '@sentry/core';\nimport { INSTRUMENTATION_NAME } from './constants';\nimport { HTTP_ON_CLIENT_REQUEST } from '@sentry/core';\nimport { NODE_VERSION } from '../../nodeVersion';\nimport { errorMonitor } from 'node:events';\nimport * as http from 'node:http';\nimport * as https from 'node:https';\n\nconst FULLY_SUPPORTS_HTTP_DIAGNOSTICS_CHANNEL =\n (NODE_VERSION.major === 22 && NODE_VERSION.minor >= 12) ||\n (NODE_VERSION.major === 23 && NODE_VERSION.minor >= 2) ||\n NODE_VERSION.major >= 24;\n\nexport type SentryHttpInstrumentationOptions = InstrumentationConfig & {\n /**\n * Whether breadcrumbs should be recorded for outgoing requests.\n *\n * @default `true`\n */\n breadcrumbs?: boolean;\n\n /**\n * Whether to propagate Sentry trace headers in outgoing requests.\n * By default this is done by the HttpInstrumentation, but if that is not added (e.g. because tracing is disabled)\n * then this instrumentation can take over.\n *\n * @default `false`\n */\n propagateTraceInOutgoingRequests?: boolean;\n\n /**\n * Whether to enable the capability to create spans for outgoing requests via diagnostic channels.\n * If enabled, spans will only be created if the `spans` option is also enabled (default: true).\n *\n * This is a feature flag that should be enabled by SDKs when the runtime supports it (Node 22.12+).\n * Individual users should not need to configure this directly.\n *\n * @default `false`\n */\n createSpansForOutgoingRequests?: boolean;\n\n /**\n * Whether to create spans for outgoing requests (user preference).\n * This only takes effect if `createSpansForOutgoingRequests` is also enabled.\n * If `createSpansForOutgoingRequests` is not enabled, this option is ignored.\n *\n * @default `true`\n */\n spans?: boolean;\n\n /**\n * Do not capture breadcrumbs for outgoing HTTP requests to URLs where the given callback returns `true`.\n * For the scope of this instrumentation, this callback only controls breadcrumb creation.\n * The same option can be passed to the top-level httpIntegration where it controls both, breadcrumb and\n * span creation.\n *\n * @param url Contains the entire URL, including query string (if any), protocol, host, etc. of the outgoing request.\n * @param request Contains the {@type RequestOptions} object used to make the outgoing request.\n */\n ignoreOutgoingRequests?: (url: string, request: http.RequestOptions) => boolean;\n\n /**\n * Hooks for outgoing request spans, called when `createSpansForOutgoingRequests` is enabled.\n * These mirror the OTEL HttpInstrumentation hooks for backwards compatibility.\n */\n outgoingRequestHook?: (span: Span, request: ClientRequest | HttpClientRequest) => void;\n outgoingResponseHook?: (span: Span, response: IncomingMessage | HttpIncomingMessage) => void;\n outgoingRequestApplyCustomAttributes?: (\n span: Span,\n request: HttpClientRequest,\n response: HttpIncomingMessage,\n ) => void;\n\n // All options below do not do anything anymore in this instrumentation, and will be removed in the future.\n // They are only kept here for backwards compatibility - the respective functionality is now handled by the httpServerIntegration/httpServerSpansIntegration.\n\n /**\n * @deprecated This no longer does anything.\n */\n extractIncomingTraceFromHeader?: boolean;\n\n /**\n * @deprecated This no longer does anything.\n */\n ignoreStaticAssets?: boolean;\n\n /**\n * @deprecated This no longer does anything.\n */\n disableIncomingRequestSpans?: boolean;\n\n /**\n * @deprecated This no longer does anything.\n */\n ignoreSpansForIncomingRequests?: (urlPath: string, request: IncomingMessage) => boolean;\n\n /**\n * @deprecated This no longer does anything.\n */\n ignoreIncomingRequestBody?: (url: string, request: http.RequestOptions) => boolean;\n\n /**\n * @deprecated This no longer does anything.\n */\n maxIncomingRequestBodySize?: 'none' | 'small' | 'medium' | 'always';\n\n /**\n * @deprecated This no longer does anything.\n */\n trackIncomingRequestsAsSessions?: boolean;\n\n /**\n * @deprecated This no longer does anything.\n */\n instrumentation?: {\n requestHook?: (span: Span, req: ClientRequest | IncomingMessage) => void;\n responseHook?: (span: Span, response: IncomingMessage | ServerResponse) => void;\n applyCustomAttributesOnSpan?: (\n span: Span,\n request: ClientRequest | IncomingMessage,\n response: IncomingMessage | ServerResponse,\n ) => void;\n };\n\n /**\n * @deprecated This no longer does anything.\n */\n sessionFlushingDelayMS?: number;\n};\n\n/**\n * This custom HTTP instrumentation handles outgoing HTTP requests.\n *\n * It provides:\n * - Breadcrumbs for all outgoing requests\n * - Trace propagation headers (when enabled)\n * - Span creation for outgoing requests (when createSpansForOutgoingRequests is enabled)\n *\n * Span creation requires Node 22+ and uses diagnostic channels to avoid monkey-patching.\n * By default, this is only enabled in the node SDK, not in node-core or other runtime SDKs.\n *\n * Important note: Contrary to other OTEL instrumentation, this one cannot be unwrapped.\n *\n * This is heavily inspired & adapted from:\n * https://github.com/open-telemetry/opentelemetry-js/blob/f8ab5592ddea5cba0a3b33bf8d74f27872c0367f/experimental/packages/opentelemetry-instrumentation-http/src/http.ts\n */\nexport class SentryHttpInstrumentation extends InstrumentationBase<SentryHttpInstrumentationOptions> {\n public constructor(config: SentryHttpInstrumentationOptions = {}) {\n super(INSTRUMENTATION_NAME, SDK_VERSION, config);\n }\n\n /** @inheritdoc */\n public init(): [InstrumentationNodeModuleDefinition, InstrumentationNodeModuleDefinition] {\n const { outgoingRequestApplyCustomAttributes: applyCustomAttributesOnSpan, ...options } = this.getConfig();\n const patchOptions: HttpInstrumentationOptions = {\n propagateTrace: options.propagateTraceInOutgoingRequests,\n applyCustomAttributesOnSpan,\n ...options,\n spans: options.createSpansForOutgoingRequests && (options.spans ?? true),\n ignoreOutgoingRequests(url, request) {\n return (\n isTracingSuppressed(context.active()) ||\n !!options.ignoreOutgoingRequests?.(url, getRequestOptions(request as ClientRequest))\n );\n },\n outgoingRequestHook(span, request) {\n options.outgoingRequestHook?.(span, request);\n // We monkey-patch `req.once('response'), which is used to trigger\n // the callback of the request, so that it runs in the active context\n // eslint-disable-next-line @typescript-eslint/unbound-method, deprecation/deprecation\n const originalOnce = request.once;\n\n const newOnce = new Proxy(originalOnce, {\n apply(target, thisArg, args: Parameters<typeof originalOnce>) {\n const [event] = args;\n if (event !== 'response') {\n return target.apply(thisArg, args);\n }\n\n const parentContext = context.active();\n const requestContext = trace.setSpan(parentContext, span);\n\n return context.with(requestContext, () => {\n return target.apply(thisArg, args);\n });\n },\n });\n\n // eslint-disable-next-line deprecation/deprecation\n request.once = newOnce;\n },\n outgoingResponseHook(span, response) {\n options.outgoingResponseHook?.(span, response);\n context.bind(context.active(), response);\n },\n errorMonitor,\n // Pass these in to detect OTel double-wrapping if we're enabling spans\n http,\n https,\n };\n\n // only generate the subscriber function if we'll actually use it\n const { [HTTP_ON_CLIENT_REQUEST]: onHttpClientRequestCreated } = FULLY_SUPPORTS_HTTP_DIAGNOSTICS_CHANNEL\n ? getHttpClientSubscriptions(patchOptions)\n : {};\n\n // guard because we cover both http and https with the same subscribers\n let hasRegisteredHandlers = false;\n const sub = onHttpClientRequestCreated\n ? <T extends HttpModuleExport>(moduleExports: T): T => {\n if (!hasRegisteredHandlers && onHttpClientRequestCreated) {\n hasRegisteredHandlers = true;\n subscribe(HTTP_ON_CLIENT_REQUEST, onHttpClientRequestCreated);\n }\n return moduleExports;\n }\n : undefined;\n\n const wrapHttp = sub ?? ((moduleExports: HttpModuleExport) => patchHttpModuleClient(moduleExports, patchOptions));\n\n const wrapHttps = sub ?? ((moduleExports: HttpModuleExport) => patchHttpModuleClient(moduleExports, patchOptions));\n\n /**\n * You may be wondering why we register these diagnostics-channel listeners\n * in such a convoluted way (as InstrumentationNodeModuleDefinition...)˝,\n * instead of simply subscribing to the events once in here.\n * The reason for this is timing semantics: These functions are called once the http or https module is loaded.\n * If we'd subscribe before that, there seem to be conflicts with the OTEL native instrumentation in some scenarios,\n * especially the \"import-on-top\" pattern of setting up ESM applications.\n */\n return [\n new InstrumentationNodeModuleDefinition('http', ['*'], wrapHttp),\n new InstrumentationNodeModuleDefinition('https', ['*'], wrapHttps),\n ];\n }\n}\n"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"SentryHttpInstrumentation.js","sources":["../../../../src/integrations/http/SentryHttpInstrumentation.ts"],"sourcesContent":["import { subscribe } from 'node:diagnostics_channel';\nimport { context, trace } from '@opentelemetry/api';\nimport { isTracingSuppressed } from '@opentelemetry/core';\nimport type { InstrumentationConfig } from '@opentelemetry/instrumentation';\nimport { InstrumentationBase, InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation';\nimport type { ClientRequest, IncomingMessage, ServerResponse } from 'node:http';\nimport type {\n HttpClientRequest,\n HttpIncomingMessage,\n HttpInstrumentationOptions,\n HttpModuleExport,\n Span,\n} from '@sentry/core';\nimport { getHttpClientSubscriptions, patchHttpModuleClient, SDK_VERSION, getRequestOptions } from '@sentry/core';\nimport { INSTRUMENTATION_NAME } from './constants';\nimport { HTTP_ON_CLIENT_REQUEST } from '@sentry/core';\nimport { NODE_VERSION } from '../../nodeVersion';\nimport { errorMonitor } from 'node:events';\nimport * as http from 'node:http';\nimport * as https from 'node:https';\n\nconst FULLY_SUPPORTS_HTTP_DIAGNOSTICS_CHANNEL =\n (NODE_VERSION.major === 22 && NODE_VERSION.minor >= 12) ||\n (NODE_VERSION.major === 23 && NODE_VERSION.minor >= 2) ||\n NODE_VERSION.major >= 24;\n\nexport type SentryHttpInstrumentationOptions = InstrumentationConfig & {\n /**\n * Whether breadcrumbs should be recorded for outgoing requests.\n *\n * @default `true`\n */\n breadcrumbs?: boolean;\n\n /**\n * Whether to propagate Sentry trace headers in outgoing requests.\n * By default this is done by the HttpInstrumentation, but if that is not added (e.g. because tracing is disabled)\n * then this instrumentation can take over.\n *\n * @default `false`\n */\n propagateTraceInOutgoingRequests?: boolean;\n\n /**\n * Whether to enable the capability to create spans for outgoing requests via diagnostic channels.\n * If enabled, spans will only be created if the `spans` option is also enabled (default: true).\n *\n * This is a feature flag that should be enabled by SDKs when the runtime supports it (Node 22.12+).\n * Individual users should not need to configure this directly.\n *\n * @default `false`\n */\n createSpansForOutgoingRequests?: boolean;\n\n /**\n * Whether to create spans for outgoing requests (user preference).\n * This only takes effect if `createSpansForOutgoingRequests` is also enabled.\n * If `createSpansForOutgoingRequests` is not enabled, this option is ignored.\n *\n * @default `true`\n */\n spans?: boolean;\n\n /**\n * Do not capture breadcrumbs for outgoing HTTP requests to URLs where the given callback returns `true`.\n * For the scope of this instrumentation, this callback only controls breadcrumb creation.\n * The same option can be passed to the top-level httpIntegration where it controls both, breadcrumb and\n * span creation.\n *\n * @param url Contains the entire URL, including query string (if any), protocol, host, etc. of the outgoing request.\n * @param request Contains the {@type RequestOptions} object used to make the outgoing request.\n */\n ignoreOutgoingRequests?: (url: string, request: http.RequestOptions) => boolean;\n\n /**\n * Hooks for outgoing request spans, called when `createSpansForOutgoingRequests` is enabled.\n * These mirror the OTEL HttpInstrumentation hooks for backwards compatibility.\n */\n outgoingRequestHook?: (span: Span, request: ClientRequest | HttpClientRequest) => void;\n outgoingResponseHook?: (span: Span, response: IncomingMessage | HttpIncomingMessage) => void;\n outgoingRequestApplyCustomAttributes?: (\n span: Span,\n request: HttpClientRequest,\n response: HttpIncomingMessage,\n ) => void;\n\n // All options below do not do anything anymore in this instrumentation, and will be removed in the future.\n // They are only kept here for backwards compatibility - the respective functionality is now handled by the httpServerIntegration/httpServerSpansIntegration.\n\n /**\n * @deprecated This no longer does anything.\n */\n extractIncomingTraceFromHeader?: boolean;\n\n /**\n * @deprecated This no longer does anything.\n */\n ignoreStaticAssets?: boolean;\n\n /**\n * @deprecated This no longer does anything.\n */\n disableIncomingRequestSpans?: boolean;\n\n /**\n * @deprecated This no longer does anything.\n */\n ignoreSpansForIncomingRequests?: (urlPath: string, request: IncomingMessage) => boolean;\n\n /**\n * @deprecated This no longer does anything.\n */\n ignoreIncomingRequestBody?: (url: string, request: http.RequestOptions) => boolean;\n\n /**\n * @deprecated This no longer does anything.\n */\n maxIncomingRequestBodySize?: 'none' | 'small' | 'medium' | 'always';\n\n /**\n * @deprecated This no longer does anything.\n */\n trackIncomingRequestsAsSessions?: boolean;\n\n /**\n * @deprecated This no longer does anything.\n */\n instrumentation?: {\n requestHook?: (span: Span, req: ClientRequest | IncomingMessage) => void;\n responseHook?: (span: Span, response: IncomingMessage | ServerResponse) => void;\n applyCustomAttributesOnSpan?: (\n span: Span,\n request: ClientRequest | IncomingMessage,\n response: IncomingMessage | ServerResponse,\n ) => void;\n };\n\n /**\n * @deprecated This no longer does anything.\n */\n sessionFlushingDelayMS?: number;\n};\n\n/**\n * This custom HTTP instrumentation handles outgoing HTTP requests.\n *\n * It provides:\n * - Breadcrumbs for all outgoing requests\n * - Trace propagation headers (when enabled)\n * - Span creation for outgoing requests (when createSpansForOutgoingRequests is enabled)\n *\n * Span creation requires Node 22+ and uses diagnostic channels to avoid monkey-patching.\n * By default, this is only enabled in the node SDK, not in node-core or other runtime SDKs.\n *\n * Important note: Contrary to other OTEL instrumentation, this one cannot be unwrapped.\n *\n * This is heavily inspired & adapted from:\n * https://github.com/open-telemetry/opentelemetry-js/blob/f8ab5592ddea5cba0a3b33bf8d74f27872c0367f/experimental/packages/opentelemetry-instrumentation-http/src/http.ts\n */\nexport class SentryHttpInstrumentation extends InstrumentationBase<SentryHttpInstrumentationOptions> {\n public constructor(config: SentryHttpInstrumentationOptions = {}) {\n super(INSTRUMENTATION_NAME, SDK_VERSION, config);\n }\n\n /** @inheritdoc */\n public init(): [InstrumentationNodeModuleDefinition, InstrumentationNodeModuleDefinition] {\n const { outgoingRequestApplyCustomAttributes: applyCustomAttributesOnSpan, ...options } = this.getConfig();\n const patchOptions: HttpInstrumentationOptions = {\n propagateTrace: options.propagateTraceInOutgoingRequests,\n applyCustomAttributesOnSpan,\n ...options,\n spans: options.createSpansForOutgoingRequests && (options.spans ?? true),\n ignoreOutgoingRequests(url, request) {\n return (\n isTracingSuppressed(context.active()) ||\n !!options.ignoreOutgoingRequests?.(url, getRequestOptions(request as ClientRequest))\n );\n },\n outgoingRequestHook(span, request) {\n options.outgoingRequestHook?.(span, request);\n // We monkey-patch `req.once('response'), which is used to trigger\n // the callback of the request, so that it runs in the active context\n // eslint-disable-next-line @typescript-eslint/unbound-method, deprecation/deprecation\n const originalOnce = request.once;\n\n const newOnce = new Proxy(originalOnce, {\n apply(target, thisArg, args: Parameters<typeof originalOnce>) {\n const [event] = args;\n if (event !== 'response') {\n return target.apply(thisArg, args);\n }\n\n const parentContext = context.active();\n const requestContext = trace.setSpan(parentContext, span);\n\n return context.with(requestContext, () => {\n return target.apply(thisArg, args);\n });\n },\n });\n\n // eslint-disable-next-line deprecation/deprecation\n request.once = newOnce;\n },\n outgoingResponseHook(span, response) {\n options.outgoingResponseHook?.(span, response);\n context.bind(context.active(), response);\n },\n errorMonitor,\n // Pass these in to detect OTel double-wrapping if we're enabling spans\n http,\n https,\n };\n\n // only generate the subscriber function if we'll actually use it\n const { [HTTP_ON_CLIENT_REQUEST]: onHttpClientRequestCreated } = FULLY_SUPPORTS_HTTP_DIAGNOSTICS_CHANNEL\n ? getHttpClientSubscriptions(patchOptions)\n : {};\n\n // guard because we cover both http and https with the same subscribers\n let hasRegisteredHandlers = false;\n const sub = onHttpClientRequestCreated\n ? <T extends HttpModuleExport>(moduleExports: T): T => {\n if (!hasRegisteredHandlers && onHttpClientRequestCreated) {\n hasRegisteredHandlers = true;\n subscribe(HTTP_ON_CLIENT_REQUEST, onHttpClientRequestCreated);\n }\n return moduleExports;\n }\n : undefined;\n\n const wrapHttp = sub ?? ((moduleExports: HttpModuleExport) => patchHttpModuleClient(moduleExports, patchOptions));\n\n const wrapHttps = sub ?? ((moduleExports: HttpModuleExport) => patchHttpModuleClient(moduleExports, patchOptions));\n\n /**\n * You may be wondering why we register these diagnostics-channel listeners\n * in such a convoluted way (as InstrumentationNodeModuleDefinition...)˝,\n * instead of simply subscribing to the events once in here.\n * The reason for this is timing semantics: These functions are called once the http or https module is loaded.\n * If we'd subscribe before that, there seem to be conflicts with the OTEL native instrumentation in some scenarios,\n * especially the \"import-on-top\" pattern of setting up ESM applications.\n */\n return [\n new InstrumentationNodeModuleDefinition('http', ['*'], wrapHttp),\n new InstrumentationNodeModuleDefinition('https', ['*'], wrapHttps),\n ];\n }\n}\n"],"names":[],"mappings":";;;;;;;;;;;AAqBA,MAAM,uCAAA,GACH,YAAA,CAAa,KAAA,KAAU,EAAA,IAAM,aAAa,KAAA,IAAS,EAAA,IACnD,YAAA,CAAa,KAAA,KAAU,EAAA,IAAM,YAAA,CAAa,KAAA,IAAS,CAAA,IACpD,aAAa,KAAA,IAAS,EAAA;AAuIjB,MAAM,kCAAkC,mBAAA,CAAsD;AAAA,EAC5F,WAAA,CAAY,MAAA,GAA2C,EAAC,EAAG;AAChE,IAAA,KAAA,CAAM,oBAAA,EAAsB,aAAa,MAAM,CAAA;AAAA,EACjD;AAAA;AAAA,EAGO,IAAA,GAAmF;AACxF,IAAA,MAAM,EAAE,oCAAA,EAAsC,2BAAA,EAA6B,GAAG,OAAA,EAAQ,GAAI,KAAK,SAAA,EAAU;AACzG,IAAA,MAAM,YAAA,GAA2C;AAAA,MAC/C,gBAAgB,OAAA,CAAQ,gCAAA;AAAA,MACxB,2BAAA;AAAA,MACA,GAAG,OAAA;AAAA,MACH,KAAA,EAAO,OAAA,CAAQ,8BAAA,KAAmC,OAAA,CAAQ,KAAA,IAAS,IAAA,CAAA;AAAA,MACnE,sBAAA,CAAuB,KAAK,OAAA,EAAS;AACnC,QAAA,OACE,mBAAA,CAAoB,OAAA,CAAQ,MAAA,EAAQ,CAAA,IACpC,CAAC,CAAC,OAAA,CAAQ,sBAAA,GAAyB,GAAA,EAAK,iBAAA,CAAkB,OAAwB,CAAC,CAAA;AAAA,MAEvF,CAAA;AAAA,MACA,mBAAA,CAAoB,MAAM,OAAA,EAAS;AACjC,QAAA,OAAA,CAAQ,mBAAA,GAAsB,MAAM,OAAO,CAAA;AAI3C,QAAA,MAAM,eAAe,OAAA,CAAQ,IAAA;AAE7B,QAAA,MAAM,OAAA,GAAU,IAAI,KAAA,CAAM,YAAA,EAAc;AAAA,UACtC,KAAA,CAAM,MAAA,EAAQ,OAAA,EAAS,IAAA,EAAuC;AAC5D,YAAA,MAAM,CAAC,KAAK,CAAA,GAAI,IAAA;AAChB,YAAA,IAAI,UAAU,UAAA,EAAY;AACxB,cAAA,OAAO,MAAA,CAAO,KAAA,CAAM,OAAA,EAAS,IAAI,CAAA;AAAA,YACnC;AAEA,YAAA,MAAM,aAAA,GAAgB,QAAQ,MAAA,EAAO;AACrC,YAAA,MAAM,cAAA,GAAiB,KAAA,CAAM,OAAA,CAAQ,aAAA,EAAe,IAAI,CAAA;AAExD,YAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,cAAA,EAAgB,MAAM;AACxC,cAAA,OAAO,MAAA,CAAO,KAAA,CAAM,OAAA,EAAS,IAAI,CAAA;AAAA,YACnC,CAAC,CAAA;AAAA,UACH;AAAA,SACD,CAAA;AAGD,QAAA,OAAA,CAAQ,IAAA,GAAO,OAAA;AAAA,MACjB,CAAA;AAAA,MACA,oBAAA,CAAqB,MAAM,QAAA,EAAU;AACnC,QAAA,OAAA,CAAQ,oBAAA,GAAuB,MAAM,QAAQ,CAAA;AAC7C,QAAA,OAAA,CAAQ,IAAA,CAAK,OAAA,CAAQ,MAAA,EAAO,EAAG,QAAQ,CAAA;AAAA,MACzC,CAAA;AAAA,MACA,YAAA;AAAA;AAAA,MAEA,IAAA;AAAA,MACA;AAAA,KACF;AAGA,IAAA,MAAM,EAAE,CAAC,sBAAsB,GAAG,0BAAA,KAA+B,uCAAA,GAC7D,0BAAA,CAA2B,YAAY,CAAA,GACvC,EAAC;AAGL,IAAA,IAAI,qBAAA,GAAwB,KAAA;AAC5B,IAAA,MAAM,GAAA,GAAM,0BAAA,GACR,CAA6B,aAAA,KAAwB;AACnD,MAAA,IAAI,CAAC,yBAAyB,0BAAA,EAA4B;AACxD,QAAA,qBAAA,GAAwB,IAAA;AACxB,QAAA,SAAA,CAAU,wBAAwB,0BAA0B,CAAA;AAAA,MAC9D;AACA,MAAA,OAAO,aAAA;AAAA,IACT,CAAA,GACA,MAAA;AAEJ,IAAA,MAAM,WAAW,GAAA,KAAQ,CAAC,aAAA,KAAoC,qBAAA,CAAsB,eAAe,YAAY,CAAA,CAAA;AAE/G,IAAA,MAAM,YAAY,GAAA,KAAQ,CAAC,aAAA,KAAoC,qBAAA,CAAsB,eAAe,YAAY,CAAA,CAAA;AAUhH,IAAA,OAAO;AAAA,MACL,IAAI,mCAAA,CAAoC,MAAA,EAAQ,CAAC,GAAG,GAAG,QAAQ,CAAA;AAAA,MAC/D,IAAI,mCAAA,CAAoC,OAAA,EAAS,CAAC,GAAG,GAAG,SAAS;AAAA,KACnE;AAAA,EACF;AACF;;;;"} |
@@ -1,23 +0,6 @@ | ||
| /** | ||
| * The key used to store the local variables on the error object. | ||
| */ | ||
| const LOCAL_VARIABLES_KEY = '__SENTRY_ERROR_LOCAL_VARIABLES__'; | ||
| /** | ||
| * Creates a rate limiter that will call the disable callback when the rate limit is reached and the enable callback | ||
| * when a timeout has occurred. | ||
| * @param maxPerSecond Maximum number of calls per second | ||
| * @param enable Callback to enable capture | ||
| * @param disable Callback to disable capture | ||
| * @returns A function to call to increment the rate limiter count | ||
| */ | ||
| function createRateLimiter( | ||
| maxPerSecond, | ||
| enable, | ||
| disable, | ||
| ) { | ||
| const LOCAL_VARIABLES_KEY = "__SENTRY_ERROR_LOCAL_VARIABLES__"; | ||
| function createRateLimiter(maxPerSecond, enable, disable) { | ||
| let count = 0; | ||
| let retrySeconds = 5; | ||
| let disabledTimeout = 0; | ||
| setInterval(() => { | ||
@@ -28,4 +11,2 @@ if (disabledTimeout === 0) { | ||
| disable(retrySeconds); | ||
| // Cap at one day | ||
| if (retrySeconds > 86400) { | ||
@@ -38,3 +19,2 @@ retrySeconds = 86400; | ||
| disabledTimeout -= 1; | ||
| if (disabledTimeout === 0) { | ||
@@ -44,6 +24,4 @@ enable(); | ||
| } | ||
| count = 0; | ||
| }, 1000).unref(); | ||
| }, 1e3).unref(); | ||
| return () => { | ||
@@ -53,13 +31,7 @@ count += 1; | ||
| } | ||
| // Add types for the exception event data | ||
| /** Could this be an anonymous function? */ | ||
| function isAnonymous(name) { | ||
| return name !== undefined && (name.length === 0 || name === '?' || name === '<anonymous>'); | ||
| return name !== void 0 && (name.length === 0 || name === "?" || name === "<anonymous>"); | ||
| } | ||
| /** Do the function names appear to match? */ | ||
| function functionNamesMatch(a, b) { | ||
| return a === b || `Object.${a}` === b || a === `Object.${b}` || (isAnonymous(a) && isAnonymous(b)); | ||
| return a === b || `Object.${a}` === b || a === `Object.${b}` || isAnonymous(a) && isAnonymous(b); | ||
| } | ||
@@ -66,0 +38,0 @@ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"common.js","sources":["../../../../src/integrations/local-variables/common.ts"],"sourcesContent":["import type { Debugger } from 'node:inspector';\n\nexport type Variables = Record<string, unknown>;\n\nexport type RateLimitIncrement = () => void;\n\n/**\n * The key used to store the local variables on the error object.\n */\nexport const LOCAL_VARIABLES_KEY = '__SENTRY_ERROR_LOCAL_VARIABLES__';\n\n/**\n * Creates a rate limiter that will call the disable callback when the rate limit is reached and the enable callback\n * when a timeout has occurred.\n * @param maxPerSecond Maximum number of calls per second\n * @param enable Callback to enable capture\n * @param disable Callback to disable capture\n * @returns A function to call to increment the rate limiter count\n */\nexport function createRateLimiter(\n maxPerSecond: number,\n enable: () => void,\n disable: (seconds: number) => void,\n): RateLimitIncrement {\n let count = 0;\n let retrySeconds = 5;\n let disabledTimeout = 0;\n\n setInterval(() => {\n if (disabledTimeout === 0) {\n if (count > maxPerSecond) {\n retrySeconds *= 2;\n disable(retrySeconds);\n\n // Cap at one day\n if (retrySeconds > 86400) {\n retrySeconds = 86400;\n }\n disabledTimeout = retrySeconds;\n }\n } else {\n disabledTimeout -= 1;\n\n if (disabledTimeout === 0) {\n enable();\n }\n }\n\n count = 0;\n }, 1_000).unref();\n\n return () => {\n count += 1;\n };\n}\n\n// Add types for the exception event data\nexport type PausedExceptionEvent = Debugger.PausedEventDataType & {\n data: {\n // This contains error.stack\n description: string;\n objectId?: string;\n };\n};\n\n/** Could this be an anonymous function? */\nexport function isAnonymous(name: string | undefined): boolean {\n return name !== undefined && (name.length === 0 || name === '?' || name === '<anonymous>');\n}\n\n/** Do the function names appear to match? */\nexport function functionNamesMatch(a: string | undefined, b: string | undefined): boolean {\n return a === b || `Object.${a}` === b || a === `Object.${b}` || (isAnonymous(a) && isAnonymous(b));\n}\n\nexport interface FrameVariables {\n function: string;\n vars?: Variables;\n}\n\nexport interface LocalVariablesIntegrationOptions {\n /**\n * Capture local variables for both caught and uncaught exceptions\n *\n * - When false, only uncaught exceptions will have local variables\n * - When true, both caught and uncaught exceptions will have local variables.\n *\n * Defaults to `true`.\n *\n * Capturing local variables for all exceptions can be expensive since the debugger pauses for every throw to collect\n * local variables.\n *\n * To reduce the likelihood of this feature impacting app performance or throughput, this feature is rate-limited.\n * Once the rate limit is reached, local variables will only be captured for uncaught exceptions until a timeout has\n * been reached.\n */\n captureAllExceptions?: boolean;\n /**\n * Maximum number of exceptions to capture local variables for per second before rate limiting is triggered.\n */\n maxExceptionsPerSecond?: number;\n /**\n * When true, local variables will be captured for all frames, including those that are not in_app.\n *\n * Defaults to `false`.\n */\n includeOutOfAppFrames?: boolean;\n}\n\nexport interface LocalVariablesWorkerArgs extends LocalVariablesIntegrationOptions {\n /**\n * Whether to enable debug logging.\n */\n debug: boolean;\n /**\n * Base path used to calculate module name.\n *\n * Defaults to `dirname(process.argv[1])` and falls back to `process.cwd()`\n */\n basePath?: string;\n}\n"],"names":[],"mappings":"AAMA;AACA;AACA;AACO,MAAM,mBAAA,GAAsB;;AAEnC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,iBAAiB;AACjC,EAAE,YAAY;AACd,EAAE,MAAM;AACR,EAAE,OAAO;AACT,EAAsB;AACtB,EAAE,IAAI,KAAA,GAAQ,CAAC;AACf,EAAE,IAAI,YAAA,GAAe,CAAC;AACtB,EAAE,IAAI,eAAA,GAAkB,CAAC;;AAEzB,EAAE,WAAW,CAAC,MAAM;AACpB,IAAI,IAAI,eAAA,KAAoB,CAAC,EAAE;AAC/B,MAAM,IAAI,KAAA,GAAQ,YAAY,EAAE;AAChC,QAAQ,YAAA,IAAgB,CAAC;AACzB,QAAQ,OAAO,CAAC,YAAY,CAAC;;AAE7B;AACA,QAAQ,IAAI,YAAA,GAAe,KAAK,EAAE;AAClC,UAAU,YAAA,GAAe,KAAK;AAC9B,QAAQ;AACR,QAAQ,eAAA,GAAkB,YAAY;AACtC,MAAM;AACN,IAAI,OAAO;AACX,MAAM,eAAA,IAAmB,CAAC;;AAE1B,MAAM,IAAI,eAAA,KAAoB,CAAC,EAAE;AACjC,QAAQ,MAAM,EAAE;AAChB,MAAM;AACN,IAAI;;AAEJ,IAAI,KAAA,GAAQ,CAAC;AACb,EAAE,CAAC,EAAE,IAAK,CAAC,CAAC,KAAK,EAAE;;AAEnB,EAAE,OAAO,MAAM;AACf,IAAI,KAAA,IAAS,CAAC;AACd,EAAE,CAAC;AACH;;AAEA;;AASA;AACO,SAAS,WAAW,CAAC,IAAI,EAA+B;AAC/D,EAAE,OAAO,IAAA,KAAS,cAAc,IAAI,CAAC,MAAA,KAAW,CAAA,IAAK,SAAS,GAAA,IAAO,IAAA,KAAS,aAAa,CAAC;AAC5F;;AAEA;AACO,SAAS,kBAAkB,CAAC,CAAC,EAAsB,CAAC,EAA+B;AAC1F,EAAE,OAAO,CAAA,KAAM,CAAA,IAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAA,KAAA,CAAA,IAAA,CAAA,KAAA,CAAA,OAAA,EAAA,CAAA,CAAA,CAAA,KAAA,WAAA,CAAA,CAAA,CAAA,IAAA,WAAA,CAAA,CAAA,CAAA,CAAA;AACA;;;;"} | ||
| {"version":3,"file":"common.js","sources":["../../../../src/integrations/local-variables/common.ts"],"sourcesContent":["import type { Debugger } from 'node:inspector';\n\nexport type Variables = Record<string, unknown>;\n\nexport type RateLimitIncrement = () => void;\n\n/**\n * The key used to store the local variables on the error object.\n */\nexport const LOCAL_VARIABLES_KEY = '__SENTRY_ERROR_LOCAL_VARIABLES__';\n\n/**\n * Creates a rate limiter that will call the disable callback when the rate limit is reached and the enable callback\n * when a timeout has occurred.\n * @param maxPerSecond Maximum number of calls per second\n * @param enable Callback to enable capture\n * @param disable Callback to disable capture\n * @returns A function to call to increment the rate limiter count\n */\nexport function createRateLimiter(\n maxPerSecond: number,\n enable: () => void,\n disable: (seconds: number) => void,\n): RateLimitIncrement {\n let count = 0;\n let retrySeconds = 5;\n let disabledTimeout = 0;\n\n setInterval(() => {\n if (disabledTimeout === 0) {\n if (count > maxPerSecond) {\n retrySeconds *= 2;\n disable(retrySeconds);\n\n // Cap at one day\n if (retrySeconds > 86400) {\n retrySeconds = 86400;\n }\n disabledTimeout = retrySeconds;\n }\n } else {\n disabledTimeout -= 1;\n\n if (disabledTimeout === 0) {\n enable();\n }\n }\n\n count = 0;\n }, 1_000).unref();\n\n return () => {\n count += 1;\n };\n}\n\n// Add types for the exception event data\nexport type PausedExceptionEvent = Debugger.PausedEventDataType & {\n data: {\n // This contains error.stack\n description: string;\n objectId?: string;\n };\n};\n\n/** Could this be an anonymous function? */\nexport function isAnonymous(name: string | undefined): boolean {\n return name !== undefined && (name.length === 0 || name === '?' || name === '<anonymous>');\n}\n\n/** Do the function names appear to match? */\nexport function functionNamesMatch(a: string | undefined, b: string | undefined): boolean {\n return a === b || `Object.${a}` === b || a === `Object.${b}` || (isAnonymous(a) && isAnonymous(b));\n}\n\nexport interface FrameVariables {\n function: string;\n vars?: Variables;\n}\n\nexport interface LocalVariablesIntegrationOptions {\n /**\n * Capture local variables for both caught and uncaught exceptions\n *\n * - When false, only uncaught exceptions will have local variables\n * - When true, both caught and uncaught exceptions will have local variables.\n *\n * Defaults to `true`.\n *\n * Capturing local variables for all exceptions can be expensive since the debugger pauses for every throw to collect\n * local variables.\n *\n * To reduce the likelihood of this feature impacting app performance or throughput, this feature is rate-limited.\n * Once the rate limit is reached, local variables will only be captured for uncaught exceptions until a timeout has\n * been reached.\n */\n captureAllExceptions?: boolean;\n /**\n * Maximum number of exceptions to capture local variables for per second before rate limiting is triggered.\n */\n maxExceptionsPerSecond?: number;\n /**\n * When true, local variables will be captured for all frames, including those that are not in_app.\n *\n * Defaults to `false`.\n */\n includeOutOfAppFrames?: boolean;\n}\n\nexport interface LocalVariablesWorkerArgs extends LocalVariablesIntegrationOptions {\n /**\n * Whether to enable debug logging.\n */\n debug: boolean;\n /**\n * Base path used to calculate module name.\n *\n * Defaults to `dirname(process.argv[1])` and falls back to `process.cwd()`\n */\n basePath?: string;\n}\n"],"names":[],"mappings":"AASO,MAAM,mBAAA,GAAsB;AAU5B,SAAS,iBAAA,CACd,YAAA,EACA,MAAA,EACA,OAAA,EACoB;AACpB,EAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,EAAA,IAAI,YAAA,GAAe,CAAA;AACnB,EAAA,IAAI,eAAA,GAAkB,CAAA;AAEtB,EAAA,WAAA,CAAY,MAAM;AAChB,IAAA,IAAI,oBAAoB,CAAA,EAAG;AACzB,MAAA,IAAI,QAAQ,YAAA,EAAc;AACxB,QAAA,YAAA,IAAgB,CAAA;AAChB,QAAA,OAAA,CAAQ,YAAY,CAAA;AAGpB,QAAA,IAAI,eAAe,KAAA,EAAO;AACxB,UAAA,YAAA,GAAe,KAAA;AAAA,QACjB;AACA,QAAA,eAAA,GAAkB,YAAA;AAAA,MACpB;AAAA,IACF,CAAA,MAAO;AACL,MAAA,eAAA,IAAmB,CAAA;AAEnB,MAAA,IAAI,oBAAoB,CAAA,EAAG;AACzB,QAAA,MAAA,EAAO;AAAA,MACT;AAAA,IACF;AAEA,IAAA,KAAA,GAAQ,CAAA;AAAA,EACV,CAAA,EAAG,GAAK,CAAA,CAAE,KAAA,EAAM;AAEhB,EAAA,OAAO,MAAM;AACX,IAAA,KAAA,IAAS,CAAA;AAAA,EACX,CAAA;AACF;AAYO,SAAS,YAAY,IAAA,EAAmC;AAC7D,EAAA,OAAO,SAAS,MAAA,KAAc,IAAA,CAAK,WAAW,CAAA,IAAK,IAAA,KAAS,OAAO,IAAA,KAAS,aAAA,CAAA;AAC9E;AAGO,SAAS,kBAAA,CAAmB,GAAuB,CAAA,EAAgC;AACxF,EAAA,OAAO,CAAA,KAAM,CAAA,IAAK,CAAA,OAAA,EAAU,CAAC,OAAO,CAAA,IAAK,CAAA,KAAM,CAAA,OAAA,EAAU,CAAC,CAAA,CAAA,IAAO,WAAA,CAAY,CAAC,CAAA,IAAK,YAAY,CAAC,CAAA;AAClG;;;;"} |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.js","sources":["../../../../src/integrations/local-variables/index.ts"],"sourcesContent":["import type { Integration } from '@sentry/core';\nimport { NODE_VERSION } from '../../nodeVersion';\nimport type { LocalVariablesIntegrationOptions } from './common';\nimport { localVariablesAsyncIntegration } from './local-variables-async';\nimport { localVariablesSyncIntegration } from './local-variables-sync';\n\nexport const localVariablesIntegration = (options: LocalVariablesIntegrationOptions = {}): Integration => {\n return NODE_VERSION.major < 19 ? localVariablesSyncIntegration(options) : localVariablesAsyncIntegration(options);\n};\n"],"names":[],"mappings":";;;;AAMO,MAAM,4BAA4B,CAAC,OAAO,GAAqC,EAAE,KAAkB;AAC1G,EAAE,OAAO,YAAY,CAAC,KAAA,GAAQ,EAAA,GAAK,6BAA6B,CAAC,OAAO,CAAA,GAAI,8BAA8B,CAAC,OAAO,CAAC;AACnH;;;;"} | ||
| {"version":3,"file":"index.js","sources":["../../../../src/integrations/local-variables/index.ts"],"sourcesContent":["import type { Integration } from '@sentry/core';\nimport { NODE_VERSION } from '../../nodeVersion';\nimport type { LocalVariablesIntegrationOptions } from './common';\nimport { localVariablesAsyncIntegration } from './local-variables-async';\nimport { localVariablesSyncIntegration } from './local-variables-sync';\n\nexport const localVariablesIntegration = (options: LocalVariablesIntegrationOptions = {}): Integration => {\n return NODE_VERSION.major < 19 ? localVariablesSyncIntegration(options) : localVariablesAsyncIntegration(options);\n};\n"],"names":[],"mappings":";;;;AAMO,MAAM,yBAAA,GAA4B,CAAC,OAAA,GAA4C,EAAC,KAAmB;AACxG,EAAA,OAAO,aAAa,KAAA,GAAQ,EAAA,GAAK,8BAA8B,OAAO,CAAA,GAAI,+BAA+B,OAAO,CAAA;AAClH;;;;"} |
@@ -6,38 +6,20 @@ import { Worker } from 'node:worker_threads'; | ||
| // This string is a placeholder that gets overwritten with the worker code. | ||
| const base64WorkerScript = 'LyohIEBzZW50cnkvbm9kZS1jb3JlIDEwLjUzLjEgKGNkOTc0MDgpIHwgaHR0cHM6Ly9naXRodWIuY29tL2dldHNlbnRyeS9zZW50cnktamF2YXNjcmlwdCAqLwppbXBvcnR7U2Vzc2lvbiBhcyBlfWZyb20ibm9kZTppbnNwZWN0b3IvcHJvbWlzZXMiO2ltcG9ydHt3b3JrZXJEYXRhIGFzIHR9ZnJvbSJub2RlOndvcmtlcl90aHJlYWRzIjtjb25zdCBuPWdsb2JhbFRoaXMsaT17fTtjb25zdCBvPSJfX1NFTlRSWV9FUlJPUl9MT0NBTF9WQVJJQUJMRVNfXyI7Y29uc3QgYT10O2Z1bmN0aW9uIHMoLi4uZSl7YS5kZWJ1ZyYmZnVuY3Rpb24oZSl7aWYoISgiY29uc29sZSJpbiBuKSlyZXR1cm4gZSgpO2NvbnN0IHQ9bi5jb25zb2xlLG89e30sYT1PYmplY3Qua2V5cyhpKTthLmZvckVhY2goZT0+e2NvbnN0IG49aVtlXTtvW2VdPXRbZV0sdFtlXT1ufSk7dHJ5e3JldHVybiBlKCl9ZmluYWxseXthLmZvckVhY2goZT0+e3RbZV09b1tlXX0pfX0oKCk9PmNvbnNvbGUubG9nKCJbTG9jYWxWYXJpYWJsZXMgV29ya2VyXSIsLi4uZSkpfWFzeW5jIGZ1bmN0aW9uIGMoZSx0LG4saSl7Y29uc3Qgbz1hd2FpdCBlLnBvc3QoIlJ1bnRpbWUuZ2V0UHJvcGVydGllcyIse29iamVjdElkOnQsb3duUHJvcGVydGllczohMH0pO2lbbl09by5yZXN1bHQuZmlsdGVyKGU9PiJsZW5ndGgiIT09ZS5uYW1lJiYhaXNOYU4ocGFyc2VJbnQoZS5uYW1lLDEwKSkpLnNvcnQoKGUsdCk9PnBhcnNlSW50KGUubmFtZSwxMCktcGFyc2VJbnQodC5uYW1lLDEwKSkubWFwKGU9PmUudmFsdWU/LnZhbHVlKX1hc3luYyBmdW5jdGlvbiByKGUsdCxuLGkpe2NvbnN0IG89YXdhaXQgZS5wb3N0KCJSdW50aW1lLmdldFByb3BlcnRpZXMiLHtvYmplY3RJZDp0LG93blByb3BlcnRpZXM6ITB9KTtpW25dPW8ucmVzdWx0Lm1hcChlPT5bZS5uYW1lLGUudmFsdWU/LnZhbHVlXSkucmVkdWNlKChlLFt0LG5dKT0+KGVbdF09bixlKSx7fSl9ZnVuY3Rpb24gdShlLHQpe2UudmFsdWUmJigidmFsdWUiaW4gZS52YWx1ZT92b2lkIDA9PT1lLnZhbHVlLnZhbHVlfHxudWxsPT09ZS52YWx1ZS52YWx1ZT90W2UubmFtZV09YDwke2UudmFsdWUudmFsdWV9PmA6dFtlLm5hbWVdPWUudmFsdWUudmFsdWU6ImRlc2NyaXB0aW9uImluIGUudmFsdWUmJiJmdW5jdGlvbiIhPT1lLnZhbHVlLnR5cGU/dFtlLm5hbWVdPWA8JHtlLnZhbHVlLmRlc2NyaXB0aW9ufT5gOiJ1bmRlZmluZWQiPT09ZS52YWx1ZS50eXBlJiYodFtlLm5hbWVdPSI8dW5kZWZpbmVkPiIpKX1hc3luYyBmdW5jdGlvbiBsKGUsdCl7Y29uc3Qgbj1hd2FpdCBlLnBvc3QoIlJ1bnRpbWUuZ2V0UHJvcGVydGllcyIse29iamVjdElkOnQsb3duUHJvcGVydGllczohMH0pLGk9e307Zm9yKGNvbnN0IHQgb2Ygbi5yZXN1bHQpaWYodC52YWx1ZT8ub2JqZWN0SWQmJiJBcnJheSI9PT10LnZhbHVlLmNsYXNzTmFtZSl7Y29uc3Qgbj10LnZhbHVlLm9iamVjdElkO2F3YWl0IGMoZSxuLHQubmFtZSxpKX1lbHNlIGlmKHQudmFsdWU/Lm9iamVjdElkJiYiT2JqZWN0Ij09PXQudmFsdWUuY2xhc3NOYW1lKXtjb25zdCBuPXQudmFsdWUub2JqZWN0SWQ7YXdhaXQgcihlLG4sdC5uYW1lLGkpfWVsc2UgdC52YWx1ZSYmdSh0LGkpO3JldHVybiBpfWxldCBmOyhhc3luYyBmdW5jdGlvbigpe2NvbnN0IHQ9bmV3IGU7dC5jb25uZWN0VG9NYWluVGhyZWFkKCkscygiQ29ubmVjdGVkIHRvIG1haW4gdGhyZWFkIik7bGV0IG49ITE7dC5vbigiRGVidWdnZXIucmVzdW1lZCIsKCk9PntuPSExfSksdC5vbigiRGVidWdnZXIucGF1c2VkIixlPT57bj0hMCxhc3luYyBmdW5jdGlvbihlLHtyZWFzb246dCxkYXRhOntvYmplY3RJZDpufSxjYWxsRnJhbWVzOml9KXtpZigiZXhjZXB0aW9uIiE9PXQmJiJwcm9taXNlUmVqZWN0aW9uIiE9PXQpcmV0dXJuO2lmKGY/LigpLG51bGw9PW4pcmV0dXJuO2NvbnN0IGE9W107Zm9yKGxldCB0PTA7dDxpLmxlbmd0aDt0Kyspe2NvbnN0e3Njb3BlQ2hhaW46bixmdW5jdGlvbk5hbWU6byx0aGlzOnN9PWlbdF0sYz1uLmZpbmQoZT0+ImxvY2FsIj09PWUudHlwZSkscj0iZ2xvYmFsIiE9PXMuY2xhc3NOYW1lJiZzLmNsYXNzTmFtZT9gJHtzLmNsYXNzTmFtZX0uJHtvfWA6bztpZih2b2lkIDA9PT1jPy5vYmplY3Qub2JqZWN0SWQpYVt0XT17ZnVuY3Rpb246cn07ZWxzZXtjb25zdCBuPWF3YWl0IGwoZSxjLm9iamVjdC5vYmplY3RJZCk7YVt0XT17ZnVuY3Rpb246cix2YXJzOm59fX1hd2FpdCBlLnBvc3QoIlJ1bnRpbWUuY2FsbEZ1bmN0aW9uT24iLHtmdW5jdGlvbkRlY2xhcmF0aW9uOmBmdW5jdGlvbigpIHsgdGhpcy4ke299ID0gdGhpcy4ke299IHx8ICR7SlNPTi5zdHJpbmdpZnkoYSl9OyB9YCxzaWxlbnQ6ITAsb2JqZWN0SWQ6bn0pLGF3YWl0IGUucG9zdCgiUnVudGltZS5yZWxlYXNlT2JqZWN0Iix7b2JqZWN0SWQ6bn0pfSh0LGUucGFyYW1zKS50aGVuKGFzeW5jKCk9PntuJiZhd2FpdCB0LnBvc3QoIkRlYnVnZ2VyLnJlc3VtZSIpfSxhc3luYyBlPT57biYmYXdhaXQgdC5wb3N0KCJEZWJ1Z2dlci5yZXN1bWUiKX0pfSksYXdhaXQgdC5wb3N0KCJEZWJ1Z2dlci5lbmFibGUiKTtjb25zdCBpPSExIT09YS5jYXB0dXJlQWxsRXhjZXB0aW9ucztpZihhd2FpdCB0LnBvc3QoIkRlYnVnZ2VyLnNldFBhdXNlT25FeGNlcHRpb25zIix7c3RhdGU6aT8iYWxsIjoidW5jYXVnaHQifSksaSl7Y29uc3QgZT1hLm1heEV4Y2VwdGlvbnNQZXJTZWNvbmR8fDUwO2Y9ZnVuY3Rpb24oZSx0LG4pe2xldCBpPTAsbz01LGE9MDtyZXR1cm4gc2V0SW50ZXJ2YWwoKCk9PnswPT09YT9pPmUmJihvKj0yLG4obyksbz44NjQwMCYmKG89ODY0MDApLGE9byk6KGEtPTEsMD09PWEmJnQoKSksaT0wfSwxZTMpLnVucmVmKCksKCk9PntpKz0xfX0oZSxhc3luYygpPT57cygiUmF0ZS1saW1pdCBsaWZ0ZWQuIiksYXdhaXQgdC5wb3N0KCJEZWJ1Z2dlci5zZXRQYXVzZU9uRXhjZXB0aW9ucyIse3N0YXRlOiJhbGwifSl9LGFzeW5jIGU9PntzKGBSYXRlLWxpbWl0IGV4Y2VlZGVkLiBEaXNhYmxpbmcgY2FwdHVyaW5nIG9mIGNhdWdodCBleGNlcHRpb25zIGZvciAke2V9IHNlY29uZHMuYCksYXdhaXQgdC5wb3N0KCJEZWJ1Z2dlci5zZXRQYXVzZU9uRXhjZXB0aW9ucyIse3N0YXRlOiJ1bmNhdWdodCJ9KX0pfX0pKCkuY2F0Y2goZT0+e3MoIkZhaWxlZCB0byBzdGFydCBkZWJ1Z2dlciIsZSl9KSxzZXRJbnRlcnZhbCgoKT0+e30sMWU0KTs='; | ||
| const base64WorkerScript = "LyohIEBzZW50cnkvbm9kZS1jb3JlIDEwLjU0LjAgKGYzMTY4NmIpIHwgaHR0cHM6Ly9naXRodWIuY29tL2dldHNlbnRyeS9zZW50cnktamF2YXNjcmlwdCAqLwppbXBvcnR7U2Vzc2lvbiBhcyBlfWZyb20ibm9kZTppbnNwZWN0b3IvcHJvbWlzZXMiO2ltcG9ydHt3b3JrZXJEYXRhIGFzIHR9ZnJvbSJub2RlOndvcmtlcl90aHJlYWRzIjtjb25zdCBuPWdsb2JhbFRoaXMsaT17fTtjb25zdCBvPSJfX1NFTlRSWV9FUlJPUl9MT0NBTF9WQVJJQUJMRVNfXyI7Y29uc3QgYT10O2Z1bmN0aW9uIHMoLi4uZSl7YS5kZWJ1ZyYmZnVuY3Rpb24oZSl7aWYoISgiY29uc29sZSJpbiBuKSlyZXR1cm4gZSgpO2NvbnN0IHQ9bi5jb25zb2xlLG89e30sYT1PYmplY3Qua2V5cyhpKTthLmZvckVhY2goZT0+e2NvbnN0IG49aVtlXTtvW2VdPXRbZV0sdFtlXT1ufSk7dHJ5e3JldHVybiBlKCl9ZmluYWxseXthLmZvckVhY2goZT0+e3RbZV09b1tlXX0pfX0oKCk9PmNvbnNvbGUubG9nKCJbTG9jYWxWYXJpYWJsZXMgV29ya2VyXSIsLi4uZSkpfWFzeW5jIGZ1bmN0aW9uIGMoZSx0LG4saSl7Y29uc3Qgbz1hd2FpdCBlLnBvc3QoIlJ1bnRpbWUuZ2V0UHJvcGVydGllcyIse29iamVjdElkOnQsb3duUHJvcGVydGllczohMH0pO2lbbl09by5yZXN1bHQuZmlsdGVyKGU9PiJsZW5ndGgiIT09ZS5uYW1lJiYhaXNOYU4ocGFyc2VJbnQoZS5uYW1lLDEwKSkpLnNvcnQoKGUsdCk9PnBhcnNlSW50KGUubmFtZSwxMCktcGFyc2VJbnQodC5uYW1lLDEwKSkubWFwKGU9PmUudmFsdWU/LnZhbHVlKX1hc3luYyBmdW5jdGlvbiByKGUsdCxuLGkpe2NvbnN0IG89YXdhaXQgZS5wb3N0KCJSdW50aW1lLmdldFByb3BlcnRpZXMiLHtvYmplY3RJZDp0LG93blByb3BlcnRpZXM6ITB9KTtpW25dPW8ucmVzdWx0Lm1hcChlPT5bZS5uYW1lLGUudmFsdWU/LnZhbHVlXSkucmVkdWNlKChlLFt0LG5dKT0+KGVbdF09bixlKSx7fSl9ZnVuY3Rpb24gdShlLHQpe2UudmFsdWUmJigidmFsdWUiaW4gZS52YWx1ZT92b2lkIDA9PT1lLnZhbHVlLnZhbHVlfHxudWxsPT09ZS52YWx1ZS52YWx1ZT90W2UubmFtZV09YDwke2UudmFsdWUudmFsdWV9PmA6dFtlLm5hbWVdPWUudmFsdWUudmFsdWU6ImRlc2NyaXB0aW9uImluIGUudmFsdWUmJiJmdW5jdGlvbiIhPT1lLnZhbHVlLnR5cGU/dFtlLm5hbWVdPWA8JHtlLnZhbHVlLmRlc2NyaXB0aW9ufT5gOiJ1bmRlZmluZWQiPT09ZS52YWx1ZS50eXBlJiYodFtlLm5hbWVdPSI8dW5kZWZpbmVkPiIpKX1hc3luYyBmdW5jdGlvbiBsKGUsdCl7Y29uc3Qgbj1hd2FpdCBlLnBvc3QoIlJ1bnRpbWUuZ2V0UHJvcGVydGllcyIse29iamVjdElkOnQsb3duUHJvcGVydGllczohMH0pLGk9e307Zm9yKGNvbnN0IHQgb2Ygbi5yZXN1bHQpaWYodC52YWx1ZT8ub2JqZWN0SWQmJiJBcnJheSI9PT10LnZhbHVlLmNsYXNzTmFtZSl7Y29uc3Qgbj10LnZhbHVlLm9iamVjdElkO2F3YWl0IGMoZSxuLHQubmFtZSxpKX1lbHNlIGlmKHQudmFsdWU/Lm9iamVjdElkJiYiT2JqZWN0Ij09PXQudmFsdWUuY2xhc3NOYW1lKXtjb25zdCBuPXQudmFsdWUub2JqZWN0SWQ7YXdhaXQgcihlLG4sdC5uYW1lLGkpfWVsc2UgdC52YWx1ZSYmdSh0LGkpO3JldHVybiBpfWxldCBmOyhhc3luYyBmdW5jdGlvbigpe2NvbnN0IHQ9bmV3IGU7dC5jb25uZWN0VG9NYWluVGhyZWFkKCkscygiQ29ubmVjdGVkIHRvIG1haW4gdGhyZWFkIik7bGV0IG49ITE7dC5vbigiRGVidWdnZXIucmVzdW1lZCIsKCk9PntuPSExfSksdC5vbigiRGVidWdnZXIucGF1c2VkIixlPT57bj0hMCxhc3luYyBmdW5jdGlvbihlLHtyZWFzb246dCxkYXRhOntvYmplY3RJZDpufSxjYWxsRnJhbWVzOml9KXtpZigiZXhjZXB0aW9uIiE9PXQmJiJwcm9taXNlUmVqZWN0aW9uIiE9PXQpcmV0dXJuO2lmKGY/LigpLG51bGw9PW4pcmV0dXJuO2NvbnN0IGE9W107Zm9yKGxldCB0PTA7dDxpLmxlbmd0aDt0Kyspe2NvbnN0e3Njb3BlQ2hhaW46bixmdW5jdGlvbk5hbWU6byx0aGlzOnN9PWlbdF0sYz1uLmZpbmQoZT0+ImxvY2FsIj09PWUudHlwZSkscj0iZ2xvYmFsIiE9PXMuY2xhc3NOYW1lJiZzLmNsYXNzTmFtZT9gJHtzLmNsYXNzTmFtZX0uJHtvfWA6bztpZih2b2lkIDA9PT1jPy5vYmplY3Qub2JqZWN0SWQpYVt0XT17ZnVuY3Rpb246cn07ZWxzZXtjb25zdCBuPWF3YWl0IGwoZSxjLm9iamVjdC5vYmplY3RJZCk7YVt0XT17ZnVuY3Rpb246cix2YXJzOm59fX1hd2FpdCBlLnBvc3QoIlJ1bnRpbWUuY2FsbEZ1bmN0aW9uT24iLHtmdW5jdGlvbkRlY2xhcmF0aW9uOmBmdW5jdGlvbigpIHsgdGhpcy4ke299ID0gdGhpcy4ke299IHx8ICR7SlNPTi5zdHJpbmdpZnkoYSl9OyB9YCxzaWxlbnQ6ITAsb2JqZWN0SWQ6bn0pLGF3YWl0IGUucG9zdCgiUnVudGltZS5yZWxlYXNlT2JqZWN0Iix7b2JqZWN0SWQ6bn0pfSh0LGUucGFyYW1zKS50aGVuKGFzeW5jKCk9PntuJiZhd2FpdCB0LnBvc3QoIkRlYnVnZ2VyLnJlc3VtZSIpfSxhc3luYyBlPT57biYmYXdhaXQgdC5wb3N0KCJEZWJ1Z2dlci5yZXN1bWUiKX0pfSksYXdhaXQgdC5wb3N0KCJEZWJ1Z2dlci5lbmFibGUiKTtjb25zdCBpPSExIT09YS5jYXB0dXJlQWxsRXhjZXB0aW9ucztpZihhd2FpdCB0LnBvc3QoIkRlYnVnZ2VyLnNldFBhdXNlT25FeGNlcHRpb25zIix7c3RhdGU6aT8iYWxsIjoidW5jYXVnaHQifSksaSl7Y29uc3QgZT1hLm1heEV4Y2VwdGlvbnNQZXJTZWNvbmR8fDUwO2Y9ZnVuY3Rpb24oZSx0LG4pe2xldCBpPTAsbz01LGE9MDtyZXR1cm4gc2V0SW50ZXJ2YWwoKCk9PnswPT09YT9pPmUmJihvKj0yLG4obyksbz44NjQwMCYmKG89ODY0MDApLGE9byk6KGEtPTEsMD09PWEmJnQoKSksaT0wfSwxZTMpLnVucmVmKCksKCk9PntpKz0xfX0oZSxhc3luYygpPT57cygiUmF0ZS1saW1pdCBsaWZ0ZWQuIiksYXdhaXQgdC5wb3N0KCJEZWJ1Z2dlci5zZXRQYXVzZU9uRXhjZXB0aW9ucyIse3N0YXRlOiJhbGwifSl9LGFzeW5jIGU9PntzKGBSYXRlLWxpbWl0IGV4Y2VlZGVkLiBEaXNhYmxpbmcgY2FwdHVyaW5nIG9mIGNhdWdodCBleGNlcHRpb25zIGZvciAke2V9IHNlY29uZHMuYCksYXdhaXQgdC5wb3N0KCJEZWJ1Z2dlci5zZXRQYXVzZU9uRXhjZXB0aW9ucyIse3N0YXRlOiJ1bmNhdWdodCJ9KX0pfX0pKCkuY2F0Y2goZT0+e3MoIkZhaWxlZCB0byBzdGFydCBkZWJ1Z2dlciIsZSl9KSxzZXRJbnRlcnZhbCgoKT0+e30sMWU0KTs="; | ||
| function log(...args) { | ||
| debug.log('[LocalVariables]', ...args); | ||
| debug.log("[LocalVariables]", ...args); | ||
| } | ||
| /** | ||
| * Adds local variables to exception frames | ||
| */ | ||
| const localVariablesAsyncIntegration = defineIntegration((( | ||
| integrationOptions = {}, | ||
| ) => { | ||
| const localVariablesAsyncIntegration = defineIntegration(((integrationOptions = {}) => { | ||
| function addLocalVariablesToException(exception, localVariables) { | ||
| // Filter out frames where the function name is `new Promise` since these are in the error.stack frames | ||
| // but do not appear in the debugger call frames | ||
| const frames = (exception.stacktrace?.frames || []).filter(frame => frame.function !== 'new Promise'); | ||
| const frames = (exception.stacktrace?.frames || []).filter((frame) => frame.function !== "new Promise"); | ||
| for (let i = 0; i < frames.length; i++) { | ||
| // Sentry frames are in reverse order | ||
| const frameIndex = frames.length - i - 1; | ||
| const frameLocalVariables = localVariables[i]; | ||
| const frame = frames[frameIndex]; | ||
| if (!frame || !frameLocalVariables) { | ||
| // Drop out if we run out of frames to match up | ||
| break; | ||
| } | ||
| if ( | ||
| // We need to have vars to add | ||
| frameLocalVariables.vars === undefined || | ||
| // Only skip out-of-app frames if includeOutOfAppFrames is not true | ||
| (frame.in_app === false && integrationOptions.includeOutOfAppFrames !== true) || | ||
| // The function names need to match | ||
| frameLocalVariables.vars === void 0 || // Only skip out-of-app frames if includeOutOfAppFrames is not true | ||
| frame.in_app === false && integrationOptions.includeOutOfAppFrames !== true || // The function names need to match | ||
| !functionNamesMatch(frame.function, frameLocalVariables.function) | ||
@@ -47,26 +29,15 @@ ) { | ||
| } | ||
| frame.vars = frameLocalVariables.vars; | ||
| } | ||
| } | ||
| function addLocalVariablesToEvent(event, hint) { | ||
| if ( | ||
| hint.originalException && | ||
| typeof hint.originalException === 'object' && | ||
| LOCAL_VARIABLES_KEY in hint.originalException && | ||
| Array.isArray(hint.originalException[LOCAL_VARIABLES_KEY]) | ||
| ) { | ||
| if (hint.originalException && typeof hint.originalException === "object" && LOCAL_VARIABLES_KEY in hint.originalException && Array.isArray(hint.originalException[LOCAL_VARIABLES_KEY])) { | ||
| for (const exception of event.exception?.values || []) { | ||
| addLocalVariablesToException(exception, hint.originalException[LOCAL_VARIABLES_KEY]); | ||
| } | ||
| hint.originalException[LOCAL_VARIABLES_KEY] = undefined; | ||
| hint.originalException[LOCAL_VARIABLES_KEY] = void 0; | ||
| } | ||
| return event; | ||
| } | ||
| async function startInspector() { | ||
| // We load inspector dynamically because on some platforms Node is built without inspector support | ||
| const inspector = await import('node:inspector'); | ||
@@ -77,3 +48,2 @@ if (!inspector.url()) { | ||
| } | ||
| function startWorker(options) { | ||
@@ -84,41 +54,30 @@ const worker = new Worker(new URL(`data:application/javascript;base64,${base64WorkerScript}`), { | ||
| execArgv: [], | ||
| env: { ...process.env, NODE_OPTIONS: undefined }, | ||
| env: { ...process.env, NODE_OPTIONS: void 0 } | ||
| }); | ||
| process.on('exit', () => { | ||
| // eslint-disable-next-line @typescript-eslint/no-floating-promises | ||
| process.on("exit", () => { | ||
| worker.terminate(); | ||
| }); | ||
| worker.once('error', (err) => { | ||
| log('Worker error', err); | ||
| worker.once("error", (err) => { | ||
| log("Worker error", err); | ||
| }); | ||
| worker.once('exit', (code) => { | ||
| log('Worker exit', code); | ||
| worker.once("exit", (code) => { | ||
| log("Worker exit", code); | ||
| }); | ||
| // Ensure this thread can't block app exit | ||
| worker.unref(); | ||
| } | ||
| return { | ||
| name: 'LocalVariablesAsync', | ||
| name: "LocalVariablesAsync", | ||
| async setup(client) { | ||
| const clientOptions = client.getOptions(); | ||
| if (!clientOptions.includeLocalVariables) { | ||
| return; | ||
| } | ||
| if (await isDebuggerEnabled()) { | ||
| debug.warn('Local variables capture has been disabled because the debugger was already enabled'); | ||
| debug.warn("Local variables capture has been disabled because the debugger was already enabled"); | ||
| return; | ||
| } | ||
| const options = { | ||
| ...integrationOptions, | ||
| debug: debug.isEnabled(), | ||
| debug: debug.isEnabled() | ||
| }; | ||
| startInspector().then( | ||
@@ -129,8 +88,8 @@ () => { | ||
| } catch (e) { | ||
| debug.error('Failed to start worker', e); | ||
| debug.error("Failed to start worker", e); | ||
| } | ||
| }, | ||
| e => { | ||
| debug.error('Failed to start inspector', e); | ||
| }, | ||
| (e) => { | ||
| debug.error("Failed to start inspector", e); | ||
| } | ||
| ); | ||
@@ -140,7 +99,7 @@ }, | ||
| return addLocalVariablesToEvent(event, hint); | ||
| }, | ||
| } | ||
| }; | ||
| }) ); | ||
| })); | ||
| export { base64WorkerScript, localVariablesAsyncIntegration }; | ||
| //# sourceMappingURL=local-variables-async.js.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"local-variables-async.js","sources":["../../../../src/integrations/local-variables/local-variables-async.ts"],"sourcesContent":["import { Worker } from 'node:worker_threads';\nimport type { Event, EventHint, Exception, IntegrationFn } from '@sentry/core';\nimport { debug, defineIntegration } from '@sentry/core';\nimport type { NodeClient } from '../../sdk/client';\nimport { isDebuggerEnabled } from '../../utils/debug';\nimport type { FrameVariables, LocalVariablesIntegrationOptions, LocalVariablesWorkerArgs } from './common';\nimport { functionNamesMatch, LOCAL_VARIABLES_KEY } from './common';\n\n// This string is a placeholder that gets overwritten with the worker code.\nexport const base64WorkerScript = '###LocalVariablesWorkerScript###';\n\nfunction log(...args: unknown[]): void {\n debug.log('[LocalVariables]', ...args);\n}\n\n/**\n * Adds local variables to exception frames\n */\nexport const localVariablesAsyncIntegration = defineIntegration(((\n integrationOptions: LocalVariablesIntegrationOptions = {},\n) => {\n function addLocalVariablesToException(exception: Exception, localVariables: FrameVariables[]): void {\n // Filter out frames where the function name is `new Promise` since these are in the error.stack frames\n // but do not appear in the debugger call frames\n const frames = (exception.stacktrace?.frames || []).filter(frame => frame.function !== 'new Promise');\n\n for (let i = 0; i < frames.length; i++) {\n // Sentry frames are in reverse order\n const frameIndex = frames.length - i - 1;\n\n const frameLocalVariables = localVariables[i];\n const frame = frames[frameIndex];\n\n if (!frame || !frameLocalVariables) {\n // Drop out if we run out of frames to match up\n break;\n }\n\n if (\n // We need to have vars to add\n frameLocalVariables.vars === undefined ||\n // Only skip out-of-app frames if includeOutOfAppFrames is not true\n (frame.in_app === false && integrationOptions.includeOutOfAppFrames !== true) ||\n // The function names need to match\n !functionNamesMatch(frame.function, frameLocalVariables.function)\n ) {\n continue;\n }\n\n frame.vars = frameLocalVariables.vars;\n }\n }\n\n function addLocalVariablesToEvent(event: Event, hint: EventHint): Event {\n if (\n hint.originalException &&\n typeof hint.originalException === 'object' &&\n LOCAL_VARIABLES_KEY in hint.originalException &&\n Array.isArray(hint.originalException[LOCAL_VARIABLES_KEY])\n ) {\n for (const exception of event.exception?.values || []) {\n addLocalVariablesToException(exception, hint.originalException[LOCAL_VARIABLES_KEY]);\n }\n\n hint.originalException[LOCAL_VARIABLES_KEY] = undefined;\n }\n\n return event;\n }\n\n async function startInspector(): Promise<void> {\n // We load inspector dynamically because on some platforms Node is built without inspector support\n const inspector = await import('node:inspector');\n if (!inspector.url()) {\n inspector.open(0);\n }\n }\n\n function startWorker(options: LocalVariablesWorkerArgs): void {\n const worker = new Worker(new URL(`data:application/javascript;base64,${base64WorkerScript}`), {\n workerData: options,\n // We don't want any Node args to be passed to the worker\n execArgv: [],\n env: { ...process.env, NODE_OPTIONS: undefined },\n });\n\n process.on('exit', () => {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n worker.terminate();\n });\n\n worker.once('error', (err: Error) => {\n log('Worker error', err);\n });\n\n worker.once('exit', (code: number) => {\n log('Worker exit', code);\n });\n\n // Ensure this thread can't block app exit\n worker.unref();\n }\n\n return {\n name: 'LocalVariablesAsync',\n async setup(client: NodeClient) {\n const clientOptions = client.getOptions();\n\n if (!clientOptions.includeLocalVariables) {\n return;\n }\n\n if (await isDebuggerEnabled()) {\n debug.warn('Local variables capture has been disabled because the debugger was already enabled');\n return;\n }\n\n const options: LocalVariablesWorkerArgs = {\n ...integrationOptions,\n debug: debug.isEnabled(),\n };\n\n startInspector().then(\n () => {\n try {\n startWorker(options);\n } catch (e) {\n debug.error('Failed to start worker', e);\n }\n },\n e => {\n debug.error('Failed to start inspector', e);\n },\n );\n },\n processEvent(event: Event, hint: EventHint): Event {\n return addLocalVariablesToEvent(event, hint);\n },\n };\n}) satisfies IntegrationFn);\n"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"local-variables-async.js","sources":["../../../../src/integrations/local-variables/local-variables-async.ts"],"sourcesContent":["import { Worker } from 'node:worker_threads';\nimport type { Event, EventHint, Exception, IntegrationFn } from '@sentry/core';\nimport { debug, defineIntegration } from '@sentry/core';\nimport type { NodeClient } from '../../sdk/client';\nimport { isDebuggerEnabled } from '../../utils/debug';\nimport type { FrameVariables, LocalVariablesIntegrationOptions, LocalVariablesWorkerArgs } from './common';\nimport { functionNamesMatch, LOCAL_VARIABLES_KEY } from './common';\n\n// This string is a placeholder that gets overwritten with the worker code.\nexport const base64WorkerScript = '###LocalVariablesWorkerScript###';\n\nfunction log(...args: unknown[]): void {\n debug.log('[LocalVariables]', ...args);\n}\n\n/**\n * Adds local variables to exception frames\n */\nexport const localVariablesAsyncIntegration = defineIntegration(((\n integrationOptions: LocalVariablesIntegrationOptions = {},\n) => {\n function addLocalVariablesToException(exception: Exception, localVariables: FrameVariables[]): void {\n // Filter out frames where the function name is `new Promise` since these are in the error.stack frames\n // but do not appear in the debugger call frames\n const frames = (exception.stacktrace?.frames || []).filter(frame => frame.function !== 'new Promise');\n\n for (let i = 0; i < frames.length; i++) {\n // Sentry frames are in reverse order\n const frameIndex = frames.length - i - 1;\n\n const frameLocalVariables = localVariables[i];\n const frame = frames[frameIndex];\n\n if (!frame || !frameLocalVariables) {\n // Drop out if we run out of frames to match up\n break;\n }\n\n if (\n // We need to have vars to add\n frameLocalVariables.vars === undefined ||\n // Only skip out-of-app frames if includeOutOfAppFrames is not true\n (frame.in_app === false && integrationOptions.includeOutOfAppFrames !== true) ||\n // The function names need to match\n !functionNamesMatch(frame.function, frameLocalVariables.function)\n ) {\n continue;\n }\n\n frame.vars = frameLocalVariables.vars;\n }\n }\n\n function addLocalVariablesToEvent(event: Event, hint: EventHint): Event {\n if (\n hint.originalException &&\n typeof hint.originalException === 'object' &&\n LOCAL_VARIABLES_KEY in hint.originalException &&\n Array.isArray(hint.originalException[LOCAL_VARIABLES_KEY])\n ) {\n for (const exception of event.exception?.values || []) {\n addLocalVariablesToException(exception, hint.originalException[LOCAL_VARIABLES_KEY]);\n }\n\n hint.originalException[LOCAL_VARIABLES_KEY] = undefined;\n }\n\n return event;\n }\n\n async function startInspector(): Promise<void> {\n // We load inspector dynamically because on some platforms Node is built without inspector support\n const inspector = await import('node:inspector');\n if (!inspector.url()) {\n inspector.open(0);\n }\n }\n\n function startWorker(options: LocalVariablesWorkerArgs): void {\n const worker = new Worker(new URL(`data:application/javascript;base64,${base64WorkerScript}`), {\n workerData: options,\n // We don't want any Node args to be passed to the worker\n execArgv: [],\n env: { ...process.env, NODE_OPTIONS: undefined },\n });\n\n process.on('exit', () => {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n worker.terminate();\n });\n\n worker.once('error', (err: Error) => {\n log('Worker error', err);\n });\n\n worker.once('exit', (code: number) => {\n log('Worker exit', code);\n });\n\n // Ensure this thread can't block app exit\n worker.unref();\n }\n\n return {\n name: 'LocalVariablesAsync',\n async setup(client: NodeClient) {\n const clientOptions = client.getOptions();\n\n if (!clientOptions.includeLocalVariables) {\n return;\n }\n\n if (await isDebuggerEnabled()) {\n debug.warn('Local variables capture has been disabled because the debugger was already enabled');\n return;\n }\n\n const options: LocalVariablesWorkerArgs = {\n ...integrationOptions,\n debug: debug.isEnabled(),\n };\n\n startInspector().then(\n () => {\n try {\n startWorker(options);\n } catch (e) {\n debug.error('Failed to start worker', e);\n }\n },\n e => {\n debug.error('Failed to start inspector', e);\n },\n );\n },\n processEvent(event: Event, hint: EventHint): Event {\n return addLocalVariablesToEvent(event, hint);\n },\n };\n}) satisfies IntegrationFn);\n"],"names":[],"mappings":"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"} |
@@ -6,29 +6,16 @@ import { defineIntegration, LRUMap, getClient, debug } from '@sentry/core'; | ||
| /** Creates a unique hash from stack frames */ | ||
| function hashFrames(frames) { | ||
| if (frames === undefined) { | ||
| if (frames === void 0) { | ||
| return; | ||
| } | ||
| // Only hash the 10 most recent frames (ie. the last 10) | ||
| return frames.slice(-10).reduce((acc, frame) => `${acc},${frame.function},${frame.lineno},${frame.colno}`, ''); | ||
| return frames.slice(-10).reduce((acc, frame) => `${acc},${frame.function},${frame.lineno},${frame.colno}`, ""); | ||
| } | ||
| /** | ||
| * We use the stack parser to create a unique hash from the exception stack trace | ||
| * This is used to lookup vars when the exception passes through the event processor | ||
| */ | ||
| function hashFromStack(stackParser, stack) { | ||
| if (stack === undefined) { | ||
| return undefined; | ||
| if (stack === void 0) { | ||
| return void 0; | ||
| } | ||
| return hashFrames(stackParser(stack, 1)); | ||
| } | ||
| /** Creates a container for callbacks to be called sequentially */ | ||
| function createCallbackList(complete) { | ||
| // A collection of callbacks to be executed last to first | ||
| let callbacks = []; | ||
| let completedCalled = false; | ||
@@ -43,97 +30,69 @@ function checkedComplete(result) { | ||
| } | ||
| // complete should be called last | ||
| callbacks.push(checkedComplete); | ||
| function add(fn) { | ||
| callbacks.push(fn); | ||
| } | ||
| function next(result) { | ||
| const popped = callbacks.pop() || checkedComplete; | ||
| try { | ||
| popped(result); | ||
| } catch { | ||
| // If there is an error, we still want to call the complete callback | ||
| checkedComplete(result); | ||
| } | ||
| } | ||
| return { add, next }; | ||
| } | ||
| /** | ||
| * Promise API is available as `Experimental` and in Node 19 only. | ||
| * | ||
| * Callback-based API is `Stable` since v14 and `Experimental` since v8. | ||
| * Because of that, we are creating our own `AsyncSession` class. | ||
| * | ||
| * https://nodejs.org/docs/latest-v19.x/api/inspector.html#promises-api | ||
| * https://nodejs.org/docs/latest-v14.x/api/inspector.html | ||
| */ | ||
| class AsyncSession { | ||
| class AsyncSession { | ||
| /** Throws if inspector API is not available */ | ||
| constructor( _session) {this._session = _session; | ||
| // | ||
| constructor(_session) { | ||
| this._session = _session; | ||
| } | ||
| static async create(orDefault) { | ||
| static async create(orDefault) { | ||
| if (orDefault) { | ||
| return orDefault; | ||
| } | ||
| const inspector = await import('node:inspector'); | ||
| return new AsyncSession(new inspector.Session()); | ||
| } | ||
| /** @inheritdoc */ | ||
| configureAndConnect(onPause, captureAll) { | ||
| configureAndConnect(onPause, captureAll) { | ||
| this._session.connect(); | ||
| this._session.on('Debugger.paused', event => { | ||
| this._session.on("Debugger.paused", (event) => { | ||
| onPause(event, () => { | ||
| // After the pause work is complete, resume execution or the exception context memory is leaked | ||
| this._session.post('Debugger.resume'); | ||
| this._session.post("Debugger.resume"); | ||
| }); | ||
| }); | ||
| this._session.post('Debugger.enable'); | ||
| this._session.post('Debugger.setPauseOnExceptions', { state: captureAll ? 'all' : 'uncaught' }); | ||
| this._session.post("Debugger.enable"); | ||
| this._session.post("Debugger.setPauseOnExceptions", { state: captureAll ? "all" : "uncaught" }); | ||
| } | ||
| setPauseOnExceptions(captureAll) { | ||
| this._session.post('Debugger.setPauseOnExceptions', { state: captureAll ? 'all' : 'uncaught' }); | ||
| setPauseOnExceptions(captureAll) { | ||
| this._session.post("Debugger.setPauseOnExceptions", { state: captureAll ? "all" : "uncaught" }); | ||
| } | ||
| /** @inheritdoc */ | ||
| getLocalVariables(objectId, complete) { | ||
| this._getProperties(objectId, props => { | ||
| getLocalVariables(objectId, complete) { | ||
| this._getProperties(objectId, (props) => { | ||
| const { add, next } = createCallbackList(complete); | ||
| for (const prop of props) { | ||
| if (prop.value?.objectId && prop.value.className === 'Array') { | ||
| if (prop.value?.objectId && prop.value.className === "Array") { | ||
| const id = prop.value.objectId; | ||
| add(vars => this._unrollArray(id, prop.name, vars, next)); | ||
| } else if (prop.value?.objectId && prop.value.className === 'Object') { | ||
| add((vars) => this._unrollArray(id, prop.name, vars, next)); | ||
| } else if (prop.value?.objectId && prop.value.className === "Object") { | ||
| const id = prop.value.objectId; | ||
| add(vars => this._unrollObject(id, prop.name, vars, next)); | ||
| add((vars) => this._unrollObject(id, prop.name, vars, next)); | ||
| } else if (prop.value) { | ||
| add(vars => this._unrollOther(prop, vars, next)); | ||
| add((vars) => this._unrollOther(prop, vars, next)); | ||
| } | ||
| } | ||
| next({}); | ||
| }); | ||
| } | ||
| /** | ||
| * Gets all the PropertyDescriptors of an object | ||
| */ | ||
| _getProperties(objectId, next) { | ||
| _getProperties(objectId, next) { | ||
| this._session.post( | ||
| 'Runtime.getProperties', | ||
| "Runtime.getProperties", | ||
| { | ||
| objectId, | ||
| ownProperties: true, | ||
| ownProperties: true | ||
| }, | ||
@@ -146,43 +105,33 @@ (err, params) => { | ||
| } | ||
| }, | ||
| } | ||
| ); | ||
| } | ||
| /** | ||
| * Unrolls an array property | ||
| */ | ||
| _unrollArray(objectId, name, vars, next) { | ||
| this._getProperties(objectId, props => { | ||
| vars[name] = props | ||
| .filter(v => v.name !== 'length' && !isNaN(parseInt(v.name, 10))) | ||
| .sort((a, b) => parseInt(a.name, 10) - parseInt(b.name, 10)) | ||
| .map(v => v.value?.value); | ||
| _unrollArray(objectId, name, vars, next) { | ||
| this._getProperties(objectId, (props) => { | ||
| vars[name] = props.filter((v) => v.name !== "length" && !isNaN(parseInt(v.name, 10))).sort((a, b) => parseInt(a.name, 10) - parseInt(b.name, 10)).map((v) => v.value?.value); | ||
| next(vars); | ||
| }); | ||
| } | ||
| /** | ||
| * Unrolls an object property | ||
| */ | ||
| _unrollObject(objectId, name, vars, next) { | ||
| this._getProperties(objectId, props => { | ||
| vars[name] = props | ||
| .map(v => [v.name, v.value?.value]) | ||
| .reduce((obj, [key, val]) => { | ||
| obj[key] = val; | ||
| return obj; | ||
| }, {} ); | ||
| _unrollObject(objectId, name, vars, next) { | ||
| this._getProperties(objectId, (props) => { | ||
| vars[name] = props.map((v) => [v.name, v.value?.value]).reduce((obj, [key, val]) => { | ||
| obj[key] = val; | ||
| return obj; | ||
| }, {}); | ||
| next(vars); | ||
| }); | ||
| } | ||
| /** | ||
| * Unrolls other properties | ||
| */ | ||
| _unrollOther(prop, vars, next) { | ||
| _unrollOther(prop, vars, next) { | ||
| if (prop.value) { | ||
| if ('value' in prop.value) { | ||
| if (prop.value.value === undefined || prop.value.value === null) { | ||
| if ("value" in prop.value) { | ||
| if (prop.value.value === void 0 || prop.value.value === null) { | ||
| vars[prop.name] = `<${prop.value.value}>`; | ||
@@ -192,63 +141,37 @@ } else { | ||
| } | ||
| } else if ('description' in prop.value && prop.value.type !== 'function') { | ||
| } else if ("description" in prop.value && prop.value.type !== "function") { | ||
| vars[prop.name] = `<${prop.value.description}>`; | ||
| } else if (prop.value.type === 'undefined') { | ||
| vars[prop.name] = '<undefined>'; | ||
| } else if (prop.value.type === "undefined") { | ||
| vars[prop.name] = "<undefined>"; | ||
| } | ||
| } | ||
| next(vars); | ||
| } | ||
| } | ||
| const INTEGRATION_NAME = 'LocalVariables'; | ||
| /** | ||
| * Adds local variables to exception frames | ||
| */ | ||
| const _localVariablesSyncIntegration = (( | ||
| options = {}, | ||
| sessionOverride, | ||
| ) => { | ||
| const INTEGRATION_NAME = "LocalVariables"; | ||
| const _localVariablesSyncIntegration = ((options = {}, sessionOverride) => { | ||
| const cachedFrames = new LRUMap(20); | ||
| let rateLimiter; | ||
| let shouldProcessEvent = false; | ||
| function addLocalVariablesToException(exception) { | ||
| const hash = hashFrames(exception.stacktrace?.frames); | ||
| if (hash === undefined) { | ||
| if (hash === void 0) { | ||
| return; | ||
| } | ||
| // Check if we have local variables for an exception that matches the hash | ||
| // remove is identical to get but also removes the entry from the cache | ||
| const cachedFrame = cachedFrames.remove(hash); | ||
| if (cachedFrame === undefined) { | ||
| if (cachedFrame === void 0) { | ||
| return; | ||
| } | ||
| // Filter out frames where the function name is `new Promise` since these are in the error.stack frames | ||
| // but do not appear in the debugger call frames | ||
| const frames = (exception.stacktrace?.frames || []).filter(frame => frame.function !== 'new Promise'); | ||
| const frames = (exception.stacktrace?.frames || []).filter((frame) => frame.function !== "new Promise"); | ||
| for (let i = 0; i < frames.length; i++) { | ||
| // Sentry frames are in reverse order | ||
| const frameIndex = frames.length - i - 1; | ||
| const cachedFrameVariable = cachedFrame[i]; | ||
| const frameVariable = frames[frameIndex]; | ||
| // Drop out if we run out of frames to match up | ||
| if (!frameVariable || !cachedFrameVariable) { | ||
| break; | ||
| } | ||
| if ( | ||
| // We need to have vars to add | ||
| cachedFrameVariable.vars === undefined || | ||
| // Only skip out-of-app frames if includeOutOfAppFrames is not true | ||
| (frameVariable.in_app === false && options.includeOutOfAppFrames !== true) || | ||
| // The function names need to match | ||
| cachedFrameVariable.vars === void 0 || // Only skip out-of-app frames if includeOutOfAppFrames is not true | ||
| frameVariable.in_app === false && options.includeOutOfAppFrames !== true || // The function names need to match | ||
| !functionNamesMatch(frameVariable.function, cachedFrameVariable.function) | ||
@@ -258,7 +181,5 @@ ) { | ||
| } | ||
| frameVariable.vars = cachedFrameVariable.vars; | ||
| } | ||
| } | ||
| function addLocalVariablesToEvent(event) { | ||
@@ -268,71 +189,43 @@ for (const exception of event.exception?.values || []) { | ||
| } | ||
| return event; | ||
| } | ||
| let setupPromise; | ||
| async function setup() { | ||
| const client = getClient(); | ||
| const clientOptions = client?.getOptions(); | ||
| if (!clientOptions?.includeLocalVariables) { | ||
| return; | ||
| } | ||
| // Only setup this integration if the Node version is >= v18 | ||
| // https://github.com/getsentry/sentry-javascript/issues/7697 | ||
| const unsupportedNodeVersion = NODE_MAJOR < 18; | ||
| if (unsupportedNodeVersion) { | ||
| debug.log('The `LocalVariables` integration is only supported on Node >= v18.'); | ||
| debug.log("The `LocalVariables` integration is only supported on Node >= v18."); | ||
| return; | ||
| } | ||
| if (await isDebuggerEnabled()) { | ||
| debug.warn('Local variables capture has been disabled because the debugger was already enabled'); | ||
| debug.warn("Local variables capture has been disabled because the debugger was already enabled"); | ||
| return; | ||
| } | ||
| try { | ||
| const session = await AsyncSession.create(sessionOverride); | ||
| const handlePaused = ( | ||
| stackParser, | ||
| { params: { reason, data, callFrames } }, | ||
| complete, | ||
| ) => { | ||
| if (reason !== 'exception' && reason !== 'promiseRejection') { | ||
| const handlePaused = (stackParser, { params: { reason, data, callFrames } }, complete) => { | ||
| if (reason !== "exception" && reason !== "promiseRejection") { | ||
| complete(); | ||
| return; | ||
| } | ||
| rateLimiter?.(); | ||
| // data.description contains the original error.stack | ||
| const exceptionHash = hashFromStack(stackParser, data.description); | ||
| if (exceptionHash == undefined) { | ||
| if (exceptionHash == void 0) { | ||
| complete(); | ||
| return; | ||
| } | ||
| const { add, next } = createCallbackList(frames => { | ||
| const { add, next } = createCallbackList((frames) => { | ||
| cachedFrames.set(exceptionHash, frames); | ||
| complete(); | ||
| }); | ||
| // Because we're queuing up and making all these calls synchronously, we can potentially overflow the stack | ||
| // For this reason we only attempt to get local variables for the first 5 frames | ||
| for (let i = 0; i < Math.min(callFrames.length, 5); i++) { | ||
| // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
| const { scopeChain, functionName, this: obj } = callFrames[i]; | ||
| const localScope = scopeChain.find(scope => scope.type === 'local'); | ||
| // obj.className is undefined in ESM modules | ||
| const fn = obj.className === 'global' || !obj.className ? functionName : `${obj.className}.${functionName}`; | ||
| if (localScope?.object.objectId === undefined) { | ||
| add(frames => { | ||
| const localScope = scopeChain.find((scope) => scope.type === "local"); | ||
| const fn = obj.className === "global" || !obj.className ? functionName : `${obj.className}.${functionName}`; | ||
| if (localScope?.object.objectId === void 0) { | ||
| add((frames) => { | ||
| frames[i] = { function: fn }; | ||
@@ -343,46 +236,38 @@ next(frames); | ||
| const id = localScope.object.objectId; | ||
| add(frames => | ||
| session.getLocalVariables(id, vars => { | ||
| add( | ||
| (frames) => session.getLocalVariables(id, (vars) => { | ||
| frames[i] = { function: fn, vars }; | ||
| next(frames); | ||
| }), | ||
| }) | ||
| ); | ||
| } | ||
| } | ||
| next([]); | ||
| }; | ||
| const captureAll = options.captureAllExceptions !== false; | ||
| session.configureAndConnect( | ||
| (ev, complete) => | ||
| handlePaused(clientOptions.stackParser, ev , complete), | ||
| captureAll, | ||
| (ev, complete) => handlePaused(clientOptions.stackParser, ev, complete), | ||
| captureAll | ||
| ); | ||
| if (captureAll) { | ||
| const max = options.maxExceptionsPerSecond || 50; | ||
| rateLimiter = createRateLimiter( | ||
| max, | ||
| () => { | ||
| debug.log('Local variables rate-limit lifted.'); | ||
| debug.log("Local variables rate-limit lifted."); | ||
| session.setPauseOnExceptions(true); | ||
| }, | ||
| seconds => { | ||
| (seconds) => { | ||
| debug.log( | ||
| `Local variables rate-limit exceeded. Disabling capturing of caught exceptions for ${seconds} seconds.`, | ||
| `Local variables rate-limit exceeded. Disabling capturing of caught exceptions for ${seconds} seconds.` | ||
| ); | ||
| session.setPauseOnExceptions(false); | ||
| }, | ||
| } | ||
| ); | ||
| } | ||
| shouldProcessEvent = true; | ||
| } catch (error) { | ||
| debug.log('The `LocalVariables` integration failed to start.', error); | ||
| debug.log("The `LocalVariables` integration failed to start.", error); | ||
| } | ||
| } | ||
| return { | ||
@@ -395,7 +280,5 @@ name: INTEGRATION_NAME, | ||
| await setupPromise; | ||
| if (shouldProcessEvent) { | ||
| return addLocalVariablesToEvent(event); | ||
| } | ||
| return event; | ||
@@ -409,9 +292,5 @@ }, | ||
| return cachedFrames.values()[0]; | ||
| }, | ||
| } | ||
| }; | ||
| }) ; | ||
| /** | ||
| * Adds local variables to exception frames. | ||
| */ | ||
| }); | ||
| const localVariablesSyncIntegration = defineIntegration(_localVariablesSyncIntegration); | ||
@@ -418,0 +297,0 @@ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"local-variables-sync.js","sources":["../../../../src/integrations/local-variables/local-variables-sync.ts"],"sourcesContent":["import type { Debugger, InspectorNotification, Runtime, Session } from 'node:inspector';\nimport type { Event, Exception, IntegrationFn, StackFrame, StackParser } from '@sentry/core';\nimport { debug, defineIntegration, getClient, LRUMap } from '@sentry/core';\nimport { NODE_MAJOR } from '../../nodeVersion';\nimport type { NodeClient } from '../../sdk/client';\nimport { isDebuggerEnabled } from '../../utils/debug';\nimport type {\n FrameVariables,\n LocalVariablesIntegrationOptions,\n PausedExceptionEvent,\n RateLimitIncrement,\n Variables,\n} from './common';\nimport { createRateLimiter, functionNamesMatch } from './common';\n\n/** Creates a unique hash from stack frames */\nexport function hashFrames(frames: StackFrame[] | undefined): string | undefined {\n if (frames === undefined) {\n return;\n }\n\n // Only hash the 10 most recent frames (ie. the last 10)\n return frames.slice(-10).reduce((acc, frame) => `${acc},${frame.function},${frame.lineno},${frame.colno}`, '');\n}\n\n/**\n * We use the stack parser to create a unique hash from the exception stack trace\n * This is used to lookup vars when the exception passes through the event processor\n */\nexport function hashFromStack(stackParser: StackParser, stack: string | undefined): string | undefined {\n if (stack === undefined) {\n return undefined;\n }\n\n return hashFrames(stackParser(stack, 1));\n}\n\ntype OnPauseEvent = InspectorNotification<Debugger.PausedEventDataType>;\nexport interface DebugSession {\n /** Configures and connects to the debug session */\n configureAndConnect(onPause: (message: OnPauseEvent, complete: () => void) => void, captureAll: boolean): void;\n /** Updates which kind of exceptions to capture */\n setPauseOnExceptions(captureAll: boolean): void;\n /** Gets local variables for an objectId */\n getLocalVariables(objectId: string, callback: (vars: Variables) => void): void;\n}\n\ntype Next<T> = (result: T) => void;\ntype Add<T> = (fn: Next<T>) => void;\ntype CallbackWrapper<T> = { add: Add<T>; next: Next<T> };\n\n/** Creates a container for callbacks to be called sequentially */\nexport function createCallbackList<T>(complete: Next<T>): CallbackWrapper<T> {\n // A collection of callbacks to be executed last to first\n let callbacks: Next<T>[] = [];\n\n let completedCalled = false;\n function checkedComplete(result: T): void {\n callbacks = [];\n if (completedCalled) {\n return;\n }\n completedCalled = true;\n complete(result);\n }\n\n // complete should be called last\n callbacks.push(checkedComplete);\n\n function add(fn: Next<T>): void {\n callbacks.push(fn);\n }\n\n function next(result: T): void {\n const popped = callbacks.pop() || checkedComplete;\n\n try {\n popped(result);\n } catch {\n // If there is an error, we still want to call the complete callback\n checkedComplete(result);\n }\n }\n\n return { add, next };\n}\n\n/**\n * Promise API is available as `Experimental` and in Node 19 only.\n *\n * Callback-based API is `Stable` since v14 and `Experimental` since v8.\n * Because of that, we are creating our own `AsyncSession` class.\n *\n * https://nodejs.org/docs/latest-v19.x/api/inspector.html#promises-api\n * https://nodejs.org/docs/latest-v14.x/api/inspector.html\n */\nclass AsyncSession implements DebugSession {\n /** Throws if inspector API is not available */\n private constructor(private readonly _session: Session) {\n //\n }\n\n public static async create(orDefault?: DebugSession | undefined): Promise<DebugSession> {\n if (orDefault) {\n return orDefault;\n }\n\n const inspector = await import('node:inspector');\n return new AsyncSession(new inspector.Session());\n }\n\n /** @inheritdoc */\n public configureAndConnect(onPause: (event: OnPauseEvent, complete: () => void) => void, captureAll: boolean): void {\n this._session.connect();\n\n this._session.on('Debugger.paused', event => {\n onPause(event, () => {\n // After the pause work is complete, resume execution or the exception context memory is leaked\n this._session.post('Debugger.resume');\n });\n });\n\n this._session.post('Debugger.enable');\n this._session.post('Debugger.setPauseOnExceptions', { state: captureAll ? 'all' : 'uncaught' });\n }\n\n public setPauseOnExceptions(captureAll: boolean): void {\n this._session.post('Debugger.setPauseOnExceptions', { state: captureAll ? 'all' : 'uncaught' });\n }\n\n /** @inheritdoc */\n public getLocalVariables(objectId: string, complete: (vars: Variables) => void): void {\n this._getProperties(objectId, props => {\n const { add, next } = createCallbackList<Variables>(complete);\n\n for (const prop of props) {\n if (prop.value?.objectId && prop.value.className === 'Array') {\n const id = prop.value.objectId;\n add(vars => this._unrollArray(id, prop.name, vars, next));\n } else if (prop.value?.objectId && prop.value.className === 'Object') {\n const id = prop.value.objectId;\n add(vars => this._unrollObject(id, prop.name, vars, next));\n } else if (prop.value) {\n add(vars => this._unrollOther(prop, vars, next));\n }\n }\n\n next({});\n });\n }\n\n /**\n * Gets all the PropertyDescriptors of an object\n */\n private _getProperties(objectId: string, next: (result: Runtime.PropertyDescriptor[]) => void): void {\n this._session.post(\n 'Runtime.getProperties',\n {\n objectId,\n ownProperties: true,\n },\n (err, params) => {\n if (err) {\n next([]);\n } else {\n next(params.result);\n }\n },\n );\n }\n\n /**\n * Unrolls an array property\n */\n private _unrollArray(objectId: string, name: string, vars: Variables, next: (vars: Variables) => void): void {\n this._getProperties(objectId, props => {\n vars[name] = props\n .filter(v => v.name !== 'length' && !isNaN(parseInt(v.name, 10)))\n .sort((a, b) => parseInt(a.name, 10) - parseInt(b.name, 10))\n .map(v => v.value?.value);\n\n next(vars);\n });\n }\n\n /**\n * Unrolls an object property\n */\n private _unrollObject(objectId: string, name: string, vars: Variables, next: (obj: Variables) => void): void {\n this._getProperties(objectId, props => {\n vars[name] = props\n .map<[string, unknown]>(v => [v.name, v.value?.value])\n .reduce((obj, [key, val]) => {\n obj[key] = val;\n return obj;\n }, {} as Variables);\n\n next(vars);\n });\n }\n\n /**\n * Unrolls other properties\n */\n private _unrollOther(prop: Runtime.PropertyDescriptor, vars: Variables, next: (vars: Variables) => void): void {\n if (prop.value) {\n if ('value' in prop.value) {\n if (prop.value.value === undefined || prop.value.value === null) {\n vars[prop.name] = `<${prop.value.value}>`;\n } else {\n vars[prop.name] = prop.value.value;\n }\n } else if ('description' in prop.value && prop.value.type !== 'function') {\n vars[prop.name] = `<${prop.value.description}>`;\n } else if (prop.value.type === 'undefined') {\n vars[prop.name] = '<undefined>';\n }\n }\n\n next(vars);\n }\n}\n\nconst INTEGRATION_NAME = 'LocalVariables';\n\n/**\n * Adds local variables to exception frames\n */\nconst _localVariablesSyncIntegration = ((\n options: LocalVariablesIntegrationOptions = {},\n sessionOverride?: DebugSession,\n) => {\n const cachedFrames: LRUMap<string, FrameVariables[]> = new LRUMap(20);\n let rateLimiter: RateLimitIncrement | undefined;\n let shouldProcessEvent = false;\n\n function addLocalVariablesToException(exception: Exception): void {\n const hash = hashFrames(exception.stacktrace?.frames);\n\n if (hash === undefined) {\n return;\n }\n\n // Check if we have local variables for an exception that matches the hash\n // remove is identical to get but also removes the entry from the cache\n const cachedFrame = cachedFrames.remove(hash);\n\n if (cachedFrame === undefined) {\n return;\n }\n\n // Filter out frames where the function name is `new Promise` since these are in the error.stack frames\n // but do not appear in the debugger call frames\n const frames = (exception.stacktrace?.frames || []).filter(frame => frame.function !== 'new Promise');\n\n for (let i = 0; i < frames.length; i++) {\n // Sentry frames are in reverse order\n const frameIndex = frames.length - i - 1;\n\n const cachedFrameVariable = cachedFrame[i];\n const frameVariable = frames[frameIndex];\n\n // Drop out if we run out of frames to match up\n if (!frameVariable || !cachedFrameVariable) {\n break;\n }\n\n if (\n // We need to have vars to add\n cachedFrameVariable.vars === undefined ||\n // Only skip out-of-app frames if includeOutOfAppFrames is not true\n (frameVariable.in_app === false && options.includeOutOfAppFrames !== true) ||\n // The function names need to match\n !functionNamesMatch(frameVariable.function, cachedFrameVariable.function)\n ) {\n continue;\n }\n\n frameVariable.vars = cachedFrameVariable.vars;\n }\n }\n\n function addLocalVariablesToEvent(event: Event): Event {\n for (const exception of event.exception?.values || []) {\n addLocalVariablesToException(exception);\n }\n\n return event;\n }\n\n let setupPromise: Promise<void> | undefined;\n\n async function setup(): Promise<void> {\n const client = getClient<NodeClient>();\n const clientOptions = client?.getOptions();\n\n if (!clientOptions?.includeLocalVariables) {\n return;\n }\n\n // Only setup this integration if the Node version is >= v18\n // https://github.com/getsentry/sentry-javascript/issues/7697\n const unsupportedNodeVersion = NODE_MAJOR < 18;\n\n if (unsupportedNodeVersion) {\n debug.log('The `LocalVariables` integration is only supported on Node >= v18.');\n return;\n }\n\n if (await isDebuggerEnabled()) {\n debug.warn('Local variables capture has been disabled because the debugger was already enabled');\n return;\n }\n\n try {\n const session = await AsyncSession.create(sessionOverride);\n\n const handlePaused = (\n stackParser: StackParser,\n { params: { reason, data, callFrames } }: InspectorNotification<PausedExceptionEvent>,\n complete: () => void,\n ): void => {\n if (reason !== 'exception' && reason !== 'promiseRejection') {\n complete();\n return;\n }\n\n rateLimiter?.();\n\n // data.description contains the original error.stack\n const exceptionHash = hashFromStack(stackParser, data.description);\n\n if (exceptionHash == undefined) {\n complete();\n return;\n }\n\n const { add, next } = createCallbackList<FrameVariables[]>(frames => {\n cachedFrames.set(exceptionHash, frames);\n complete();\n });\n\n // Because we're queuing up and making all these calls synchronously, we can potentially overflow the stack\n // For this reason we only attempt to get local variables for the first 5 frames\n for (let i = 0; i < Math.min(callFrames.length, 5); i++) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const { scopeChain, functionName, this: obj } = callFrames[i]!;\n\n const localScope = scopeChain.find(scope => scope.type === 'local');\n\n // obj.className is undefined in ESM modules\n const fn = obj.className === 'global' || !obj.className ? functionName : `${obj.className}.${functionName}`;\n\n if (localScope?.object.objectId === undefined) {\n add(frames => {\n frames[i] = { function: fn };\n next(frames);\n });\n } else {\n const id = localScope.object.objectId;\n add(frames =>\n session.getLocalVariables(id, vars => {\n frames[i] = { function: fn, vars };\n next(frames);\n }),\n );\n }\n }\n\n next([]);\n };\n\n const captureAll = options.captureAllExceptions !== false;\n\n session.configureAndConnect(\n (ev, complete) =>\n handlePaused(clientOptions.stackParser, ev as InspectorNotification<PausedExceptionEvent>, complete),\n captureAll,\n );\n\n if (captureAll) {\n const max = options.maxExceptionsPerSecond || 50;\n\n rateLimiter = createRateLimiter(\n max,\n () => {\n debug.log('Local variables rate-limit lifted.');\n session.setPauseOnExceptions(true);\n },\n seconds => {\n debug.log(\n `Local variables rate-limit exceeded. Disabling capturing of caught exceptions for ${seconds} seconds.`,\n );\n session.setPauseOnExceptions(false);\n },\n );\n }\n\n shouldProcessEvent = true;\n } catch (error) {\n debug.log('The `LocalVariables` integration failed to start.', error);\n }\n }\n\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n setupPromise = setup();\n },\n async processEvent(event: Event): Promise<Event> {\n await setupPromise;\n\n if (shouldProcessEvent) {\n return addLocalVariablesToEvent(event);\n }\n\n return event;\n },\n // These are entirely for testing\n _getCachedFramesCount(): number {\n return cachedFrames.size;\n },\n _getFirstCachedFrame(): FrameVariables[] | undefined {\n return cachedFrames.values()[0];\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Adds local variables to exception frames.\n */\nexport const localVariablesSyncIntegration = defineIntegration(_localVariablesSyncIntegration);\n"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"local-variables-sync.js","sources":["../../../../src/integrations/local-variables/local-variables-sync.ts"],"sourcesContent":["import type { Debugger, InspectorNotification, Runtime, Session } from 'node:inspector';\nimport type { Event, Exception, IntegrationFn, StackFrame, StackParser } from '@sentry/core';\nimport { debug, defineIntegration, getClient, LRUMap } from '@sentry/core';\nimport { NODE_MAJOR } from '../../nodeVersion';\nimport type { NodeClient } from '../../sdk/client';\nimport { isDebuggerEnabled } from '../../utils/debug';\nimport type {\n FrameVariables,\n LocalVariablesIntegrationOptions,\n PausedExceptionEvent,\n RateLimitIncrement,\n Variables,\n} from './common';\nimport { createRateLimiter, functionNamesMatch } from './common';\n\n/** Creates a unique hash from stack frames */\nexport function hashFrames(frames: StackFrame[] | undefined): string | undefined {\n if (frames === undefined) {\n return;\n }\n\n // Only hash the 10 most recent frames (ie. the last 10)\n return frames.slice(-10).reduce((acc, frame) => `${acc},${frame.function},${frame.lineno},${frame.colno}`, '');\n}\n\n/**\n * We use the stack parser to create a unique hash from the exception stack trace\n * This is used to lookup vars when the exception passes through the event processor\n */\nexport function hashFromStack(stackParser: StackParser, stack: string | undefined): string | undefined {\n if (stack === undefined) {\n return undefined;\n }\n\n return hashFrames(stackParser(stack, 1));\n}\n\ntype OnPauseEvent = InspectorNotification<Debugger.PausedEventDataType>;\nexport interface DebugSession {\n /** Configures and connects to the debug session */\n configureAndConnect(onPause: (message: OnPauseEvent, complete: () => void) => void, captureAll: boolean): void;\n /** Updates which kind of exceptions to capture */\n setPauseOnExceptions(captureAll: boolean): void;\n /** Gets local variables for an objectId */\n getLocalVariables(objectId: string, callback: (vars: Variables) => void): void;\n}\n\ntype Next<T> = (result: T) => void;\ntype Add<T> = (fn: Next<T>) => void;\ntype CallbackWrapper<T> = { add: Add<T>; next: Next<T> };\n\n/** Creates a container for callbacks to be called sequentially */\nexport function createCallbackList<T>(complete: Next<T>): CallbackWrapper<T> {\n // A collection of callbacks to be executed last to first\n let callbacks: Next<T>[] = [];\n\n let completedCalled = false;\n function checkedComplete(result: T): void {\n callbacks = [];\n if (completedCalled) {\n return;\n }\n completedCalled = true;\n complete(result);\n }\n\n // complete should be called last\n callbacks.push(checkedComplete);\n\n function add(fn: Next<T>): void {\n callbacks.push(fn);\n }\n\n function next(result: T): void {\n const popped = callbacks.pop() || checkedComplete;\n\n try {\n popped(result);\n } catch {\n // If there is an error, we still want to call the complete callback\n checkedComplete(result);\n }\n }\n\n return { add, next };\n}\n\n/**\n * Promise API is available as `Experimental` and in Node 19 only.\n *\n * Callback-based API is `Stable` since v14 and `Experimental` since v8.\n * Because of that, we are creating our own `AsyncSession` class.\n *\n * https://nodejs.org/docs/latest-v19.x/api/inspector.html#promises-api\n * https://nodejs.org/docs/latest-v14.x/api/inspector.html\n */\nclass AsyncSession implements DebugSession {\n /** Throws if inspector API is not available */\n private constructor(private readonly _session: Session) {\n //\n }\n\n public static async create(orDefault?: DebugSession | undefined): Promise<DebugSession> {\n if (orDefault) {\n return orDefault;\n }\n\n const inspector = await import('node:inspector');\n return new AsyncSession(new inspector.Session());\n }\n\n /** @inheritdoc */\n public configureAndConnect(onPause: (event: OnPauseEvent, complete: () => void) => void, captureAll: boolean): void {\n this._session.connect();\n\n this._session.on('Debugger.paused', event => {\n onPause(event, () => {\n // After the pause work is complete, resume execution or the exception context memory is leaked\n this._session.post('Debugger.resume');\n });\n });\n\n this._session.post('Debugger.enable');\n this._session.post('Debugger.setPauseOnExceptions', { state: captureAll ? 'all' : 'uncaught' });\n }\n\n public setPauseOnExceptions(captureAll: boolean): void {\n this._session.post('Debugger.setPauseOnExceptions', { state: captureAll ? 'all' : 'uncaught' });\n }\n\n /** @inheritdoc */\n public getLocalVariables(objectId: string, complete: (vars: Variables) => void): void {\n this._getProperties(objectId, props => {\n const { add, next } = createCallbackList<Variables>(complete);\n\n for (const prop of props) {\n if (prop.value?.objectId && prop.value.className === 'Array') {\n const id = prop.value.objectId;\n add(vars => this._unrollArray(id, prop.name, vars, next));\n } else if (prop.value?.objectId && prop.value.className === 'Object') {\n const id = prop.value.objectId;\n add(vars => this._unrollObject(id, prop.name, vars, next));\n } else if (prop.value) {\n add(vars => this._unrollOther(prop, vars, next));\n }\n }\n\n next({});\n });\n }\n\n /**\n * Gets all the PropertyDescriptors of an object\n */\n private _getProperties(objectId: string, next: (result: Runtime.PropertyDescriptor[]) => void): void {\n this._session.post(\n 'Runtime.getProperties',\n {\n objectId,\n ownProperties: true,\n },\n (err, params) => {\n if (err) {\n next([]);\n } else {\n next(params.result);\n }\n },\n );\n }\n\n /**\n * Unrolls an array property\n */\n private _unrollArray(objectId: string, name: string, vars: Variables, next: (vars: Variables) => void): void {\n this._getProperties(objectId, props => {\n vars[name] = props\n .filter(v => v.name !== 'length' && !isNaN(parseInt(v.name, 10)))\n .sort((a, b) => parseInt(a.name, 10) - parseInt(b.name, 10))\n .map(v => v.value?.value);\n\n next(vars);\n });\n }\n\n /**\n * Unrolls an object property\n */\n private _unrollObject(objectId: string, name: string, vars: Variables, next: (obj: Variables) => void): void {\n this._getProperties(objectId, props => {\n vars[name] = props\n .map<[string, unknown]>(v => [v.name, v.value?.value])\n .reduce((obj, [key, val]) => {\n obj[key] = val;\n return obj;\n }, {} as Variables);\n\n next(vars);\n });\n }\n\n /**\n * Unrolls other properties\n */\n private _unrollOther(prop: Runtime.PropertyDescriptor, vars: Variables, next: (vars: Variables) => void): void {\n if (prop.value) {\n if ('value' in prop.value) {\n if (prop.value.value === undefined || prop.value.value === null) {\n vars[prop.name] = `<${prop.value.value}>`;\n } else {\n vars[prop.name] = prop.value.value;\n }\n } else if ('description' in prop.value && prop.value.type !== 'function') {\n vars[prop.name] = `<${prop.value.description}>`;\n } else if (prop.value.type === 'undefined') {\n vars[prop.name] = '<undefined>';\n }\n }\n\n next(vars);\n }\n}\n\nconst INTEGRATION_NAME = 'LocalVariables';\n\n/**\n * Adds local variables to exception frames\n */\nconst _localVariablesSyncIntegration = ((\n options: LocalVariablesIntegrationOptions = {},\n sessionOverride?: DebugSession,\n) => {\n const cachedFrames: LRUMap<string, FrameVariables[]> = new LRUMap(20);\n let rateLimiter: RateLimitIncrement | undefined;\n let shouldProcessEvent = false;\n\n function addLocalVariablesToException(exception: Exception): void {\n const hash = hashFrames(exception.stacktrace?.frames);\n\n if (hash === undefined) {\n return;\n }\n\n // Check if we have local variables for an exception that matches the hash\n // remove is identical to get but also removes the entry from the cache\n const cachedFrame = cachedFrames.remove(hash);\n\n if (cachedFrame === undefined) {\n return;\n }\n\n // Filter out frames where the function name is `new Promise` since these are in the error.stack frames\n // but do not appear in the debugger call frames\n const frames = (exception.stacktrace?.frames || []).filter(frame => frame.function !== 'new Promise');\n\n for (let i = 0; i < frames.length; i++) {\n // Sentry frames are in reverse order\n const frameIndex = frames.length - i - 1;\n\n const cachedFrameVariable = cachedFrame[i];\n const frameVariable = frames[frameIndex];\n\n // Drop out if we run out of frames to match up\n if (!frameVariable || !cachedFrameVariable) {\n break;\n }\n\n if (\n // We need to have vars to add\n cachedFrameVariable.vars === undefined ||\n // Only skip out-of-app frames if includeOutOfAppFrames is not true\n (frameVariable.in_app === false && options.includeOutOfAppFrames !== true) ||\n // The function names need to match\n !functionNamesMatch(frameVariable.function, cachedFrameVariable.function)\n ) {\n continue;\n }\n\n frameVariable.vars = cachedFrameVariable.vars;\n }\n }\n\n function addLocalVariablesToEvent(event: Event): Event {\n for (const exception of event.exception?.values || []) {\n addLocalVariablesToException(exception);\n }\n\n return event;\n }\n\n let setupPromise: Promise<void> | undefined;\n\n async function setup(): Promise<void> {\n const client = getClient<NodeClient>();\n const clientOptions = client?.getOptions();\n\n if (!clientOptions?.includeLocalVariables) {\n return;\n }\n\n // Only setup this integration if the Node version is >= v18\n // https://github.com/getsentry/sentry-javascript/issues/7697\n const unsupportedNodeVersion = NODE_MAJOR < 18;\n\n if (unsupportedNodeVersion) {\n debug.log('The `LocalVariables` integration is only supported on Node >= v18.');\n return;\n }\n\n if (await isDebuggerEnabled()) {\n debug.warn('Local variables capture has been disabled because the debugger was already enabled');\n return;\n }\n\n try {\n const session = await AsyncSession.create(sessionOverride);\n\n const handlePaused = (\n stackParser: StackParser,\n { params: { reason, data, callFrames } }: InspectorNotification<PausedExceptionEvent>,\n complete: () => void,\n ): void => {\n if (reason !== 'exception' && reason !== 'promiseRejection') {\n complete();\n return;\n }\n\n rateLimiter?.();\n\n // data.description contains the original error.stack\n const exceptionHash = hashFromStack(stackParser, data.description);\n\n if (exceptionHash == undefined) {\n complete();\n return;\n }\n\n const { add, next } = createCallbackList<FrameVariables[]>(frames => {\n cachedFrames.set(exceptionHash, frames);\n complete();\n });\n\n // Because we're queuing up and making all these calls synchronously, we can potentially overflow the stack\n // For this reason we only attempt to get local variables for the first 5 frames\n for (let i = 0; i < Math.min(callFrames.length, 5); i++) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const { scopeChain, functionName, this: obj } = callFrames[i]!;\n\n const localScope = scopeChain.find(scope => scope.type === 'local');\n\n // obj.className is undefined in ESM modules\n const fn = obj.className === 'global' || !obj.className ? functionName : `${obj.className}.${functionName}`;\n\n if (localScope?.object.objectId === undefined) {\n add(frames => {\n frames[i] = { function: fn };\n next(frames);\n });\n } else {\n const id = localScope.object.objectId;\n add(frames =>\n session.getLocalVariables(id, vars => {\n frames[i] = { function: fn, vars };\n next(frames);\n }),\n );\n }\n }\n\n next([]);\n };\n\n const captureAll = options.captureAllExceptions !== false;\n\n session.configureAndConnect(\n (ev, complete) =>\n handlePaused(clientOptions.stackParser, ev as InspectorNotification<PausedExceptionEvent>, complete),\n captureAll,\n );\n\n if (captureAll) {\n const max = options.maxExceptionsPerSecond || 50;\n\n rateLimiter = createRateLimiter(\n max,\n () => {\n debug.log('Local variables rate-limit lifted.');\n session.setPauseOnExceptions(true);\n },\n seconds => {\n debug.log(\n `Local variables rate-limit exceeded. Disabling capturing of caught exceptions for ${seconds} seconds.`,\n );\n session.setPauseOnExceptions(false);\n },\n );\n }\n\n shouldProcessEvent = true;\n } catch (error) {\n debug.log('The `LocalVariables` integration failed to start.', error);\n }\n }\n\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n setupPromise = setup();\n },\n async processEvent(event: Event): Promise<Event> {\n await setupPromise;\n\n if (shouldProcessEvent) {\n return addLocalVariablesToEvent(event);\n }\n\n return event;\n },\n // These are entirely for testing\n _getCachedFramesCount(): number {\n return cachedFrames.size;\n },\n _getFirstCachedFrame(): FrameVariables[] | undefined {\n return cachedFrames.values()[0];\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Adds local variables to exception frames.\n */\nexport const localVariablesSyncIntegration = defineIntegration(_localVariablesSyncIntegration);\n"],"names":[],"mappings":"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"} |
@@ -1,2 +0,2 @@ | ||
| /*! @sentry/node-core 10.53.1 (cd97408) | https://github.com/getsentry/sentry-javascript */ | ||
| /*! @sentry/node-core 10.54.0 (f31686b) | https://github.com/getsentry/sentry-javascript */ | ||
| import{Session as e}from"node:inspector/promises";import{workerData as t}from"node:worker_threads";const n=globalThis,i={};const o="__SENTRY_ERROR_LOCAL_VARIABLES__";const a=t;function s(...e){a.debug&&function(e){if(!("console"in n))return e();const t=n.console,o={},a=Object.keys(i);a.forEach(e=>{const n=i[e];o[e]=t[e],t[e]=n});try{return e()}finally{a.forEach(e=>{t[e]=o[e]})}}(()=>console.log("[LocalVariables Worker]",...e))}async function c(e,t,n,i){const o=await e.post("Runtime.getProperties",{objectId:t,ownProperties:!0});i[n]=o.result.filter(e=>"length"!==e.name&&!isNaN(parseInt(e.name,10))).sort((e,t)=>parseInt(e.name,10)-parseInt(t.name,10)).map(e=>e.value?.value)}async function r(e,t,n,i){const o=await e.post("Runtime.getProperties",{objectId:t,ownProperties:!0});i[n]=o.result.map(e=>[e.name,e.value?.value]).reduce((e,[t,n])=>(e[t]=n,e),{})}function u(e,t){e.value&&("value"in e.value?void 0===e.value.value||null===e.value.value?t[e.name]=`<${e.value.value}>`:t[e.name]=e.value.value:"description"in e.value&&"function"!==e.value.type?t[e.name]=`<${e.value.description}>`:"undefined"===e.value.type&&(t[e.name]="<undefined>"))}async function l(e,t){const n=await e.post("Runtime.getProperties",{objectId:t,ownProperties:!0}),i={};for(const t of n.result)if(t.value?.objectId&&"Array"===t.value.className){const n=t.value.objectId;await c(e,n,t.name,i)}else if(t.value?.objectId&&"Object"===t.value.className){const n=t.value.objectId;await r(e,n,t.name,i)}else t.value&&u(t,i);return i}let f;(async function(){const t=new e;t.connectToMainThread(),s("Connected to main thread");let n=!1;t.on("Debugger.resumed",()=>{n=!1}),t.on("Debugger.paused",e=>{n=!0,async function(e,{reason:t,data:{objectId:n},callFrames:i}){if("exception"!==t&&"promiseRejection"!==t)return;if(f?.(),null==n)return;const a=[];for(let t=0;t<i.length;t++){const{scopeChain:n,functionName:o,this:s}=i[t],c=n.find(e=>"local"===e.type),r="global"!==s.className&&s.className?`${s.className}.${o}`:o;if(void 0===c?.object.objectId)a[t]={function:r};else{const n=await l(e,c.object.objectId);a[t]={function:r,vars:n}}}await e.post("Runtime.callFunctionOn",{functionDeclaration:`function() { this.${o} = this.${o} || ${JSON.stringify(a)}; }`,silent:!0,objectId:n}),await e.post("Runtime.releaseObject",{objectId:n})}(t,e.params).then(async()=>{n&&await t.post("Debugger.resume")},async e=>{n&&await t.post("Debugger.resume")})}),await t.post("Debugger.enable");const i=!1!==a.captureAllExceptions;if(await t.post("Debugger.setPauseOnExceptions",{state:i?"all":"uncaught"}),i){const e=a.maxExceptionsPerSecond||50;f=function(e,t,n){let i=0,o=5,a=0;return setInterval(()=>{0===a?i>e&&(o*=2,n(o),o>86400&&(o=86400),a=o):(a-=1,0===a&&t()),i=0},1e3).unref(),()=>{i+=1}}(e,async()=>{s("Rate-limit lifted."),await t.post("Debugger.setPauseOnExceptions",{state:"all"})},async e=>{s(`Rate-limit exceeded. Disabling capturing of caught exceptions for ${e} seconds.`),await t.post("Debugger.setPauseOnExceptions",{state:"uncaught"})})}})().catch(e=>{s("Failed to start debugger",e)}),setInterval(()=>{},1e4); |
@@ -6,11 +6,4 @@ import { existsSync, readFileSync } from 'node:fs'; | ||
| let moduleCache; | ||
| const INTEGRATION_NAME = 'Modules'; | ||
| /** | ||
| * `__SENTRY_SERVER_MODULES__` can be replaced at build time with the modules loaded by the server. | ||
| * Right now, we leverage this in Next.js to circumvent the problem that we do not get access to these things at runtime. | ||
| */ | ||
| const SERVER_MODULES = typeof __SENTRY_SERVER_MODULES__ === 'undefined' ? {} : __SENTRY_SERVER_MODULES__; | ||
| const INTEGRATION_NAME = "Modules"; | ||
| const SERVER_MODULES = typeof __SENTRY_SERVER_MODULES__ === "undefined" ? {} : __SENTRY_SERVER_MODULES__; | ||
| const _modulesIntegration = (() => { | ||
@@ -22,23 +15,13 @@ return { | ||
| ...event.modules, | ||
| ..._getModules(), | ||
| ..._getModules() | ||
| }; | ||
| return event; | ||
| }, | ||
| getModules: _getModules, | ||
| getModules: _getModules | ||
| }; | ||
| }) ; | ||
| /** | ||
| * Add node modules / packages to the event. | ||
| * For this, multiple sources are used: | ||
| * - They can be injected at build time into the __SENTRY_SERVER_MODULES__ variable (e.g. in Next.js) | ||
| * - They are extracted from the dependencies & devDependencies in the package.json file | ||
| * - They are extracted from the require.cache (CJS only) | ||
| */ | ||
| }); | ||
| const modulesIntegration = _modulesIntegration; | ||
| function getRequireCachePaths() { | ||
| try { | ||
| return require.cache ? Object.keys(require.cache ) : []; | ||
| return require.cache ? Object.keys(require.cache) : []; | ||
| } catch { | ||
@@ -48,4 +31,2 @@ return []; | ||
| } | ||
| /** Extract information about package.json modules */ | ||
| function collectModules() { | ||
@@ -55,26 +36,17 @@ return { | ||
| ...getModulesFromPackageJson(), | ||
| ...(isCjs() ? collectRequireModules() : {}), | ||
| ...isCjs() ? collectRequireModules() : {} | ||
| }; | ||
| } | ||
| /** Extract information about package.json modules from require.cache */ | ||
| function collectRequireModules() { | ||
| const mainPaths = require.main?.paths || []; | ||
| const paths = getRequireCachePaths(); | ||
| // We start with the modules from package.json (if possible) | ||
| // These may be overwritten by more specific versions from the require.cache | ||
| const infos = {}; | ||
| const seen = new Set(); | ||
| paths.forEach(path => { | ||
| const seen = /* @__PURE__ */ new Set(); | ||
| paths.forEach((path) => { | ||
| let dir = path; | ||
| /** Traverse directories upward in the search of package.json file */ | ||
| const updir = () => { | ||
| const orig = dir; | ||
| dir = dirname(orig); | ||
| if (!dir || orig === dir || seen.has(orig)) { | ||
| return undefined; | ||
| return void 0; | ||
| } | ||
@@ -84,27 +56,17 @@ if (mainPaths.indexOf(dir) < 0) { | ||
| } | ||
| const pkgfile = join(orig, 'package.json'); | ||
| const pkgfile = join(orig, "package.json"); | ||
| seen.add(orig); | ||
| if (!existsSync(pkgfile)) { | ||
| return updir(); | ||
| } | ||
| try { | ||
| const info = JSON.parse(readFileSync(pkgfile, 'utf8')) | ||
| ; | ||
| const info = JSON.parse(readFileSync(pkgfile, "utf8")); | ||
| infos[info.name] = info.version; | ||
| } catch { | ||
| // no-empty | ||
| } | ||
| }; | ||
| updir(); | ||
| }); | ||
| return infos; | ||
| } | ||
| /** Fetches the list of modules and the versions loaded by the entry file for your node.js app. */ | ||
| function _getModules() { | ||
@@ -116,8 +78,6 @@ if (!moduleCache) { | ||
| } | ||
| function getPackageJson() { | ||
| try { | ||
| const filePath = join(process.cwd(), 'package.json'); | ||
| const packageJson = JSON.parse(readFileSync(filePath, 'utf8')) ; | ||
| const filePath = join(process.cwd(), "package.json"); | ||
| const packageJson = JSON.parse(readFileSync(filePath, "utf8")); | ||
| return packageJson; | ||
@@ -128,9 +88,7 @@ } catch { | ||
| } | ||
| function getModulesFromPackageJson() { | ||
| const packageJson = getPackageJson(); | ||
| return { | ||
| ...packageJson.dependencies, | ||
| ...packageJson.devDependencies, | ||
| ...packageJson.devDependencies | ||
| }; | ||
@@ -137,0 +95,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"modules.js","sources":["../../../src/integrations/modules.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport type { IntegrationFn } from '@sentry/core';\nimport { isCjs } from '../utils/detection';\n\ntype ModuleInfo = Record<string, string>;\n\nlet moduleCache: ModuleInfo | undefined;\n\nconst INTEGRATION_NAME = 'Modules';\n\ndeclare const __SENTRY_SERVER_MODULES__: Record<string, string>;\n\n/**\n * `__SENTRY_SERVER_MODULES__` can be replaced at build time with the modules loaded by the server.\n * Right now, we leverage this in Next.js to circumvent the problem that we do not get access to these things at runtime.\n */\nconst SERVER_MODULES = typeof __SENTRY_SERVER_MODULES__ === 'undefined' ? {} : __SENTRY_SERVER_MODULES__;\n\nconst _modulesIntegration = (() => {\n return {\n name: INTEGRATION_NAME,\n processEvent(event) {\n event.modules = {\n ...event.modules,\n ..._getModules(),\n };\n\n return event;\n },\n getModules: _getModules,\n };\n}) satisfies IntegrationFn;\n\n/**\n * Add node modules / packages to the event.\n * For this, multiple sources are used:\n * - They can be injected at build time into the __SENTRY_SERVER_MODULES__ variable (e.g. in Next.js)\n * - They are extracted from the dependencies & devDependencies in the package.json file\n * - They are extracted from the require.cache (CJS only)\n */\nexport const modulesIntegration = _modulesIntegration;\n\nfunction getRequireCachePaths(): string[] {\n try {\n return require.cache ? Object.keys(require.cache as Record<string, unknown>) : [];\n } catch {\n return [];\n }\n}\n\n/** Extract information about package.json modules */\nfunction collectModules(): ModuleInfo {\n return {\n ...SERVER_MODULES,\n ...getModulesFromPackageJson(),\n ...(isCjs() ? collectRequireModules() : {}),\n };\n}\n\n/** Extract information about package.json modules from require.cache */\nfunction collectRequireModules(): ModuleInfo {\n const mainPaths = require.main?.paths || [];\n const paths = getRequireCachePaths();\n\n // We start with the modules from package.json (if possible)\n // These may be overwritten by more specific versions from the require.cache\n const infos: ModuleInfo = {};\n const seen = new Set<string>();\n\n paths.forEach(path => {\n let dir = path;\n\n /** Traverse directories upward in the search of package.json file */\n const updir = (): void | (() => void) => {\n const orig = dir;\n dir = dirname(orig);\n\n if (!dir || orig === dir || seen.has(orig)) {\n return undefined;\n }\n if (mainPaths.indexOf(dir) < 0) {\n return updir();\n }\n\n const pkgfile = join(orig, 'package.json');\n seen.add(orig);\n\n if (!existsSync(pkgfile)) {\n return updir();\n }\n\n try {\n const info = JSON.parse(readFileSync(pkgfile, 'utf8')) as {\n name: string;\n version: string;\n };\n infos[info.name] = info.version;\n } catch {\n // no-empty\n }\n };\n\n updir();\n });\n\n return infos;\n}\n\n/** Fetches the list of modules and the versions loaded by the entry file for your node.js app. */\nfunction _getModules(): ModuleInfo {\n if (!moduleCache) {\n moduleCache = collectModules();\n }\n return moduleCache;\n}\n\ninterface PackageJson {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n}\n\nfunction getPackageJson(): PackageJson {\n try {\n const filePath = join(process.cwd(), 'package.json');\n const packageJson = JSON.parse(readFileSync(filePath, 'utf8')) as PackageJson;\n\n return packageJson;\n } catch {\n return {};\n }\n}\n\nfunction getModulesFromPackageJson(): ModuleInfo {\n const packageJson = getPackageJson();\n\n return {\n ...packageJson.dependencies,\n ...packageJson.devDependencies,\n };\n}\n"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"modules.js","sources":["../../../src/integrations/modules.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport type { IntegrationFn } from '@sentry/core';\nimport { isCjs } from '../utils/detection';\n\ntype ModuleInfo = Record<string, string>;\n\nlet moduleCache: ModuleInfo | undefined;\n\nconst INTEGRATION_NAME = 'Modules';\n\ndeclare const __SENTRY_SERVER_MODULES__: Record<string, string>;\n\n/**\n * `__SENTRY_SERVER_MODULES__` can be replaced at build time with the modules loaded by the server.\n * Right now, we leverage this in Next.js to circumvent the problem that we do not get access to these things at runtime.\n */\nconst SERVER_MODULES = typeof __SENTRY_SERVER_MODULES__ === 'undefined' ? {} : __SENTRY_SERVER_MODULES__;\n\nconst _modulesIntegration = (() => {\n return {\n name: INTEGRATION_NAME,\n processEvent(event) {\n event.modules = {\n ...event.modules,\n ..._getModules(),\n };\n\n return event;\n },\n getModules: _getModules,\n };\n}) satisfies IntegrationFn;\n\n/**\n * Add node modules / packages to the event.\n * For this, multiple sources are used:\n * - They can be injected at build time into the __SENTRY_SERVER_MODULES__ variable (e.g. in Next.js)\n * - They are extracted from the dependencies & devDependencies in the package.json file\n * - They are extracted from the require.cache (CJS only)\n */\nexport const modulesIntegration = _modulesIntegration;\n\nfunction getRequireCachePaths(): string[] {\n try {\n return require.cache ? Object.keys(require.cache as Record<string, unknown>) : [];\n } catch {\n return [];\n }\n}\n\n/** Extract information about package.json modules */\nfunction collectModules(): ModuleInfo {\n return {\n ...SERVER_MODULES,\n ...getModulesFromPackageJson(),\n ...(isCjs() ? collectRequireModules() : {}),\n };\n}\n\n/** Extract information about package.json modules from require.cache */\nfunction collectRequireModules(): ModuleInfo {\n const mainPaths = require.main?.paths || [];\n const paths = getRequireCachePaths();\n\n // We start with the modules from package.json (if possible)\n // These may be overwritten by more specific versions from the require.cache\n const infos: ModuleInfo = {};\n const seen = new Set<string>();\n\n paths.forEach(path => {\n let dir = path;\n\n /** Traverse directories upward in the search of package.json file */\n const updir = (): void | (() => void) => {\n const orig = dir;\n dir = dirname(orig);\n\n if (!dir || orig === dir || seen.has(orig)) {\n return undefined;\n }\n if (mainPaths.indexOf(dir) < 0) {\n return updir();\n }\n\n const pkgfile = join(orig, 'package.json');\n seen.add(orig);\n\n if (!existsSync(pkgfile)) {\n return updir();\n }\n\n try {\n const info = JSON.parse(readFileSync(pkgfile, 'utf8')) as {\n name: string;\n version: string;\n };\n infos[info.name] = info.version;\n } catch {\n // no-empty\n }\n };\n\n updir();\n });\n\n return infos;\n}\n\n/** Fetches the list of modules and the versions loaded by the entry file for your node.js app. */\nfunction _getModules(): ModuleInfo {\n if (!moduleCache) {\n moduleCache = collectModules();\n }\n return moduleCache;\n}\n\ninterface PackageJson {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n}\n\nfunction getPackageJson(): PackageJson {\n try {\n const filePath = join(process.cwd(), 'package.json');\n const packageJson = JSON.parse(readFileSync(filePath, 'utf8')) as PackageJson;\n\n return packageJson;\n } catch {\n return {};\n }\n}\n\nfunction getModulesFromPackageJson(): ModuleInfo {\n const packageJson = getPackageJson();\n\n return {\n ...packageJson.dependencies,\n ...packageJson.devDependencies,\n };\n}\n"],"names":[],"mappings":"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"} |
@@ -5,4 +5,3 @@ import { defineIntegration } from '@sentry/core'; | ||
| const INTEGRATION_NAME = 'NodeFetch'; | ||
| const INTEGRATION_NAME = "NodeFetch"; | ||
| const instrumentSentryNodeFetch = generateInstrumentOnce( | ||
@@ -13,14 +12,12 @@ `${INTEGRATION_NAME}.sentry`, | ||
| return options; | ||
| }, | ||
| } | ||
| ); | ||
| const _nativeNodeFetchIntegration = ((options = {}) => { | ||
| return { | ||
| name: 'NodeFetch', | ||
| name: "NodeFetch", | ||
| setupOnce() { | ||
| instrumentSentryNodeFetch(options); | ||
| }, | ||
| } | ||
| }; | ||
| }) ; | ||
| }); | ||
| const nativeNodeFetchIntegration = defineIntegration(_nativeNodeFetchIntegration); | ||
@@ -27,0 +24,0 @@ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.js","sources":["../../../../src/integrations/node-fetch/index.ts"],"sourcesContent":["import type { IntegrationFn } from '@sentry/core';\nimport { defineIntegration } from '@sentry/core';\nimport { generateInstrumentOnce } from '../../otel/instrument';\nimport { SentryNodeFetchInstrumentation } from './SentryNodeFetchInstrumentation';\n\nconst INTEGRATION_NAME = 'NodeFetch';\n\ninterface NodeFetchOptions {\n /**\n * Whether breadcrumbs should be recorded for requests.\n * Defaults to true\n */\n breadcrumbs?: boolean;\n\n /**\n * Whether to inject trace propagation headers (sentry-trace, baggage, traceparent) into outgoing fetch requests.\n *\n * @default `true`\n */\n tracePropagation?: boolean;\n\n /**\n * Do not capture spans or breadcrumbs for outgoing fetch requests to URLs where the given callback returns `true`.\n * This controls both span & breadcrumb creation - spans will be non recording if tracing is disabled.\n */\n ignoreOutgoingRequests?: (url: string) => boolean;\n}\n\nconst instrumentSentryNodeFetch = generateInstrumentOnce(\n `${INTEGRATION_NAME}.sentry`,\n SentryNodeFetchInstrumentation,\n (options: NodeFetchOptions) => {\n return options;\n },\n);\n\nconst _nativeNodeFetchIntegration = ((options: NodeFetchOptions = {}) => {\n return {\n name: 'NodeFetch',\n setupOnce() {\n instrumentSentryNodeFetch(options);\n },\n };\n}) satisfies IntegrationFn;\n\nexport const nativeNodeFetchIntegration = defineIntegration(_nativeNodeFetchIntegration);\n"],"names":[],"mappings":";;;;AAKA,MAAM,gBAAA,GAAmB,WAAW;;AAuBpC,MAAM,yBAAA,GAA4B,sBAAsB;AACxD,EAAE,CAAC,EAAA,gBAAA,CAAA,OAAA,CAAA;AACA,EAAA,8BAAA;AACA,EAAA,CAAA,OAAA,KAAA;AACA,IAAA,OAAA,OAAA;AACA,EAAA,CAAA;AACA,CAAA;;AAEA,MAAA,2BAAA,IAAA,CAAA,OAAA,GAAA,EAAA,KAAA;AACA,EAAA,OAAA;AACA,IAAA,IAAA,EAAA,WAAA;AACA,IAAA,SAAA,GAAA;AACA,MAAA,yBAAA,CAAA,OAAA,CAAA;AACA,IAAA,CAAA;AACA,GAAA;AACA,CAAA,CAAA;;AAEA,MAAA,0BAAA,GAAA,iBAAA,CAAA,2BAAA;;;;"} | ||
| {"version":3,"file":"index.js","sources":["../../../../src/integrations/node-fetch/index.ts"],"sourcesContent":["import type { IntegrationFn } from '@sentry/core';\nimport { defineIntegration } from '@sentry/core';\nimport { generateInstrumentOnce } from '../../otel/instrument';\nimport { SentryNodeFetchInstrumentation } from './SentryNodeFetchInstrumentation';\n\nconst INTEGRATION_NAME = 'NodeFetch';\n\ninterface NodeFetchOptions {\n /**\n * Whether breadcrumbs should be recorded for requests.\n * Defaults to true\n */\n breadcrumbs?: boolean;\n\n /**\n * Whether to inject trace propagation headers (sentry-trace, baggage, traceparent) into outgoing fetch requests.\n *\n * @default `true`\n */\n tracePropagation?: boolean;\n\n /**\n * Do not capture spans or breadcrumbs for outgoing fetch requests to URLs where the given callback returns `true`.\n * This controls both span & breadcrumb creation - spans will be non recording if tracing is disabled.\n */\n ignoreOutgoingRequests?: (url: string) => boolean;\n}\n\nconst instrumentSentryNodeFetch = generateInstrumentOnce(\n `${INTEGRATION_NAME}.sentry`,\n SentryNodeFetchInstrumentation,\n (options: NodeFetchOptions) => {\n return options;\n },\n);\n\nconst _nativeNodeFetchIntegration = ((options: NodeFetchOptions = {}) => {\n return {\n name: 'NodeFetch',\n setupOnce() {\n instrumentSentryNodeFetch(options);\n },\n };\n}) satisfies IntegrationFn;\n\nexport const nativeNodeFetchIntegration = defineIntegration(_nativeNodeFetchIntegration);\n"],"names":[],"mappings":";;;;AAKA,MAAM,gBAAA,GAAmB,WAAA;AAuBzB,MAAM,yBAAA,GAA4B,sBAAA;AAAA,EAChC,GAAG,gBAAgB,CAAA,OAAA,CAAA;AAAA,EACnB,8BAAA;AAAA,EACA,CAAC,OAAA,KAA8B;AAC7B,IAAA,OAAO,OAAA;AAAA,EACT;AACF,CAAA;AAEA,MAAM,2BAAA,IAA+B,CAAC,OAAA,GAA4B,EAAC,KAAM;AACvE,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,WAAA;AAAA,IACN,SAAA,GAAY;AACV,MAAA,yBAAA,CAA0B,OAAO,CAAA;AAAA,IACnC;AAAA,GACF;AACF,CAAA,CAAA;AAEO,MAAM,0BAAA,GAA6B,kBAAkB,2BAA2B;;;;"} |
@@ -9,61 +9,29 @@ import { context } from '@opentelemetry/api'; | ||
| /** | ||
| * This custom node-fetch instrumentation is used to instrument outgoing fetch requests. | ||
| * It does not emit any spans. | ||
| * | ||
| * The reason this is isolated from the OpenTelemetry instrumentation is that users may overwrite this, | ||
| * which would lead to Sentry not working as expected. | ||
| * | ||
| * This is heavily inspired & adapted from: | ||
| * https://github.com/open-telemetry/opentelemetry-js-contrib/blob/28e209a9da36bc4e1f8c2b0db7360170ed46cb80/plugins/node/instrumentation-undici/src/undici.ts | ||
| */ | ||
| class SentryNodeFetchInstrumentation extends InstrumentationBase { | ||
| // Keep ref to avoid https://github.com/nodejs/node/issues/42170 bug and for | ||
| // unsubscribing. | ||
| constructor(config = {}) { | ||
| super('@sentry/instrumentation-node-fetch', SDK_VERSION, config); | ||
| constructor(config = {}) { | ||
| super("@sentry/instrumentation-node-fetch", SDK_VERSION, config); | ||
| this._channelSubs = []; | ||
| this._propagationDecisionMap = new LRUMap(100); | ||
| this._ignoreOutgoingRequestsMap = new WeakMap(); | ||
| this._ignoreOutgoingRequestsMap = /* @__PURE__ */ new WeakMap(); | ||
| } | ||
| /** No need to instrument files/modules. */ | ||
| init() { | ||
| return undefined; | ||
| init() { | ||
| return void 0; | ||
| } | ||
| /** Disable the instrumentation. */ | ||
| disable() { | ||
| disable() { | ||
| super.disable(); | ||
| this._channelSubs.forEach(sub => sub.unsubscribe()); | ||
| this._channelSubs.forEach((sub) => sub.unsubscribe()); | ||
| this._channelSubs = []; | ||
| } | ||
| /** Enable the instrumentation. */ | ||
| enable() { | ||
| // "enabled" handling is currently a bit messy with InstrumentationBase. | ||
| // If constructed with `{enabled: false}`, this `.enable()` is still called, | ||
| // and `this.getConfig().enabled !== this.isEnabled()`, creating confusion. | ||
| // | ||
| // For now, this class will setup for instrumenting if `.enable()` is | ||
| // called, but use `this.getConfig().enabled` to determine if | ||
| // instrumentation should be generated. This covers the more likely common | ||
| // case of config being given a construction time, rather than later via | ||
| // `instance.enable()`, `.disable()`, or `.setConfig()` calls. | ||
| enable() { | ||
| super.enable(); | ||
| // This method is called by the super-class constructor before ours is | ||
| // called. So we need to ensure the property is initalized. | ||
| this._channelSubs = this._channelSubs || []; | ||
| // Avoid to duplicate subscriptions | ||
| if (this._channelSubs.length > 0) { | ||
| return; | ||
| } | ||
| this._subscribeToChannel('undici:request:create', this._onRequestCreated.bind(this)); | ||
| this._subscribeToChannel('undici:request:headers', this._onResponseHeaders.bind(this)); | ||
| this._subscribeToChannel("undici:request:create", this._onRequestCreated.bind(this)); | ||
| this._subscribeToChannel("undici:request:headers", this._onResponseHeaders.bind(this)); | ||
| } | ||
| /** | ||
@@ -73,19 +41,13 @@ * This method is called when a request is created. | ||
| */ | ||
| _onRequestCreated({ request }) { | ||
| _onRequestCreated({ request }) { | ||
| const config = this.getConfig(); | ||
| const enabled = config.enabled !== false; | ||
| if (!enabled) { | ||
| return; | ||
| } | ||
| const shouldIgnore = this._shouldIgnoreOutgoingRequest(request); | ||
| // We store this decisision for later so we do not need to re-evaluate it | ||
| // Additionally, the active context is not correct in _onResponseHeaders, so we need to make sure it is evaluated here | ||
| this._ignoreOutgoingRequestsMap.set(request, shouldIgnore); | ||
| if (shouldIgnore) { | ||
| return; | ||
| } | ||
| if (config.tracePropagation !== false) { | ||
@@ -95,19 +57,14 @@ addTracePropagationHeadersToFetchRequest(request, this._propagationDecisionMap); | ||
| } | ||
| /** | ||
| * This method is called when a response is received. | ||
| */ | ||
| _onResponseHeaders({ request, response }) { | ||
| _onResponseHeaders({ request, response }) { | ||
| const config = this.getConfig(); | ||
| const enabled = config.enabled !== false; | ||
| if (!enabled) { | ||
| return; | ||
| } | ||
| const _breadcrumbs = config.breadcrumbs; | ||
| const breadCrumbsEnabled = typeof _breadcrumbs === 'undefined' ? true : _breadcrumbs; | ||
| const breadCrumbsEnabled = typeof _breadcrumbs === "undefined" ? true : _breadcrumbs; | ||
| const shouldIgnore = this._ignoreOutgoingRequestsMap.get(request); | ||
| if (breadCrumbsEnabled && !shouldIgnore) { | ||
@@ -117,12 +74,5 @@ addFetchRequestBreadcrumb(request, response); | ||
| } | ||
| /** Subscribe to a diagnostics channel. */ | ||
| _subscribeToChannel( | ||
| diagnosticChannel, | ||
| onMessage, | ||
| ) { | ||
| // `diagnostics_channel` had a ref counting bug until v18.19.0. | ||
| // https://github.com/nodejs/node/pull/47520 | ||
| const useNewSubscribe = NODE_MAJOR > 18 || (NODE_MAJOR === 18 && NODE_MINOR >= 19); | ||
| _subscribeToChannel(diagnosticChannel, onMessage) { | ||
| const useNewSubscribe = NODE_MAJOR > 18 || NODE_MAJOR === 18 && NODE_MINOR >= 19; | ||
| let unsubscribe; | ||
@@ -137,25 +87,19 @@ if (useNewSubscribe) { | ||
| } | ||
| this._channelSubs.push({ | ||
| name: diagnosticChannel, | ||
| unsubscribe, | ||
| unsubscribe | ||
| }); | ||
| } | ||
| /** | ||
| * Check if the given outgoing request should be ignored. | ||
| */ | ||
| _shouldIgnoreOutgoingRequest(request) { | ||
| _shouldIgnoreOutgoingRequest(request) { | ||
| if (isTracingSuppressed(context.active())) { | ||
| return true; | ||
| } | ||
| // Add trace propagation headers | ||
| const url = getAbsoluteUrl(request.origin, request.path); | ||
| const ignoreOutgoingRequests = this.getConfig().ignoreOutgoingRequests; | ||
| if (typeof ignoreOutgoingRequests !== 'function' || !url) { | ||
| if (typeof ignoreOutgoingRequests !== "function" || !url) { | ||
| return false; | ||
| } | ||
| return ignoreOutgoingRequests(url); | ||
@@ -162,0 +106,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"SentryNodeFetchInstrumentation.js","sources":["../../../../src/integrations/node-fetch/SentryNodeFetchInstrumentation.ts"],"sourcesContent":["import { context } from '@opentelemetry/api';\nimport { isTracingSuppressed } from '@opentelemetry/core';\nimport type { InstrumentationConfig } from '@opentelemetry/instrumentation';\nimport { InstrumentationBase } from '@opentelemetry/instrumentation';\nimport { LRUMap, SDK_VERSION } from '@sentry/core';\nimport * as diagch from 'diagnostics_channel';\nimport { NODE_MAJOR, NODE_MINOR } from '../../nodeVersion';\nimport {\n addFetchRequestBreadcrumb,\n addTracePropagationHeadersToFetchRequest,\n getAbsoluteUrl,\n} from '../../utils/outgoingFetchRequest';\nimport type { UndiciRequest, UndiciResponse } from './types';\n\nexport type SentryNodeFetchInstrumentationOptions = InstrumentationConfig & {\n /**\n * Whether breadcrumbs should be recorded for requests.\n *\n * @default `true`\n */\n breadcrumbs?: boolean;\n\n /**\n * Whether to inject trace propagation headers (sentry-trace, baggage, traceparent) into outgoing fetch requests.\n *\n * When set to `false`, Sentry will not inject any trace propagation headers, but will still create breadcrumbs\n * (if `breadcrumbs` is enabled). This is useful when `skipOpenTelemetrySetup: true` is configured and you want\n * to avoid duplicate trace headers being injected by both Sentry and OpenTelemetry's UndiciInstrumentation.\n *\n * @default `true`\n */\n tracePropagation?: boolean;\n\n /**\n * Do not capture breadcrumbs or inject headers for outgoing fetch requests to URLs where the given callback returns `true`.\n * The same option can be passed to the top-level httpIntegration where it controls both, breadcrumb and\n * span creation.\n *\n * @param url Contains the entire URL, including query string (if any), protocol, host, etc. of the outgoing request.\n */\n ignoreOutgoingRequests?: (url: string) => boolean;\n};\n\ninterface ListenerRecord {\n name: string;\n unsubscribe: () => void;\n}\n\n/**\n * This custom node-fetch instrumentation is used to instrument outgoing fetch requests.\n * It does not emit any spans.\n *\n * The reason this is isolated from the OpenTelemetry instrumentation is that users may overwrite this,\n * which would lead to Sentry not working as expected.\n *\n * This is heavily inspired & adapted from:\n * https://github.com/open-telemetry/opentelemetry-js-contrib/blob/28e209a9da36bc4e1f8c2b0db7360170ed46cb80/plugins/node/instrumentation-undici/src/undici.ts\n */\nexport class SentryNodeFetchInstrumentation extends InstrumentationBase<SentryNodeFetchInstrumentationOptions> {\n // Keep ref to avoid https://github.com/nodejs/node/issues/42170 bug and for\n // unsubscribing.\n private _channelSubs: Array<ListenerRecord>;\n private _propagationDecisionMap: LRUMap<string, boolean>;\n private _ignoreOutgoingRequestsMap: WeakMap<UndiciRequest, boolean>;\n\n public constructor(config: SentryNodeFetchInstrumentationOptions = {}) {\n super('@sentry/instrumentation-node-fetch', SDK_VERSION, config);\n this._channelSubs = [];\n this._propagationDecisionMap = new LRUMap<string, boolean>(100);\n this._ignoreOutgoingRequestsMap = new WeakMap<UndiciRequest, boolean>();\n }\n\n /** No need to instrument files/modules. */\n public init(): void {\n return undefined;\n }\n\n /** Disable the instrumentation. */\n public disable(): void {\n super.disable();\n this._channelSubs.forEach(sub => sub.unsubscribe());\n this._channelSubs = [];\n }\n\n /** Enable the instrumentation. */\n public enable(): void {\n // \"enabled\" handling is currently a bit messy with InstrumentationBase.\n // If constructed with `{enabled: false}`, this `.enable()` is still called,\n // and `this.getConfig().enabled !== this.isEnabled()`, creating confusion.\n //\n // For now, this class will setup for instrumenting if `.enable()` is\n // called, but use `this.getConfig().enabled` to determine if\n // instrumentation should be generated. This covers the more likely common\n // case of config being given a construction time, rather than later via\n // `instance.enable()`, `.disable()`, or `.setConfig()` calls.\n super.enable();\n\n // This method is called by the super-class constructor before ours is\n // called. So we need to ensure the property is initalized.\n this._channelSubs = this._channelSubs || [];\n\n // Avoid to duplicate subscriptions\n if (this._channelSubs.length > 0) {\n return;\n }\n\n this._subscribeToChannel('undici:request:create', this._onRequestCreated.bind(this));\n this._subscribeToChannel('undici:request:headers', this._onResponseHeaders.bind(this));\n }\n\n /**\n * This method is called when a request is created.\n * You can still mutate the request here before it is sent.\n */\n private _onRequestCreated({ request }: { request: UndiciRequest }): void {\n const config = this.getConfig();\n const enabled = config.enabled !== false;\n\n if (!enabled) {\n return;\n }\n\n const shouldIgnore = this._shouldIgnoreOutgoingRequest(request);\n // We store this decisision for later so we do not need to re-evaluate it\n // Additionally, the active context is not correct in _onResponseHeaders, so we need to make sure it is evaluated here\n this._ignoreOutgoingRequestsMap.set(request, shouldIgnore);\n\n if (shouldIgnore) {\n return;\n }\n\n if (config.tracePropagation !== false) {\n addTracePropagationHeadersToFetchRequest(request, this._propagationDecisionMap);\n }\n }\n\n /**\n * This method is called when a response is received.\n */\n private _onResponseHeaders({ request, response }: { request: UndiciRequest; response: UndiciResponse }): void {\n const config = this.getConfig();\n const enabled = config.enabled !== false;\n\n if (!enabled) {\n return;\n }\n\n const _breadcrumbs = config.breadcrumbs;\n const breadCrumbsEnabled = typeof _breadcrumbs === 'undefined' ? true : _breadcrumbs;\n\n const shouldIgnore = this._ignoreOutgoingRequestsMap.get(request);\n\n if (breadCrumbsEnabled && !shouldIgnore) {\n addFetchRequestBreadcrumb(request, response);\n }\n }\n\n /** Subscribe to a diagnostics channel. */\n private _subscribeToChannel(\n diagnosticChannel: string,\n onMessage: (message: unknown, name: string | symbol) => void,\n ): void {\n // `diagnostics_channel` had a ref counting bug until v18.19.0.\n // https://github.com/nodejs/node/pull/47520\n const useNewSubscribe = NODE_MAJOR > 18 || (NODE_MAJOR === 18 && NODE_MINOR >= 19);\n\n let unsubscribe: () => void;\n if (useNewSubscribe) {\n diagch.subscribe?.(diagnosticChannel, onMessage);\n unsubscribe = () => diagch.unsubscribe?.(diagnosticChannel, onMessage);\n } else {\n const channel = diagch.channel(diagnosticChannel);\n channel.subscribe(onMessage);\n unsubscribe = () => channel.unsubscribe(onMessage);\n }\n\n this._channelSubs.push({\n name: diagnosticChannel,\n unsubscribe,\n });\n }\n\n /**\n * Check if the given outgoing request should be ignored.\n */\n private _shouldIgnoreOutgoingRequest(request: UndiciRequest): boolean {\n if (isTracingSuppressed(context.active())) {\n return true;\n }\n\n // Add trace propagation headers\n const url = getAbsoluteUrl(request.origin, request.path);\n const ignoreOutgoingRequests = this.getConfig().ignoreOutgoingRequests;\n\n if (typeof ignoreOutgoingRequests !== 'function' || !url) {\n return false;\n }\n\n return ignoreOutgoingRequests(url);\n }\n}\n"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"SentryNodeFetchInstrumentation.js","sources":["../../../../src/integrations/node-fetch/SentryNodeFetchInstrumentation.ts"],"sourcesContent":["import { context } from '@opentelemetry/api';\nimport { isTracingSuppressed } from '@opentelemetry/core';\nimport type { InstrumentationConfig } from '@opentelemetry/instrumentation';\nimport { InstrumentationBase } from '@opentelemetry/instrumentation';\nimport { LRUMap, SDK_VERSION } from '@sentry/core';\nimport * as diagch from 'diagnostics_channel';\nimport { NODE_MAJOR, NODE_MINOR } from '../../nodeVersion';\nimport {\n addFetchRequestBreadcrumb,\n addTracePropagationHeadersToFetchRequest,\n getAbsoluteUrl,\n} from '../../utils/outgoingFetchRequest';\nimport type { UndiciRequest, UndiciResponse } from './types';\n\nexport type SentryNodeFetchInstrumentationOptions = InstrumentationConfig & {\n /**\n * Whether breadcrumbs should be recorded for requests.\n *\n * @default `true`\n */\n breadcrumbs?: boolean;\n\n /**\n * Whether to inject trace propagation headers (sentry-trace, baggage, traceparent) into outgoing fetch requests.\n *\n * When set to `false`, Sentry will not inject any trace propagation headers, but will still create breadcrumbs\n * (if `breadcrumbs` is enabled). This is useful when `skipOpenTelemetrySetup: true` is configured and you want\n * to avoid duplicate trace headers being injected by both Sentry and OpenTelemetry's UndiciInstrumentation.\n *\n * @default `true`\n */\n tracePropagation?: boolean;\n\n /**\n * Do not capture breadcrumbs or inject headers for outgoing fetch requests to URLs where the given callback returns `true`.\n * The same option can be passed to the top-level httpIntegration where it controls both, breadcrumb and\n * span creation.\n *\n * @param url Contains the entire URL, including query string (if any), protocol, host, etc. of the outgoing request.\n */\n ignoreOutgoingRequests?: (url: string) => boolean;\n};\n\ninterface ListenerRecord {\n name: string;\n unsubscribe: () => void;\n}\n\n/**\n * This custom node-fetch instrumentation is used to instrument outgoing fetch requests.\n * It does not emit any spans.\n *\n * The reason this is isolated from the OpenTelemetry instrumentation is that users may overwrite this,\n * which would lead to Sentry not working as expected.\n *\n * This is heavily inspired & adapted from:\n * https://github.com/open-telemetry/opentelemetry-js-contrib/blob/28e209a9da36bc4e1f8c2b0db7360170ed46cb80/plugins/node/instrumentation-undici/src/undici.ts\n */\nexport class SentryNodeFetchInstrumentation extends InstrumentationBase<SentryNodeFetchInstrumentationOptions> {\n // Keep ref to avoid https://github.com/nodejs/node/issues/42170 bug and for\n // unsubscribing.\n private _channelSubs: Array<ListenerRecord>;\n private _propagationDecisionMap: LRUMap<string, boolean>;\n private _ignoreOutgoingRequestsMap: WeakMap<UndiciRequest, boolean>;\n\n public constructor(config: SentryNodeFetchInstrumentationOptions = {}) {\n super('@sentry/instrumentation-node-fetch', SDK_VERSION, config);\n this._channelSubs = [];\n this._propagationDecisionMap = new LRUMap<string, boolean>(100);\n this._ignoreOutgoingRequestsMap = new WeakMap<UndiciRequest, boolean>();\n }\n\n /** No need to instrument files/modules. */\n public init(): void {\n return undefined;\n }\n\n /** Disable the instrumentation. */\n public disable(): void {\n super.disable();\n this._channelSubs.forEach(sub => sub.unsubscribe());\n this._channelSubs = [];\n }\n\n /** Enable the instrumentation. */\n public enable(): void {\n // \"enabled\" handling is currently a bit messy with InstrumentationBase.\n // If constructed with `{enabled: false}`, this `.enable()` is still called,\n // and `this.getConfig().enabled !== this.isEnabled()`, creating confusion.\n //\n // For now, this class will setup for instrumenting if `.enable()` is\n // called, but use `this.getConfig().enabled` to determine if\n // instrumentation should be generated. This covers the more likely common\n // case of config being given a construction time, rather than later via\n // `instance.enable()`, `.disable()`, or `.setConfig()` calls.\n super.enable();\n\n // This method is called by the super-class constructor before ours is\n // called. So we need to ensure the property is initalized.\n this._channelSubs = this._channelSubs || [];\n\n // Avoid to duplicate subscriptions\n if (this._channelSubs.length > 0) {\n return;\n }\n\n this._subscribeToChannel('undici:request:create', this._onRequestCreated.bind(this));\n this._subscribeToChannel('undici:request:headers', this._onResponseHeaders.bind(this));\n }\n\n /**\n * This method is called when a request is created.\n * You can still mutate the request here before it is sent.\n */\n private _onRequestCreated({ request }: { request: UndiciRequest }): void {\n const config = this.getConfig();\n const enabled = config.enabled !== false;\n\n if (!enabled) {\n return;\n }\n\n const shouldIgnore = this._shouldIgnoreOutgoingRequest(request);\n // We store this decisision for later so we do not need to re-evaluate it\n // Additionally, the active context is not correct in _onResponseHeaders, so we need to make sure it is evaluated here\n this._ignoreOutgoingRequestsMap.set(request, shouldIgnore);\n\n if (shouldIgnore) {\n return;\n }\n\n if (config.tracePropagation !== false) {\n addTracePropagationHeadersToFetchRequest(request, this._propagationDecisionMap);\n }\n }\n\n /**\n * This method is called when a response is received.\n */\n private _onResponseHeaders({ request, response }: { request: UndiciRequest; response: UndiciResponse }): void {\n const config = this.getConfig();\n const enabled = config.enabled !== false;\n\n if (!enabled) {\n return;\n }\n\n const _breadcrumbs = config.breadcrumbs;\n const breadCrumbsEnabled = typeof _breadcrumbs === 'undefined' ? true : _breadcrumbs;\n\n const shouldIgnore = this._ignoreOutgoingRequestsMap.get(request);\n\n if (breadCrumbsEnabled && !shouldIgnore) {\n addFetchRequestBreadcrumb(request, response);\n }\n }\n\n /** Subscribe to a diagnostics channel. */\n private _subscribeToChannel(\n diagnosticChannel: string,\n onMessage: (message: unknown, name: string | symbol) => void,\n ): void {\n // `diagnostics_channel` had a ref counting bug until v18.19.0.\n // https://github.com/nodejs/node/pull/47520\n const useNewSubscribe = NODE_MAJOR > 18 || (NODE_MAJOR === 18 && NODE_MINOR >= 19);\n\n let unsubscribe: () => void;\n if (useNewSubscribe) {\n diagch.subscribe?.(diagnosticChannel, onMessage);\n unsubscribe = () => diagch.unsubscribe?.(diagnosticChannel, onMessage);\n } else {\n const channel = diagch.channel(diagnosticChannel);\n channel.subscribe(onMessage);\n unsubscribe = () => channel.unsubscribe(onMessage);\n }\n\n this._channelSubs.push({\n name: diagnosticChannel,\n unsubscribe,\n });\n }\n\n /**\n * Check if the given outgoing request should be ignored.\n */\n private _shouldIgnoreOutgoingRequest(request: UndiciRequest): boolean {\n if (isTracingSuppressed(context.active())) {\n return true;\n }\n\n // Add trace propagation headers\n const url = getAbsoluteUrl(request.origin, request.path);\n const ignoreOutgoingRequests = this.getConfig().ignoreOutgoingRequests;\n\n if (typeof ignoreOutgoingRequests !== 'function' || !url) {\n return false;\n }\n\n return ignoreOutgoingRequests(url);\n }\n}\n"],"names":[],"mappings":"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"} |
| import { monitorEventLoopDelay, performance } from 'perf_hooks'; | ||
| import { defineIntegration, _INTERNAL_safeDateNow, _INTERNAL_safeUnref, metrics } from '@sentry/core'; | ||
| const INTEGRATION_NAME = 'NodeRuntimeMetrics'; | ||
| const DEFAULT_INTERVAL_MS = 30000; | ||
| const MIN_COLLECTION_INTERVAL_MS = 1000; | ||
| const INTEGRATION_NAME = "NodeRuntimeMetrics"; | ||
| const DEFAULT_INTERVAL_MS = 3e4; | ||
| const MIN_COLLECTION_INTERVAL_MS = 1e3; | ||
| const EVENT_LOOP_DELAY_RESOLUTION_MS = 10; | ||
| /** | ||
| * Normalizes a `collectionIntervalMs` value, enforcing a minimum of 1000ms. | ||
| * - Non-finite values (NaN, Infinity): warns and falls back to `defaultInterval`. | ||
| * - Values below the minimum: warns and clamps to 1000ms. | ||
| * @internal | ||
| */ | ||
| function _INTERNAL_normalizeCollectionInterval( | ||
| rawInterval, | ||
| integrationName, | ||
| defaultInterval, | ||
| ) { | ||
| function _INTERNAL_normalizeCollectionInterval(rawInterval, integrationName, defaultInterval) { | ||
| if (!Number.isFinite(rawInterval)) { | ||
| // eslint-disable-next-line no-console | ||
| console.warn( | ||
| `[Sentry] ${integrationName}: collectionIntervalMs (${rawInterval}) is invalid. Using default of ${defaultInterval}ms.`, | ||
| `[Sentry] ${integrationName}: collectionIntervalMs (${rawInterval}) is invalid. Using default of ${defaultInterval}ms.` | ||
| ); | ||
@@ -28,5 +16,4 @@ return defaultInterval; | ||
| if (rawInterval < MIN_COLLECTION_INTERVAL_MS) { | ||
| // eslint-disable-next-line no-console | ||
| console.warn( | ||
| `[Sentry] ${integrationName}: collectionIntervalMs (${rawInterval}) is below the minimum of ${MIN_COLLECTION_INTERVAL_MS}ms. Using minimum of ${MIN_COLLECTION_INTERVAL_MS}ms.`, | ||
| `[Sentry] ${integrationName}: collectionIntervalMs (${rawInterval}) is below the minimum of ${MIN_COLLECTION_INTERVAL_MS}ms. Using minimum of ${MIN_COLLECTION_INTERVAL_MS}ms.` | ||
| ); | ||
@@ -37,15 +24,2 @@ return MIN_COLLECTION_INTERVAL_MS; | ||
| } | ||
| /** | ||
| * Automatically collects Node.js runtime metrics and emits them to Sentry. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * Sentry.init({ | ||
| * integrations: [ | ||
| * Sentry.nodeRuntimeMetricsIntegration(), | ||
| * ], | ||
| * }); | ||
| * ``` | ||
| */ | ||
| const nodeRuntimeMetricsIntegration = defineIntegration((options = {}) => { | ||
@@ -55,3 +29,3 @@ const collectionIntervalMs = _INTERNAL_normalizeCollectionInterval( | ||
| INTEGRATION_NAME, | ||
| DEFAULT_INTERVAL_MS, | ||
| DEFAULT_INTERVAL_MS | ||
| ); | ||
@@ -75,15 +49,6 @@ const collect = { | ||
| eventLoopDelayP90: false, | ||
| ...options.collect, | ||
| ...options.collect | ||
| }; | ||
| const needsEventLoopDelay = | ||
| collect.eventLoopDelayP99 || | ||
| collect.eventLoopDelayMin || | ||
| collect.eventLoopDelayMax || | ||
| collect.eventLoopDelayMean || | ||
| collect.eventLoopDelayP50 || | ||
| collect.eventLoopDelayP90; | ||
| const needsEventLoopDelay = collect.eventLoopDelayP99 || collect.eventLoopDelayMin || collect.eventLoopDelayMax || collect.eventLoopDelayMean || collect.eventLoopDelayP50 || collect.eventLoopDelayP90; | ||
| const needsCpu = collect.cpuUtilization || collect.cpuTime; | ||
| let intervalId; | ||
@@ -94,57 +59,47 @@ let prevCpuUsage; | ||
| let eventLoopDelayHistogram; | ||
| const resolutionNs = EVENT_LOOP_DELAY_RESOLUTION_MS * 1e6; | ||
| const nsToS = (ns) => Math.max(0, (ns - resolutionNs) / 1e9); | ||
| const METRIC_ATTRIBUTES = { attributes: { 'sentry.origin': 'auto.node.runtime_metrics' } }; | ||
| const METRIC_ATTRIBUTES_BYTE = { unit: 'byte', attributes: { 'sentry.origin': 'auto.node.runtime_metrics' } }; | ||
| const METRIC_ATTRIBUTES_SECOND = { unit: 'second', attributes: { 'sentry.origin': 'auto.node.runtime_metrics' } }; | ||
| const METRIC_ATTRIBUTES = { attributes: { "sentry.origin": "auto.node.runtime_metrics" } }; | ||
| const METRIC_ATTRIBUTES_BYTE = { unit: "byte", attributes: { "sentry.origin": "auto.node.runtime_metrics" } }; | ||
| const METRIC_ATTRIBUTES_SECOND = { unit: "second", attributes: { "sentry.origin": "auto.node.runtime_metrics" } }; | ||
| function collectMetrics() { | ||
| const now = _INTERNAL_safeDateNow(); | ||
| const elapsed = now - prevFlushTime; | ||
| if (needsCpu && prevCpuUsage !== undefined) { | ||
| if (needsCpu && prevCpuUsage !== void 0) { | ||
| const delta = process.cpuUsage(prevCpuUsage); | ||
| if (collect.cpuTime) { | ||
| metrics.gauge('node.runtime.cpu.user', delta.user / 1e6, METRIC_ATTRIBUTES_SECOND); | ||
| metrics.gauge('node.runtime.cpu.system', delta.system / 1e6, METRIC_ATTRIBUTES_SECOND); | ||
| metrics.gauge("node.runtime.cpu.user", delta.user / 1e6, METRIC_ATTRIBUTES_SECOND); | ||
| metrics.gauge("node.runtime.cpu.system", delta.system / 1e6, METRIC_ATTRIBUTES_SECOND); | ||
| } | ||
| if (collect.cpuUtilization && elapsed > 0) { | ||
| // Ratio of CPU time to wall-clock time. Can exceed 1.0 on multi-core systems. | ||
| // TODO: In cluster mode, add a runtime_id/process_id attribute to disambiguate per-worker metrics. | ||
| metrics.gauge( | ||
| 'node.runtime.cpu.utilization', | ||
| (delta.user + delta.system) / (elapsed * 1000), | ||
| METRIC_ATTRIBUTES, | ||
| "node.runtime.cpu.utilization", | ||
| (delta.user + delta.system) / (elapsed * 1e3), | ||
| METRIC_ATTRIBUTES | ||
| ); | ||
| } | ||
| prevCpuUsage = process.cpuUsage(); | ||
| } | ||
| if (collect.memRss || collect.memHeapUsed || collect.memHeapTotal || collect.memExternal) { | ||
| const mem = process.memoryUsage(); | ||
| if (collect.memRss) { | ||
| metrics.gauge('node.runtime.mem.rss', mem.rss, METRIC_ATTRIBUTES_BYTE); | ||
| metrics.gauge("node.runtime.mem.rss", mem.rss, METRIC_ATTRIBUTES_BYTE); | ||
| } | ||
| if (collect.memHeapUsed) { | ||
| metrics.gauge('node.runtime.mem.heap_used', mem.heapUsed, METRIC_ATTRIBUTES_BYTE); | ||
| metrics.gauge("node.runtime.mem.heap_used", mem.heapUsed, METRIC_ATTRIBUTES_BYTE); | ||
| } | ||
| if (collect.memHeapTotal) { | ||
| metrics.gauge('node.runtime.mem.heap_total', mem.heapTotal, METRIC_ATTRIBUTES_BYTE); | ||
| metrics.gauge("node.runtime.mem.heap_total", mem.heapTotal, METRIC_ATTRIBUTES_BYTE); | ||
| } | ||
| if (collect.memExternal) { | ||
| metrics.gauge('node.runtime.mem.external', mem.external, METRIC_ATTRIBUTES_BYTE); | ||
| metrics.gauge('node.runtime.mem.array_buffers', mem.arrayBuffers, METRIC_ATTRIBUTES_BYTE); | ||
| metrics.gauge("node.runtime.mem.external", mem.external, METRIC_ATTRIBUTES_BYTE); | ||
| metrics.gauge("node.runtime.mem.array_buffers", mem.arrayBuffers, METRIC_ATTRIBUTES_BYTE); | ||
| } | ||
| } | ||
| if (needsEventLoopDelay && eventLoopDelayHistogram) { | ||
| if (collect.eventLoopDelayMin) { | ||
| metrics.gauge( | ||
| 'node.runtime.event_loop.delay.min', | ||
| "node.runtime.event_loop.delay.min", | ||
| nsToS(eventLoopDelayHistogram.min), | ||
| METRIC_ATTRIBUTES_SECOND, | ||
| METRIC_ATTRIBUTES_SECOND | ||
| ); | ||
@@ -154,5 +109,5 @@ } | ||
| metrics.gauge( | ||
| 'node.runtime.event_loop.delay.max', | ||
| "node.runtime.event_loop.delay.max", | ||
| nsToS(eventLoopDelayHistogram.max), | ||
| METRIC_ATTRIBUTES_SECOND, | ||
| METRIC_ATTRIBUTES_SECOND | ||
| ); | ||
@@ -162,5 +117,5 @@ } | ||
| metrics.gauge( | ||
| 'node.runtime.event_loop.delay.mean', | ||
| "node.runtime.event_loop.delay.mean", | ||
| nsToS(eventLoopDelayHistogram.mean), | ||
| METRIC_ATTRIBUTES_SECOND, | ||
| METRIC_ATTRIBUTES_SECOND | ||
| ); | ||
@@ -170,5 +125,5 @@ } | ||
| metrics.gauge( | ||
| 'node.runtime.event_loop.delay.p50', | ||
| "node.runtime.event_loop.delay.p50", | ||
| nsToS(eventLoopDelayHistogram.percentile(50)), | ||
| METRIC_ATTRIBUTES_SECOND, | ||
| METRIC_ATTRIBUTES_SECOND | ||
| ); | ||
@@ -178,5 +133,5 @@ } | ||
| metrics.gauge( | ||
| 'node.runtime.event_loop.delay.p90', | ||
| "node.runtime.event_loop.delay.p90", | ||
| nsToS(eventLoopDelayHistogram.percentile(90)), | ||
| METRIC_ATTRIBUTES_SECOND, | ||
| METRIC_ATTRIBUTES_SECOND | ||
| ); | ||
@@ -186,31 +141,24 @@ } | ||
| metrics.gauge( | ||
| 'node.runtime.event_loop.delay.p99', | ||
| "node.runtime.event_loop.delay.p99", | ||
| nsToS(eventLoopDelayHistogram.percentile(99)), | ||
| METRIC_ATTRIBUTES_SECOND, | ||
| METRIC_ATTRIBUTES_SECOND | ||
| ); | ||
| } | ||
| eventLoopDelayHistogram.reset(); | ||
| } | ||
| if (collect.eventLoopUtilization && prevElu !== undefined) { | ||
| if (collect.eventLoopUtilization && prevElu !== void 0) { | ||
| const currentElu = performance.eventLoopUtilization(); | ||
| const delta = performance.eventLoopUtilization(currentElu, prevElu); | ||
| metrics.gauge('node.runtime.event_loop.utilization', delta.utilization, METRIC_ATTRIBUTES); | ||
| metrics.gauge("node.runtime.event_loop.utilization", delta.utilization, METRIC_ATTRIBUTES); | ||
| prevElu = currentElu; | ||
| } | ||
| if (collect.uptime && elapsed > 0) { | ||
| metrics.count('node.runtime.process.uptime', elapsed / 1000, METRIC_ATTRIBUTES_SECOND); | ||
| metrics.count("node.runtime.process.uptime", elapsed / 1e3, METRIC_ATTRIBUTES_SECOND); | ||
| } | ||
| prevFlushTime = now; | ||
| } | ||
| return { | ||
| name: INTEGRATION_NAME, | ||
| setup() { | ||
| if (needsEventLoopDelay) { | ||
| // Disable any previous histogram before overwriting (prevents native resource leak on re-init). | ||
| eventLoopDelayHistogram?.disable(); | ||
@@ -221,8 +169,5 @@ try { | ||
| } catch { | ||
| // Not available in all runtimes (e.g. Bun throws NotImplementedError). | ||
| eventLoopDelayHistogram = undefined; | ||
| eventLoopDelayHistogram = void 0; | ||
| } | ||
| } | ||
| // Prime baselines before the first collection interval. | ||
| if (needsCpu) { | ||
@@ -235,4 +180,2 @@ prevCpuUsage = process.cpuUsage(); | ||
| prevFlushTime = _INTERNAL_safeDateNow(); | ||
| // Guard against double setup (e.g. re-init). | ||
| if (intervalId) { | ||
@@ -242,3 +185,3 @@ clearInterval(intervalId); | ||
| intervalId = _INTERNAL_safeUnref(setInterval(collectMetrics, collectionIntervalMs)); | ||
| }, | ||
| } | ||
| }; | ||
@@ -245,0 +188,0 @@ }); |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"nodeRuntimeMetrics.js","sources":["../../../src/integrations/nodeRuntimeMetrics.ts"],"sourcesContent":["import { monitorEventLoopDelay, performance } from 'perf_hooks';\nimport { _INTERNAL_safeDateNow, _INTERNAL_safeUnref, defineIntegration, metrics } from '@sentry/core';\n\nconst INTEGRATION_NAME = 'NodeRuntimeMetrics';\nconst DEFAULT_INTERVAL_MS = 30_000;\nconst MIN_COLLECTION_INTERVAL_MS = 1_000;\nconst EVENT_LOOP_DELAY_RESOLUTION_MS = 10;\n\n/**\n * Normalizes a `collectionIntervalMs` value, enforcing a minimum of 1000ms.\n * - Non-finite values (NaN, Infinity): warns and falls back to `defaultInterval`.\n * - Values below the minimum: warns and clamps to 1000ms.\n * @internal\n */\nexport function _INTERNAL_normalizeCollectionInterval(\n rawInterval: number,\n integrationName: string,\n defaultInterval: number,\n): number {\n if (!Number.isFinite(rawInterval)) {\n // eslint-disable-next-line no-console\n console.warn(\n `[Sentry] ${integrationName}: collectionIntervalMs (${rawInterval}) is invalid. Using default of ${defaultInterval}ms.`,\n );\n return defaultInterval;\n }\n if (rawInterval < MIN_COLLECTION_INTERVAL_MS) {\n // eslint-disable-next-line no-console\n console.warn(\n `[Sentry] ${integrationName}: collectionIntervalMs (${rawInterval}) is below the minimum of ${MIN_COLLECTION_INTERVAL_MS}ms. Using minimum of ${MIN_COLLECTION_INTERVAL_MS}ms.`,\n );\n return MIN_COLLECTION_INTERVAL_MS;\n }\n return rawInterval;\n}\n\nexport interface NodeRuntimeMetricsOptions {\n /**\n * Which metrics to collect.\n *\n * Default on (8 metrics):\n * - `cpuUtilization` — CPU utilization ratio\n * - `memRss` — Resident Set Size (actual memory footprint)\n * - `memHeapUsed` — V8 heap currently in use\n * - `memHeapTotal` — total V8 heap allocated (headroom paired with `memHeapUsed`)\n * - `eventLoopDelayP50` — median event loop delay (baseline latency)\n * - `eventLoopDelayP99` — 99th percentile event loop delay (tail latency / spikes)\n * - `eventLoopUtilization` — fraction of time the event loop was active\n * - `uptime` — process uptime (detect restarts/crashes)\n *\n * Default off (opt-in):\n * - `cpuTime` — raw user/system CPU time in seconds\n * - `memExternal` — external/ArrayBuffer memory (relevant for native addons)\n * - `eventLoopDelayMin` / `eventLoopDelayMax` / `eventLoopDelayMean` / `eventLoopDelayP90`\n */\n collect?: {\n // Default on\n cpuUtilization?: boolean;\n memHeapUsed?: boolean;\n memRss?: boolean;\n eventLoopDelayP99?: boolean;\n eventLoopUtilization?: boolean;\n uptime?: boolean;\n // Default off\n cpuTime?: boolean;\n memHeapTotal?: boolean;\n memExternal?: boolean;\n eventLoopDelayMin?: boolean;\n eventLoopDelayMax?: boolean;\n eventLoopDelayMean?: boolean;\n eventLoopDelayP50?: boolean;\n eventLoopDelayP90?: boolean;\n };\n /**\n * How often to collect metrics, in milliseconds.\n * Minimum allowed value is 1000ms.\n * @default 30000\n * @minimum 1000\n */\n collectionIntervalMs?: number;\n}\n\n/**\n * Automatically collects Node.js runtime metrics and emits them to Sentry.\n *\n * @example\n * ```ts\n * Sentry.init({\n * integrations: [\n * Sentry.nodeRuntimeMetricsIntegration(),\n * ],\n * });\n * ```\n */\nexport const nodeRuntimeMetricsIntegration = defineIntegration((options: NodeRuntimeMetricsOptions = {}) => {\n const collectionIntervalMs = _INTERNAL_normalizeCollectionInterval(\n options.collectionIntervalMs ?? DEFAULT_INTERVAL_MS,\n INTEGRATION_NAME,\n DEFAULT_INTERVAL_MS,\n );\n const collect = {\n // Default on\n cpuUtilization: true,\n memHeapUsed: true,\n memHeapTotal: true,\n memRss: true,\n eventLoopDelayP50: true,\n eventLoopDelayP99: true,\n eventLoopUtilization: true,\n uptime: true,\n // Default off\n cpuTime: false,\n memExternal: false,\n eventLoopDelayMin: false,\n eventLoopDelayMax: false,\n eventLoopDelayMean: false,\n eventLoopDelayP90: false,\n ...options.collect,\n };\n\n const needsEventLoopDelay =\n collect.eventLoopDelayP99 ||\n collect.eventLoopDelayMin ||\n collect.eventLoopDelayMax ||\n collect.eventLoopDelayMean ||\n collect.eventLoopDelayP50 ||\n collect.eventLoopDelayP90;\n\n const needsCpu = collect.cpuUtilization || collect.cpuTime;\n\n let intervalId: ReturnType<typeof setInterval> | undefined;\n let prevCpuUsage: NodeJS.CpuUsage | undefined;\n let prevElu: ReturnType<typeof performance.eventLoopUtilization> | undefined;\n let prevFlushTime: number = 0;\n let eventLoopDelayHistogram: ReturnType<typeof monitorEventLoopDelay> | undefined;\n\n const resolutionNs = EVENT_LOOP_DELAY_RESOLUTION_MS * 1e6;\n const nsToS = (ns: number): number => Math.max(0, (ns - resolutionNs) / 1e9);\n\n const METRIC_ATTRIBUTES = { attributes: { 'sentry.origin': 'auto.node.runtime_metrics' } };\n const METRIC_ATTRIBUTES_BYTE = { unit: 'byte', attributes: { 'sentry.origin': 'auto.node.runtime_metrics' } };\n const METRIC_ATTRIBUTES_SECOND = { unit: 'second', attributes: { 'sentry.origin': 'auto.node.runtime_metrics' } };\n\n function collectMetrics(): void {\n const now = _INTERNAL_safeDateNow();\n const elapsed = now - prevFlushTime;\n\n if (needsCpu && prevCpuUsage !== undefined) {\n const delta = process.cpuUsage(prevCpuUsage);\n\n if (collect.cpuTime) {\n metrics.gauge('node.runtime.cpu.user', delta.user / 1e6, METRIC_ATTRIBUTES_SECOND);\n metrics.gauge('node.runtime.cpu.system', delta.system / 1e6, METRIC_ATTRIBUTES_SECOND);\n }\n if (collect.cpuUtilization && elapsed > 0) {\n // Ratio of CPU time to wall-clock time. Can exceed 1.0 on multi-core systems.\n // TODO: In cluster mode, add a runtime_id/process_id attribute to disambiguate per-worker metrics.\n metrics.gauge(\n 'node.runtime.cpu.utilization',\n (delta.user + delta.system) / (elapsed * 1000),\n METRIC_ATTRIBUTES,\n );\n }\n\n prevCpuUsage = process.cpuUsage();\n }\n\n if (collect.memRss || collect.memHeapUsed || collect.memHeapTotal || collect.memExternal) {\n const mem = process.memoryUsage();\n if (collect.memRss) {\n metrics.gauge('node.runtime.mem.rss', mem.rss, METRIC_ATTRIBUTES_BYTE);\n }\n if (collect.memHeapUsed) {\n metrics.gauge('node.runtime.mem.heap_used', mem.heapUsed, METRIC_ATTRIBUTES_BYTE);\n }\n if (collect.memHeapTotal) {\n metrics.gauge('node.runtime.mem.heap_total', mem.heapTotal, METRIC_ATTRIBUTES_BYTE);\n }\n if (collect.memExternal) {\n metrics.gauge('node.runtime.mem.external', mem.external, METRIC_ATTRIBUTES_BYTE);\n metrics.gauge('node.runtime.mem.array_buffers', mem.arrayBuffers, METRIC_ATTRIBUTES_BYTE);\n }\n }\n\n if (needsEventLoopDelay && eventLoopDelayHistogram) {\n if (collect.eventLoopDelayMin) {\n metrics.gauge(\n 'node.runtime.event_loop.delay.min',\n nsToS(eventLoopDelayHistogram.min),\n METRIC_ATTRIBUTES_SECOND,\n );\n }\n if (collect.eventLoopDelayMax) {\n metrics.gauge(\n 'node.runtime.event_loop.delay.max',\n nsToS(eventLoopDelayHistogram.max),\n METRIC_ATTRIBUTES_SECOND,\n );\n }\n if (collect.eventLoopDelayMean) {\n metrics.gauge(\n 'node.runtime.event_loop.delay.mean',\n nsToS(eventLoopDelayHistogram.mean),\n METRIC_ATTRIBUTES_SECOND,\n );\n }\n if (collect.eventLoopDelayP50) {\n metrics.gauge(\n 'node.runtime.event_loop.delay.p50',\n nsToS(eventLoopDelayHistogram.percentile(50)),\n METRIC_ATTRIBUTES_SECOND,\n );\n }\n if (collect.eventLoopDelayP90) {\n metrics.gauge(\n 'node.runtime.event_loop.delay.p90',\n nsToS(eventLoopDelayHistogram.percentile(90)),\n METRIC_ATTRIBUTES_SECOND,\n );\n }\n if (collect.eventLoopDelayP99) {\n metrics.gauge(\n 'node.runtime.event_loop.delay.p99',\n nsToS(eventLoopDelayHistogram.percentile(99)),\n METRIC_ATTRIBUTES_SECOND,\n );\n }\n\n eventLoopDelayHistogram.reset();\n }\n\n if (collect.eventLoopUtilization && prevElu !== undefined) {\n const currentElu = performance.eventLoopUtilization();\n const delta = performance.eventLoopUtilization(currentElu, prevElu);\n metrics.gauge('node.runtime.event_loop.utilization', delta.utilization, METRIC_ATTRIBUTES);\n prevElu = currentElu;\n }\n\n if (collect.uptime && elapsed > 0) {\n metrics.count('node.runtime.process.uptime', elapsed / 1000, METRIC_ATTRIBUTES_SECOND);\n }\n\n prevFlushTime = now;\n }\n\n return {\n name: INTEGRATION_NAME,\n\n setup(): void {\n if (needsEventLoopDelay) {\n // Disable any previous histogram before overwriting (prevents native resource leak on re-init).\n eventLoopDelayHistogram?.disable();\n try {\n eventLoopDelayHistogram = monitorEventLoopDelay({ resolution: EVENT_LOOP_DELAY_RESOLUTION_MS });\n eventLoopDelayHistogram.enable();\n } catch {\n // Not available in all runtimes (e.g. Bun throws NotImplementedError).\n eventLoopDelayHistogram = undefined;\n }\n }\n\n // Prime baselines before the first collection interval.\n if (needsCpu) {\n prevCpuUsage = process.cpuUsage();\n }\n if (collect.eventLoopUtilization) {\n prevElu = performance.eventLoopUtilization();\n }\n prevFlushTime = _INTERNAL_safeDateNow();\n\n // Guard against double setup (e.g. re-init).\n if (intervalId) {\n clearInterval(intervalId);\n }\n intervalId = _INTERNAL_safeUnref(setInterval(collectMetrics, collectionIntervalMs));\n },\n };\n});\n"],"names":[],"mappings":";;;AAGA,MAAM,gBAAA,GAAmB,oBAAoB;AAC7C,MAAM,mBAAA,GAAsB,KAAM;AAClC,MAAM,0BAAA,GAA6B,IAAK;AACxC,MAAM,8BAAA,GAAiC,EAAE;;AAEzC;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,qCAAqC;AACrD,EAAE,WAAW;AACb,EAAE,eAAe;AACjB,EAAE,eAAe;AACjB,EAAU;AACV,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE;AACrC;AACA,IAAI,OAAO,CAAC,IAAI;AAChB,MAAM,CAAC,SAAS,EAAE,eAAe,CAAC,wBAAwB,EAAE,WAAW,CAAC,+BAA+B,EAAE,eAAe,CAAC,GAAG,CAAC;AAC7H,KAAK;AACL,IAAI,OAAO,eAAe;AAC1B,EAAE;AACF,EAAE,IAAI,WAAA,GAAc,0BAA0B,EAAE;AAChD;AACA,IAAI,OAAO,CAAC,IAAI;AAChB,MAAM,CAAC,SAAS,EAAE,eAAe,CAAC,wBAAwB,EAAE,WAAW,CAAC,0BAA0B,EAAE,0BAA0B,CAAC,qBAAqB,EAAE,0BAA0B,CAAC,GAAG,CAAC;AACrL,KAAK;AACL,IAAI,OAAO,0BAA0B;AACrC,EAAE;AACF,EAAE,OAAO,WAAW;AACpB;;AAgDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAM,6BAAA,GAAgC,iBAAiB,CAAC,CAAC,OAAO,GAA8B,EAAE,KAAK;AAC5G,EAAE,MAAM,oBAAA,GAAuB,qCAAqC;AACpE,IAAI,OAAO,CAAC,oBAAA,IAAwB,mBAAmB;AACvD,IAAI,gBAAgB;AACpB,IAAI,mBAAmB;AACvB,GAAG;AACH,EAAE,MAAM,UAAU;AAClB;AACA,IAAI,cAAc,EAAE,IAAI;AACxB,IAAI,WAAW,EAAE,IAAI;AACrB,IAAI,YAAY,EAAE,IAAI;AACtB,IAAI,MAAM,EAAE,IAAI;AAChB,IAAI,iBAAiB,EAAE,IAAI;AAC3B,IAAI,iBAAiB,EAAE,IAAI;AAC3B,IAAI,oBAAoB,EAAE,IAAI;AAC9B,IAAI,MAAM,EAAE,IAAI;AAChB;AACA,IAAI,OAAO,EAAE,KAAK;AAClB,IAAI,WAAW,EAAE,KAAK;AACtB,IAAI,iBAAiB,EAAE,KAAK;AAC5B,IAAI,iBAAiB,EAAE,KAAK;AAC5B,IAAI,kBAAkB,EAAE,KAAK;AAC7B,IAAI,iBAAiB,EAAE,KAAK;AAC5B,IAAI,GAAG,OAAO,CAAC,OAAO;AACtB,GAAG;;AAEH,EAAE,MAAM,mBAAA;AACR,IAAI,OAAO,CAAC,iBAAA;AACZ,IAAI,OAAO,CAAC,iBAAA;AACZ,IAAI,OAAO,CAAC,iBAAA;AACZ,IAAI,OAAO,CAAC,kBAAA;AACZ,IAAI,OAAO,CAAC,iBAAA;AACZ,IAAI,OAAO,CAAC,iBAAiB;;AAE7B,EAAE,MAAM,WAAW,OAAO,CAAC,cAAA,IAAkB,OAAO,CAAC,OAAO;;AAE5D,EAAE,IAAI,UAAU;AAChB,EAAE,IAAI,YAAY;AAClB,EAAE,IAAI,OAAO;AACb,EAAE,IAAI,aAAa,GAAW,CAAC;AAC/B,EAAE,IAAI,uBAAuB;;AAE7B,EAAE,MAAM,YAAA,GAAe,8BAAA,GAAiC,GAAG;AAC3D,EAAE,MAAM,KAAA,GAAQ,CAAC,EAAE,KAAqB,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,EAAA,GAAK,YAAY,IAAI,GAAG,CAAC;;AAE9E,EAAE,MAAM,iBAAA,GAAoB,EAAE,UAAU,EAAE,EAAE,eAAe,EAAE,2BAAA,EAA4B,EAAG;AAC5F,EAAE,MAAM,sBAAA,GAAyB,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,eAAe,EAAE,2BAAA,IAA+B;AAC/G,EAAE,MAAM,wBAAA,GAA2B,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,eAAe,EAAE,2BAAA,IAA+B;;AAEnH,EAAE,SAAS,cAAc,GAAS;AAClC,IAAI,MAAM,GAAA,GAAM,qBAAqB,EAAE;AACvC,IAAI,MAAM,OAAA,GAAU,GAAA,GAAM,aAAa;;AAEvC,IAAI,IAAI,QAAA,IAAY,YAAA,KAAiB,SAAS,EAAE;AAChD,MAAM,MAAM,QAAQ,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC;;AAElD,MAAM,IAAI,OAAO,CAAC,OAAO,EAAE;AAC3B,QAAQ,OAAO,CAAC,KAAK,CAAC,uBAAuB,EAAE,KAAK,CAAC,IAAA,GAAO,GAAG,EAAE,wBAAwB,CAAC;AAC1F,QAAQ,OAAO,CAAC,KAAK,CAAC,yBAAyB,EAAE,KAAK,CAAC,MAAA,GAAS,GAAG,EAAE,wBAAwB,CAAC;AAC9F,MAAM;AACN,MAAM,IAAI,OAAO,CAAC,kBAAkB,OAAA,GAAU,CAAC,EAAE;AACjD;AACA;AACA,QAAQ,OAAO,CAAC,KAAK;AACrB,UAAU,8BAA8B;AACxC,UAAU,CAAC,KAAK,CAAC,IAAA,GAAO,KAAK,CAAC,MAAM,KAAK,OAAA,GAAU,IAAI,CAAC;AACxD,UAAU,iBAAiB;AAC3B,SAAS;AACT,MAAM;;AAEN,MAAM,eAAe,OAAO,CAAC,QAAQ,EAAE;AACvC,IAAI;;AAEJ,IAAI,IAAI,OAAO,CAAC,MAAA,IAAU,OAAO,CAAC,WAAA,IAAe,OAAO,CAAC,YAAA,IAAgB,OAAO,CAAC,WAAW,EAAE;AAC9F,MAAM,MAAM,GAAA,GAAM,OAAO,CAAC,WAAW,EAAE;AACvC,MAAM,IAAI,OAAO,CAAC,MAAM,EAAE;AAC1B,QAAQ,OAAO,CAAC,KAAK,CAAC,sBAAsB,EAAE,GAAG,CAAC,GAAG,EAAE,sBAAsB,CAAC;AAC9E,MAAM;AACN,MAAM,IAAI,OAAO,CAAC,WAAW,EAAE;AAC/B,QAAQ,OAAO,CAAC,KAAK,CAAC,4BAA4B,EAAE,GAAG,CAAC,QAAQ,EAAE,sBAAsB,CAAC;AACzF,MAAM;AACN,MAAM,IAAI,OAAO,CAAC,YAAY,EAAE;AAChC,QAAQ,OAAO,CAAC,KAAK,CAAC,6BAA6B,EAAE,GAAG,CAAC,SAAS,EAAE,sBAAsB,CAAC;AAC3F,MAAM;AACN,MAAM,IAAI,OAAO,CAAC,WAAW,EAAE;AAC/B,QAAQ,OAAO,CAAC,KAAK,CAAC,2BAA2B,EAAE,GAAG,CAAC,QAAQ,EAAE,sBAAsB,CAAC;AACxF,QAAQ,OAAO,CAAC,KAAK,CAAC,gCAAgC,EAAE,GAAG,CAAC,YAAY,EAAE,sBAAsB,CAAC;AACjG,MAAM;AACN,IAAI;;AAEJ,IAAI,IAAI,mBAAA,IAAuB,uBAAuB,EAAE;AACxD,MAAM,IAAI,OAAO,CAAC,iBAAiB,EAAE;AACrC,QAAQ,OAAO,CAAC,KAAK;AACrB,UAAU,mCAAmC;AAC7C,UAAU,KAAK,CAAC,uBAAuB,CAAC,GAAG,CAAC;AAC5C,UAAU,wBAAwB;AAClC,SAAS;AACT,MAAM;AACN,MAAM,IAAI,OAAO,CAAC,iBAAiB,EAAE;AACrC,QAAQ,OAAO,CAAC,KAAK;AACrB,UAAU,mCAAmC;AAC7C,UAAU,KAAK,CAAC,uBAAuB,CAAC,GAAG,CAAC;AAC5C,UAAU,wBAAwB;AAClC,SAAS;AACT,MAAM;AACN,MAAM,IAAI,OAAO,CAAC,kBAAkB,EAAE;AACtC,QAAQ,OAAO,CAAC,KAAK;AACrB,UAAU,oCAAoC;AAC9C,UAAU,KAAK,CAAC,uBAAuB,CAAC,IAAI,CAAC;AAC7C,UAAU,wBAAwB;AAClC,SAAS;AACT,MAAM;AACN,MAAM,IAAI,OAAO,CAAC,iBAAiB,EAAE;AACrC,QAAQ,OAAO,CAAC,KAAK;AACrB,UAAU,mCAAmC;AAC7C,UAAU,KAAK,CAAC,uBAAuB,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;AACvD,UAAU,wBAAwB;AAClC,SAAS;AACT,MAAM;AACN,MAAM,IAAI,OAAO,CAAC,iBAAiB,EAAE;AACrC,QAAQ,OAAO,CAAC,KAAK;AACrB,UAAU,mCAAmC;AAC7C,UAAU,KAAK,CAAC,uBAAuB,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;AACvD,UAAU,wBAAwB;AAClC,SAAS;AACT,MAAM;AACN,MAAM,IAAI,OAAO,CAAC,iBAAiB,EAAE;AACrC,QAAQ,OAAO,CAAC,KAAK;AACrB,UAAU,mCAAmC;AAC7C,UAAU,KAAK,CAAC,uBAAuB,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;AACvD,UAAU,wBAAwB;AAClC,SAAS;AACT,MAAM;;AAEN,MAAM,uBAAuB,CAAC,KAAK,EAAE;AACrC,IAAI;;AAEJ,IAAI,IAAI,OAAO,CAAC,wBAAwB,OAAA,KAAY,SAAS,EAAE;AAC/D,MAAM,MAAM,UAAA,GAAa,WAAW,CAAC,oBAAoB,EAAE;AAC3D,MAAM,MAAM,KAAA,GAAQ,WAAW,CAAC,oBAAoB,CAAC,UAAU,EAAE,OAAO,CAAC;AACzE,MAAM,OAAO,CAAC,KAAK,CAAC,qCAAqC,EAAE,KAAK,CAAC,WAAW,EAAE,iBAAiB,CAAC;AAChG,MAAM,OAAA,GAAU,UAAU;AAC1B,IAAI;;AAEJ,IAAI,IAAI,OAAO,CAAC,UAAU,OAAA,GAAU,CAAC,EAAE;AACvC,MAAM,OAAO,CAAC,KAAK,CAAC,6BAA6B,EAAE,OAAA,GAAU,IAAI,EAAE,wBAAwB,CAAC;AAC5F,IAAI;;AAEJ,IAAI,aAAA,GAAgB,GAAG;AACvB,EAAE;;AAEF,EAAE,OAAO;AACT,IAAI,IAAI,EAAE,gBAAgB;;AAE1B,IAAI,KAAK,GAAS;AAClB,MAAM,IAAI,mBAAmB,EAAE;AAC/B;AACA,QAAQ,uBAAuB,EAAE,OAAO,EAAE;AAC1C,QAAQ,IAAI;AACZ,UAAU,uBAAA,GAA0B,qBAAqB,CAAC,EAAE,UAAU,EAAE,8BAAA,EAAgC,CAAC;AACzG,UAAU,uBAAuB,CAAC,MAAM,EAAE;AAC1C,QAAQ,EAAE,MAAM;AAChB;AACA,UAAU,uBAAA,GAA0B,SAAS;AAC7C,QAAQ;AACR,MAAM;;AAEN;AACA,MAAM,IAAI,QAAQ,EAAE;AACpB,QAAQ,eAAe,OAAO,CAAC,QAAQ,EAAE;AACzC,MAAM;AACN,MAAM,IAAI,OAAO,CAAC,oBAAoB,EAAE;AACxC,QAAQ,UAAU,WAAW,CAAC,oBAAoB,EAAE;AACpD,MAAM;AACN,MAAM,aAAA,GAAgB,qBAAqB,EAAE;;AAE7C;AACA,MAAM,IAAI,UAAU,EAAE;AACtB,QAAQ,aAAa,CAAC,UAAU,CAAC;AACjC,MAAM;AACN,MAAM,UAAA,GAAa,mBAAmB,CAAC,WAAW,CAAC,cAAc,EAAE,oBAAoB,CAAC,CAAC;AACzF,IAAI,CAAC;AACL,GAAG;AACH,CAAC;;;;"} | ||
| {"version":3,"file":"nodeRuntimeMetrics.js","sources":["../../../src/integrations/nodeRuntimeMetrics.ts"],"sourcesContent":["import { monitorEventLoopDelay, performance } from 'perf_hooks';\nimport { _INTERNAL_safeDateNow, _INTERNAL_safeUnref, defineIntegration, metrics } from '@sentry/core';\n\nconst INTEGRATION_NAME = 'NodeRuntimeMetrics';\nconst DEFAULT_INTERVAL_MS = 30_000;\nconst MIN_COLLECTION_INTERVAL_MS = 1_000;\nconst EVENT_LOOP_DELAY_RESOLUTION_MS = 10;\n\n/**\n * Normalizes a `collectionIntervalMs` value, enforcing a minimum of 1000ms.\n * - Non-finite values (NaN, Infinity): warns and falls back to `defaultInterval`.\n * - Values below the minimum: warns and clamps to 1000ms.\n * @internal\n */\nexport function _INTERNAL_normalizeCollectionInterval(\n rawInterval: number,\n integrationName: string,\n defaultInterval: number,\n): number {\n if (!Number.isFinite(rawInterval)) {\n // eslint-disable-next-line no-console\n console.warn(\n `[Sentry] ${integrationName}: collectionIntervalMs (${rawInterval}) is invalid. Using default of ${defaultInterval}ms.`,\n );\n return defaultInterval;\n }\n if (rawInterval < MIN_COLLECTION_INTERVAL_MS) {\n // eslint-disable-next-line no-console\n console.warn(\n `[Sentry] ${integrationName}: collectionIntervalMs (${rawInterval}) is below the minimum of ${MIN_COLLECTION_INTERVAL_MS}ms. Using minimum of ${MIN_COLLECTION_INTERVAL_MS}ms.`,\n );\n return MIN_COLLECTION_INTERVAL_MS;\n }\n return rawInterval;\n}\n\nexport interface NodeRuntimeMetricsOptions {\n /**\n * Which metrics to collect.\n *\n * Default on (8 metrics):\n * - `cpuUtilization` — CPU utilization ratio\n * - `memRss` — Resident Set Size (actual memory footprint)\n * - `memHeapUsed` — V8 heap currently in use\n * - `memHeapTotal` — total V8 heap allocated (headroom paired with `memHeapUsed`)\n * - `eventLoopDelayP50` — median event loop delay (baseline latency)\n * - `eventLoopDelayP99` — 99th percentile event loop delay (tail latency / spikes)\n * - `eventLoopUtilization` — fraction of time the event loop was active\n * - `uptime` — process uptime (detect restarts/crashes)\n *\n * Default off (opt-in):\n * - `cpuTime` — raw user/system CPU time in seconds\n * - `memExternal` — external/ArrayBuffer memory (relevant for native addons)\n * - `eventLoopDelayMin` / `eventLoopDelayMax` / `eventLoopDelayMean` / `eventLoopDelayP90`\n */\n collect?: {\n // Default on\n cpuUtilization?: boolean;\n memHeapUsed?: boolean;\n memRss?: boolean;\n eventLoopDelayP99?: boolean;\n eventLoopUtilization?: boolean;\n uptime?: boolean;\n // Default off\n cpuTime?: boolean;\n memHeapTotal?: boolean;\n memExternal?: boolean;\n eventLoopDelayMin?: boolean;\n eventLoopDelayMax?: boolean;\n eventLoopDelayMean?: boolean;\n eventLoopDelayP50?: boolean;\n eventLoopDelayP90?: boolean;\n };\n /**\n * How often to collect metrics, in milliseconds.\n * Minimum allowed value is 1000ms.\n * @default 30000\n * @minimum 1000\n */\n collectionIntervalMs?: number;\n}\n\n/**\n * Automatically collects Node.js runtime metrics and emits them to Sentry.\n *\n * @example\n * ```ts\n * Sentry.init({\n * integrations: [\n * Sentry.nodeRuntimeMetricsIntegration(),\n * ],\n * });\n * ```\n */\nexport const nodeRuntimeMetricsIntegration = defineIntegration((options: NodeRuntimeMetricsOptions = {}) => {\n const collectionIntervalMs = _INTERNAL_normalizeCollectionInterval(\n options.collectionIntervalMs ?? DEFAULT_INTERVAL_MS,\n INTEGRATION_NAME,\n DEFAULT_INTERVAL_MS,\n );\n const collect = {\n // Default on\n cpuUtilization: true,\n memHeapUsed: true,\n memHeapTotal: true,\n memRss: true,\n eventLoopDelayP50: true,\n eventLoopDelayP99: true,\n eventLoopUtilization: true,\n uptime: true,\n // Default off\n cpuTime: false,\n memExternal: false,\n eventLoopDelayMin: false,\n eventLoopDelayMax: false,\n eventLoopDelayMean: false,\n eventLoopDelayP90: false,\n ...options.collect,\n };\n\n const needsEventLoopDelay =\n collect.eventLoopDelayP99 ||\n collect.eventLoopDelayMin ||\n collect.eventLoopDelayMax ||\n collect.eventLoopDelayMean ||\n collect.eventLoopDelayP50 ||\n collect.eventLoopDelayP90;\n\n const needsCpu = collect.cpuUtilization || collect.cpuTime;\n\n let intervalId: ReturnType<typeof setInterval> | undefined;\n let prevCpuUsage: NodeJS.CpuUsage | undefined;\n let prevElu: ReturnType<typeof performance.eventLoopUtilization> | undefined;\n let prevFlushTime: number = 0;\n let eventLoopDelayHistogram: ReturnType<typeof monitorEventLoopDelay> | undefined;\n\n const resolutionNs = EVENT_LOOP_DELAY_RESOLUTION_MS * 1e6;\n const nsToS = (ns: number): number => Math.max(0, (ns - resolutionNs) / 1e9);\n\n const METRIC_ATTRIBUTES = { attributes: { 'sentry.origin': 'auto.node.runtime_metrics' } };\n const METRIC_ATTRIBUTES_BYTE = { unit: 'byte', attributes: { 'sentry.origin': 'auto.node.runtime_metrics' } };\n const METRIC_ATTRIBUTES_SECOND = { unit: 'second', attributes: { 'sentry.origin': 'auto.node.runtime_metrics' } };\n\n function collectMetrics(): void {\n const now = _INTERNAL_safeDateNow();\n const elapsed = now - prevFlushTime;\n\n if (needsCpu && prevCpuUsage !== undefined) {\n const delta = process.cpuUsage(prevCpuUsage);\n\n if (collect.cpuTime) {\n metrics.gauge('node.runtime.cpu.user', delta.user / 1e6, METRIC_ATTRIBUTES_SECOND);\n metrics.gauge('node.runtime.cpu.system', delta.system / 1e6, METRIC_ATTRIBUTES_SECOND);\n }\n if (collect.cpuUtilization && elapsed > 0) {\n // Ratio of CPU time to wall-clock time. Can exceed 1.0 on multi-core systems.\n // TODO: In cluster mode, add a runtime_id/process_id attribute to disambiguate per-worker metrics.\n metrics.gauge(\n 'node.runtime.cpu.utilization',\n (delta.user + delta.system) / (elapsed * 1000),\n METRIC_ATTRIBUTES,\n );\n }\n\n prevCpuUsage = process.cpuUsage();\n }\n\n if (collect.memRss || collect.memHeapUsed || collect.memHeapTotal || collect.memExternal) {\n const mem = process.memoryUsage();\n if (collect.memRss) {\n metrics.gauge('node.runtime.mem.rss', mem.rss, METRIC_ATTRIBUTES_BYTE);\n }\n if (collect.memHeapUsed) {\n metrics.gauge('node.runtime.mem.heap_used', mem.heapUsed, METRIC_ATTRIBUTES_BYTE);\n }\n if (collect.memHeapTotal) {\n metrics.gauge('node.runtime.mem.heap_total', mem.heapTotal, METRIC_ATTRIBUTES_BYTE);\n }\n if (collect.memExternal) {\n metrics.gauge('node.runtime.mem.external', mem.external, METRIC_ATTRIBUTES_BYTE);\n metrics.gauge('node.runtime.mem.array_buffers', mem.arrayBuffers, METRIC_ATTRIBUTES_BYTE);\n }\n }\n\n if (needsEventLoopDelay && eventLoopDelayHistogram) {\n if (collect.eventLoopDelayMin) {\n metrics.gauge(\n 'node.runtime.event_loop.delay.min',\n nsToS(eventLoopDelayHistogram.min),\n METRIC_ATTRIBUTES_SECOND,\n );\n }\n if (collect.eventLoopDelayMax) {\n metrics.gauge(\n 'node.runtime.event_loop.delay.max',\n nsToS(eventLoopDelayHistogram.max),\n METRIC_ATTRIBUTES_SECOND,\n );\n }\n if (collect.eventLoopDelayMean) {\n metrics.gauge(\n 'node.runtime.event_loop.delay.mean',\n nsToS(eventLoopDelayHistogram.mean),\n METRIC_ATTRIBUTES_SECOND,\n );\n }\n if (collect.eventLoopDelayP50) {\n metrics.gauge(\n 'node.runtime.event_loop.delay.p50',\n nsToS(eventLoopDelayHistogram.percentile(50)),\n METRIC_ATTRIBUTES_SECOND,\n );\n }\n if (collect.eventLoopDelayP90) {\n metrics.gauge(\n 'node.runtime.event_loop.delay.p90',\n nsToS(eventLoopDelayHistogram.percentile(90)),\n METRIC_ATTRIBUTES_SECOND,\n );\n }\n if (collect.eventLoopDelayP99) {\n metrics.gauge(\n 'node.runtime.event_loop.delay.p99',\n nsToS(eventLoopDelayHistogram.percentile(99)),\n METRIC_ATTRIBUTES_SECOND,\n );\n }\n\n eventLoopDelayHistogram.reset();\n }\n\n if (collect.eventLoopUtilization && prevElu !== undefined) {\n const currentElu = performance.eventLoopUtilization();\n const delta = performance.eventLoopUtilization(currentElu, prevElu);\n metrics.gauge('node.runtime.event_loop.utilization', delta.utilization, METRIC_ATTRIBUTES);\n prevElu = currentElu;\n }\n\n if (collect.uptime && elapsed > 0) {\n metrics.count('node.runtime.process.uptime', elapsed / 1000, METRIC_ATTRIBUTES_SECOND);\n }\n\n prevFlushTime = now;\n }\n\n return {\n name: INTEGRATION_NAME,\n\n setup(): void {\n if (needsEventLoopDelay) {\n // Disable any previous histogram before overwriting (prevents native resource leak on re-init).\n eventLoopDelayHistogram?.disable();\n try {\n eventLoopDelayHistogram = monitorEventLoopDelay({ resolution: EVENT_LOOP_DELAY_RESOLUTION_MS });\n eventLoopDelayHistogram.enable();\n } catch {\n // Not available in all runtimes (e.g. Bun throws NotImplementedError).\n eventLoopDelayHistogram = undefined;\n }\n }\n\n // Prime baselines before the first collection interval.\n if (needsCpu) {\n prevCpuUsage = process.cpuUsage();\n }\n if (collect.eventLoopUtilization) {\n prevElu = performance.eventLoopUtilization();\n }\n prevFlushTime = _INTERNAL_safeDateNow();\n\n // Guard against double setup (e.g. re-init).\n if (intervalId) {\n clearInterval(intervalId);\n }\n intervalId = _INTERNAL_safeUnref(setInterval(collectMetrics, collectionIntervalMs));\n },\n };\n});\n"],"names":[],"mappings":"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"} |
@@ -6,30 +6,20 @@ import { defineIntegration, getClient, captureException, debug } from '@sentry/core'; | ||
| const INTEGRATION_NAME = 'OnUncaughtException'; | ||
| /** | ||
| * Add a global exception handler. | ||
| */ | ||
| const INTEGRATION_NAME = "OnUncaughtException"; | ||
| const onUncaughtExceptionIntegration = defineIntegration((options = {}) => { | ||
| const optionsWithDefaults = { | ||
| exitEvenIfOtherHandlersAreRegistered: false, | ||
| ...options, | ||
| ...options | ||
| }; | ||
| return { | ||
| name: INTEGRATION_NAME, | ||
| setup(client) { | ||
| // errors in worker threads are already handled by the childProcessIntegration | ||
| // also we don't want to exit the Node process on worker thread errors | ||
| if (!isMainThread) { | ||
| return; | ||
| } | ||
| global.process.on('uncaughtException', makeErrorHandler(client, optionsWithDefaults)); | ||
| }, | ||
| global.process.on("uncaughtException", makeErrorHandler(client, optionsWithDefaults)); | ||
| } | ||
| }; | ||
| }); | ||
| /** Exported only for tests */ | ||
| function makeErrorHandler(client, options) { | ||
| const timeout = 2000; | ||
| const timeout = 2e3; | ||
| let caughtFirstError = false; | ||
@@ -39,44 +29,26 @@ let caughtSecondError = false; | ||
| let firstError; | ||
| const clientOptions = client.getOptions(); | ||
| return Object.assign( | ||
| (error) => { | ||
| let onFatalError = logAndExitProcess; | ||
| if (options.onFatalError) { | ||
| onFatalError = options.onFatalError; | ||
| } else if (clientOptions.onFatalError) { | ||
| onFatalError = clientOptions.onFatalError ; | ||
| onFatalError = clientOptions.onFatalError; | ||
| } | ||
| // Attaching a listener to `uncaughtException` will prevent the node process from exiting. We generally do not | ||
| // want to alter this behaviour so we check for other listeners that users may have attached themselves and adjust | ||
| // exit behaviour of the SDK accordingly: | ||
| // - If other listeners are attached, do not exit. | ||
| // - If the only listener attached is ours, exit. | ||
| const userProvidedListenersCount = (global.process.listeners('uncaughtException') ).filter( | ||
| listener => { | ||
| // There are 3 listeners we ignore: | ||
| const userProvidedListenersCount = global.process.listeners("uncaughtException").filter( | ||
| (listener) => { | ||
| return ( | ||
| // as soon as we're using domains this listener is attached by node itself | ||
| listener.name !== 'domainUncaughtExceptionClear' && | ||
| // the handler we register for tracing | ||
| listener.tag !== 'sentry_tracingErrorCallback' && | ||
| // the handler we register in this integration | ||
| (listener )._errorHandler !== true | ||
| listener.name !== "domainUncaughtExceptionClear" && // the handler we register for tracing | ||
| listener.tag !== "sentry_tracingErrorCallback" && // the handler we register in this integration | ||
| listener._errorHandler !== true | ||
| ); | ||
| }, | ||
| } | ||
| ).length; | ||
| const processWouldExit = userProvidedListenersCount === 0; | ||
| const shouldApplyFatalHandlingLogic = options.exitEvenIfOtherHandlersAreRegistered || processWouldExit; | ||
| if (!caughtFirstError) { | ||
| // this is the first uncaught error and the ultimate reason for shutting down | ||
| // we want to do absolutely everything possible to ensure it gets captured | ||
| // also we want to make sure we don't go recursion crazy if more errors happen after this one | ||
| firstError = error; | ||
| caughtFirstError = true; | ||
| if (getClient() === client) { | ||
@@ -86,11 +58,10 @@ captureException(error, { | ||
| captureContext: { | ||
| level: 'fatal', | ||
| level: "fatal" | ||
| }, | ||
| mechanism: { | ||
| handled: false, | ||
| type: 'auto.node.onuncaughtexception', | ||
| }, | ||
| type: "auto.node.onuncaughtexception" | ||
| } | ||
| }); | ||
| } | ||
| if (!calledFatalError && shouldApplyFatalHandlingLogic) { | ||
@@ -103,31 +74,14 @@ calledFatalError = true; | ||
| if (calledFatalError) { | ||
| // we hit an error *after* calling onFatalError - pretty boned at this point, just shut it down | ||
| DEBUG_BUILD && | ||
| debug.warn( | ||
| 'uncaught exception after calling fatal error shutdown callback - this is bad! forcing shutdown', | ||
| ); | ||
| DEBUG_BUILD && debug.warn( | ||
| "uncaught exception after calling fatal error shutdown callback - this is bad! forcing shutdown" | ||
| ); | ||
| logAndExitProcess(error); | ||
| } else if (!caughtSecondError) { | ||
| // two cases for how we can hit this branch: | ||
| // - capturing of first error blew up and we just caught the exception from that | ||
| // - quit trying to capture, proceed with shutdown | ||
| // - a second independent error happened while waiting for first error to capture | ||
| // - want to avoid causing premature shutdown before first error capture finishes | ||
| // it's hard to immediately tell case 1 from case 2 without doing some fancy/questionable domain stuff | ||
| // so let's instead just delay a bit before we proceed with our action here | ||
| // in case 1, we just wait a bit unnecessarily but ultimately do the same thing | ||
| // in case 2, the delay hopefully made us wait long enough for the capture to finish | ||
| // two potential nonideal outcomes: | ||
| // nonideal case 1: capturing fails fast, we sit around for a few seconds unnecessarily before proceeding correctly by calling onFatalError | ||
| // nonideal case 2: case 2 happens, 1st error is captured but slowly, timeout completes before capture and we treat second error as the sendErr of (nonexistent) failure from trying to capture first error | ||
| // note that after hitting this branch, we might catch more errors where (caughtSecondError && !calledFatalError) | ||
| // we ignore them - they don't matter to us, we're just waiting for the second error timeout to finish | ||
| caughtSecondError = true; | ||
| setTimeout(() => { | ||
| if (!calledFatalError) { | ||
| // it was probably case 1, let's treat err as the sendErr and call onFatalError | ||
| calledFatalError = true; | ||
| onFatalError(firstError, error); | ||
| } | ||
| }, timeout); // capturing could take at least sendTimeout to fail, plus an arbitrary second for how long it takes to collect surrounding source etc | ||
| }, timeout); | ||
| } | ||
@@ -137,3 +91,3 @@ } | ||
| }, | ||
| { _errorHandler: true }, | ||
| { _errorHandler: true } | ||
| ); | ||
@@ -140,0 +94,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"onuncaughtexception.js","sources":["../../../src/integrations/onuncaughtexception.ts"],"sourcesContent":["import { captureException, debug, defineIntegration, getClient } from '@sentry/core';\nimport { isMainThread } from 'worker_threads';\nimport { DEBUG_BUILD } from '../debug-build';\nimport type { NodeClient } from '../sdk/client';\nimport { logAndExitProcess } from '../utils/errorhandling';\n\ntype OnFatalErrorHandler = (firstError: Error, secondError?: Error) => void;\n\ntype TaggedListener = NodeJS.UncaughtExceptionListener & {\n tag?: string;\n};\n\ninterface OnUncaughtExceptionOptions {\n /**\n * Controls if the SDK should register a handler to exit the process on uncaught errors:\n * - `true`: The SDK will exit the process on all uncaught errors.\n * - `false`: The SDK will only exit the process when there are no other `uncaughtException` handlers attached.\n *\n * Default: `false`\n */\n exitEvenIfOtherHandlersAreRegistered: boolean;\n\n /**\n * This is called when an uncaught error would cause the process to exit.\n *\n * @param firstError Uncaught error causing the process to exit\n * @param secondError Will be set if the handler was called multiple times. This can happen either because\n * `onFatalError` itself threw, or because an independent error happened somewhere else while `onFatalError`\n * was running.\n */\n onFatalError?(this: void, firstError: Error, secondError?: Error): void;\n}\n\nconst INTEGRATION_NAME = 'OnUncaughtException';\n\n/**\n * Add a global exception handler.\n */\nexport const onUncaughtExceptionIntegration = defineIntegration((options: Partial<OnUncaughtExceptionOptions> = {}) => {\n const optionsWithDefaults = {\n exitEvenIfOtherHandlersAreRegistered: false,\n ...options,\n };\n\n return {\n name: INTEGRATION_NAME,\n setup(client: NodeClient) {\n // errors in worker threads are already handled by the childProcessIntegration\n // also we don't want to exit the Node process on worker thread errors\n if (!isMainThread) {\n return;\n }\n\n global.process.on('uncaughtException', makeErrorHandler(client, optionsWithDefaults));\n },\n };\n});\n\ntype ErrorHandler = { _errorHandler: boolean } & ((error: Error) => void);\n\n/** Exported only for tests */\nexport function makeErrorHandler(client: NodeClient, options: OnUncaughtExceptionOptions): ErrorHandler {\n const timeout = 2000;\n let caughtFirstError: boolean = false;\n let caughtSecondError: boolean = false;\n let calledFatalError: boolean = false;\n let firstError: Error;\n\n const clientOptions = client.getOptions();\n\n return Object.assign(\n (error: Error): void => {\n let onFatalError: OnFatalErrorHandler = logAndExitProcess;\n\n if (options.onFatalError) {\n onFatalError = options.onFatalError;\n } else if (clientOptions.onFatalError) {\n onFatalError = clientOptions.onFatalError as OnFatalErrorHandler;\n }\n\n // Attaching a listener to `uncaughtException` will prevent the node process from exiting. We generally do not\n // want to alter this behaviour so we check for other listeners that users may have attached themselves and adjust\n // exit behaviour of the SDK accordingly:\n // - If other listeners are attached, do not exit.\n // - If the only listener attached is ours, exit.\n const userProvidedListenersCount = (global.process.listeners('uncaughtException') as TaggedListener[]).filter(\n listener => {\n // There are 3 listeners we ignore:\n return (\n // as soon as we're using domains this listener is attached by node itself\n listener.name !== 'domainUncaughtExceptionClear' &&\n // the handler we register for tracing\n listener.tag !== 'sentry_tracingErrorCallback' &&\n // the handler we register in this integration\n (listener as ErrorHandler)._errorHandler !== true\n );\n },\n ).length;\n\n const processWouldExit = userProvidedListenersCount === 0;\n const shouldApplyFatalHandlingLogic = options.exitEvenIfOtherHandlersAreRegistered || processWouldExit;\n\n if (!caughtFirstError) {\n // this is the first uncaught error and the ultimate reason for shutting down\n // we want to do absolutely everything possible to ensure it gets captured\n // also we want to make sure we don't go recursion crazy if more errors happen after this one\n firstError = error;\n caughtFirstError = true;\n\n if (getClient() === client) {\n captureException(error, {\n originalException: error,\n captureContext: {\n level: 'fatal',\n },\n mechanism: {\n handled: false,\n type: 'auto.node.onuncaughtexception',\n },\n });\n }\n\n if (!calledFatalError && shouldApplyFatalHandlingLogic) {\n calledFatalError = true;\n onFatalError(error);\n }\n } else {\n if (shouldApplyFatalHandlingLogic) {\n if (calledFatalError) {\n // we hit an error *after* calling onFatalError - pretty boned at this point, just shut it down\n DEBUG_BUILD &&\n debug.warn(\n 'uncaught exception after calling fatal error shutdown callback - this is bad! forcing shutdown',\n );\n logAndExitProcess(error);\n } else if (!caughtSecondError) {\n // two cases for how we can hit this branch:\n // - capturing of first error blew up and we just caught the exception from that\n // - quit trying to capture, proceed with shutdown\n // - a second independent error happened while waiting for first error to capture\n // - want to avoid causing premature shutdown before first error capture finishes\n // it's hard to immediately tell case 1 from case 2 without doing some fancy/questionable domain stuff\n // so let's instead just delay a bit before we proceed with our action here\n // in case 1, we just wait a bit unnecessarily but ultimately do the same thing\n // in case 2, the delay hopefully made us wait long enough for the capture to finish\n // two potential nonideal outcomes:\n // nonideal case 1: capturing fails fast, we sit around for a few seconds unnecessarily before proceeding correctly by calling onFatalError\n // nonideal case 2: case 2 happens, 1st error is captured but slowly, timeout completes before capture and we treat second error as the sendErr of (nonexistent) failure from trying to capture first error\n // note that after hitting this branch, we might catch more errors where (caughtSecondError && !calledFatalError)\n // we ignore them - they don't matter to us, we're just waiting for the second error timeout to finish\n caughtSecondError = true;\n setTimeout(() => {\n if (!calledFatalError) {\n // it was probably case 1, let's treat err as the sendErr and call onFatalError\n calledFatalError = true;\n onFatalError(firstError, error);\n } else {\n // it was probably case 2, our first error finished capturing while we waited, cool, do nothing\n }\n }, timeout); // capturing could take at least sendTimeout to fail, plus an arbitrary second for how long it takes to collect surrounding source etc\n }\n }\n }\n },\n { _errorHandler: true },\n );\n}\n"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"onuncaughtexception.js","sources":["../../../src/integrations/onuncaughtexception.ts"],"sourcesContent":["import { captureException, debug, defineIntegration, getClient } from '@sentry/core';\nimport { isMainThread } from 'worker_threads';\nimport { DEBUG_BUILD } from '../debug-build';\nimport type { NodeClient } from '../sdk/client';\nimport { logAndExitProcess } from '../utils/errorhandling';\n\ntype OnFatalErrorHandler = (firstError: Error, secondError?: Error) => void;\n\ntype TaggedListener = NodeJS.UncaughtExceptionListener & {\n tag?: string;\n};\n\ninterface OnUncaughtExceptionOptions {\n /**\n * Controls if the SDK should register a handler to exit the process on uncaught errors:\n * - `true`: The SDK will exit the process on all uncaught errors.\n * - `false`: The SDK will only exit the process when there are no other `uncaughtException` handlers attached.\n *\n * Default: `false`\n */\n exitEvenIfOtherHandlersAreRegistered: boolean;\n\n /**\n * This is called when an uncaught error would cause the process to exit.\n *\n * @param firstError Uncaught error causing the process to exit\n * @param secondError Will be set if the handler was called multiple times. This can happen either because\n * `onFatalError` itself threw, or because an independent error happened somewhere else while `onFatalError`\n * was running.\n */\n onFatalError?(this: void, firstError: Error, secondError?: Error): void;\n}\n\nconst INTEGRATION_NAME = 'OnUncaughtException';\n\n/**\n * Add a global exception handler.\n */\nexport const onUncaughtExceptionIntegration = defineIntegration((options: Partial<OnUncaughtExceptionOptions> = {}) => {\n const optionsWithDefaults = {\n exitEvenIfOtherHandlersAreRegistered: false,\n ...options,\n };\n\n return {\n name: INTEGRATION_NAME,\n setup(client: NodeClient) {\n // errors in worker threads are already handled by the childProcessIntegration\n // also we don't want to exit the Node process on worker thread errors\n if (!isMainThread) {\n return;\n }\n\n global.process.on('uncaughtException', makeErrorHandler(client, optionsWithDefaults));\n },\n };\n});\n\ntype ErrorHandler = { _errorHandler: boolean } & ((error: Error) => void);\n\n/** Exported only for tests */\nexport function makeErrorHandler(client: NodeClient, options: OnUncaughtExceptionOptions): ErrorHandler {\n const timeout = 2000;\n let caughtFirstError: boolean = false;\n let caughtSecondError: boolean = false;\n let calledFatalError: boolean = false;\n let firstError: Error;\n\n const clientOptions = client.getOptions();\n\n return Object.assign(\n (error: Error): void => {\n let onFatalError: OnFatalErrorHandler = logAndExitProcess;\n\n if (options.onFatalError) {\n onFatalError = options.onFatalError;\n } else if (clientOptions.onFatalError) {\n onFatalError = clientOptions.onFatalError as OnFatalErrorHandler;\n }\n\n // Attaching a listener to `uncaughtException` will prevent the node process from exiting. We generally do not\n // want to alter this behaviour so we check for other listeners that users may have attached themselves and adjust\n // exit behaviour of the SDK accordingly:\n // - If other listeners are attached, do not exit.\n // - If the only listener attached is ours, exit.\n const userProvidedListenersCount = (global.process.listeners('uncaughtException') as TaggedListener[]).filter(\n listener => {\n // There are 3 listeners we ignore:\n return (\n // as soon as we're using domains this listener is attached by node itself\n listener.name !== 'domainUncaughtExceptionClear' &&\n // the handler we register for tracing\n listener.tag !== 'sentry_tracingErrorCallback' &&\n // the handler we register in this integration\n (listener as ErrorHandler)._errorHandler !== true\n );\n },\n ).length;\n\n const processWouldExit = userProvidedListenersCount === 0;\n const shouldApplyFatalHandlingLogic = options.exitEvenIfOtherHandlersAreRegistered || processWouldExit;\n\n if (!caughtFirstError) {\n // this is the first uncaught error and the ultimate reason for shutting down\n // we want to do absolutely everything possible to ensure it gets captured\n // also we want to make sure we don't go recursion crazy if more errors happen after this one\n firstError = error;\n caughtFirstError = true;\n\n if (getClient() === client) {\n captureException(error, {\n originalException: error,\n captureContext: {\n level: 'fatal',\n },\n mechanism: {\n handled: false,\n type: 'auto.node.onuncaughtexception',\n },\n });\n }\n\n if (!calledFatalError && shouldApplyFatalHandlingLogic) {\n calledFatalError = true;\n onFatalError(error);\n }\n } else {\n if (shouldApplyFatalHandlingLogic) {\n if (calledFatalError) {\n // we hit an error *after* calling onFatalError - pretty boned at this point, just shut it down\n DEBUG_BUILD &&\n debug.warn(\n 'uncaught exception after calling fatal error shutdown callback - this is bad! forcing shutdown',\n );\n logAndExitProcess(error);\n } else if (!caughtSecondError) {\n // two cases for how we can hit this branch:\n // - capturing of first error blew up and we just caught the exception from that\n // - quit trying to capture, proceed with shutdown\n // - a second independent error happened while waiting for first error to capture\n // - want to avoid causing premature shutdown before first error capture finishes\n // it's hard to immediately tell case 1 from case 2 without doing some fancy/questionable domain stuff\n // so let's instead just delay a bit before we proceed with our action here\n // in case 1, we just wait a bit unnecessarily but ultimately do the same thing\n // in case 2, the delay hopefully made us wait long enough for the capture to finish\n // two potential nonideal outcomes:\n // nonideal case 1: capturing fails fast, we sit around for a few seconds unnecessarily before proceeding correctly by calling onFatalError\n // nonideal case 2: case 2 happens, 1st error is captured but slowly, timeout completes before capture and we treat second error as the sendErr of (nonexistent) failure from trying to capture first error\n // note that after hitting this branch, we might catch more errors where (caughtSecondError && !calledFatalError)\n // we ignore them - they don't matter to us, we're just waiting for the second error timeout to finish\n caughtSecondError = true;\n setTimeout(() => {\n if (!calledFatalError) {\n // it was probably case 1, let's treat err as the sendErr and call onFatalError\n calledFatalError = true;\n onFatalError(firstError, error);\n } else {\n // it was probably case 2, our first error finished capturing while we waited, cool, do nothing\n }\n }, timeout); // capturing could take at least sendTimeout to fail, plus an arbitrary second for how long it takes to collect surrounding source etc\n }\n }\n }\n },\n { _errorHandler: true },\n );\n}\n"],"names":[],"mappings":"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"} |
| import { defineIntegration, getClient, withActiveSpan, consoleSandbox, captureException, isMatchingPattern } from '@sentry/core'; | ||
| import { logAndExitProcess } from '../utils/errorhandling.js'; | ||
| const INTEGRATION_NAME = 'OnUnhandledRejection'; | ||
| const INTEGRATION_NAME = "OnUnhandledRejection"; | ||
| const DEFAULT_IGNORES = [ | ||
| { | ||
| name: 'AI_NoOutputGeneratedError', // When stream aborts in Vercel AI SDK V5, Vercel flush() fails with an error | ||
| name: "AI_NoOutputGeneratedError" | ||
| // When stream aborts in Vercel AI SDK V5, Vercel flush() fails with an error | ||
| }, | ||
| { | ||
| name: 'AbortError', // When stream aborts in Vercel AI SDK V6 | ||
| }, | ||
| name: "AbortError" | ||
| // When stream aborts in Vercel AI SDK V6 | ||
| } | ||
| ]; | ||
| const _onUnhandledRejectionIntegration = ((options = {}) => { | ||
| const opts = { | ||
| mode: options.mode ?? 'warn', | ||
| ignore: [...DEFAULT_IGNORES, ...(options.ignore ?? [])], | ||
| mode: options.mode ?? "warn", | ||
| ignore: [...DEFAULT_IGNORES, ...options.ignore ?? []] | ||
| }; | ||
| return { | ||
| name: INTEGRATION_NAME, | ||
| setup(client) { | ||
| global.process.on('unhandledRejection', makeUnhandledPromiseHandler(client, opts)); | ||
| }, | ||
| global.process.on("unhandledRejection", makeUnhandledPromiseHandler(client, opts)); | ||
| } | ||
| }; | ||
| }) ; | ||
| }); | ||
| const onUnhandledRejectionIntegration = defineIntegration(_onUnhandledRejectionIntegration); | ||
| /** Extract error info safely */ | ||
| function extractErrorInfo(reason) { | ||
| // Check if reason is an object (including Error instances, not just plain objects) | ||
| if (typeof reason !== 'object' || reason === null) { | ||
| return { name: '', message: String(reason ?? '') }; | ||
| if (typeof reason !== "object" || reason === null) { | ||
| return { name: "", message: String(reason ?? "") }; | ||
| } | ||
| const errorLike = reason ; | ||
| const name = typeof errorLike.name === 'string' ? errorLike.name : ''; | ||
| const message = typeof errorLike.message === 'string' ? errorLike.message : String(reason); | ||
| const errorLike = reason; | ||
| const name = typeof errorLike.name === "string" ? errorLike.name : ""; | ||
| const message = typeof errorLike.message === "string" ? errorLike.message : String(reason); | ||
| return { name, message }; | ||
| } | ||
| /** Check if a matcher matches the reason */ | ||
| function isMatchingReason(matcher, errorInfo) { | ||
| // name/message matcher | ||
| const nameMatches = matcher.name === undefined || isMatchingPattern(errorInfo.name, matcher.name, true); | ||
| const messageMatches = matcher.message === undefined || isMatchingPattern(errorInfo.message, matcher.message); | ||
| const nameMatches = matcher.name === void 0 || isMatchingPattern(errorInfo.name, matcher.name, true); | ||
| const messageMatches = matcher.message === void 0 || isMatchingPattern(errorInfo.message, matcher.message); | ||
| return nameMatches && messageMatches; | ||
| } | ||
| /** Match helper */ | ||
| function matchesIgnore(list, reason) { | ||
| const errorInfo = extractErrorInfo(reason); | ||
| return list.some(matcher => isMatchingReason(matcher, errorInfo)); | ||
| return list.some((matcher) => isMatchingReason(matcher, errorInfo)); | ||
| } | ||
| /** Core handler */ | ||
| function makeUnhandledPromiseHandler( | ||
| client, | ||
| options, | ||
| ) { | ||
| function makeUnhandledPromiseHandler(client, options) { | ||
| return function sendUnhandledPromise(reason, _promise) { | ||
| // Only handle for the active client | ||
| if (getClient() !== client) { | ||
| return; | ||
| } | ||
| // Skip if configured to ignore | ||
| if (matchesIgnore(options.ignore ?? [], reason)) { | ||
| return; | ||
| } | ||
| const level = options.mode === 'strict' ? 'fatal' : 'error'; | ||
| // this can be set in places where we cannot reliably get access to the active span/error | ||
| // when the error bubbles up to this handler, we can use this to set the active span | ||
| const activeSpanForError = | ||
| reason && typeof reason === 'object' ? (reason )._sentry_active_span : undefined; | ||
| const activeSpanWrapper = activeSpanForError | ||
| ? (fn) => withActiveSpan(activeSpanForError, fn) | ||
| : (fn) => fn(); | ||
| const level = options.mode === "strict" ? "fatal" : "error"; | ||
| const activeSpanForError = reason && typeof reason === "object" ? reason._sentry_active_span : void 0; | ||
| const activeSpanWrapper = activeSpanForError ? (fn) => withActiveSpan(activeSpanForError, fn) : (fn) => fn(); | ||
| activeSpanWrapper(() => { | ||
@@ -93,33 +62,21 @@ captureException(reason, { | ||
| extra: { unhandledPromiseRejection: true }, | ||
| level, | ||
| level | ||
| }, | ||
| mechanism: { | ||
| handled: false, | ||
| type: 'auto.node.onunhandledrejection', | ||
| }, | ||
| type: "auto.node.onunhandledrejection" | ||
| } | ||
| }); | ||
| }); | ||
| handleRejection(reason, options.mode); | ||
| }; | ||
| } | ||
| /** | ||
| * Handler for `mode` option | ||
| */ | ||
| function handleRejection(reason, mode) { | ||
| // https://github.com/nodejs/node/blob/7cf6f9e964aa00772965391c23acda6d71972a9a/lib/internal/process/promises.js#L234-L240 | ||
| const rejectionWarning = | ||
| 'This error originated either by ' + | ||
| 'throwing inside of an async function without a catch block, ' + | ||
| 'or by rejecting a promise which was not handled with .catch().' + | ||
| ' The promise rejected with the reason:'; | ||
| /* eslint-disable no-console */ | ||
| if (mode === 'warn') { | ||
| const rejectionWarning = "This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason:"; | ||
| if (mode === "warn") { | ||
| consoleSandbox(() => { | ||
| console.warn(rejectionWarning); | ||
| console.error(reason && typeof reason === 'object' && 'stack' in reason ? reason.stack : reason); | ||
| console.error(reason && typeof reason === "object" && "stack" in reason ? reason.stack : reason); | ||
| }); | ||
| } else if (mode === 'strict') { | ||
| } else if (mode === "strict") { | ||
| consoleSandbox(() => { | ||
@@ -130,3 +87,2 @@ console.warn(rejectionWarning); | ||
| } | ||
| /* eslint-enable no-console */ | ||
| } | ||
@@ -133,0 +89,0 @@ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"onunhandledrejection.js","sources":["../../../src/integrations/onunhandledrejection.ts"],"sourcesContent":["import type { Client, IntegrationFn, SeverityLevel, Span } from '@sentry/core';\nimport {\n captureException,\n consoleSandbox,\n defineIntegration,\n getClient,\n isMatchingPattern,\n withActiveSpan,\n} from '@sentry/core';\nimport { logAndExitProcess } from '../utils/errorhandling';\n\ntype UnhandledRejectionMode = 'none' | 'warn' | 'strict';\n\ntype IgnoreMatcher = { name?: string | RegExp; message?: string | RegExp };\n\ninterface OnUnhandledRejectionOptions {\n /**\n * Option deciding what to do after capturing unhandledRejection,\n * that mimicks behavior of node's --unhandled-rejection flag.\n */\n mode: UnhandledRejectionMode;\n /** Rejection Errors to ignore (don't capture or warn). */\n ignore?: IgnoreMatcher[];\n}\n\nconst INTEGRATION_NAME = 'OnUnhandledRejection';\n\nconst DEFAULT_IGNORES: IgnoreMatcher[] = [\n {\n name: 'AI_NoOutputGeneratedError', // When stream aborts in Vercel AI SDK V5, Vercel flush() fails with an error\n },\n {\n name: 'AbortError', // When stream aborts in Vercel AI SDK V6\n },\n];\n\nconst _onUnhandledRejectionIntegration = ((options: Partial<OnUnhandledRejectionOptions> = {}) => {\n const opts: OnUnhandledRejectionOptions = {\n mode: options.mode ?? 'warn',\n ignore: [...DEFAULT_IGNORES, ...(options.ignore ?? [])],\n };\n\n return {\n name: INTEGRATION_NAME,\n setup(client) {\n global.process.on('unhandledRejection', makeUnhandledPromiseHandler(client, opts));\n },\n };\n}) satisfies IntegrationFn;\n\nexport const onUnhandledRejectionIntegration = defineIntegration(_onUnhandledRejectionIntegration);\n\n/** Extract error info safely */\nfunction extractErrorInfo(reason: unknown): { name: string; message: string } {\n // Check if reason is an object (including Error instances, not just plain objects)\n if (typeof reason !== 'object' || reason === null) {\n return { name: '', message: String(reason ?? '') };\n }\n\n const errorLike = reason as Record<string, unknown>;\n const name = typeof errorLike.name === 'string' ? errorLike.name : '';\n const message = typeof errorLike.message === 'string' ? errorLike.message : String(reason);\n\n return { name, message };\n}\n\n/** Check if a matcher matches the reason */\nfunction isMatchingReason(matcher: IgnoreMatcher, errorInfo: ReturnType<typeof extractErrorInfo>): boolean {\n // name/message matcher\n const nameMatches = matcher.name === undefined || isMatchingPattern(errorInfo.name, matcher.name, true);\n\n const messageMatches = matcher.message === undefined || isMatchingPattern(errorInfo.message, matcher.message);\n\n return nameMatches && messageMatches;\n}\n\n/** Match helper */\nfunction matchesIgnore(list: IgnoreMatcher[], reason: unknown): boolean {\n const errorInfo = extractErrorInfo(reason);\n return list.some(matcher => isMatchingReason(matcher, errorInfo));\n}\n\n/** Core handler */\nexport function makeUnhandledPromiseHandler(\n client: Client,\n options: OnUnhandledRejectionOptions,\n): (reason: unknown, promise: unknown) => void {\n return function sendUnhandledPromise(reason: unknown, _promise: unknown): void {\n // Only handle for the active client\n if (getClient() !== client) {\n return;\n }\n\n // Skip if configured to ignore\n if (matchesIgnore(options.ignore ?? [], reason)) {\n return;\n }\n\n const level: SeverityLevel = options.mode === 'strict' ? 'fatal' : 'error';\n\n // this can be set in places where we cannot reliably get access to the active span/error\n // when the error bubbles up to this handler, we can use this to set the active span\n const activeSpanForError =\n reason && typeof reason === 'object' ? (reason as { _sentry_active_span?: Span })._sentry_active_span : undefined;\n\n const activeSpanWrapper = activeSpanForError\n ? (fn: () => void) => withActiveSpan(activeSpanForError, fn)\n : (fn: () => void) => fn();\n\n activeSpanWrapper(() => {\n captureException(reason, {\n originalException: reason,\n captureContext: {\n extra: { unhandledPromiseRejection: true },\n level,\n },\n mechanism: {\n handled: false,\n type: 'auto.node.onunhandledrejection',\n },\n });\n });\n\n handleRejection(reason, options.mode);\n };\n}\n\n/**\n * Handler for `mode` option\n */\nfunction handleRejection(reason: unknown, mode: UnhandledRejectionMode): void {\n // https://github.com/nodejs/node/blob/7cf6f9e964aa00772965391c23acda6d71972a9a/lib/internal/process/promises.js#L234-L240\n const rejectionWarning =\n 'This error originated either by ' +\n 'throwing inside of an async function without a catch block, ' +\n 'or by rejecting a promise which was not handled with .catch().' +\n ' The promise rejected with the reason:';\n\n /* eslint-disable no-console */\n if (mode === 'warn') {\n consoleSandbox(() => {\n console.warn(rejectionWarning);\n console.error(reason && typeof reason === 'object' && 'stack' in reason ? reason.stack : reason);\n });\n } else if (mode === 'strict') {\n consoleSandbox(() => {\n console.warn(rejectionWarning);\n });\n logAndExitProcess(reason);\n }\n /* eslint-enable no-console */\n}\n"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"onunhandledrejection.js","sources":["../../../src/integrations/onunhandledrejection.ts"],"sourcesContent":["import type { Client, IntegrationFn, SeverityLevel, Span } from '@sentry/core';\nimport {\n captureException,\n consoleSandbox,\n defineIntegration,\n getClient,\n isMatchingPattern,\n withActiveSpan,\n} from '@sentry/core';\nimport { logAndExitProcess } from '../utils/errorhandling';\n\ntype UnhandledRejectionMode = 'none' | 'warn' | 'strict';\n\ntype IgnoreMatcher = { name?: string | RegExp; message?: string | RegExp };\n\ninterface OnUnhandledRejectionOptions {\n /**\n * Option deciding what to do after capturing unhandledRejection,\n * that mimicks behavior of node's --unhandled-rejection flag.\n */\n mode: UnhandledRejectionMode;\n /** Rejection Errors to ignore (don't capture or warn). */\n ignore?: IgnoreMatcher[];\n}\n\nconst INTEGRATION_NAME = 'OnUnhandledRejection';\n\nconst DEFAULT_IGNORES: IgnoreMatcher[] = [\n {\n name: 'AI_NoOutputGeneratedError', // When stream aborts in Vercel AI SDK V5, Vercel flush() fails with an error\n },\n {\n name: 'AbortError', // When stream aborts in Vercel AI SDK V6\n },\n];\n\nconst _onUnhandledRejectionIntegration = ((options: Partial<OnUnhandledRejectionOptions> = {}) => {\n const opts: OnUnhandledRejectionOptions = {\n mode: options.mode ?? 'warn',\n ignore: [...DEFAULT_IGNORES, ...(options.ignore ?? [])],\n };\n\n return {\n name: INTEGRATION_NAME,\n setup(client) {\n global.process.on('unhandledRejection', makeUnhandledPromiseHandler(client, opts));\n },\n };\n}) satisfies IntegrationFn;\n\nexport const onUnhandledRejectionIntegration = defineIntegration(_onUnhandledRejectionIntegration);\n\n/** Extract error info safely */\nfunction extractErrorInfo(reason: unknown): { name: string; message: string } {\n // Check if reason is an object (including Error instances, not just plain objects)\n if (typeof reason !== 'object' || reason === null) {\n return { name: '', message: String(reason ?? '') };\n }\n\n const errorLike = reason as Record<string, unknown>;\n const name = typeof errorLike.name === 'string' ? errorLike.name : '';\n const message = typeof errorLike.message === 'string' ? errorLike.message : String(reason);\n\n return { name, message };\n}\n\n/** Check if a matcher matches the reason */\nfunction isMatchingReason(matcher: IgnoreMatcher, errorInfo: ReturnType<typeof extractErrorInfo>): boolean {\n // name/message matcher\n const nameMatches = matcher.name === undefined || isMatchingPattern(errorInfo.name, matcher.name, true);\n\n const messageMatches = matcher.message === undefined || isMatchingPattern(errorInfo.message, matcher.message);\n\n return nameMatches && messageMatches;\n}\n\n/** Match helper */\nfunction matchesIgnore(list: IgnoreMatcher[], reason: unknown): boolean {\n const errorInfo = extractErrorInfo(reason);\n return list.some(matcher => isMatchingReason(matcher, errorInfo));\n}\n\n/** Core handler */\nexport function makeUnhandledPromiseHandler(\n client: Client,\n options: OnUnhandledRejectionOptions,\n): (reason: unknown, promise: unknown) => void {\n return function sendUnhandledPromise(reason: unknown, _promise: unknown): void {\n // Only handle for the active client\n if (getClient() !== client) {\n return;\n }\n\n // Skip if configured to ignore\n if (matchesIgnore(options.ignore ?? [], reason)) {\n return;\n }\n\n const level: SeverityLevel = options.mode === 'strict' ? 'fatal' : 'error';\n\n // this can be set in places where we cannot reliably get access to the active span/error\n // when the error bubbles up to this handler, we can use this to set the active span\n const activeSpanForError =\n reason && typeof reason === 'object' ? (reason as { _sentry_active_span?: Span })._sentry_active_span : undefined;\n\n const activeSpanWrapper = activeSpanForError\n ? (fn: () => void) => withActiveSpan(activeSpanForError, fn)\n : (fn: () => void) => fn();\n\n activeSpanWrapper(() => {\n captureException(reason, {\n originalException: reason,\n captureContext: {\n extra: { unhandledPromiseRejection: true },\n level,\n },\n mechanism: {\n handled: false,\n type: 'auto.node.onunhandledrejection',\n },\n });\n });\n\n handleRejection(reason, options.mode);\n };\n}\n\n/**\n * Handler for `mode` option\n */\nfunction handleRejection(reason: unknown, mode: UnhandledRejectionMode): void {\n // https://github.com/nodejs/node/blob/7cf6f9e964aa00772965391c23acda6d71972a9a/lib/internal/process/promises.js#L234-L240\n const rejectionWarning =\n 'This error originated either by ' +\n 'throwing inside of an async function without a catch block, ' +\n 'or by rejecting a promise which was not handled with .catch().' +\n ' The promise rejected with the reason:';\n\n /* eslint-disable no-console */\n if (mode === 'warn') {\n consoleSandbox(() => {\n console.warn(rejectionWarning);\n console.error(reason && typeof reason === 'object' && 'stack' in reason ? reason.stack : reason);\n });\n } else if (mode === 'strict') {\n consoleSandbox(() => {\n console.warn(rejectionWarning);\n });\n logAndExitProcess(reason);\n }\n /* eslint-enable no-console */\n}\n"],"names":[],"mappings":"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"} |
| import * as diagnosticsChannel from 'node:diagnostics_channel'; | ||
| import { defineIntegration, _INTERNAL_captureLog, severityLevelFromString, withScope, addExceptionMechanism, captureException, captureMessage } from '@sentry/core'; | ||
| const SENTRY_TRACK_SYMBOL = Symbol('sentry-track-pino-logger'); | ||
| /** | ||
| * Gets a custom Pino key from a logger instance by searching for the symbol. | ||
| * Pino uses non-global symbols like Symbol('pino.messageKey'): https://github.com/pinojs/pino/blob/8a816c0b1f72de5ae9181f3bb402109b66f7d812/lib/symbols.js | ||
| */ | ||
| const SENTRY_TRACK_SYMBOL = /* @__PURE__ */ Symbol("sentry-track-pino-logger"); | ||
| function getPinoKey(logger, symbolName, defaultKey) { | ||
@@ -16,3 +11,3 @@ const symbols = Object.getOwnPropertySymbols(logger); | ||
| const value = logger[sym]; | ||
| return typeof value === 'string' ? value : defaultKey; | ||
| return typeof value === "string" ? value : defaultKey; | ||
| } | ||
@@ -22,14 +17,10 @@ } | ||
| } | ||
| const DEFAULT_OPTIONS = { | ||
| error: { levels: [], handled: true }, | ||
| log: { levels: ['trace', 'debug', 'info', 'warn', 'error', 'fatal'] }, | ||
| log: { levels: ["trace", "debug", "info", "warn", "error", "fatal"] } | ||
| }; | ||
| function stripIgnoredFields(result) { | ||
| // eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
| const { level, time, pid, hostname, ...rest } = result; | ||
| return rest; | ||
| } | ||
| const _pinoIntegration = defineIntegration((userOptions = {}) => { | ||
@@ -39,17 +30,13 @@ const options = { | ||
| error: { ...DEFAULT_OPTIONS.error, ...userOptions.error }, | ||
| log: { ...DEFAULT_OPTIONS.log, ...userOptions.log }, | ||
| log: { ...DEFAULT_OPTIONS.log, ...userOptions.log } | ||
| }; | ||
| function shouldTrackLogger(logger) { | ||
| const override = logger[SENTRY_TRACK_SYMBOL]; | ||
| return override === 'track' || (override !== 'ignore' && options.autoInstrument); | ||
| return override === "track" || override !== "ignore" && options.autoInstrument; | ||
| } | ||
| return { | ||
| name: 'Pino', | ||
| setup: client => { | ||
| name: "Pino", | ||
| setup: (client) => { | ||
| const enableLogs = !!client.getOptions().enableLogs; | ||
| const integratedChannel = diagnosticsChannel.tracingChannel('pino_asJson'); | ||
| const integratedChannel = diagnosticsChannel.tracingChannel("pino_asJson"); | ||
| function onPinoStart(self, args, result) { | ||
@@ -59,38 +46,29 @@ if (!shouldTrackLogger(self)) { | ||
| } | ||
| const resultObj = stripIgnoredFields(result); | ||
| const [captureObj, message, levelNumber] = args; | ||
| const level = self?.levels?.labels?.[levelNumber] || 'info'; | ||
| const messageKey = getPinoKey(self, 'pino.messageKey', 'msg'); | ||
| const logMessage = message || (resultObj?.[messageKey] ) || ''; | ||
| const level = self?.levels?.labels?.[levelNumber] || "info"; | ||
| const messageKey = getPinoKey(self, "pino.messageKey", "msg"); | ||
| const logMessage = message || resultObj?.[messageKey] || ""; | ||
| if (enableLogs && options.log.levels.includes(level)) { | ||
| const attributes = { | ||
| ...resultObj, | ||
| 'sentry.origin': 'auto.log.pino', | ||
| 'pino.logger.level': levelNumber, | ||
| "sentry.origin": "auto.log.pino", | ||
| "pino.logger.level": levelNumber | ||
| }; | ||
| _INTERNAL_captureLog({ level, message: logMessage, attributes }); | ||
| } | ||
| if (options.error.levels.includes(level)) { | ||
| const captureContext = { | ||
| level: severityLevelFromString(level), | ||
| level: severityLevelFromString(level) | ||
| }; | ||
| withScope(scope => { | ||
| scope.addEventProcessor(event => { | ||
| event.logger = 'pino'; | ||
| withScope((scope) => { | ||
| scope.addEventProcessor((event) => { | ||
| event.logger = "pino"; | ||
| addExceptionMechanism(event, { | ||
| handled: options.error.handled, | ||
| type: 'auto.log.pino', | ||
| type: "auto.log.pino" | ||
| }); | ||
| return event; | ||
| }); | ||
| const error = captureObj[getPinoKey(self, 'pino.errorKey', 'err')]; | ||
| const error = captureObj[getPinoKey(self, "pino.errorKey", "err")]; | ||
| if (error) { | ||
@@ -100,3 +78,2 @@ captureException(error, captureContext); | ||
| } | ||
| captureMessage(logMessage, captureContext); | ||
@@ -106,39 +83,27 @@ }); | ||
| } | ||
| integratedChannel.end.subscribe(data => { | ||
| integratedChannel.end.subscribe((data) => { | ||
| const { | ||
| instance, | ||
| arguments: args, | ||
| result, | ||
| } = data ; | ||
| result | ||
| } = data; | ||
| onPinoStart(instance, args, JSON.parse(result)); | ||
| }); | ||
| }, | ||
| } | ||
| }; | ||
| }) ; | ||
| /** | ||
| * Integration for Pino logging library. | ||
| * Captures Pino logs as Sentry logs and optionally captures some log levels as events. | ||
| * | ||
| * By default, all Pino loggers will be captured. To ignore a specific logger, use `pinoIntegration.untrackLogger(logger)`. | ||
| * | ||
| * If you disable automatic instrumentation with `autoInstrument: false`, you can mark specific loggers to be tracked with `pinoIntegration.trackLogger(logger)`. | ||
| * | ||
| * Requires Pino >=v8.0.0 and Node >=20.6.0 or >=18.19.0 | ||
| */ | ||
| }); | ||
| const pinoIntegration = Object.assign(_pinoIntegration, { | ||
| trackLogger(logger) { | ||
| if (logger && typeof logger === 'object' && 'levels' in logger) { | ||
| (logger )[SENTRY_TRACK_SYMBOL] = 'track'; | ||
| if (logger && typeof logger === "object" && "levels" in logger) { | ||
| logger[SENTRY_TRACK_SYMBOL] = "track"; | ||
| } | ||
| }, | ||
| untrackLogger(logger) { | ||
| if (logger && typeof logger === 'object' && 'levels' in logger) { | ||
| (logger )[SENTRY_TRACK_SYMBOL] = 'ignore'; | ||
| if (logger && typeof logger === "object" && "levels" in logger) { | ||
| logger[SENTRY_TRACK_SYMBOL] = "ignore"; | ||
| } | ||
| }, | ||
| }) ; | ||
| } | ||
| }); | ||
| export { pinoIntegration }; | ||
| //# sourceMappingURL=pino.js.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"pino.js","sources":["../../../src/integrations/pino.ts"],"sourcesContent":["import * as diagnosticsChannel from 'node:diagnostics_channel';\nimport type { Integration, IntegrationFn, LogSeverityLevel } from '@sentry/core';\nimport {\n _INTERNAL_captureLog,\n addExceptionMechanism,\n captureException,\n captureMessage,\n defineIntegration,\n severityLevelFromString,\n withScope,\n} from '@sentry/core';\n\nconst SENTRY_TRACK_SYMBOL = Symbol('sentry-track-pino-logger');\n\ntype LevelMapping = {\n // Fortunately pino uses the same levels as Sentry\n labels: { [level: number]: LogSeverityLevel };\n};\n\ntype Pino = {\n [key: symbol]: unknown;\n levels: LevelMapping;\n [SENTRY_TRACK_SYMBOL]?: 'track' | 'ignore';\n};\n\n/**\n * Gets a custom Pino key from a logger instance by searching for the symbol.\n * Pino uses non-global symbols like Symbol('pino.messageKey'): https://github.com/pinojs/pino/blob/8a816c0b1f72de5ae9181f3bb402109b66f7d812/lib/symbols.js\n */\nfunction getPinoKey(logger: Pino, symbolName: string, defaultKey: string): string {\n const symbols = Object.getOwnPropertySymbols(logger);\n const symbolString = `Symbol(${symbolName})`;\n for (const sym of symbols) {\n if (sym.toString() === symbolString) {\n const value = logger[sym];\n return typeof value === 'string' ? value : defaultKey;\n }\n }\n return defaultKey;\n}\n\ntype MergeObject = {\n [key: string]: unknown;\n err?: Error;\n};\n\ntype PinoHookArgs = [MergeObject, string, number];\n\ntype PinoOptions = {\n /**\n * Automatically instrument all Pino loggers.\n *\n * When set to `false`, only loggers marked with `pinoIntegration.trackLogger(logger)` will be captured.\n *\n * @default true\n */\n autoInstrument: boolean;\n /**\n * Options to enable capturing of error events.\n */\n error: {\n /**\n * Levels that trigger capturing of events.\n *\n * @default []\n */\n levels: LogSeverityLevel[];\n /**\n * By default, Sentry will mark captured errors as handled.\n * Set this to `false` if you want to mark them as unhandled instead.\n *\n * @default true\n */\n handled: boolean;\n };\n /**\n * Options to enable capturing of logs.\n */\n log: {\n /**\n * Levels that trigger capturing of logs. Logs are only captured if\n * `enableLogs` is enabled.\n *\n * @default [\"trace\", \"debug\", \"info\", \"warn\", \"error\", \"fatal\"]\n */\n levels: LogSeverityLevel[];\n };\n};\n\nconst DEFAULT_OPTIONS: PinoOptions = {\n autoInstrument: true,\n error: { levels: [], handled: true },\n log: { levels: ['trace', 'debug', 'info', 'warn', 'error', 'fatal'] },\n};\n\ntype DeepPartial<T> = {\n [P in keyof T]?: T[P] extends object ? Partial<T[P]> : T[P];\n};\n\ntype PinoResult = {\n level?: string;\n time?: string;\n pid?: number;\n hostname?: string;\n} & Record<string, unknown>;\n\nfunction stripIgnoredFields(result: PinoResult): PinoResult {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { level, time, pid, hostname, ...rest } = result;\n return rest;\n}\n\nconst _pinoIntegration = defineIntegration((userOptions: DeepPartial<PinoOptions> = {}) => {\n const options: PinoOptions = {\n autoInstrument: userOptions.autoInstrument !== false,\n error: { ...DEFAULT_OPTIONS.error, ...userOptions.error },\n log: { ...DEFAULT_OPTIONS.log, ...userOptions.log },\n };\n\n function shouldTrackLogger(logger: Pino): boolean {\n const override = logger[SENTRY_TRACK_SYMBOL];\n return override === 'track' || (override !== 'ignore' && options.autoInstrument);\n }\n\n return {\n name: 'Pino',\n setup: client => {\n const enableLogs = !!client.getOptions().enableLogs;\n\n const integratedChannel = diagnosticsChannel.tracingChannel('pino_asJson');\n\n function onPinoStart(self: Pino, args: PinoHookArgs, result: PinoResult): void {\n if (!shouldTrackLogger(self)) {\n return;\n }\n\n const resultObj = stripIgnoredFields(result);\n\n const [captureObj, message, levelNumber] = args;\n const level = self?.levels?.labels?.[levelNumber] || 'info';\n const messageKey = getPinoKey(self, 'pino.messageKey', 'msg');\n const logMessage = message || (resultObj?.[messageKey] as string | undefined) || '';\n\n if (enableLogs && options.log.levels.includes(level)) {\n const attributes: Record<string, unknown> = {\n ...resultObj,\n 'sentry.origin': 'auto.log.pino',\n 'pino.logger.level': levelNumber,\n };\n\n _INTERNAL_captureLog({ level, message: logMessage, attributes });\n }\n\n if (options.error.levels.includes(level)) {\n const captureContext = {\n level: severityLevelFromString(level),\n };\n\n withScope(scope => {\n scope.addEventProcessor(event => {\n event.logger = 'pino';\n\n addExceptionMechanism(event, {\n handled: options.error.handled,\n type: 'auto.log.pino',\n });\n\n return event;\n });\n\n const error = captureObj[getPinoKey(self, 'pino.errorKey', 'err')];\n if (error) {\n captureException(error, captureContext);\n return;\n }\n\n captureMessage(logMessage, captureContext);\n });\n }\n }\n\n integratedChannel.end.subscribe(data => {\n const {\n instance,\n arguments: args,\n result,\n } = data as { instance: Pino; arguments: PinoHookArgs; result: string };\n onPinoStart(instance, args, JSON.parse(result));\n });\n },\n };\n}) satisfies IntegrationFn;\n\ninterface PinoIntegrationFunction {\n (userOptions?: DeepPartial<PinoOptions>): Integration;\n /**\n * Marks a Pino logger to be tracked by the Pino integration.\n *\n * @param logger A Pino logger instance.\n */\n trackLogger(logger: unknown): void;\n /**\n * Marks a Pino logger to be ignored by the Pino integration.\n *\n * @param logger A Pino logger instance.\n */\n untrackLogger(logger: unknown): void;\n}\n\n/**\n * Integration for Pino logging library.\n * Captures Pino logs as Sentry logs and optionally captures some log levels as events.\n *\n * By default, all Pino loggers will be captured. To ignore a specific logger, use `pinoIntegration.untrackLogger(logger)`.\n *\n * If you disable automatic instrumentation with `autoInstrument: false`, you can mark specific loggers to be tracked with `pinoIntegration.trackLogger(logger)`.\n *\n * Requires Pino >=v8.0.0 and Node >=20.6.0 or >=18.19.0\n */\nexport const pinoIntegration = Object.assign(_pinoIntegration, {\n trackLogger(logger: unknown): void {\n if (logger && typeof logger === 'object' && 'levels' in logger) {\n (logger as Pino)[SENTRY_TRACK_SYMBOL] = 'track';\n }\n },\n untrackLogger(logger: unknown): void {\n if (logger && typeof logger === 'object' && 'levels' in logger) {\n (logger as Pino)[SENTRY_TRACK_SYMBOL] = 'ignore';\n }\n },\n}) as PinoIntegrationFunction;\n"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"pino.js","sources":["../../../src/integrations/pino.ts"],"sourcesContent":["import * as diagnosticsChannel from 'node:diagnostics_channel';\nimport type { Integration, IntegrationFn, LogSeverityLevel } from '@sentry/core';\nimport {\n _INTERNAL_captureLog,\n addExceptionMechanism,\n captureException,\n captureMessage,\n defineIntegration,\n severityLevelFromString,\n withScope,\n} from '@sentry/core';\n\nconst SENTRY_TRACK_SYMBOL = Symbol('sentry-track-pino-logger');\n\ntype LevelMapping = {\n // Fortunately pino uses the same levels as Sentry\n labels: { [level: number]: LogSeverityLevel };\n};\n\ntype Pino = {\n [key: symbol]: unknown;\n levels: LevelMapping;\n [SENTRY_TRACK_SYMBOL]?: 'track' | 'ignore';\n};\n\n/**\n * Gets a custom Pino key from a logger instance by searching for the symbol.\n * Pino uses non-global symbols like Symbol('pino.messageKey'): https://github.com/pinojs/pino/blob/8a816c0b1f72de5ae9181f3bb402109b66f7d812/lib/symbols.js\n */\nfunction getPinoKey(logger: Pino, symbolName: string, defaultKey: string): string {\n const symbols = Object.getOwnPropertySymbols(logger);\n const symbolString = `Symbol(${symbolName})`;\n for (const sym of symbols) {\n if (sym.toString() === symbolString) {\n const value = logger[sym];\n return typeof value === 'string' ? value : defaultKey;\n }\n }\n return defaultKey;\n}\n\ntype MergeObject = {\n [key: string]: unknown;\n err?: Error;\n};\n\ntype PinoHookArgs = [MergeObject, string, number];\n\ntype PinoOptions = {\n /**\n * Automatically instrument all Pino loggers.\n *\n * When set to `false`, only loggers marked with `pinoIntegration.trackLogger(logger)` will be captured.\n *\n * @default true\n */\n autoInstrument: boolean;\n /**\n * Options to enable capturing of error events.\n */\n error: {\n /**\n * Levels that trigger capturing of events.\n *\n * @default []\n */\n levels: LogSeverityLevel[];\n /**\n * By default, Sentry will mark captured errors as handled.\n * Set this to `false` if you want to mark them as unhandled instead.\n *\n * @default true\n */\n handled: boolean;\n };\n /**\n * Options to enable capturing of logs.\n */\n log: {\n /**\n * Levels that trigger capturing of logs. Logs are only captured if\n * `enableLogs` is enabled.\n *\n * @default [\"trace\", \"debug\", \"info\", \"warn\", \"error\", \"fatal\"]\n */\n levels: LogSeverityLevel[];\n };\n};\n\nconst DEFAULT_OPTIONS: PinoOptions = {\n autoInstrument: true,\n error: { levels: [], handled: true },\n log: { levels: ['trace', 'debug', 'info', 'warn', 'error', 'fatal'] },\n};\n\ntype DeepPartial<T> = {\n [P in keyof T]?: T[P] extends object ? Partial<T[P]> : T[P];\n};\n\ntype PinoResult = {\n level?: string;\n time?: string;\n pid?: number;\n hostname?: string;\n} & Record<string, unknown>;\n\nfunction stripIgnoredFields(result: PinoResult): PinoResult {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { level, time, pid, hostname, ...rest } = result;\n return rest;\n}\n\nconst _pinoIntegration = defineIntegration((userOptions: DeepPartial<PinoOptions> = {}) => {\n const options: PinoOptions = {\n autoInstrument: userOptions.autoInstrument !== false,\n error: { ...DEFAULT_OPTIONS.error, ...userOptions.error },\n log: { ...DEFAULT_OPTIONS.log, ...userOptions.log },\n };\n\n function shouldTrackLogger(logger: Pino): boolean {\n const override = logger[SENTRY_TRACK_SYMBOL];\n return override === 'track' || (override !== 'ignore' && options.autoInstrument);\n }\n\n return {\n name: 'Pino',\n setup: client => {\n const enableLogs = !!client.getOptions().enableLogs;\n\n const integratedChannel = diagnosticsChannel.tracingChannel('pino_asJson');\n\n function onPinoStart(self: Pino, args: PinoHookArgs, result: PinoResult): void {\n if (!shouldTrackLogger(self)) {\n return;\n }\n\n const resultObj = stripIgnoredFields(result);\n\n const [captureObj, message, levelNumber] = args;\n const level = self?.levels?.labels?.[levelNumber] || 'info';\n const messageKey = getPinoKey(self, 'pino.messageKey', 'msg');\n const logMessage = message || (resultObj?.[messageKey] as string | undefined) || '';\n\n if (enableLogs && options.log.levels.includes(level)) {\n const attributes: Record<string, unknown> = {\n ...resultObj,\n 'sentry.origin': 'auto.log.pino',\n 'pino.logger.level': levelNumber,\n };\n\n _INTERNAL_captureLog({ level, message: logMessage, attributes });\n }\n\n if (options.error.levels.includes(level)) {\n const captureContext = {\n level: severityLevelFromString(level),\n };\n\n withScope(scope => {\n scope.addEventProcessor(event => {\n event.logger = 'pino';\n\n addExceptionMechanism(event, {\n handled: options.error.handled,\n type: 'auto.log.pino',\n });\n\n return event;\n });\n\n const error = captureObj[getPinoKey(self, 'pino.errorKey', 'err')];\n if (error) {\n captureException(error, captureContext);\n return;\n }\n\n captureMessage(logMessage, captureContext);\n });\n }\n }\n\n integratedChannel.end.subscribe(data => {\n const {\n instance,\n arguments: args,\n result,\n } = data as { instance: Pino; arguments: PinoHookArgs; result: string };\n onPinoStart(instance, args, JSON.parse(result));\n });\n },\n };\n}) satisfies IntegrationFn;\n\ninterface PinoIntegrationFunction {\n (userOptions?: DeepPartial<PinoOptions>): Integration;\n /**\n * Marks a Pino logger to be tracked by the Pino integration.\n *\n * @param logger A Pino logger instance.\n */\n trackLogger(logger: unknown): void;\n /**\n * Marks a Pino logger to be ignored by the Pino integration.\n *\n * @param logger A Pino logger instance.\n */\n untrackLogger(logger: unknown): void;\n}\n\n/**\n * Integration for Pino logging library.\n * Captures Pino logs as Sentry logs and optionally captures some log levels as events.\n *\n * By default, all Pino loggers will be captured. To ignore a specific logger, use `pinoIntegration.untrackLogger(logger)`.\n *\n * If you disable automatic instrumentation with `autoInstrument: false`, you can mark specific loggers to be tracked with `pinoIntegration.trackLogger(logger)`.\n *\n * Requires Pino >=v8.0.0 and Node >=20.6.0 or >=18.19.0\n */\nexport const pinoIntegration = Object.assign(_pinoIntegration, {\n trackLogger(logger: unknown): void {\n if (logger && typeof logger === 'object' && 'levels' in logger) {\n (logger as Pino)[SENTRY_TRACK_SYMBOL] = 'track';\n }\n },\n untrackLogger(logger: unknown): void {\n if (logger && typeof logger === 'object' && 'levels' in logger) {\n (logger as Pino)[SENTRY_TRACK_SYMBOL] = 'ignore';\n }\n },\n}) as PinoIntegrationFunction;\n"],"names":[],"mappings":"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"} |
| import { defineIntegration, startSession, getIsolationScope, endSession } from '@sentry/core'; | ||
| const INTEGRATION_NAME = 'ProcessSession'; | ||
| /** | ||
| * Records a Session for the current process to track release health. | ||
| */ | ||
| const INTEGRATION_NAME = "ProcessSession"; | ||
| const processSessionIntegration = defineIntegration(() => { | ||
@@ -13,19 +9,9 @@ return { | ||
| startSession(); | ||
| // Emitted in the case of healthy sessions, error of `mechanism.handled: true` and unhandledrejections because | ||
| // The 'beforeExit' event is not emitted for conditions causing explicit termination, | ||
| // such as calling process.exit() or uncaught exceptions. | ||
| // Ref: https://nodejs.org/api/process.html#process_event_beforeexit | ||
| process.on('beforeExit', () => { | ||
| process.on("beforeExit", () => { | ||
| const session = getIsolationScope().getSession(); | ||
| // Only call endSession, if the Session exists on Scope and SessionStatus is not a | ||
| // Terminal Status i.e. Exited or Crashed because | ||
| // "When a session is moved away from ok it must not be updated anymore." | ||
| // Ref: https://develop.sentry.dev/sdk/sessions/ | ||
| if (session?.status !== 'ok') { | ||
| if (session?.status !== "ok") { | ||
| endSession(); | ||
| } | ||
| }); | ||
| }, | ||
| } | ||
| }; | ||
@@ -32,0 +18,0 @@ }); |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"processSession.js","sources":["../../../src/integrations/processSession.ts"],"sourcesContent":["import { defineIntegration, endSession, getIsolationScope, startSession } from '@sentry/core';\n\nconst INTEGRATION_NAME = 'ProcessSession';\n\n/**\n * Records a Session for the current process to track release health.\n */\nexport const processSessionIntegration = defineIntegration(() => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n startSession();\n\n // Emitted in the case of healthy sessions, error of `mechanism.handled: true` and unhandledrejections because\n // The 'beforeExit' event is not emitted for conditions causing explicit termination,\n // such as calling process.exit() or uncaught exceptions.\n // Ref: https://nodejs.org/api/process.html#process_event_beforeexit\n process.on('beforeExit', () => {\n const session = getIsolationScope().getSession();\n\n // Only call endSession, if the Session exists on Scope and SessionStatus is not a\n // Terminal Status i.e. Exited or Crashed because\n // \"When a session is moved away from ok it must not be updated anymore.\"\n // Ref: https://develop.sentry.dev/sdk/sessions/\n if (session?.status !== 'ok') {\n endSession();\n }\n });\n },\n };\n});\n"],"names":[],"mappings":";;AAEA,MAAM,gBAAA,GAAmB,gBAAgB;;AAEzC;AACA;AACA;MACa,yBAAA,GAA4B,iBAAiB,CAAC,MAAM;AACjE,EAAE,OAAO;AACT,IAAI,IAAI,EAAE,gBAAgB;AAC1B,IAAI,SAAS,GAAG;AAChB,MAAM,YAAY,EAAE;;AAEpB;AACA;AACA;AACA;AACA,MAAM,OAAO,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM;AACrC,QAAQ,MAAM,UAAU,iBAAiB,EAAE,CAAC,UAAU,EAAE;;AAExD;AACA;AACA;AACA;AACA,QAAQ,IAAI,OAAO,EAAE,MAAA,KAAW,IAAI,EAAE;AACtC,UAAU,UAAU,EAAE;AACtB,QAAQ;AACR,MAAM,CAAC,CAAC;AACR,IAAI,CAAC;AACL,GAAG;AACH,CAAC;;;;"} | ||
| {"version":3,"file":"processSession.js","sources":["../../../src/integrations/processSession.ts"],"sourcesContent":["import { defineIntegration, endSession, getIsolationScope, startSession } from '@sentry/core';\n\nconst INTEGRATION_NAME = 'ProcessSession';\n\n/**\n * Records a Session for the current process to track release health.\n */\nexport const processSessionIntegration = defineIntegration(() => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n startSession();\n\n // Emitted in the case of healthy sessions, error of `mechanism.handled: true` and unhandledrejections because\n // The 'beforeExit' event is not emitted for conditions causing explicit termination,\n // such as calling process.exit() or uncaught exceptions.\n // Ref: https://nodejs.org/api/process.html#process_event_beforeexit\n process.on('beforeExit', () => {\n const session = getIsolationScope().getSession();\n\n // Only call endSession, if the Session exists on Scope and SessionStatus is not a\n // Terminal Status i.e. Exited or Crashed because\n // \"When a session is moved away from ok it must not be updated anymore.\"\n // Ref: https://develop.sentry.dev/sdk/sessions/\n if (session?.status !== 'ok') {\n endSession();\n }\n });\n },\n };\n});\n"],"names":[],"mappings":";;AAEA,MAAM,gBAAA,GAAmB,gBAAA;AAKlB,MAAM,yBAAA,GAA4B,kBAAkB,MAAM;AAC/D,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,SAAA,GAAY;AACV,MAAA,YAAA,EAAa;AAMb,MAAA,OAAA,CAAQ,EAAA,CAAG,cAAc,MAAM;AAC7B,QAAA,MAAM,OAAA,GAAU,iBAAA,EAAkB,CAAE,UAAA,EAAW;AAM/C,QAAA,IAAI,OAAA,EAAS,WAAW,IAAA,EAAM;AAC5B,UAAA,UAAA,EAAW;AAAA,QACb;AAAA,MACF,CAAC,CAAA;AAAA,IACH;AAAA,GACF;AACF,CAAC;;;;"} |
| import * as http from 'node:http'; | ||
| import { defineIntegration, debug, serializeEnvelope, suppressTracing } from '@sentry/core'; | ||
| const INTEGRATION_NAME = 'Spotlight'; | ||
| const INTEGRATION_NAME = "Spotlight"; | ||
| const _spotlightIntegration = ((options = {}) => { | ||
| const _options = { | ||
| sidecarUrl: options.sidecarUrl || 'http://localhost:8969/stream', | ||
| sidecarUrl: options.sidecarUrl || "http://localhost:8969/stream" | ||
| }; | ||
| return { | ||
@@ -15,22 +13,12 @@ name: INTEGRATION_NAME, | ||
| try { | ||
| if (process.env.NODE_ENV && process.env.NODE_ENV !== 'development') { | ||
| if (process.env.NODE_ENV && process.env.NODE_ENV !== "development") { | ||
| debug.warn("[Spotlight] It seems you're not in dev mode. Do you really want to have Spotlight enabled?"); | ||
| } | ||
| } catch { | ||
| // ignore | ||
| } | ||
| connectToSpotlight(client, _options); | ||
| }, | ||
| } | ||
| }; | ||
| }) ; | ||
| /** | ||
| * Use this integration to send errors and transactions to Spotlight. | ||
| * | ||
| * Learn more about spotlight at https://spotlightjs.com | ||
| * | ||
| * Important: This integration only works with Node 18 or newer. | ||
| */ | ||
| }); | ||
| const spotlightIntegration = defineIntegration(_spotlightIntegration); | ||
| function connectToSpotlight(client, options) { | ||
@@ -41,11 +29,8 @@ const spotlightUrl = parseSidecarUrl(options.sidecarUrl); | ||
| } | ||
| let failedRequests = 0; | ||
| client.on('beforeEnvelope', (envelope) => { | ||
| client.on("beforeEnvelope", (envelope) => { | ||
| if (failedRequests > 3) { | ||
| debug.warn('[Spotlight] Disabled Sentry -> Spotlight integration due to too many failed requests'); | ||
| debug.warn("[Spotlight] Disabled Sentry -> Spotlight integration due to too many failed requests"); | ||
| return; | ||
| } | ||
| const serializedEnvelope = serializeEnvelope(envelope); | ||
@@ -55,3 +40,3 @@ suppressTracing(() => { | ||
| { | ||
| method: 'POST', | ||
| method: "POST", | ||
| path: spotlightUrl.pathname, | ||
@@ -61,24 +46,19 @@ hostname: spotlightUrl.hostname, | ||
| headers: { | ||
| 'Content-Type': 'application/x-sentry-envelope', | ||
| }, | ||
| "Content-Type": "application/x-sentry-envelope" | ||
| } | ||
| }, | ||
| res => { | ||
| (res) => { | ||
| if (res.statusCode && res.statusCode >= 200 && res.statusCode < 400) { | ||
| // Reset failed requests counter on success | ||
| failedRequests = 0; | ||
| } | ||
| res.on('data', () => { | ||
| // Drain socket | ||
| res.on("data", () => { | ||
| }); | ||
| res.on('end', () => { | ||
| // Drain socket | ||
| res.on("end", () => { | ||
| }); | ||
| res.setEncoding('utf8'); | ||
| }, | ||
| res.setEncoding("utf8"); | ||
| } | ||
| ); | ||
| req.on('error', () => { | ||
| req.on("error", () => { | ||
| failedRequests++; | ||
| debug.warn('[Spotlight] Failed to send envelope to Spotlight Sidecar'); | ||
| debug.warn("[Spotlight] Failed to send envelope to Spotlight Sidecar"); | ||
| }); | ||
@@ -90,3 +70,2 @@ req.write(serializedEnvelope); | ||
| } | ||
| function parseSidecarUrl(url) { | ||
@@ -97,3 +76,3 @@ try { | ||
| debug.warn(`[Spotlight] Invalid sidecar URL: ${url}`); | ||
| return undefined; | ||
| return void 0; | ||
| } | ||
@@ -100,0 +79,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"spotlight.js","sources":["../../../src/integrations/spotlight.ts"],"sourcesContent":["import * as http from 'node:http';\nimport type { Client, Envelope, IntegrationFn } from '@sentry/core';\nimport { debug, defineIntegration, serializeEnvelope, suppressTracing } from '@sentry/core';\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';\n\nconst _spotlightIntegration = ((options: Partial<SpotlightConnectionOptions> = {}) => {\n const _options = {\n sidecarUrl: options.sidecarUrl || 'http://localhost:8969/stream',\n };\n\n return {\n name: INTEGRATION_NAME,\n setup(client) {\n try {\n if (process.env.NODE_ENV && process.env.NODE_ENV !== 'development') {\n debug.warn(\"[Spotlight] It seems you're not in dev mode. Do you really want to have Spotlight enabled?\");\n }\n } catch {\n // ignore\n }\n connectToSpotlight(client, _options);\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 only works with Node 18 or newer.\n */\nexport const spotlightIntegration = defineIntegration(_spotlightIntegration);\n\nfunction connectToSpotlight(client: Client, options: Required<SpotlightConnectionOptions>): void {\n const spotlightUrl = parseSidecarUrl(options.sidecarUrl);\n if (!spotlightUrl) {\n return;\n }\n\n let failedRequests = 0;\n\n client.on('beforeEnvelope', (envelope: Envelope) => {\n if (failedRequests > 3) {\n debug.warn('[Spotlight] Disabled Sentry -> Spotlight integration due to too many failed requests');\n return;\n }\n\n const serializedEnvelope = serializeEnvelope(envelope);\n suppressTracing(() => {\n const req = http.request(\n {\n method: 'POST',\n path: spotlightUrl.pathname,\n hostname: spotlightUrl.hostname,\n port: spotlightUrl.port,\n headers: {\n 'Content-Type': 'application/x-sentry-envelope',\n },\n },\n res => {\n if (res.statusCode && res.statusCode >= 200 && res.statusCode < 400) {\n // Reset failed requests counter on success\n failedRequests = 0;\n }\n res.on('data', () => {\n // Drain socket\n });\n\n res.on('end', () => {\n // Drain socket\n });\n res.setEncoding('utf8');\n },\n );\n\n req.on('error', () => {\n failedRequests++;\n debug.warn('[Spotlight] Failed to send envelope to Spotlight Sidecar');\n });\n req.write(serializedEnvelope);\n req.end();\n });\n });\n}\n\nfunction parseSidecarUrl(url: string): URL | undefined {\n try {\n return new URL(`${url}`);\n } catch {\n debug.warn(`[Spotlight] Invalid sidecar URL: ${url}`);\n return undefined;\n }\n}\n"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"spotlight.js","sources":["../../../src/integrations/spotlight.ts"],"sourcesContent":["import * as http from 'node:http';\nimport type { Client, Envelope, IntegrationFn } from '@sentry/core';\nimport { debug, defineIntegration, serializeEnvelope, suppressTracing } from '@sentry/core';\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';\n\nconst _spotlightIntegration = ((options: Partial<SpotlightConnectionOptions> = {}) => {\n const _options = {\n sidecarUrl: options.sidecarUrl || 'http://localhost:8969/stream',\n };\n\n return {\n name: INTEGRATION_NAME,\n setup(client) {\n try {\n if (process.env.NODE_ENV && process.env.NODE_ENV !== 'development') {\n debug.warn(\"[Spotlight] It seems you're not in dev mode. Do you really want to have Spotlight enabled?\");\n }\n } catch {\n // ignore\n }\n connectToSpotlight(client, _options);\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 only works with Node 18 or newer.\n */\nexport const spotlightIntegration = defineIntegration(_spotlightIntegration);\n\nfunction connectToSpotlight(client: Client, options: Required<SpotlightConnectionOptions>): void {\n const spotlightUrl = parseSidecarUrl(options.sidecarUrl);\n if (!spotlightUrl) {\n return;\n }\n\n let failedRequests = 0;\n\n client.on('beforeEnvelope', (envelope: Envelope) => {\n if (failedRequests > 3) {\n debug.warn('[Spotlight] Disabled Sentry -> Spotlight integration due to too many failed requests');\n return;\n }\n\n const serializedEnvelope = serializeEnvelope(envelope);\n suppressTracing(() => {\n const req = http.request(\n {\n method: 'POST',\n path: spotlightUrl.pathname,\n hostname: spotlightUrl.hostname,\n port: spotlightUrl.port,\n headers: {\n 'Content-Type': 'application/x-sentry-envelope',\n },\n },\n res => {\n if (res.statusCode && res.statusCode >= 200 && res.statusCode < 400) {\n // Reset failed requests counter on success\n failedRequests = 0;\n }\n res.on('data', () => {\n // Drain socket\n });\n\n res.on('end', () => {\n // Drain socket\n });\n res.setEncoding('utf8');\n },\n );\n\n req.on('error', () => {\n failedRequests++;\n debug.warn('[Spotlight] Failed to send envelope to Spotlight Sidecar');\n });\n req.write(serializedEnvelope);\n req.end();\n });\n });\n}\n\nfunction parseSidecarUrl(url: string): URL | undefined {\n try {\n return new URL(`${url}`);\n } catch {\n debug.warn(`[Spotlight] Invalid sidecar URL: ${url}`);\n return undefined;\n }\n}\n"],"names":[],"mappings":";;;AAYO,MAAM,gBAAA,GAAmB;AAEhC,MAAM,qBAAA,IAAyB,CAAC,OAAA,GAA+C,EAAC,KAAM;AACpF,EAAA,MAAM,QAAA,GAAW;AAAA,IACf,UAAA,EAAY,QAAQ,UAAA,IAAc;AAAA,GACpC;AAEA,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,MAAM,MAAA,EAAQ;AACZ,MAAA,IAAI;AACF,QAAA,IAAI,QAAQ,GAAA,CAAI,QAAA,IAAY,OAAA,CAAQ,GAAA,CAAI,aAAa,aAAA,EAAe;AAClE,UAAA,KAAA,CAAM,KAAK,4FAA4F,CAAA;AAAA,QACzG;AAAA,MACF,CAAA,CAAA,MAAQ;AAAA,MAER;AACA,MAAA,kBAAA,CAAmB,QAAQ,QAAQ,CAAA;AAAA,IACrC;AAAA,GACF;AACF,CAAA,CAAA;AASO,MAAM,oBAAA,GAAuB,kBAAkB,qBAAqB;AAE3E,SAAS,kBAAA,CAAmB,QAAgB,OAAA,EAAqD;AAC/F,EAAA,MAAM,YAAA,GAAe,eAAA,CAAgB,OAAA,CAAQ,UAAU,CAAA;AACvD,EAAA,IAAI,CAAC,YAAA,EAAc;AACjB,IAAA;AAAA,EACF;AAEA,EAAA,IAAI,cAAA,GAAiB,CAAA;AAErB,EAAA,MAAA,CAAO,EAAA,CAAG,gBAAA,EAAkB,CAAC,QAAA,KAAuB;AAClD,IAAA,IAAI,iBAAiB,CAAA,EAAG;AACtB,MAAA,KAAA,CAAM,KAAK,sFAAsF,CAAA;AACjG,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,kBAAA,GAAqB,kBAAkB,QAAQ,CAAA;AACrD,IAAA,eAAA,CAAgB,MAAM;AACpB,MAAA,MAAM,MAAM,IAAA,CAAK,OAAA;AAAA,QACf;AAAA,UACE,MAAA,EAAQ,MAAA;AAAA,UACR,MAAM,YAAA,CAAa,QAAA;AAAA,UACnB,UAAU,YAAA,CAAa,QAAA;AAAA,UACvB,MAAM,YAAA,CAAa,IAAA;AAAA,UACnB,OAAA,EAAS;AAAA,YACP,cAAA,EAAgB;AAAA;AAClB,SACF;AAAA,QACA,CAAA,GAAA,KAAO;AACL,UAAA,IAAI,IAAI,UAAA,IAAc,GAAA,CAAI,cAAc,GAAA,IAAO,GAAA,CAAI,aAAa,GAAA,EAAK;AAEnE,YAAA,cAAA,GAAiB,CAAA;AAAA,UACnB;AACA,UAAA,GAAA,CAAI,EAAA,CAAG,QAAQ,MAAM;AAAA,UAErB,CAAC,CAAA;AAED,UAAA,GAAA,CAAI,EAAA,CAAG,OAAO,MAAM;AAAA,UAEpB,CAAC,CAAA;AACD,UAAA,GAAA,CAAI,YAAY,MAAM,CAAA;AAAA,QACxB;AAAA,OACF;AAEA,MAAA,GAAA,CAAI,EAAA,CAAG,SAAS,MAAM;AACpB,QAAA,cAAA,EAAA;AACA,QAAA,KAAA,CAAM,KAAK,0DAA0D,CAAA;AAAA,MACvE,CAAC,CAAA;AACD,MAAA,GAAA,CAAI,MAAM,kBAAkB,CAAA;AAC5B,MAAA,GAAA,CAAI,GAAA,EAAI;AAAA,IACV,CAAC,CAAA;AAAA,EACH,CAAC,CAAA;AACH;AAEA,SAAS,gBAAgB,GAAA,EAA8B;AACrD,EAAA,IAAI;AACF,IAAA,OAAO,IAAI,GAAA,CAAI,CAAA,EAAG,GAAG,CAAA,CAAE,CAAA;AAAA,EACzB,CAAA,CAAA,MAAQ;AACN,IAAA,KAAA,CAAM,IAAA,CAAK,CAAA,iCAAA,EAAoC,GAAG,CAAA,CAAE,CAAA;AACpD,IAAA,OAAO,MAAA;AAAA,EACT;AACF;;;;"} |
| import * as util from 'node:util'; | ||
| import { defineIntegration } from '@sentry/core'; | ||
| const INTEGRATION_NAME = 'NodeSystemError'; | ||
| const INTEGRATION_NAME = "NodeSystemError"; | ||
| function isSystemError(error) { | ||
@@ -10,22 +9,10 @@ if (!(error instanceof Error)) { | ||
| } | ||
| if (!('errno' in error) || typeof error.errno !== 'number') { | ||
| if (!("errno" in error) || typeof error.errno !== "number") { | ||
| return false; | ||
| } | ||
| // Workaround for Bun where getSystemErrorMap doesn't exist | ||
| // Can be removed once Bun supports getSystemErrorMap | ||
| // https://github.com/oven-sh/bun/issues/22872 | ||
| if (typeof util.getSystemErrorMap !== 'function') { | ||
| if (typeof util.getSystemErrorMap !== "function") { | ||
| return false; | ||
| } | ||
| // Appears this is the recommended way to check for Node.js SystemError | ||
| // https://github.com/nodejs/node/issues/46869 | ||
| return util.getSystemErrorMap().has(error.errno); | ||
| } | ||
| /** | ||
| * Captures context for Node.js SystemError errors. | ||
| */ | ||
| const systemErrorIntegration = defineIntegration((options = {}) => { | ||
@@ -38,9 +25,6 @@ return { | ||
| } | ||
| const error = hint.originalException; | ||
| const errorContext = { | ||
| ...(error ), | ||
| ...error | ||
| }; | ||
| if (!client.getOptions().sendDefaultPii && options.includePaths !== true) { | ||
@@ -50,21 +34,18 @@ delete errorContext.path; | ||
| } | ||
| event.contexts = { | ||
| ...event.contexts, | ||
| node_system_error: errorContext, | ||
| node_system_error: errorContext | ||
| }; | ||
| for (const exception of event.exception?.values || []) { | ||
| if (exception.value) { | ||
| if (error.path && exception.value.includes(error.path)) { | ||
| exception.value = exception.value.replace(`'${error.path}'`, '').trim(); | ||
| exception.value = exception.value.replace(`'${error.path}'`, "").trim(); | ||
| } | ||
| if (error.dest && exception.value.includes(error.dest)) { | ||
| exception.value = exception.value.replace(`'${error.dest}'`, '').trim(); | ||
| exception.value = exception.value.replace(`'${error.dest}'`, "").trim(); | ||
| } | ||
| } | ||
| } | ||
| return event; | ||
| }, | ||
| } | ||
| }; | ||
@@ -71,0 +52,0 @@ }); |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"systemError.js","sources":["../../../src/integrations/systemError.ts"],"sourcesContent":["import * as util from 'node:util';\nimport { defineIntegration } from '@sentry/core';\n\nconst INTEGRATION_NAME = 'NodeSystemError';\n\ntype SystemErrorContext = {\n dest?: string; // If present, the file path destination when reporting a file system error\n errno: number; // The system-provided error number\n path?: string; // If present, the file path when reporting a file system error\n};\n\ntype SystemError = Error & SystemErrorContext;\n\nfunction isSystemError(error: unknown): error is SystemError {\n if (!(error instanceof Error)) {\n return false;\n }\n\n if (!('errno' in error) || typeof error.errno !== 'number') {\n return false;\n }\n\n // Workaround for Bun where getSystemErrorMap doesn't exist\n // Can be removed once Bun supports getSystemErrorMap\n // https://github.com/oven-sh/bun/issues/22872\n if (typeof util.getSystemErrorMap !== 'function') {\n return false;\n }\n\n // Appears this is the recommended way to check for Node.js SystemError\n // https://github.com/nodejs/node/issues/46869\n return util.getSystemErrorMap().has(error.errno);\n}\n\ntype Options = {\n /**\n * If true, includes the `path` and `dest` properties in the error context.\n */\n includePaths?: boolean;\n};\n\n/**\n * Captures context for Node.js SystemError errors.\n */\nexport const systemErrorIntegration = defineIntegration((options: Options = {}) => {\n return {\n name: INTEGRATION_NAME,\n processEvent: (event, hint, client) => {\n if (!isSystemError(hint.originalException)) {\n return event;\n }\n\n const error = hint.originalException;\n\n const errorContext: SystemErrorContext = {\n ...(error as SystemErrorContext),\n };\n\n if (!client.getOptions().sendDefaultPii && options.includePaths !== true) {\n delete errorContext.path;\n delete errorContext.dest;\n }\n\n event.contexts = {\n ...event.contexts,\n node_system_error: errorContext,\n };\n\n for (const exception of event.exception?.values || []) {\n if (exception.value) {\n if (error.path && exception.value.includes(error.path)) {\n exception.value = exception.value.replace(`'${error.path}'`, '').trim();\n }\n if (error.dest && exception.value.includes(error.dest)) {\n exception.value = exception.value.replace(`'${error.dest}'`, '').trim();\n }\n }\n }\n\n return event;\n },\n };\n});\n"],"names":[],"mappings":";;;AAGA,MAAM,gBAAA,GAAmB,iBAAiB;;AAU1C,SAAS,aAAa,CAAC,KAAK,EAAiC;AAC7D,EAAE,IAAI,EAAE,iBAAiB,KAAK,CAAC,EAAE;AACjC,IAAI,OAAO,KAAK;AAChB,EAAE;;AAEF,EAAE,IAAI,EAAE,WAAW,KAAK,CAAA,IAAK,OAAO,KAAK,CAAC,KAAA,KAAU,QAAQ,EAAE;AAC9D,IAAI,OAAO,KAAK;AAChB,EAAE;;AAEF;AACA;AACA;AACA,EAAE,IAAI,OAAO,IAAI,CAAC,iBAAA,KAAsB,UAAU,EAAE;AACpD,IAAI,OAAO,KAAK;AAChB,EAAE;;AAEF;AACA;AACA,EAAE,OAAO,IAAI,CAAC,iBAAiB,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC;AAClD;;AASA;AACA;AACA;AACO,MAAM,sBAAA,GAAyB,iBAAiB,CAAC,CAAC,OAAO,GAAY,EAAE,KAAK;AACnF,EAAE,OAAO;AACT,IAAI,IAAI,EAAE,gBAAgB;AAC1B,IAAI,YAAY,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,MAAM,KAAK;AAC3C,MAAM,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,iBAAiB,CAAC,EAAE;AAClD,QAAQ,OAAO,KAAK;AACpB,MAAM;;AAEN,MAAM,MAAM,KAAA,GAAQ,IAAI,CAAC,iBAAiB;;AAE1C,MAAM,MAAM,YAAY,GAAuB;AAC/C,QAAQ,IAAI,KAAA,EAA4B;AACxC,OAAO;;AAEP,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,cAAA,IAAkB,OAAO,CAAC,YAAA,KAAiB,IAAI,EAAE;AAChF,QAAQ,OAAO,YAAY,CAAC,IAAI;AAChC,QAAQ,OAAO,YAAY,CAAC,IAAI;AAChC,MAAM;;AAEN,MAAM,KAAK,CAAC,QAAA,GAAW;AACvB,QAAQ,GAAG,KAAK,CAAC,QAAQ;AACzB,QAAQ,iBAAiB,EAAE,YAAY;AACvC,OAAO;;AAEP,MAAM,KAAK,MAAM,SAAA,IAAa,KAAK,CAAC,SAAS,EAAE,MAAA,IAAU,EAAE,EAAE;AAC7D,QAAQ,IAAI,SAAS,CAAC,KAAK,EAAE;AAC7B,UAAU,IAAI,KAAK,CAAC,IAAA,IAAQ,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE;AAClE,YAAY,SAAS,CAAC,KAAA,GAAQ,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE;AACnF,UAAU;AACV,UAAU,IAAI,KAAK,CAAC,IAAA,IAAQ,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE;AAClE,YAAY,SAAS,CAAC,KAAA,GAAQ,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE;AACnF,UAAU;AACV,QAAQ;AACR,MAAM;;AAEN,MAAM,OAAO,KAAK;AAClB,IAAI,CAAC;AACL,GAAG;AACH,CAAC;;;;"} | ||
| {"version":3,"file":"systemError.js","sources":["../../../src/integrations/systemError.ts"],"sourcesContent":["import * as util from 'node:util';\nimport { defineIntegration } from '@sentry/core';\n\nconst INTEGRATION_NAME = 'NodeSystemError';\n\ntype SystemErrorContext = {\n dest?: string; // If present, the file path destination when reporting a file system error\n errno: number; // The system-provided error number\n path?: string; // If present, the file path when reporting a file system error\n};\n\ntype SystemError = Error & SystemErrorContext;\n\nfunction isSystemError(error: unknown): error is SystemError {\n if (!(error instanceof Error)) {\n return false;\n }\n\n if (!('errno' in error) || typeof error.errno !== 'number') {\n return false;\n }\n\n // Workaround for Bun where getSystemErrorMap doesn't exist\n // Can be removed once Bun supports getSystemErrorMap\n // https://github.com/oven-sh/bun/issues/22872\n if (typeof util.getSystemErrorMap !== 'function') {\n return false;\n }\n\n // Appears this is the recommended way to check for Node.js SystemError\n // https://github.com/nodejs/node/issues/46869\n return util.getSystemErrorMap().has(error.errno);\n}\n\ntype Options = {\n /**\n * If true, includes the `path` and `dest` properties in the error context.\n */\n includePaths?: boolean;\n};\n\n/**\n * Captures context for Node.js SystemError errors.\n */\nexport const systemErrorIntegration = defineIntegration((options: Options = {}) => {\n return {\n name: INTEGRATION_NAME,\n processEvent: (event, hint, client) => {\n if (!isSystemError(hint.originalException)) {\n return event;\n }\n\n const error = hint.originalException;\n\n const errorContext: SystemErrorContext = {\n ...(error as SystemErrorContext),\n };\n\n if (!client.getOptions().sendDefaultPii && options.includePaths !== true) {\n delete errorContext.path;\n delete errorContext.dest;\n }\n\n event.contexts = {\n ...event.contexts,\n node_system_error: errorContext,\n };\n\n for (const exception of event.exception?.values || []) {\n if (exception.value) {\n if (error.path && exception.value.includes(error.path)) {\n exception.value = exception.value.replace(`'${error.path}'`, '').trim();\n }\n if (error.dest && exception.value.includes(error.dest)) {\n exception.value = exception.value.replace(`'${error.dest}'`, '').trim();\n }\n }\n }\n\n return event;\n },\n };\n});\n"],"names":[],"mappings":";;;AAGA,MAAM,gBAAA,GAAmB,iBAAA;AAUzB,SAAS,cAAc,KAAA,EAAsC;AAC3D,EAAA,IAAI,EAAE,iBAAiB,KAAA,CAAA,EAAQ;AAC7B,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,IAAI,EAAE,OAAA,IAAW,KAAA,CAAA,IAAU,OAAO,KAAA,CAAM,UAAU,QAAA,EAAU;AAC1D,IAAA,OAAO,KAAA;AAAA,EACT;AAKA,EAAA,IAAI,OAAO,IAAA,CAAK,iBAAA,KAAsB,UAAA,EAAY;AAChD,IAAA,OAAO,KAAA;AAAA,EACT;AAIA,EAAA,OAAO,IAAA,CAAK,iBAAA,EAAkB,CAAE,GAAA,CAAI,MAAM,KAAK,CAAA;AACjD;AAYO,MAAM,sBAAA,GAAyB,iBAAA,CAAkB,CAAC,OAAA,GAAmB,EAAC,KAAM;AACjF,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,YAAA,EAAc,CAAC,KAAA,EAAO,IAAA,EAAM,MAAA,KAAW;AACrC,MAAA,IAAI,CAAC,aAAA,CAAc,IAAA,CAAK,iBAAiB,CAAA,EAAG;AAC1C,QAAA,OAAO,KAAA;AAAA,MACT;AAEA,MAAA,MAAM,QAAQ,IAAA,CAAK,iBAAA;AAEnB,MAAA,MAAM,YAAA,GAAmC;AAAA,QACvC,GAAI;AAAA,OACN;AAEA,MAAA,IAAI,CAAC,MAAA,CAAO,UAAA,GAAa,cAAA,IAAkB,OAAA,CAAQ,iBAAiB,IAAA,EAAM;AACxE,QAAA,OAAO,YAAA,CAAa,IAAA;AACpB,QAAA,OAAO,YAAA,CAAa,IAAA;AAAA,MACtB;AAEA,MAAA,KAAA,CAAM,QAAA,GAAW;AAAA,QACf,GAAG,KAAA,CAAM,QAAA;AAAA,QACT,iBAAA,EAAmB;AAAA,OACrB;AAEA,MAAA,KAAA,MAAW,SAAA,IAAa,KAAA,CAAM,SAAA,EAAW,MAAA,IAAU,EAAC,EAAG;AACrD,QAAA,IAAI,UAAU,KAAA,EAAO;AACnB,UAAA,IAAI,MAAM,IAAA,IAAQ,SAAA,CAAU,MAAM,QAAA,CAAS,KAAA,CAAM,IAAI,CAAA,EAAG;AACtD,YAAA,SAAA,CAAU,KAAA,GAAQ,SAAA,CAAU,KAAA,CAAM,OAAA,CAAQ,CAAA,CAAA,EAAI,MAAM,IAAI,CAAA,CAAA,CAAA,EAAK,EAAE,CAAA,CAAE,IAAA,EAAK;AAAA,UACxE;AACA,UAAA,IAAI,MAAM,IAAA,IAAQ,SAAA,CAAU,MAAM,QAAA,CAAS,KAAA,CAAM,IAAI,CAAA,EAAG;AACtD,YAAA,SAAA,CAAU,KAAA,GAAQ,SAAA,CAAU,KAAA,CAAM,OAAA,CAAQ,CAAA,CAAA,EAAI,MAAM,IAAI,CAAA,CAAA,CAAA,EAAK,EAAE,CAAA,CAAE,IAAA,EAAK;AAAA,UACxE;AAAA,QACF;AAAA,MACF;AAEA,MAAA,OAAO,KAAA;AAAA,IACT;AAAA,GACF;AACF,CAAC;;;;"} |
@@ -5,90 +5,44 @@ import { debug } from '@sentry/core'; | ||
| const DEFAULT_CAPTURED_LEVELS = ['trace', 'debug', 'info', 'warn', 'error', 'fatal']; | ||
| // See: https://github.com/winstonjs/triple-beam | ||
| const LEVEL_SYMBOL = Symbol.for('level'); | ||
| const MESSAGE_SYMBOL = Symbol.for('message'); | ||
| const SPLAT_SYMBOL = Symbol.for('splat'); | ||
| /** | ||
| * Options for the Sentry Winston transport. | ||
| */ | ||
| /** | ||
| * Creates a new Sentry Winston transport that fowards logs to Sentry. Requires the `enableLogs` option to be enabled. | ||
| * | ||
| * Supports Winston 3.x.x. | ||
| * | ||
| * @param TransportClass - The Winston transport class to extend. | ||
| * @returns The extended transport class. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * const winston = require('winston'); | ||
| * const Transport = require('winston-transport'); | ||
| * | ||
| * const SentryWinstonTransport = Sentry.createSentryWinstonTransport(Transport); | ||
| * | ||
| * const logger = winston.createLogger({ | ||
| * transports: [new SentryWinstonTransport()], | ||
| * }); | ||
| * ``` | ||
| */ | ||
| function createSentryWinstonTransport( | ||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| TransportClass, | ||
| sentryWinstonOptions, | ||
| ) { | ||
| // @ts-ignore - We know this is safe because SentryWinstonTransport extends TransportClass | ||
| const DEFAULT_CAPTURED_LEVELS = ["trace", "debug", "info", "warn", "error", "fatal"]; | ||
| const LEVEL_SYMBOL = /* @__PURE__ */ Symbol.for("level"); | ||
| const MESSAGE_SYMBOL = /* @__PURE__ */ Symbol.for("message"); | ||
| const SPLAT_SYMBOL = /* @__PURE__ */ Symbol.for("splat"); | ||
| function createSentryWinstonTransport(TransportClass, sentryWinstonOptions) { | ||
| class SentryWinstonTransport extends TransportClass { | ||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| constructor(options) { | ||
| constructor(options) { | ||
| super(options); | ||
| this._levels = new Set(sentryWinstonOptions?.levels ?? DEFAULT_CAPTURED_LEVELS); | ||
| } | ||
| /** | ||
| * Forwards a winston log to the Sentry SDK. | ||
| */ | ||
| log(info, callback) { | ||
| log(info, callback) { | ||
| try { | ||
| setImmediate(() => { | ||
| // @ts-ignore - We know this is safe because SentryWinstonTransport extends TransportClass | ||
| this.emit('logged', info); | ||
| this.emit("logged", info); | ||
| }); | ||
| if (!isObject(info)) { | ||
| return; | ||
| } | ||
| const levelFromSymbol = info[LEVEL_SYMBOL]; | ||
| // See: https://github.com/winstonjs/winston?tab=readme-ov-file#streams-objectmode-and-info-objects | ||
| // eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
| const { level, message, timestamp, ...attributes } = info; | ||
| // Remove all symbols from the remaining attributes | ||
| attributes[LEVEL_SYMBOL] = undefined; | ||
| attributes[MESSAGE_SYMBOL] = undefined; | ||
| attributes[SPLAT_SYMBOL] = undefined; | ||
| const customLevel = sentryWinstonOptions?.customLevelMap?.[levelFromSymbol ]; | ||
| const winstonLogLevel = WINSTON_LEVEL_TO_LOG_SEVERITY_LEVEL_MAP[levelFromSymbol ]; | ||
| const logSeverityLevel = customLevel ?? winstonLogLevel ?? 'info'; | ||
| attributes[LEVEL_SYMBOL] = void 0; | ||
| attributes[MESSAGE_SYMBOL] = void 0; | ||
| attributes[SPLAT_SYMBOL] = void 0; | ||
| const customLevel = sentryWinstonOptions?.customLevelMap?.[levelFromSymbol]; | ||
| const winstonLogLevel = WINSTON_LEVEL_TO_LOG_SEVERITY_LEVEL_MAP[levelFromSymbol]; | ||
| const logSeverityLevel = customLevel ?? winstonLogLevel ?? "info"; | ||
| if (this._levels.has(logSeverityLevel)) { | ||
| captureLog(logSeverityLevel, message , { | ||
| captureLog(logSeverityLevel, message, { | ||
| ...attributes, | ||
| 'sentry.origin': 'auto.log.winston', | ||
| "sentry.origin": "auto.log.winston" | ||
| }); | ||
| } else if (!customLevel && !winstonLogLevel) { | ||
| DEBUG_BUILD && | ||
| debug.log( | ||
| `Winston log level ${levelFromSymbol} is not captured by Sentry. Please add ${levelFromSymbol} to the "customLevelMap" option of the Sentry Winston transport.`, | ||
| ); | ||
| DEBUG_BUILD && debug.log( | ||
| `Winston log level ${levelFromSymbol} is not captured by Sentry. Please add ${levelFromSymbol} to the "customLevelMap" option of the Sentry Winston transport.` | ||
| ); | ||
| } | ||
| } catch { | ||
| // do nothing | ||
| } | ||
| if (callback) { | ||
@@ -99,57 +53,32 @@ callback(); | ||
| } | ||
| return SentryWinstonTransport ; | ||
| return SentryWinstonTransport; | ||
| } | ||
| function isObject(anything) { | ||
| return typeof anything === 'object' && anything != null; | ||
| return typeof anything === "object" && anything != null; | ||
| } | ||
| // npm | ||
| // { | ||
| // error: 0, | ||
| // warn: 1, | ||
| // info: 2, | ||
| // http: 3, | ||
| // verbose: 4, | ||
| // debug: 5, | ||
| // silly: 6 | ||
| // } | ||
| // | ||
| // syslog | ||
| // { | ||
| // emerg: 0, | ||
| // alert: 1, | ||
| // crit: 2, | ||
| // error: 3, | ||
| // warning: 4, | ||
| // notice: 5, | ||
| // info: 6, | ||
| // debug: 7, | ||
| // } | ||
| const WINSTON_LEVEL_TO_LOG_SEVERITY_LEVEL_MAP = { | ||
| // npm | ||
| silly: 'trace', | ||
| silly: "trace", | ||
| // npm and syslog | ||
| debug: 'debug', | ||
| debug: "debug", | ||
| // npm | ||
| verbose: 'debug', | ||
| verbose: "debug", | ||
| // npm | ||
| http: 'debug', | ||
| http: "debug", | ||
| // npm and syslog | ||
| info: 'info', | ||
| info: "info", | ||
| // syslog | ||
| notice: 'info', | ||
| notice: "info", | ||
| // npm | ||
| warn: 'warn', | ||
| warn: "warn", | ||
| // syslog | ||
| warning: 'warn', | ||
| warning: "warn", | ||
| // npm and syslog | ||
| error: 'error', | ||
| error: "error", | ||
| // syslog | ||
| emerg: 'fatal', | ||
| emerg: "fatal", | ||
| // syslog | ||
| alert: 'fatal', | ||
| alert: "fatal", | ||
| // syslog | ||
| crit: 'fatal', | ||
| crit: "fatal" | ||
| }; | ||
@@ -156,0 +85,0 @@ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"winston.js","sources":["../../../src/integrations/winston.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/ban-ts-comment */\nimport type { LogSeverityLevel } from '@sentry/core';\nimport { debug } from '@sentry/core';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { captureLog } from '../logs/capture';\n\nconst DEFAULT_CAPTURED_LEVELS: Array<LogSeverityLevel> = ['trace', 'debug', 'info', 'warn', 'error', 'fatal'];\n\n// See: https://github.com/winstonjs/triple-beam\nconst LEVEL_SYMBOL = Symbol.for('level');\nconst MESSAGE_SYMBOL = Symbol.for('message');\nconst SPLAT_SYMBOL = Symbol.for('splat');\n\n/**\n * Options for the Sentry Winston transport.\n */\ninterface WinstonTransportOptions {\n /**\n * Use this option to filter which levels should be captured. By default, all levels are captured.\n *\n * @example\n * ```ts\n * const SentryWinstonTransport = Sentry.createSentryWinstonTransport(Transport, {\n * // Only capture error and warn logs\n * levels: ['error', 'warn'],\n * });\n * ```\n */\n levels?: Array<LogSeverityLevel>;\n\n /**\n * Use this option to map custom levels to Sentry log severity levels.\n *\n * @example\n * ```ts\n * const SentryWinstonTransport = Sentry.createSentryWinstonTransport(Transport, {\n * customLevelMap: {\n * myCustomLevel: 'info',\n * customError: 'error',\n * },\n * });\n * ```\n */\n customLevelMap?: Record<string, LogSeverityLevel>;\n}\n\n/**\n * Creates a new Sentry Winston transport that fowards logs to Sentry. Requires the `enableLogs` option to be enabled.\n *\n * Supports Winston 3.x.x.\n *\n * @param TransportClass - The Winston transport class to extend.\n * @returns The extended transport class.\n *\n * @example\n * ```ts\n * const winston = require('winston');\n * const Transport = require('winston-transport');\n *\n * const SentryWinstonTransport = Sentry.createSentryWinstonTransport(Transport);\n *\n * const logger = winston.createLogger({\n * transports: [new SentryWinstonTransport()],\n * });\n * ```\n */\nexport function createSentryWinstonTransport<TransportStreamInstance extends object>(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n TransportClass: new (options?: any) => TransportStreamInstance,\n sentryWinstonOptions?: WinstonTransportOptions,\n): typeof TransportClass {\n // @ts-ignore - We know this is safe because SentryWinstonTransport extends TransportClass\n class SentryWinstonTransport extends TransportClass {\n private _levels: Set<LogSeverityLevel>;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n public constructor(options?: any) {\n super(options);\n this._levels = new Set(sentryWinstonOptions?.levels ?? DEFAULT_CAPTURED_LEVELS);\n }\n\n /**\n * Forwards a winston log to the Sentry SDK.\n */\n public log(info: unknown, callback: () => void): void {\n try {\n setImmediate(() => {\n // @ts-ignore - We know this is safe because SentryWinstonTransport extends TransportClass\n this.emit('logged', info);\n });\n\n if (!isObject(info)) {\n return;\n }\n\n const levelFromSymbol = info[LEVEL_SYMBOL];\n\n // See: https://github.com/winstonjs/winston?tab=readme-ov-file#streams-objectmode-and-info-objects\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { level, message, timestamp, ...attributes } = info;\n // Remove all symbols from the remaining attributes\n attributes[LEVEL_SYMBOL] = undefined;\n attributes[MESSAGE_SYMBOL] = undefined;\n attributes[SPLAT_SYMBOL] = undefined;\n\n const customLevel = sentryWinstonOptions?.customLevelMap?.[levelFromSymbol as string];\n const winstonLogLevel = WINSTON_LEVEL_TO_LOG_SEVERITY_LEVEL_MAP[levelFromSymbol as string];\n const logSeverityLevel = customLevel ?? winstonLogLevel ?? 'info';\n\n if (this._levels.has(logSeverityLevel)) {\n captureLog(logSeverityLevel, message as string, {\n ...attributes,\n 'sentry.origin': 'auto.log.winston',\n });\n } else if (!customLevel && !winstonLogLevel) {\n DEBUG_BUILD &&\n debug.log(\n `Winston log level ${levelFromSymbol} is not captured by Sentry. Please add ${levelFromSymbol} to the \"customLevelMap\" option of the Sentry Winston transport.`,\n );\n }\n } catch {\n // do nothing\n }\n\n if (callback) {\n callback();\n }\n }\n }\n\n return SentryWinstonTransport as typeof TransportClass;\n}\n\nfunction isObject(anything: unknown): anything is Record<string | symbol, unknown> {\n return typeof anything === 'object' && anything != null;\n}\n\n// npm\n// {\n// error: 0,\n// warn: 1,\n// info: 2,\n// http: 3,\n// verbose: 4,\n// debug: 5,\n// silly: 6\n// }\n//\n// syslog\n// {\n// emerg: 0,\n// alert: 1,\n// crit: 2,\n// error: 3,\n// warning: 4,\n// notice: 5,\n// info: 6,\n// debug: 7,\n// }\nconst WINSTON_LEVEL_TO_LOG_SEVERITY_LEVEL_MAP: Record<string, LogSeverityLevel> = {\n // npm\n silly: 'trace',\n // npm and syslog\n debug: 'debug',\n // npm\n verbose: 'debug',\n // npm\n http: 'debug',\n // npm and syslog\n info: 'info',\n // syslog\n notice: 'info',\n // npm\n warn: 'warn',\n // syslog\n warning: 'warn',\n // npm and syslog\n error: 'error',\n // syslog\n emerg: 'fatal',\n // syslog\n alert: 'fatal',\n // syslog\n crit: 'fatal',\n};\n"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"winston.js","sources":["../../../src/integrations/winston.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/ban-ts-comment */\nimport type { LogSeverityLevel } from '@sentry/core';\nimport { debug } from '@sentry/core';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { captureLog } from '../logs/capture';\n\nconst DEFAULT_CAPTURED_LEVELS: Array<LogSeverityLevel> = ['trace', 'debug', 'info', 'warn', 'error', 'fatal'];\n\n// See: https://github.com/winstonjs/triple-beam\nconst LEVEL_SYMBOL = Symbol.for('level');\nconst MESSAGE_SYMBOL = Symbol.for('message');\nconst SPLAT_SYMBOL = Symbol.for('splat');\n\n/**\n * Options for the Sentry Winston transport.\n */\ninterface WinstonTransportOptions {\n /**\n * Use this option to filter which levels should be captured. By default, all levels are captured.\n *\n * @example\n * ```ts\n * const SentryWinstonTransport = Sentry.createSentryWinstonTransport(Transport, {\n * // Only capture error and warn logs\n * levels: ['error', 'warn'],\n * });\n * ```\n */\n levels?: Array<LogSeverityLevel>;\n\n /**\n * Use this option to map custom levels to Sentry log severity levels.\n *\n * @example\n * ```ts\n * const SentryWinstonTransport = Sentry.createSentryWinstonTransport(Transport, {\n * customLevelMap: {\n * myCustomLevel: 'info',\n * customError: 'error',\n * },\n * });\n * ```\n */\n customLevelMap?: Record<string, LogSeverityLevel>;\n}\n\n/**\n * Creates a new Sentry Winston transport that fowards logs to Sentry. Requires the `enableLogs` option to be enabled.\n *\n * Supports Winston 3.x.x.\n *\n * @param TransportClass - The Winston transport class to extend.\n * @returns The extended transport class.\n *\n * @example\n * ```ts\n * const winston = require('winston');\n * const Transport = require('winston-transport');\n *\n * const SentryWinstonTransport = Sentry.createSentryWinstonTransport(Transport);\n *\n * const logger = winston.createLogger({\n * transports: [new SentryWinstonTransport()],\n * });\n * ```\n */\nexport function createSentryWinstonTransport<TransportStreamInstance extends object>(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n TransportClass: new (options?: any) => TransportStreamInstance,\n sentryWinstonOptions?: WinstonTransportOptions,\n): typeof TransportClass {\n // @ts-ignore - We know this is safe because SentryWinstonTransport extends TransportClass\n class SentryWinstonTransport extends TransportClass {\n private _levels: Set<LogSeverityLevel>;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n public constructor(options?: any) {\n super(options);\n this._levels = new Set(sentryWinstonOptions?.levels ?? DEFAULT_CAPTURED_LEVELS);\n }\n\n /**\n * Forwards a winston log to the Sentry SDK.\n */\n public log(info: unknown, callback: () => void): void {\n try {\n setImmediate(() => {\n // @ts-ignore - We know this is safe because SentryWinstonTransport extends TransportClass\n this.emit('logged', info);\n });\n\n if (!isObject(info)) {\n return;\n }\n\n const levelFromSymbol = info[LEVEL_SYMBOL];\n\n // See: https://github.com/winstonjs/winston?tab=readme-ov-file#streams-objectmode-and-info-objects\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { level, message, timestamp, ...attributes } = info;\n // Remove all symbols from the remaining attributes\n attributes[LEVEL_SYMBOL] = undefined;\n attributes[MESSAGE_SYMBOL] = undefined;\n attributes[SPLAT_SYMBOL] = undefined;\n\n const customLevel = sentryWinstonOptions?.customLevelMap?.[levelFromSymbol as string];\n const winstonLogLevel = WINSTON_LEVEL_TO_LOG_SEVERITY_LEVEL_MAP[levelFromSymbol as string];\n const logSeverityLevel = customLevel ?? winstonLogLevel ?? 'info';\n\n if (this._levels.has(logSeverityLevel)) {\n captureLog(logSeverityLevel, message as string, {\n ...attributes,\n 'sentry.origin': 'auto.log.winston',\n });\n } else if (!customLevel && !winstonLogLevel) {\n DEBUG_BUILD &&\n debug.log(\n `Winston log level ${levelFromSymbol} is not captured by Sentry. Please add ${levelFromSymbol} to the \"customLevelMap\" option of the Sentry Winston transport.`,\n );\n }\n } catch {\n // do nothing\n }\n\n if (callback) {\n callback();\n }\n }\n }\n\n return SentryWinstonTransport as typeof TransportClass;\n}\n\nfunction isObject(anything: unknown): anything is Record<string | symbol, unknown> {\n return typeof anything === 'object' && anything != null;\n}\n\n// npm\n// {\n// error: 0,\n// warn: 1,\n// info: 2,\n// http: 3,\n// verbose: 4,\n// debug: 5,\n// silly: 6\n// }\n//\n// syslog\n// {\n// emerg: 0,\n// alert: 1,\n// crit: 2,\n// error: 3,\n// warning: 4,\n// notice: 5,\n// info: 6,\n// debug: 7,\n// }\nconst WINSTON_LEVEL_TO_LOG_SEVERITY_LEVEL_MAP: Record<string, LogSeverityLevel> = {\n // npm\n silly: 'trace',\n // npm and syslog\n debug: 'debug',\n // npm\n verbose: 'debug',\n // npm\n http: 'debug',\n // npm and syslog\n info: 'info',\n // syslog\n notice: 'info',\n // npm\n warn: 'warn',\n // syslog\n warning: 'warn',\n // npm and syslog\n error: 'error',\n // syslog\n emerg: 'fatal',\n // syslog\n alert: 'fatal',\n // syslog\n crit: 'fatal',\n};\n"],"names":[],"mappings":"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"} |
| import { AsyncLocalStorage } from 'node:async_hooks'; | ||
| import { setAsyncContextStrategy, SUPPRESS_TRACING_KEY, getDefaultIsolationScope, getDefaultCurrentScope } from '@sentry/core'; | ||
| /** | ||
| * Sets the async context strategy to use AsyncLocalStorage. | ||
| * | ||
| * This is a lightweight alternative to the OpenTelemetry-based strategy. | ||
| * It uses Node's native AsyncLocalStorage directly without any OpenTelemetry dependencies. | ||
| */ | ||
| function setAsyncLocalStorageAsyncContextStrategy() { | ||
| const asyncStorage = new AsyncLocalStorage | ||
| (); | ||
| const asyncStorage = new AsyncLocalStorage(); | ||
| function getScopes() { | ||
| const scopes = asyncStorage.getStore(); | ||
| if (scopes) { | ||
| return scopes; | ||
| } | ||
| // fallback behavior: | ||
| // if, for whatever reason, we can't find scopes on the context here, we have to fix this somehow | ||
| return { | ||
| scope: getDefaultCurrentScope(), | ||
| isolationScope: getDefaultIsolationScope(), | ||
| isolationScope: getDefaultIsolationScope() | ||
| }; | ||
| } | ||
| function withScope(callback) { | ||
@@ -37,3 +23,2 @@ const scope = getScopes().scope.clone(); | ||
| } | ||
| function withSetScope(scope, callback) { | ||
@@ -45,3 +30,2 @@ const isolationScope = getScopes().isolationScope.clone(); | ||
| } | ||
| function withIsolationScope(callback) { | ||
@@ -54,3 +38,2 @@ const scope = getScopes().scope.clone(); | ||
| } | ||
| function withSetIsolationScope(isolationScope, callback) { | ||
@@ -62,6 +45,4 @@ const scope = getScopes().scope.clone(); | ||
| } | ||
| // In contrast to the browser, we can rely on async context isolation here | ||
| function suppressTracing(callback) { | ||
| return withScope(scope => { | ||
| return withScope((scope) => { | ||
| scope.setSDKProcessingMetadata({ [SUPPRESS_TRACING_KEY]: true }); | ||
@@ -71,3 +52,2 @@ return callback(); | ||
| } | ||
| setAsyncContextStrategy({ | ||
@@ -80,3 +60,3 @@ suppressTracing, | ||
| getCurrentScope: () => getScopes().scope, | ||
| getIsolationScope: () => getScopes().isolationScope, | ||
| getIsolationScope: () => getScopes().isolationScope | ||
| }); | ||
@@ -83,0 +63,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"asyncLocalStorageStrategy.js","sources":["../../../src/light/asyncLocalStorageStrategy.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks';\nimport type { Scope } from '@sentry/core';\nimport {\n getDefaultCurrentScope,\n getDefaultIsolationScope,\n setAsyncContextStrategy,\n SUPPRESS_TRACING_KEY,\n} from '@sentry/core';\n\n/**\n * Sets the async context strategy to use AsyncLocalStorage.\n *\n * This is a lightweight alternative to the OpenTelemetry-based strategy.\n * It uses Node's native AsyncLocalStorage directly without any OpenTelemetry dependencies.\n */\nexport function setAsyncLocalStorageAsyncContextStrategy(): void {\n const asyncStorage = new AsyncLocalStorage<{\n scope: Scope;\n isolationScope: Scope;\n }>();\n\n function getScopes(): { scope: Scope; isolationScope: Scope } {\n const scopes = asyncStorage.getStore();\n\n if (scopes) {\n return scopes;\n }\n\n // fallback behavior:\n // if, for whatever reason, we can't find scopes on the context here, we have to fix this somehow\n return {\n scope: getDefaultCurrentScope(),\n isolationScope: getDefaultIsolationScope(),\n };\n }\n\n function withScope<T>(callback: (scope: Scope) => T): T {\n const scope = getScopes().scope.clone();\n const isolationScope = getScopes().isolationScope;\n return asyncStorage.run({ scope, isolationScope }, () => {\n return callback(scope);\n });\n }\n\n function withSetScope<T>(scope: Scope, callback: (scope: Scope) => T): T {\n const isolationScope = getScopes().isolationScope.clone();\n return asyncStorage.run({ scope, isolationScope }, () => {\n return callback(scope);\n });\n }\n\n function withIsolationScope<T>(callback: (isolationScope: Scope) => T): T {\n const scope = getScopes().scope.clone();\n const isolationScope = getScopes().isolationScope.clone();\n return asyncStorage.run({ scope, isolationScope }, () => {\n return callback(isolationScope);\n });\n }\n\n function withSetIsolationScope<T>(isolationScope: Scope, callback: (isolationScope: Scope) => T): T {\n const scope = getScopes().scope.clone();\n return asyncStorage.run({ scope, isolationScope }, () => {\n return callback(isolationScope);\n });\n }\n\n // In contrast to the browser, we can rely on async context isolation here\n function suppressTracing<T>(callback: () => T): T {\n return withScope(scope => {\n scope.setSDKProcessingMetadata({ [SUPPRESS_TRACING_KEY]: true });\n return callback();\n });\n }\n\n setAsyncContextStrategy({\n suppressTracing,\n withScope,\n withSetScope,\n withIsolationScope,\n withSetIsolationScope,\n getCurrentScope: () => getScopes().scope,\n getIsolationScope: () => getScopes().isolationScope,\n });\n}\n"],"names":[],"mappings":";;;AASA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,wCAAwC,GAAS;AACjE,EAAE,MAAM,YAAA,GAAe,IAAI;;AAGzB,EAAI;;AAEN,EAAE,SAAS,SAAS,GAA4C;AAChE,IAAI,MAAM,MAAA,GAAS,YAAY,CAAC,QAAQ,EAAE;;AAE1C,IAAI,IAAI,MAAM,EAAE;AAChB,MAAM,OAAO,MAAM;AACnB,IAAI;;AAEJ;AACA;AACA,IAAI,OAAO;AACX,MAAM,KAAK,EAAE,sBAAsB,EAAE;AACrC,MAAM,cAAc,EAAE,wBAAwB,EAAE;AAChD,KAAK;AACL,EAAE;;AAEF,EAAE,SAAS,SAAS,CAAI,QAAQ,EAA0B;AAC1D,IAAI,MAAM,KAAA,GAAQ,SAAS,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE;AAC3C,IAAI,MAAM,cAAA,GAAiB,SAAS,EAAE,CAAC,cAAc;AACrD,IAAI,OAAO,YAAY,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,cAAA,EAAgB,EAAE,MAAM;AAC7D,MAAM,OAAO,QAAQ,CAAC,KAAK,CAAC;AAC5B,IAAI,CAAC,CAAC;AACN,EAAE;;AAEF,EAAE,SAAS,YAAY,CAAI,KAAK,EAAS,QAAQ,EAA0B;AAC3E,IAAI,MAAM,cAAA,GAAiB,SAAS,EAAE,CAAC,cAAc,CAAC,KAAK,EAAE;AAC7D,IAAI,OAAO,YAAY,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,cAAA,EAAgB,EAAE,MAAM;AAC7D,MAAM,OAAO,QAAQ,CAAC,KAAK,CAAC;AAC5B,IAAI,CAAC,CAAC;AACN,EAAE;;AAEF,EAAE,SAAS,kBAAkB,CAAI,QAAQ,EAAmC;AAC5E,IAAI,MAAM,KAAA,GAAQ,SAAS,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE;AAC3C,IAAI,MAAM,cAAA,GAAiB,SAAS,EAAE,CAAC,cAAc,CAAC,KAAK,EAAE;AAC7D,IAAI,OAAO,YAAY,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,cAAA,EAAgB,EAAE,MAAM;AAC7D,MAAM,OAAO,QAAQ,CAAC,cAAc,CAAC;AACrC,IAAI,CAAC,CAAC;AACN,EAAE;;AAEF,EAAE,SAAS,qBAAqB,CAAI,cAAc,EAAS,QAAQ,EAAmC;AACtG,IAAI,MAAM,KAAA,GAAQ,SAAS,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE;AAC3C,IAAI,OAAO,YAAY,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,cAAA,EAAgB,EAAE,MAAM;AAC7D,MAAM,OAAO,QAAQ,CAAC,cAAc,CAAC;AACrC,IAAI,CAAC,CAAC;AACN,EAAE;;AAEF;AACA,EAAE,SAAS,eAAe,CAAI,QAAQ,EAAc;AACpD,IAAI,OAAO,SAAS,CAAC,KAAA,IAAS;AAC9B,MAAM,KAAK,CAAC,wBAAwB,CAAC,EAAE,CAAC,oBAAoB,GAAG,IAAA,EAAM,CAAC;AACtE,MAAM,OAAO,QAAQ,EAAE;AACvB,IAAI,CAAC,CAAC;AACN,EAAE;;AAEF,EAAE,uBAAuB,CAAC;AAC1B,IAAI,eAAe;AACnB,IAAI,SAAS;AACb,IAAI,YAAY;AAChB,IAAI,kBAAkB;AACtB,IAAI,qBAAqB;AACzB,IAAI,eAAe,EAAE,MAAM,SAAS,EAAE,CAAC,KAAK;AAC5C,IAAI,iBAAiB,EAAE,MAAM,SAAS,EAAE,CAAC,cAAc;AACvD,GAAG,CAAC;AACJ;;;;"} | ||
| {"version":3,"file":"asyncLocalStorageStrategy.js","sources":["../../../src/light/asyncLocalStorageStrategy.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks';\nimport type { Scope } from '@sentry/core';\nimport {\n getDefaultCurrentScope,\n getDefaultIsolationScope,\n setAsyncContextStrategy,\n SUPPRESS_TRACING_KEY,\n} from '@sentry/core';\n\n/**\n * Sets the async context strategy to use AsyncLocalStorage.\n *\n * This is a lightweight alternative to the OpenTelemetry-based strategy.\n * It uses Node's native AsyncLocalStorage directly without any OpenTelemetry dependencies.\n */\nexport function setAsyncLocalStorageAsyncContextStrategy(): void {\n const asyncStorage = new AsyncLocalStorage<{\n scope: Scope;\n isolationScope: Scope;\n }>();\n\n function getScopes(): { scope: Scope; isolationScope: Scope } {\n const scopes = asyncStorage.getStore();\n\n if (scopes) {\n return scopes;\n }\n\n // fallback behavior:\n // if, for whatever reason, we can't find scopes on the context here, we have to fix this somehow\n return {\n scope: getDefaultCurrentScope(),\n isolationScope: getDefaultIsolationScope(),\n };\n }\n\n function withScope<T>(callback: (scope: Scope) => T): T {\n const scope = getScopes().scope.clone();\n const isolationScope = getScopes().isolationScope;\n return asyncStorage.run({ scope, isolationScope }, () => {\n return callback(scope);\n });\n }\n\n function withSetScope<T>(scope: Scope, callback: (scope: Scope) => T): T {\n const isolationScope = getScopes().isolationScope.clone();\n return asyncStorage.run({ scope, isolationScope }, () => {\n return callback(scope);\n });\n }\n\n function withIsolationScope<T>(callback: (isolationScope: Scope) => T): T {\n const scope = getScopes().scope.clone();\n const isolationScope = getScopes().isolationScope.clone();\n return asyncStorage.run({ scope, isolationScope }, () => {\n return callback(isolationScope);\n });\n }\n\n function withSetIsolationScope<T>(isolationScope: Scope, callback: (isolationScope: Scope) => T): T {\n const scope = getScopes().scope.clone();\n return asyncStorage.run({ scope, isolationScope }, () => {\n return callback(isolationScope);\n });\n }\n\n // In contrast to the browser, we can rely on async context isolation here\n function suppressTracing<T>(callback: () => T): T {\n return withScope(scope => {\n scope.setSDKProcessingMetadata({ [SUPPRESS_TRACING_KEY]: true });\n return callback();\n });\n }\n\n setAsyncContextStrategy({\n suppressTracing,\n withScope,\n withSetScope,\n withIsolationScope,\n withSetIsolationScope,\n getCurrentScope: () => getScopes().scope,\n getIsolationScope: () => getScopes().isolationScope,\n });\n}\n"],"names":[],"mappings":";;;AAeO,SAAS,wCAAA,GAAiD;AAC/D,EAAA,MAAM,YAAA,GAAe,IAAI,iBAAA,EAGtB;AAEH,EAAA,SAAS,SAAA,GAAqD;AAC5D,IAAA,MAAM,MAAA,GAAS,aAAa,QAAA,EAAS;AAErC,IAAA,IAAI,MAAA,EAAQ;AACV,MAAA,OAAO,MAAA;AAAA,IACT;AAIA,IAAA,OAAO;AAAA,MACL,OAAO,sBAAA,EAAuB;AAAA,MAC9B,gBAAgB,wBAAA;AAAyB,KAC3C;AAAA,EACF;AAEA,EAAA,SAAS,UAAa,QAAA,EAAkC;AACtD,IAAA,MAAM,KAAA,GAAQ,SAAA,EAAU,CAAE,KAAA,CAAM,KAAA,EAAM;AACtC,IAAA,MAAM,cAAA,GAAiB,WAAU,CAAE,cAAA;AACnC,IAAA,OAAO,aAAa,GAAA,CAAI,EAAE,KAAA,EAAO,cAAA,IAAkB,MAAM;AACvD,MAAA,OAAO,SAAS,KAAK,CAAA;AAAA,IACvB,CAAC,CAAA;AAAA,EACH;AAEA,EAAA,SAAS,YAAA,CAAgB,OAAc,QAAA,EAAkC;AACvE,IAAA,MAAM,cAAA,GAAiB,SAAA,EAAU,CAAE,cAAA,CAAe,KAAA,EAAM;AACxD,IAAA,OAAO,aAAa,GAAA,CAAI,EAAE,KAAA,EAAO,cAAA,IAAkB,MAAM;AACvD,MAAA,OAAO,SAAS,KAAK,CAAA;AAAA,IACvB,CAAC,CAAA;AAAA,EACH;AAEA,EAAA,SAAS,mBAAsB,QAAA,EAA2C;AACxE,IAAA,MAAM,KAAA,GAAQ,SAAA,EAAU,CAAE,KAAA,CAAM,KAAA,EAAM;AACtC,IAAA,MAAM,cAAA,GAAiB,SAAA,EAAU,CAAE,cAAA,CAAe,KAAA,EAAM;AACxD,IAAA,OAAO,aAAa,GAAA,CAAI,EAAE,KAAA,EAAO,cAAA,IAAkB,MAAM;AACvD,MAAA,OAAO,SAAS,cAAc,CAAA;AAAA,IAChC,CAAC,CAAA;AAAA,EACH;AAEA,EAAA,SAAS,qBAAA,CAAyB,gBAAuB,QAAA,EAA2C;AAClG,IAAA,MAAM,KAAA,GAAQ,SAAA,EAAU,CAAE,KAAA,CAAM,KAAA,EAAM;AACtC,IAAA,OAAO,aAAa,GAAA,CAAI,EAAE,KAAA,EAAO,cAAA,IAAkB,MAAM;AACvD,MAAA,OAAO,SAAS,cAAc,CAAA;AAAA,IAChC,CAAC,CAAA;AAAA,EACH;AAGA,EAAA,SAAS,gBAAmB,QAAA,EAAsB;AAChD,IAAA,OAAO,UAAU,CAAA,KAAA,KAAS;AACxB,MAAA,KAAA,CAAM,yBAAyB,EAAE,CAAC,oBAAoB,GAAG,MAAM,CAAA;AAC/D,MAAA,OAAO,QAAA,EAAS;AAAA,IAClB,CAAC,CAAA;AAAA,EACH;AAEA,EAAA,uBAAA,CAAwB;AAAA,IACtB,eAAA;AAAA,IACA,SAAA;AAAA,IACA,YAAA;AAAA,IACA,kBAAA;AAAA,IACA,qBAAA;AAAA,IACA,eAAA,EAAiB,MAAM,SAAA,EAAU,CAAE,KAAA;AAAA,IACnC,iBAAA,EAAmB,MAAM,SAAA,EAAU,CAAE;AAAA,GACtC,CAAA;AACH;;;;"} |
@@ -6,26 +6,15 @@ import * as os from 'node:os'; | ||
| const DEFAULT_CLIENT_REPORT_FLUSH_INTERVAL_MS = 60000; // 60s was chosen arbitrarily | ||
| /** A lightweight client for using Sentry with Node without OpenTelemetry. */ | ||
| const DEFAULT_CLIENT_REPORT_FLUSH_INTERVAL_MS = 6e4; | ||
| class LightNodeClient extends ServerRuntimeClient { | ||
| constructor(options) { | ||
| const serverName = | ||
| options.includeServerName === false | ||
| ? undefined | ||
| : options.serverName || global.process.env.SENTRY_NAME || os.hostname(); | ||
| constructor(options) { | ||
| const serverName = options.includeServerName === false ? void 0 : options.serverName || global.process.env.SENTRY_NAME || os.hostname(); | ||
| const clientOptions = { | ||
| ...options, | ||
| platform: 'node', | ||
| runtime: { name: 'node', version: global.process.version }, | ||
| serverName, | ||
| platform: "node", | ||
| runtime: { name: "node", version: global.process.version }, | ||
| serverName | ||
| }; | ||
| applySdkMetadata(clientOptions, 'node-light', ['node-core']); | ||
| debug.log(`Initializing Sentry: process: ${process.pid}, thread: ${isMainThread ? 'main' : `worker-${threadId}`}.`); | ||
| applySdkMetadata(clientOptions, "node-light", ["node-core"]); | ||
| debug.log(`Initializing Sentry: process: ${process.pid}, thread: ${isMainThread ? "main" : `worker-${threadId}`}.`); | ||
| super(clientOptions); | ||
| if (this.getOptions().enableLogs) { | ||
@@ -35,44 +24,35 @@ this._logOnExitFlushListener = () => { | ||
| }; | ||
| if (serverName) { | ||
| this.on('beforeCaptureLog', log => { | ||
| this.on("beforeCaptureLog", (log) => { | ||
| log.attributes = { | ||
| ...log.attributes, | ||
| 'server.address': serverName, | ||
| "server.address": serverName | ||
| }; | ||
| }); | ||
| } | ||
| process.on('beforeExit', this._logOnExitFlushListener); | ||
| process.on("beforeExit", this._logOnExitFlushListener); | ||
| } | ||
| } | ||
| /** @inheritDoc */ | ||
| // @ts-expect-error - PromiseLike is a subset of Promise | ||
| async flush(timeout) { | ||
| async flush(timeout) { | ||
| if (this.getOptions().sendClientReports) { | ||
| this._flushOutcomes(); | ||
| } | ||
| return super.flush(timeout); | ||
| } | ||
| /** @inheritDoc */ | ||
| // @ts-expect-error - PromiseLike is a subset of Promise | ||
| async close(timeout) { | ||
| async close(timeout) { | ||
| if (this._clientReportInterval) { | ||
| clearInterval(this._clientReportInterval); | ||
| } | ||
| if (this._clientReportOnExitFlushListener) { | ||
| process.off('beforeExit', this._clientReportOnExitFlushListener); | ||
| process.off("beforeExit", this._clientReportOnExitFlushListener); | ||
| } | ||
| if (this._logOnExitFlushListener) { | ||
| process.off('beforeExit', this._logOnExitFlushListener); | ||
| process.off("beforeExit", this._logOnExitFlushListener); | ||
| } | ||
| return super.close(timeout); | ||
| } | ||
| /** | ||
@@ -93,3 +73,3 @@ * Will start tracking client reports for this client. | ||
| // collected, but it did not work, because the cleanup function never got called. | ||
| startClientReportTracking() { | ||
| startClientReportTracking() { | ||
| const clientOptions = this.getOptions(); | ||
@@ -100,11 +80,7 @@ if (clientOptions.sendClientReports) { | ||
| }; | ||
| this._clientReportInterval = setInterval(() => { | ||
| DEBUG_BUILD && debug.log('Flushing client reports based on interval.'); | ||
| DEBUG_BUILD && debug.log("Flushing client reports based on interval."); | ||
| this._flushOutcomes(); | ||
| }, clientOptions.clientReportFlushInterval ?? DEFAULT_CLIENT_REPORT_FLUSH_INTERVAL_MS) | ||
| // Unref is critical for not preventing the process from exiting because the interval is active. | ||
| .unref(); | ||
| process.on('beforeExit', this._clientReportOnExitFlushListener); | ||
| }, clientOptions.clientReportFlushInterval ?? DEFAULT_CLIENT_REPORT_FLUSH_INTERVAL_MS).unref(); | ||
| process.on("beforeExit", this._clientReportOnExitFlushListener); | ||
| } | ||
@@ -111,0 +87,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"client.js","sources":["../../../src/light/client.ts"],"sourcesContent":["import * as os from 'node:os';\nimport type { ServerRuntimeClientOptions } from '@sentry/core';\nimport { _INTERNAL_flushLogsBuffer, applySdkMetadata, debug, ServerRuntimeClient } from '@sentry/core';\nimport { isMainThread, threadId } from 'worker_threads';\nimport { DEBUG_BUILD } from '../debug-build';\nimport type { NodeClientOptions } from '../types';\n\nconst DEFAULT_CLIENT_REPORT_FLUSH_INTERVAL_MS = 60_000; // 60s was chosen arbitrarily\n\n/** A lightweight client for using Sentry with Node without OpenTelemetry. */\nexport class LightNodeClient extends ServerRuntimeClient<NodeClientOptions> {\n private _clientReportInterval: NodeJS.Timeout | undefined;\n private _clientReportOnExitFlushListener: (() => void) | undefined;\n private _logOnExitFlushListener: (() => void) | undefined;\n\n public constructor(options: NodeClientOptions) {\n const serverName =\n options.includeServerName === false\n ? undefined\n : options.serverName || global.process.env.SENTRY_NAME || os.hostname();\n\n const clientOptions: ServerRuntimeClientOptions = {\n ...options,\n platform: 'node',\n runtime: { name: 'node', version: global.process.version },\n serverName,\n };\n\n applySdkMetadata(clientOptions, 'node-light', ['node-core']);\n\n debug.log(`Initializing Sentry: process: ${process.pid}, thread: ${isMainThread ? 'main' : `worker-${threadId}`}.`);\n\n super(clientOptions);\n\n if (this.getOptions().enableLogs) {\n this._logOnExitFlushListener = () => {\n _INTERNAL_flushLogsBuffer(this);\n };\n\n if (serverName) {\n this.on('beforeCaptureLog', log => {\n log.attributes = {\n ...log.attributes,\n 'server.address': serverName,\n };\n });\n }\n\n process.on('beforeExit', this._logOnExitFlushListener);\n }\n }\n\n /** @inheritDoc */\n // @ts-expect-error - PromiseLike is a subset of Promise\n public async flush(timeout?: number): PromiseLike<boolean> {\n if (this.getOptions().sendClientReports) {\n this._flushOutcomes();\n }\n\n return super.flush(timeout);\n }\n\n /** @inheritDoc */\n // @ts-expect-error - PromiseLike is a subset of Promise\n public async close(timeout?: number | undefined): PromiseLike<boolean> {\n if (this._clientReportInterval) {\n clearInterval(this._clientReportInterval);\n }\n\n if (this._clientReportOnExitFlushListener) {\n process.off('beforeExit', this._clientReportOnExitFlushListener);\n }\n\n if (this._logOnExitFlushListener) {\n process.off('beforeExit', this._logOnExitFlushListener);\n }\n\n return super.close(timeout);\n }\n\n /**\n * Will start tracking client reports for this client.\n *\n * NOTICE: This method will create an interval that is periodically called and attach a `process.on('beforeExit')`\n * hook. To clean up these resources, call `.close()` when you no longer intend to use the client. Not doing so will\n * result in a memory leak.\n */\n // The reason client reports need to be manually activated with this method instead of just enabling them in a\n // constructor, is that if users periodically and unboundedly create new clients, we will create more and more\n // intervals and beforeExit listeners, thus leaking memory. In these situations, users are required to call\n // `client.close()` in order to dispose of the acquired resources.\n // We assume that calling this method in Sentry.init() is a sensible default, because calling Sentry.init() over and\n // over again would also result in memory leaks.\n // Note: We have experimented with using `FinalizationRegisty` to clear the interval when the client is garbage\n // collected, but it did not work, because the cleanup function never got called.\n public startClientReportTracking(): void {\n const clientOptions = this.getOptions();\n if (clientOptions.sendClientReports) {\n this._clientReportOnExitFlushListener = () => {\n this._flushOutcomes();\n };\n\n this._clientReportInterval = setInterval(() => {\n DEBUG_BUILD && debug.log('Flushing client reports based on interval.');\n this._flushOutcomes();\n }, clientOptions.clientReportFlushInterval ?? DEFAULT_CLIENT_REPORT_FLUSH_INTERVAL_MS)\n // Unref is critical for not preventing the process from exiting because the interval is active.\n .unref();\n\n process.on('beforeExit', this._clientReportOnExitFlushListener);\n }\n }\n}\n"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"client.js","sources":["../../../src/light/client.ts"],"sourcesContent":["import * as os from 'node:os';\nimport type { ServerRuntimeClientOptions } from '@sentry/core';\nimport { _INTERNAL_flushLogsBuffer, applySdkMetadata, debug, ServerRuntimeClient } from '@sentry/core';\nimport { isMainThread, threadId } from 'worker_threads';\nimport { DEBUG_BUILD } from '../debug-build';\nimport type { NodeClientOptions } from '../types';\n\nconst DEFAULT_CLIENT_REPORT_FLUSH_INTERVAL_MS = 60_000; // 60s was chosen arbitrarily\n\n/** A lightweight client for using Sentry with Node without OpenTelemetry. */\nexport class LightNodeClient extends ServerRuntimeClient<NodeClientOptions> {\n private _clientReportInterval: NodeJS.Timeout | undefined;\n private _clientReportOnExitFlushListener: (() => void) | undefined;\n private _logOnExitFlushListener: (() => void) | undefined;\n\n public constructor(options: NodeClientOptions) {\n const serverName =\n options.includeServerName === false\n ? undefined\n : options.serverName || global.process.env.SENTRY_NAME || os.hostname();\n\n const clientOptions: ServerRuntimeClientOptions = {\n ...options,\n platform: 'node',\n runtime: { name: 'node', version: global.process.version },\n serverName,\n };\n\n applySdkMetadata(clientOptions, 'node-light', ['node-core']);\n\n debug.log(`Initializing Sentry: process: ${process.pid}, thread: ${isMainThread ? 'main' : `worker-${threadId}`}.`);\n\n super(clientOptions);\n\n if (this.getOptions().enableLogs) {\n this._logOnExitFlushListener = () => {\n _INTERNAL_flushLogsBuffer(this);\n };\n\n if (serverName) {\n this.on('beforeCaptureLog', log => {\n log.attributes = {\n ...log.attributes,\n 'server.address': serverName,\n };\n });\n }\n\n process.on('beforeExit', this._logOnExitFlushListener);\n }\n }\n\n /** @inheritDoc */\n // @ts-expect-error - PromiseLike is a subset of Promise\n public async flush(timeout?: number): PromiseLike<boolean> {\n if (this.getOptions().sendClientReports) {\n this._flushOutcomes();\n }\n\n return super.flush(timeout);\n }\n\n /** @inheritDoc */\n // @ts-expect-error - PromiseLike is a subset of Promise\n public async close(timeout?: number | undefined): PromiseLike<boolean> {\n if (this._clientReportInterval) {\n clearInterval(this._clientReportInterval);\n }\n\n if (this._clientReportOnExitFlushListener) {\n process.off('beforeExit', this._clientReportOnExitFlushListener);\n }\n\n if (this._logOnExitFlushListener) {\n process.off('beforeExit', this._logOnExitFlushListener);\n }\n\n return super.close(timeout);\n }\n\n /**\n * Will start tracking client reports for this client.\n *\n * NOTICE: This method will create an interval that is periodically called and attach a `process.on('beforeExit')`\n * hook. To clean up these resources, call `.close()` when you no longer intend to use the client. Not doing so will\n * result in a memory leak.\n */\n // The reason client reports need to be manually activated with this method instead of just enabling them in a\n // constructor, is that if users periodically and unboundedly create new clients, we will create more and more\n // intervals and beforeExit listeners, thus leaking memory. In these situations, users are required to call\n // `client.close()` in order to dispose of the acquired resources.\n // We assume that calling this method in Sentry.init() is a sensible default, because calling Sentry.init() over and\n // over again would also result in memory leaks.\n // Note: We have experimented with using `FinalizationRegisty` to clear the interval when the client is garbage\n // collected, but it did not work, because the cleanup function never got called.\n public startClientReportTracking(): void {\n const clientOptions = this.getOptions();\n if (clientOptions.sendClientReports) {\n this._clientReportOnExitFlushListener = () => {\n this._flushOutcomes();\n };\n\n this._clientReportInterval = setInterval(() => {\n DEBUG_BUILD && debug.log('Flushing client reports based on interval.');\n this._flushOutcomes();\n }, clientOptions.clientReportFlushInterval ?? DEFAULT_CLIENT_REPORT_FLUSH_INTERVAL_MS)\n // Unref is critical for not preventing the process from exiting because the interval is active.\n .unref();\n\n process.on('beforeExit', this._clientReportOnExitFlushListener);\n }\n }\n}\n"],"names":[],"mappings":"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"} |
| import { subscribe } from 'node:diagnostics_channel'; | ||
| import { getHttpClientSubscriptions, getRequestOptions, HTTP_ON_CLIENT_REQUEST, getCurrentScope, SUPPRESS_TRACING_KEY, getRequestUrlFromClientRequest, addOutgoingRequestBreadcrumb, debug, getIsolationScope, httpRequestToRequestData, stripUrlQueryAndFragment, withIsolationScope, continueTrace, generateSpanId } from '@sentry/core'; | ||
| import { DEBUG_BUILD } from '../../debug-build.js'; | ||
| import { patchRequestToCaptureBody } from '../../utils/captureRequestBody.js'; | ||
| import { getHttpServerSubscriptions, HTTP_ON_SERVER_REQUEST, getHttpClientSubscriptions, getRequestOptions, HTTP_ON_CLIENT_REQUEST, getCurrentScope, SUPPRESS_TRACING_KEY, getRequestUrlFromClientRequest, addOutgoingRequestBreadcrumb } from '@sentry/core'; | ||
| import { errorMonitor } from 'node:events'; | ||
| import { NODE_VERSION } from '../../nodeVersion.js'; | ||
| const INTEGRATION_NAME = 'Http'; | ||
| const FULLY_SUPPORTS_HTTP_DIAGNOSTICS_CHANNEL = | ||
| (NODE_VERSION.major === 22 && NODE_VERSION.minor >= 12) || | ||
| (NODE_VERSION.major === 23 && NODE_VERSION.minor >= 2) || | ||
| NODE_VERSION.major >= 24; | ||
| // We keep track of emit functions we wrapped, to avoid double wrapping | ||
| const wrappedEmitFns = new WeakSet(); | ||
| const INTEGRATION_NAME = "Http"; | ||
| const FULLY_SUPPORTS_HTTP_DIAGNOSTICS_CHANNEL = NODE_VERSION.major === 22 && NODE_VERSION.minor >= 12 || NODE_VERSION.major === 23 && NODE_VERSION.minor >= 2 || NODE_VERSION.major >= 24; | ||
| const _httpIntegration = ((options = {}) => { | ||
| const _options = { | ||
| ...options, | ||
| maxRequestBodySize: options.maxRequestBodySize ?? 'medium', | ||
| sessions: false, | ||
| maxRequestBodySize: options.maxRequestBodySize ?? "medium", | ||
| ignoreRequestBody: options.ignoreRequestBody, | ||
@@ -26,41 +17,28 @@ breadcrumbs: options.breadcrumbs ?? true, | ||
| ignoreOutgoingRequests: options.ignoreOutgoingRequests, | ||
| // no spans created in light mode | ||
| spans: false, | ||
| errorMonitor | ||
| }; | ||
| return { | ||
| name: INTEGRATION_NAME, | ||
| setupOnce() { | ||
| const onHttpServerRequestStart = (_data) => { | ||
| const data = _data ; | ||
| instrumentServer(data.server, _options); | ||
| }; | ||
| const { [HTTP_ON_SERVER_REQUEST]: onHttpServerRequestStart } = getHttpServerSubscriptions(_options); | ||
| const { ignoreOutgoingRequests } = _options; | ||
| const { [HTTP_ON_CLIENT_REQUEST]: onHttpClientRequestCreated } = getHttpClientSubscriptions({ | ||
| breadcrumbs: _options.breadcrumbs, | ||
| propagateTrace: _options.tracePropagation, | ||
| ignoreOutgoingRequests: ignoreOutgoingRequests | ||
| ? (url, request) => ignoreOutgoingRequests(url, getRequestOptions(request )) | ||
| : undefined, | ||
| ignoreOutgoingRequests: ignoreOutgoingRequests ? (url, request) => ignoreOutgoingRequests(url, getRequestOptions(request)) : void 0, | ||
| // No spans in light mode | ||
| // means we don't have pass modules to detect OTel double-wrap | ||
| spans: false, | ||
| errorMonitor, | ||
| errorMonitor | ||
| }); | ||
| subscribe('http.server.request.start', onHttpServerRequestStart); | ||
| // Subscribe on the request creation in node versions that support it | ||
| subscribe(HTTP_ON_SERVER_REQUEST, onHttpServerRequestStart); | ||
| subscribe(HTTP_ON_CLIENT_REQUEST, onHttpClientRequestCreated); | ||
| // fall back to just doing breadcrumbs on the request.end() channel | ||
| // if we do not have earlier access to the request object at creation | ||
| // time. The http.client.request.error channel is only available on | ||
| // the same node versions as client.request.created, so no help. | ||
| if (_options.breadcrumbs && !FULLY_SUPPORTS_HTTP_DIAGNOSTICS_CHANNEL) { | ||
| subscribe('http.client.request.start', (data) => { | ||
| const { request } = data ; | ||
| request.on(errorMonitor, () => onOutgoingResponseFinish(request, undefined, _options)); | ||
| request.prependListener('response', response => { | ||
| if (request.listenerCount('response') <= 1) { | ||
| subscribe("http.client.request.start", (data) => { | ||
| const { request } = data; | ||
| request.on(errorMonitor, () => onOutgoingResponseFinish(request, void 0, _options)); | ||
| request.prependListener("response", (response) => { | ||
| if (request.listenerCount("response") <= 1) { | ||
| response.resume(); | ||
@@ -72,17 +50,9 @@ } | ||
| } | ||
| }, | ||
| } | ||
| }; | ||
| }) ; | ||
| function onOutgoingResponseFinish( | ||
| request, | ||
| response, | ||
| options | ||
| , | ||
| ) { | ||
| }); | ||
| function onOutgoingResponseFinish(request, response, options) { | ||
| if (!options.breadcrumbs) { | ||
| return; | ||
| } | ||
| // Check if tracing is suppressed (e.g. for Sentry's own transport requests) | ||
| if (getCurrentScope().getScopeData().sdkProcessingMetadata[SUPPRESS_TRACING_KEY]) { | ||
@@ -93,4 +63,4 @@ return; | ||
| if (ignoreOutgoingRequests) { | ||
| const url = getRequestUrlFromClientRequest(request ); | ||
| if (ignoreOutgoingRequests(url, getRequestOptions(request ))) { | ||
| const url = getRequestUrlFromClientRequest(request); | ||
| if (ignoreOutgoingRequests(url, getRequestOptions(request))) { | ||
| return; | ||
@@ -101,100 +71,5 @@ } | ||
| } | ||
| const httpIntegration = _httpIntegration; | ||
| /** | ||
| * This integration handles incoming and outgoing HTTP requests in light mode (without OpenTelemetry). | ||
| * | ||
| * It uses Node's native diagnostics channels (Node.js 22+) for request isolation, | ||
| * trace propagation, and breadcrumb creation. | ||
| */ | ||
| const httpIntegration = _httpIntegration | ||
| ; | ||
| /** | ||
| * Instrument a server to capture incoming requests. | ||
| */ | ||
| function instrumentServer( | ||
| server, | ||
| { | ||
| ignoreRequestBody, | ||
| maxRequestBodySize, | ||
| } | ||
| , | ||
| ) { | ||
| // eslint-disable-next-line @typescript-eslint/unbound-method | ||
| const originalEmit = server.emit; | ||
| if (wrappedEmitFns.has(originalEmit)) { | ||
| return; | ||
| } | ||
| const newEmit = new Proxy(originalEmit, { | ||
| apply(target, thisArg, args) { | ||
| // Only handle request events | ||
| if (args[0] !== 'request') { | ||
| return target.apply(thisArg, args); | ||
| } | ||
| const client = getCurrentScope().getClient(); | ||
| if (!client) { | ||
| return target.apply(thisArg, args); | ||
| } | ||
| DEBUG_BUILD && debug.log(INTEGRATION_NAME, 'Handling incoming request'); | ||
| const isolationScope = getIsolationScope().clone(); | ||
| const request = args[1] ; | ||
| const normalizedRequest = httpRequestToRequestData(request); | ||
| // request.ip is non-standard but some frameworks set this | ||
| const ipAddress = (request ).ip || request.socket?.remoteAddress; | ||
| const url = request.url || '/'; | ||
| if (maxRequestBodySize !== 'none' && !ignoreRequestBody?.(url, request)) { | ||
| patchRequestToCaptureBody(request, isolationScope, maxRequestBodySize, INTEGRATION_NAME); | ||
| } | ||
| // Update the isolation scope, isolate this request | ||
| isolationScope.setSDKProcessingMetadata({ normalizedRequest, ipAddress }); | ||
| // attempt to update the scope's `transactionName` based on the request URL | ||
| // Ideally, framework instrumentations coming after the HttpInstrumentation | ||
| // update the transactionName once we get a parameterized route. | ||
| const httpMethod = (request.method || 'GET').toUpperCase(); | ||
| const httpTargetWithoutQueryFragment = stripUrlQueryAndFragment(url); | ||
| const bestEffortTransactionName = `${httpMethod} ${httpTargetWithoutQueryFragment}`; | ||
| isolationScope.setTransactionName(bestEffortTransactionName); | ||
| return withIsolationScope(isolationScope, () => { | ||
| // Handle trace propagation using Sentry's continueTrace | ||
| // This replaces OpenTelemetry's propagation.extract() + context.with() | ||
| const sentryTrace = normalizedRequest.headers?.['sentry-trace']; | ||
| const baggage = normalizedRequest.headers?.['baggage']; | ||
| return continueTrace( | ||
| { | ||
| sentryTrace: Array.isArray(sentryTrace) ? sentryTrace[0] : sentryTrace, | ||
| baggage: Array.isArray(baggage) ? baggage[0] : baggage, | ||
| }, | ||
| () => { | ||
| // Set propagationSpanId after continueTrace because it calls withScope + | ||
| // setPropagationContext internally, which would overwrite any previously set value. | ||
| getCurrentScope().getPropagationContext().propagationSpanId = generateSpanId(); | ||
| return target.apply(thisArg, args); | ||
| }, | ||
| ); | ||
| }); | ||
| }, | ||
| }); | ||
| wrappedEmitFns.add(newEmit); | ||
| server.emit = newEmit; | ||
| } | ||
| export { httpIntegration }; | ||
| //# sourceMappingURL=httpIntegration.js.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"httpIntegration.js","sources":["../../../../src/light/integrations/httpIntegration.ts"],"sourcesContent":["import { subscribe } from 'node:diagnostics_channel';\nimport type { RequestOptions } from 'node:http';\nimport type { HttpClientRequest, HttpIncomingMessage, Integration, IntegrationFn } from '@sentry/core';\nimport {\n addOutgoingRequestBreadcrumb,\n continueTrace,\n debug,\n generateSpanId,\n getCurrentScope,\n getHttpClientSubscriptions,\n getIsolationScope,\n HTTP_ON_CLIENT_REQUEST,\n httpRequestToRequestData,\n stripUrlQueryAndFragment,\n SUPPRESS_TRACING_KEY,\n withIsolationScope,\n getRequestOptions,\n getRequestUrlFromClientRequest,\n} from '@sentry/core';\nimport type { ClientRequest, IncomingMessage, Server } from 'node:http';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport { patchRequestToCaptureBody } from '../../utils/captureRequestBody';\nimport type { LightNodeClient } from '../client';\nimport { errorMonitor } from 'node:events';\nimport { NODE_VERSION } from '../../nodeVersion';\n\nconst INTEGRATION_NAME = 'Http';\n\nconst FULLY_SUPPORTS_HTTP_DIAGNOSTICS_CHANNEL =\n (NODE_VERSION.major === 22 && NODE_VERSION.minor >= 12) ||\n (NODE_VERSION.major === 23 && NODE_VERSION.minor >= 2) ||\n NODE_VERSION.major >= 24;\n\n// We keep track of emit functions we wrapped, to avoid double wrapping\nconst wrappedEmitFns = new WeakSet<typeof Server.prototype.emit>();\n\nexport interface HttpIntegrationOptions {\n /**\n * Do not capture the request body for incoming HTTP requests to URLs where the given callback returns `true`.\n * This can be useful for long running requests where the body is not needed and we want to avoid capturing it.\n *\n * @param url Contains the entire URL, including query string (if any), protocol, host, etc. of the incoming request.\n * @param request Contains the {@type RequestOptions} object used to make the incoming request.\n */\n ignoreRequestBody?: (url: string, request: RequestOptions) => boolean;\n\n /**\n * Controls the maximum size of incoming HTTP request bodies attached to events.\n *\n * Available options:\n * - 'none': No request bodies will be attached\n * - 'small': Request bodies up to 1,000 bytes will be attached\n * - 'medium': Request bodies up to 10,000 bytes will be attached (default)\n * - 'always': Request bodies will always be attached\n *\n * Note that even with 'always' setting, bodies exceeding 1MB will never be attached\n * for performance and security reasons.\n *\n * @default 'medium'\n */\n maxRequestBodySize?: 'none' | 'small' | 'medium' | 'always';\n\n /**\n * Whether breadcrumbs should be recorded for outgoing requests.\n *\n * @default `true`\n */\n breadcrumbs?: boolean;\n\n /**\n * Whether to inject trace propagation headers (sentry-trace, baggage, traceparent) into outgoing HTTP requests.\n *\n * When set to `false`, Sentry will not inject any trace propagation headers, but will still create breadcrumbs\n * (if `breadcrumbs` is enabled).\n *\n * @default `true`\n */\n tracePropagation?: boolean;\n\n /**\n * Do not capture breadcrumbs or propagate trace headers for outgoing HTTP requests to URLs\n * where the given callback returns `true`.\n *\n * @param url Contains the entire URL, including query string (if any), protocol, host, etc. of the outgoing request.\n * @param request Contains the {@type RequestOptions} object used to make the outgoing request.\n */\n ignoreOutgoingRequests?: (url: string, request: RequestOptions) => boolean;\n}\n\nconst _httpIntegration = ((options: HttpIntegrationOptions = {}) => {\n const _options = {\n ...options,\n sessions: false,\n maxRequestBodySize: options.maxRequestBodySize ?? 'medium',\n ignoreRequestBody: options.ignoreRequestBody,\n breadcrumbs: options.breadcrumbs ?? true,\n tracePropagation: options.tracePropagation ?? true,\n ignoreOutgoingRequests: options.ignoreOutgoingRequests,\n };\n\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n const onHttpServerRequestStart = (_data: unknown) => {\n const data = _data as { server: Server };\n instrumentServer(data.server, _options);\n };\n\n const { ignoreOutgoingRequests } = _options;\n\n const { [HTTP_ON_CLIENT_REQUEST]: onHttpClientRequestCreated } = getHttpClientSubscriptions({\n breadcrumbs: _options.breadcrumbs,\n propagateTrace: _options.tracePropagation,\n ignoreOutgoingRequests: ignoreOutgoingRequests\n ? (url, request) => ignoreOutgoingRequests(url, getRequestOptions(request as ClientRequest))\n : undefined,\n // No spans in light mode\n // means we don't have pass modules to detect OTel double-wrap\n spans: false,\n errorMonitor,\n });\n\n subscribe('http.server.request.start', onHttpServerRequestStart);\n\n // Subscribe on the request creation in node versions that support it\n subscribe(HTTP_ON_CLIENT_REQUEST, onHttpClientRequestCreated);\n\n // fall back to just doing breadcrumbs on the request.end() channel\n // if we do not have earlier access to the request object at creation\n // time. The http.client.request.error channel is only available on\n // the same node versions as client.request.created, so no help.\n if (_options.breadcrumbs && !FULLY_SUPPORTS_HTTP_DIAGNOSTICS_CHANNEL) {\n subscribe('http.client.request.start', (data: unknown) => {\n const { request } = data as { request: HttpClientRequest };\n request.on(errorMonitor, () => onOutgoingResponseFinish(request, undefined, _options));\n request.prependListener('response', response => {\n if (request.listenerCount('response') <= 1) {\n response.resume();\n }\n onOutgoingResponseFinish(request, response, _options);\n });\n });\n }\n },\n };\n}) satisfies IntegrationFn;\n\nfunction onOutgoingResponseFinish(\n request: HttpClientRequest,\n response: HttpIncomingMessage | undefined,\n options: {\n breadcrumbs: boolean;\n ignoreOutgoingRequests?: (url: string, request: RequestOptions) => boolean;\n },\n): void {\n if (!options.breadcrumbs) {\n return;\n }\n // Check if tracing is suppressed (e.g. for Sentry's own transport requests)\n if (getCurrentScope().getScopeData().sdkProcessingMetadata[SUPPRESS_TRACING_KEY]) {\n return;\n }\n const { ignoreOutgoingRequests } = options;\n if (ignoreOutgoingRequests) {\n const url = getRequestUrlFromClientRequest(request as ClientRequest);\n if (ignoreOutgoingRequests(url, getRequestOptions(request as ClientRequest))) {\n return;\n }\n }\n addOutgoingRequestBreadcrumb(request, response);\n}\n\n/**\n * This integration handles incoming and outgoing HTTP requests in light mode (without OpenTelemetry).\n *\n * It uses Node's native diagnostics channels (Node.js 22+) for request isolation,\n * trace propagation, and breadcrumb creation.\n */\nexport const httpIntegration = _httpIntegration as (options?: HttpIntegrationOptions) => Integration & {\n name: 'Http';\n setupOnce: () => void;\n};\n\n/**\n * Instrument a server to capture incoming requests.\n */\nfunction instrumentServer(\n server: Server,\n {\n ignoreRequestBody,\n maxRequestBodySize,\n }: {\n ignoreRequestBody?: (url: string, request: IncomingMessage) => boolean;\n maxRequestBodySize: 'small' | 'medium' | 'always' | 'none';\n },\n): void {\n // eslint-disable-next-line @typescript-eslint/unbound-method\n const originalEmit: typeof Server.prototype.emit = server.emit;\n\n if (wrappedEmitFns.has(originalEmit)) {\n return;\n }\n\n const newEmit = new Proxy(originalEmit, {\n apply(target, thisArg, args: [event: string, ...args: unknown[]]) {\n // Only handle request events\n if (args[0] !== 'request') {\n return target.apply(thisArg, args);\n }\n\n const client = getCurrentScope().getClient<LightNodeClient>();\n\n if (!client) {\n return target.apply(thisArg, args);\n }\n\n DEBUG_BUILD && debug.log(INTEGRATION_NAME, 'Handling incoming request');\n\n const isolationScope = getIsolationScope().clone();\n const request = args[1] as IncomingMessage;\n\n const normalizedRequest = httpRequestToRequestData(request);\n\n // request.ip is non-standard but some frameworks set this\n const ipAddress = (request as { ip?: string }).ip || request.socket?.remoteAddress;\n\n const url = request.url || '/';\n if (maxRequestBodySize !== 'none' && !ignoreRequestBody?.(url, request)) {\n patchRequestToCaptureBody(request, isolationScope, maxRequestBodySize, INTEGRATION_NAME);\n }\n\n // Update the isolation scope, isolate this request\n isolationScope.setSDKProcessingMetadata({ normalizedRequest, ipAddress });\n\n // attempt to update the scope's `transactionName` based on the request URL\n // Ideally, framework instrumentations coming after the HttpInstrumentation\n // update the transactionName once we get a parameterized route.\n const httpMethod = (request.method || 'GET').toUpperCase();\n const httpTargetWithoutQueryFragment = stripUrlQueryAndFragment(url);\n\n const bestEffortTransactionName = `${httpMethod} ${httpTargetWithoutQueryFragment}`;\n\n isolationScope.setTransactionName(bestEffortTransactionName);\n\n return withIsolationScope(isolationScope, () => {\n // Handle trace propagation using Sentry's continueTrace\n // This replaces OpenTelemetry's propagation.extract() + context.with()\n const sentryTrace = normalizedRequest.headers?.['sentry-trace'];\n const baggage = normalizedRequest.headers?.['baggage'];\n\n return continueTrace(\n {\n sentryTrace: Array.isArray(sentryTrace) ? sentryTrace[0] : sentryTrace,\n baggage: Array.isArray(baggage) ? baggage[0] : baggage,\n },\n () => {\n // Set propagationSpanId after continueTrace because it calls withScope +\n // setPropagationContext internally, which would overwrite any previously set value.\n getCurrentScope().getPropagationContext().propagationSpanId = generateSpanId();\n return target.apply(thisArg, args);\n },\n );\n });\n },\n });\n\n wrappedEmitFns.add(newEmit);\n server.emit = newEmit;\n}\n"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"httpIntegration.js","sources":["../../../../src/light/integrations/httpIntegration.ts"],"sourcesContent":["import { subscribe } from 'node:diagnostics_channel';\nimport type { RequestOptions } from 'node:http';\nimport type { HttpClientRequest, HttpIncomingMessage, Integration, IntegrationFn } from '@sentry/core';\nimport {\n addOutgoingRequestBreadcrumb,\n getCurrentScope,\n getHttpClientSubscriptions,\n getHttpServerSubscriptions,\n getRequestOptions,\n getRequestUrlFromClientRequest,\n HTTP_ON_CLIENT_REQUEST,\n HTTP_ON_SERVER_REQUEST,\n SUPPRESS_TRACING_KEY,\n} from '@sentry/core';\nimport type { ClientRequest } from 'node:http';\nimport { errorMonitor } from 'node:events';\nimport { NODE_VERSION } from '../../nodeVersion';\n\nconst INTEGRATION_NAME = 'Http';\n\nconst FULLY_SUPPORTS_HTTP_DIAGNOSTICS_CHANNEL =\n (NODE_VERSION.major === 22 && NODE_VERSION.minor >= 12) ||\n (NODE_VERSION.major === 23 && NODE_VERSION.minor >= 2) ||\n NODE_VERSION.major >= 24;\n\nexport interface HttpIntegrationOptions {\n /**\n * Do not capture the request body for incoming HTTP requests to URLs where the given callback returns `true`.\n * This can be useful for long running requests where the body is not needed and we want to avoid capturing it.\n *\n * @param url Contains the entire URL, including query string (if any), protocol, host, etc. of the incoming request.\n * @param request Contains the {@type RequestOptions} object used to make the incoming request.\n */\n ignoreRequestBody?: (url: string, request: RequestOptions) => boolean;\n\n /**\n * Controls the maximum size of incoming HTTP request bodies attached to events.\n *\n * Available options:\n * - 'none': No request bodies will be attached\n * - 'small': Request bodies up to 1,000 bytes will be attached\n * - 'medium': Request bodies up to 10,000 bytes will be attached (default)\n * - 'always': Request bodies will always be attached\n *\n * Note that even with 'always' setting, bodies exceeding 1MB will never be attached\n * for performance and security reasons.\n *\n * @default 'medium'\n */\n maxRequestBodySize?: 'none' | 'small' | 'medium' | 'always';\n\n /**\n * Whether breadcrumbs should be recorded for outgoing requests.\n *\n * @default `true`\n */\n breadcrumbs?: boolean;\n\n /**\n * Whether to inject trace propagation headers (sentry-trace, baggage, traceparent) into outgoing HTTP requests.\n *\n * When set to `false`, Sentry will not inject any trace propagation headers, but will still create breadcrumbs\n * (if `breadcrumbs` is enabled).\n *\n * @default `true`\n */\n tracePropagation?: boolean;\n\n /**\n * Do not capture breadcrumbs or propagate trace headers for outgoing HTTP requests to URLs\n * where the given callback returns `true`.\n *\n * @param url Contains the entire URL, including query string (if any), protocol, host, etc. of the outgoing request.\n * @param request Contains the {@type RequestOptions} object used to make the outgoing request.\n */\n ignoreOutgoingRequests?: (url: string, request: RequestOptions) => boolean;\n}\n\nconst _httpIntegration = ((options: HttpIntegrationOptions = {}) => {\n const _options = {\n ...options,\n sessions: false,\n maxRequestBodySize: options.maxRequestBodySize ?? 'medium',\n ignoreRequestBody: options.ignoreRequestBody,\n breadcrumbs: options.breadcrumbs ?? true,\n tracePropagation: options.tracePropagation ?? true,\n ignoreOutgoingRequests: options.ignoreOutgoingRequests,\n // no spans created in light mode\n spans: false,\n errorMonitor,\n };\n\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n const { [HTTP_ON_SERVER_REQUEST]: onHttpServerRequestStart } = getHttpServerSubscriptions(_options);\n\n const { ignoreOutgoingRequests } = _options;\n\n const { [HTTP_ON_CLIENT_REQUEST]: onHttpClientRequestCreated } = getHttpClientSubscriptions({\n breadcrumbs: _options.breadcrumbs,\n propagateTrace: _options.tracePropagation,\n ignoreOutgoingRequests: ignoreOutgoingRequests\n ? (url, request) => ignoreOutgoingRequests(url, getRequestOptions(request as ClientRequest))\n : undefined,\n // No spans in light mode\n // means we don't have pass modules to detect OTel double-wrap\n spans: false,\n errorMonitor,\n });\n\n subscribe(HTTP_ON_SERVER_REQUEST, onHttpServerRequestStart);\n\n // Subscribe on the request creation in node versions that support it\n subscribe(HTTP_ON_CLIENT_REQUEST, onHttpClientRequestCreated);\n\n // fall back to just doing breadcrumbs on the request.end() channel\n // if we do not have earlier access to the request object at creation\n // time. The http.client.request.error channel is only available on\n // the same node versions as client.request.created, so no help.\n if (_options.breadcrumbs && !FULLY_SUPPORTS_HTTP_DIAGNOSTICS_CHANNEL) {\n subscribe('http.client.request.start', (data: unknown) => {\n const { request } = data as { request: HttpClientRequest };\n request.on(errorMonitor, () => onOutgoingResponseFinish(request, undefined, _options));\n request.prependListener('response', response => {\n if (request.listenerCount('response') <= 1) {\n response.resume();\n }\n onOutgoingResponseFinish(request, response, _options);\n });\n });\n }\n },\n };\n}) satisfies IntegrationFn;\n\nfunction onOutgoingResponseFinish(\n request: HttpClientRequest,\n response: HttpIncomingMessage | undefined,\n options: {\n breadcrumbs: boolean;\n ignoreOutgoingRequests?: (url: string, request: RequestOptions) => boolean;\n },\n): void {\n if (!options.breadcrumbs) {\n return;\n }\n // Check if tracing is suppressed (e.g. for Sentry's own transport requests)\n if (getCurrentScope().getScopeData().sdkProcessingMetadata[SUPPRESS_TRACING_KEY]) {\n return;\n }\n const { ignoreOutgoingRequests } = options;\n if (ignoreOutgoingRequests) {\n const url = getRequestUrlFromClientRequest(request as ClientRequest);\n if (ignoreOutgoingRequests(url, getRequestOptions(request as ClientRequest))) {\n return;\n }\n }\n addOutgoingRequestBreadcrumb(request, response);\n}\n\n/**\n * This integration handles incoming and outgoing HTTP requests in light mode (without OpenTelemetry).\n *\n * It uses Node's native diagnostics channels (Node.js 22+) for request isolation,\n * trace propagation, and breadcrumb creation.\n */\nexport const httpIntegration = _httpIntegration as (options?: HttpIntegrationOptions) => Integration & {\n name: 'Http';\n setupOnce: () => void;\n};\n"],"names":[],"mappings":"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"} |
@@ -5,4 +5,3 @@ import { subscribe } from 'node:diagnostics_channel'; | ||
| const INTEGRATION_NAME = 'NodeFetch'; | ||
| const INTEGRATION_NAME = "NodeFetch"; | ||
| const _nativeNodeFetchIntegration = ((options = {}) => { | ||
@@ -12,8 +11,6 @@ const _options = { | ||
| tracePropagation: options.tracePropagation ?? true, | ||
| ignoreOutgoingRequests: options.ignoreOutgoingRequests, | ||
| ignoreOutgoingRequests: options.ignoreOutgoingRequests | ||
| }; | ||
| const propagationDecisionMap = new LRUMap(100); | ||
| const ignoreOutgoingRequestsMap = new WeakMap(); | ||
| const ignoreOutgoingRequestsMap = /* @__PURE__ */ new WeakMap(); | ||
| return { | ||
@@ -23,38 +20,21 @@ name: INTEGRATION_NAME, | ||
| const onRequestCreated = ((_data) => { | ||
| const data = _data ; | ||
| const data = _data; | ||
| onUndiciRequestCreated(data.request, _options, propagationDecisionMap, ignoreOutgoingRequestsMap); | ||
| }) ; | ||
| }); | ||
| const onResponseHeaders = ((_data) => { | ||
| const data = _data ; | ||
| const data = _data; | ||
| onUndiciResponseHeaders(data.request, data.response, _options, ignoreOutgoingRequestsMap); | ||
| }) ; | ||
| subscribe('undici:request:create', onRequestCreated); | ||
| subscribe('undici:request:headers', onResponseHeaders); | ||
| }, | ||
| }); | ||
| subscribe("undici:request:create", onRequestCreated); | ||
| subscribe("undici:request:headers", onResponseHeaders); | ||
| } | ||
| }; | ||
| }) ; | ||
| /** | ||
| * This integration handles outgoing fetch (undici) requests in light mode (without OpenTelemetry). | ||
| * It propagates trace headers and creates breadcrumbs for responses. | ||
| */ | ||
| const nativeNodeFetchIntegration = _nativeNodeFetchIntegration | ||
| ; | ||
| function onUndiciRequestCreated( | ||
| request, | ||
| options, | ||
| propagationDecisionMap, | ||
| ignoreOutgoingRequestsMap, | ||
| ) { | ||
| }); | ||
| const nativeNodeFetchIntegration = _nativeNodeFetchIntegration; | ||
| function onUndiciRequestCreated(request, options, propagationDecisionMap, ignoreOutgoingRequestsMap) { | ||
| const shouldIgnore = shouldIgnoreRequest(request, options); | ||
| ignoreOutgoingRequestsMap.set(request, shouldIgnore); | ||
| if (shouldIgnore) { | ||
| return; | ||
| } | ||
| if (options.tracePropagation) { | ||
@@ -64,13 +44,6 @@ addTracePropagationHeadersToFetchRequest(request, propagationDecisionMap); | ||
| } | ||
| function onUndiciResponseHeaders( | ||
| request, | ||
| response, | ||
| options, | ||
| ignoreOutgoingRequestsMap, | ||
| ) { | ||
| function onUndiciResponseHeaders(request, response, options, ignoreOutgoingRequestsMap) { | ||
| if (!options.breadcrumbs) { | ||
| return; | ||
| } | ||
| const shouldIgnore = ignoreOutgoingRequestsMap.get(request); | ||
@@ -80,22 +53,12 @@ if (shouldIgnore) { | ||
| } | ||
| addFetchRequestBreadcrumb(request, response); | ||
| } | ||
| /** Check if the given outgoing request should be ignored. */ | ||
| function shouldIgnoreRequest( | ||
| request, | ||
| options, | ||
| ) { | ||
| // Check if tracing is suppressed (e.g. for Sentry's own transport requests) | ||
| function shouldIgnoreRequest(request, options) { | ||
| if (getCurrentScope().getScopeData().sdkProcessingMetadata.__SENTRY_SUPPRESS_TRACING__) { | ||
| return true; | ||
| } | ||
| const { ignoreOutgoingRequests } = options; | ||
| if (!ignoreOutgoingRequests) { | ||
| return false; | ||
| } | ||
| const url = getAbsoluteUrl(request.origin, request.path); | ||
@@ -102,0 +65,0 @@ return ignoreOutgoingRequests(url); |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"nativeNodeFetchIntegration.js","sources":["../../../../src/light/integrations/nativeNodeFetchIntegration.ts"],"sourcesContent":["import type { ChannelListener } from 'node:diagnostics_channel';\nimport { subscribe } from 'node:diagnostics_channel';\nimport type { Integration, IntegrationFn } from '@sentry/core';\nimport { getCurrentScope, LRUMap } from '@sentry/core';\nimport type { UndiciRequest, UndiciResponse } from '../../integrations/node-fetch/types';\nimport {\n addFetchRequestBreadcrumb,\n addTracePropagationHeadersToFetchRequest,\n getAbsoluteUrl,\n} from '../../utils/outgoingFetchRequest';\n\nconst INTEGRATION_NAME = 'NodeFetch';\n\nexport interface NativeNodeFetchIntegrationOptions {\n /**\n * Whether breadcrumbs should be recorded for requests.\n *\n * @default `true`\n */\n breadcrumbs?: boolean;\n\n /**\n * Whether to inject trace propagation headers (sentry-trace, baggage, traceparent) into outgoing fetch requests.\n *\n * When set to `false`, Sentry will not inject any trace propagation headers, but will still create breadcrumbs\n * (if `breadcrumbs` is enabled).\n *\n * @default `true`\n */\n tracePropagation?: boolean;\n\n /**\n * Do not capture breadcrumbs or inject headers for outgoing fetch requests to URLs\n * where the given callback returns `true`.\n *\n * @param url Contains the entire URL, including query string (if any), protocol, host, etc. of the outgoing request.\n */\n ignoreOutgoingRequests?: (url: string) => boolean;\n}\n\nconst _nativeNodeFetchIntegration = ((options: NativeNodeFetchIntegrationOptions = {}) => {\n const _options = {\n breadcrumbs: options.breadcrumbs ?? true,\n tracePropagation: options.tracePropagation ?? true,\n ignoreOutgoingRequests: options.ignoreOutgoingRequests,\n };\n\n const propagationDecisionMap = new LRUMap<string, boolean>(100);\n const ignoreOutgoingRequestsMap = new WeakMap<UndiciRequest, boolean>();\n\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n const onRequestCreated = ((_data: unknown) => {\n const data = _data as { request: UndiciRequest };\n onUndiciRequestCreated(data.request, _options, propagationDecisionMap, ignoreOutgoingRequestsMap);\n }) satisfies ChannelListener;\n\n const onResponseHeaders = ((_data: unknown) => {\n const data = _data as { request: UndiciRequest; response: UndiciResponse };\n onUndiciResponseHeaders(data.request, data.response, _options, ignoreOutgoingRequestsMap);\n }) satisfies ChannelListener;\n\n subscribe('undici:request:create', onRequestCreated);\n subscribe('undici:request:headers', onResponseHeaders);\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * This integration handles outgoing fetch (undici) requests in light mode (without OpenTelemetry).\n * It propagates trace headers and creates breadcrumbs for responses.\n */\nexport const nativeNodeFetchIntegration = _nativeNodeFetchIntegration as (\n options?: NativeNodeFetchIntegrationOptions,\n) => Integration & {\n name: 'NodeFetch';\n setupOnce: () => void;\n};\n\nfunction onUndiciRequestCreated(\n request: UndiciRequest,\n options: { tracePropagation: boolean; ignoreOutgoingRequests?: (url: string) => boolean },\n propagationDecisionMap: LRUMap<string, boolean>,\n ignoreOutgoingRequestsMap: WeakMap<UndiciRequest, boolean>,\n): void {\n const shouldIgnore = shouldIgnoreRequest(request, options);\n ignoreOutgoingRequestsMap.set(request, shouldIgnore);\n\n if (shouldIgnore) {\n return;\n }\n\n if (options.tracePropagation) {\n addTracePropagationHeadersToFetchRequest(request, propagationDecisionMap);\n }\n}\n\nfunction onUndiciResponseHeaders(\n request: UndiciRequest,\n response: UndiciResponse,\n options: { breadcrumbs: boolean },\n ignoreOutgoingRequestsMap: WeakMap<UndiciRequest, boolean>,\n): void {\n if (!options.breadcrumbs) {\n return;\n }\n\n const shouldIgnore = ignoreOutgoingRequestsMap.get(request);\n if (shouldIgnore) {\n return;\n }\n\n addFetchRequestBreadcrumb(request, response);\n}\n\n/** Check if the given outgoing request should be ignored. */\nfunction shouldIgnoreRequest(\n request: UndiciRequest,\n options: { ignoreOutgoingRequests?: (url: string) => boolean },\n): boolean {\n // Check if tracing is suppressed (e.g. for Sentry's own transport requests)\n if (getCurrentScope().getScopeData().sdkProcessingMetadata.__SENTRY_SUPPRESS_TRACING__) {\n return true;\n }\n\n const { ignoreOutgoingRequests } = options;\n\n if (!ignoreOutgoingRequests) {\n return false;\n }\n\n const url = getAbsoluteUrl(request.origin, request.path);\n return ignoreOutgoingRequests(url);\n}\n"],"names":[],"mappings":";;;;AAWA,MAAM,gBAAA,GAAmB,WAAW;;AA6BpC,MAAM,2BAAA,IAA+B,CAAC,OAAO,GAAsC,EAAE,KAAK;AAC1F,EAAE,MAAM,WAAW;AACnB,IAAI,WAAW,EAAE,OAAO,CAAC,WAAA,IAAe,IAAI;AAC5C,IAAI,gBAAgB,EAAE,OAAO,CAAC,gBAAA,IAAoB,IAAI;AACtD,IAAI,sBAAsB,EAAE,OAAO,CAAC,sBAAsB;AAC1D,GAAG;;AAEH,EAAE,MAAM,sBAAA,GAAyB,IAAI,MAAM,CAAkB,GAAG,CAAC;AACjE,EAAE,MAAM,yBAAA,GAA4B,IAAI,OAAO,EAA0B;;AAEzE,EAAE,OAAO;AACT,IAAI,IAAI,EAAE,gBAAgB;AAC1B,IAAI,SAAS,GAAG;AAChB,MAAM,MAAM,gBAAA,IAAoB,CAAC,KAAK,KAAc;AACpD,QAAQ,MAAM,IAAA,GAAO,KAAA;AACrB,QAAQ,sBAAsB,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,sBAAsB,EAAE,yBAAyB,CAAC;AACzG,MAAM,CAAC,CAAA;;AAEP,MAAM,MAAM,iBAAA,IAAqB,CAAC,KAAK,KAAc;AACrD,QAAQ,MAAM,IAAA,GAAO,KAAA;AACrB,QAAQ,uBAAuB,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,yBAAyB,CAAC;AACjG,MAAM,CAAC,CAAA;;AAEP,MAAM,SAAS,CAAC,uBAAuB,EAAE,gBAAgB,CAAC;AAC1D,MAAM,SAAS,CAAC,wBAAwB,EAAE,iBAAiB,CAAC;AAC5D,IAAI,CAAC;AACL,GAAG;AACH,CAAC,CAAA;;AAED;AACA;AACA;AACA;AACO,MAAM,0BAAA,GAA6B;;;;AAO1C,SAAS,sBAAsB;AAC/B,EAAE,OAAO;AACT,EAAE,OAAO;AACT,EAAE,sBAAsB;AACxB,EAAE,yBAAyB;AAC3B,EAAQ;AACR,EAAE,MAAM,eAAe,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC;AAC5D,EAAE,yBAAyB,CAAC,GAAG,CAAC,OAAO,EAAE,YAAY,CAAC;;AAEtD,EAAE,IAAI,YAAY,EAAE;AACpB,IAAI;AACJ,EAAE;;AAEF,EAAE,IAAI,OAAO,CAAC,gBAAgB,EAAE;AAChC,IAAI,wCAAwC,CAAC,OAAO,EAAE,sBAAsB,CAAC;AAC7E,EAAE;AACF;;AAEA,SAAS,uBAAuB;AAChC,EAAE,OAAO;AACT,EAAE,QAAQ;AACV,EAAE,OAAO;AACT,EAAE,yBAAyB;AAC3B,EAAQ;AACR,EAAE,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE;AAC5B,IAAI;AACJ,EAAE;;AAEF,EAAE,MAAM,eAAe,yBAAyB,CAAC,GAAG,CAAC,OAAO,CAAC;AAC7D,EAAE,IAAI,YAAY,EAAE;AACpB,IAAI;AACJ,EAAE;;AAEF,EAAE,yBAAyB,CAAC,OAAO,EAAE,QAAQ,CAAC;AAC9C;;AAEA;AACA,SAAS,mBAAmB;AAC5B,EAAE,OAAO;AACT,EAAE,OAAO;AACT,EAAW;AACX;AACA,EAAE,IAAI,eAAe,EAAE,CAAC,YAAY,EAAE,CAAC,qBAAqB,CAAC,2BAA2B,EAAE;AAC1F,IAAI,OAAO,IAAI;AACf,EAAE;;AAEF,EAAE,MAAM,EAAE,sBAAA,EAAuB,GAAI,OAAO;;AAE5C,EAAE,IAAI,CAAC,sBAAsB,EAAE;AAC/B,IAAI,OAAO,KAAK;AAChB,EAAE;;AAEF,EAAE,MAAM,GAAA,GAAM,cAAc,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC;AAC1D,EAAE,OAAO,sBAAsB,CAAC,GAAG,CAAC;AACpC;;;;"} | ||
| {"version":3,"file":"nativeNodeFetchIntegration.js","sources":["../../../../src/light/integrations/nativeNodeFetchIntegration.ts"],"sourcesContent":["import type { ChannelListener } from 'node:diagnostics_channel';\nimport { subscribe } from 'node:diagnostics_channel';\nimport type { Integration, IntegrationFn } from '@sentry/core';\nimport { getCurrentScope, LRUMap } from '@sentry/core';\nimport type { UndiciRequest, UndiciResponse } from '../../integrations/node-fetch/types';\nimport {\n addFetchRequestBreadcrumb,\n addTracePropagationHeadersToFetchRequest,\n getAbsoluteUrl,\n} from '../../utils/outgoingFetchRequest';\n\nconst INTEGRATION_NAME = 'NodeFetch';\n\nexport interface NativeNodeFetchIntegrationOptions {\n /**\n * Whether breadcrumbs should be recorded for requests.\n *\n * @default `true`\n */\n breadcrumbs?: boolean;\n\n /**\n * Whether to inject trace propagation headers (sentry-trace, baggage, traceparent) into outgoing fetch requests.\n *\n * When set to `false`, Sentry will not inject any trace propagation headers, but will still create breadcrumbs\n * (if `breadcrumbs` is enabled).\n *\n * @default `true`\n */\n tracePropagation?: boolean;\n\n /**\n * Do not capture breadcrumbs or inject headers for outgoing fetch requests to URLs\n * where the given callback returns `true`.\n *\n * @param url Contains the entire URL, including query string (if any), protocol, host, etc. of the outgoing request.\n */\n ignoreOutgoingRequests?: (url: string) => boolean;\n}\n\nconst _nativeNodeFetchIntegration = ((options: NativeNodeFetchIntegrationOptions = {}) => {\n const _options = {\n breadcrumbs: options.breadcrumbs ?? true,\n tracePropagation: options.tracePropagation ?? true,\n ignoreOutgoingRequests: options.ignoreOutgoingRequests,\n };\n\n const propagationDecisionMap = new LRUMap<string, boolean>(100);\n const ignoreOutgoingRequestsMap = new WeakMap<UndiciRequest, boolean>();\n\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n const onRequestCreated = ((_data: unknown) => {\n const data = _data as { request: UndiciRequest };\n onUndiciRequestCreated(data.request, _options, propagationDecisionMap, ignoreOutgoingRequestsMap);\n }) satisfies ChannelListener;\n\n const onResponseHeaders = ((_data: unknown) => {\n const data = _data as { request: UndiciRequest; response: UndiciResponse };\n onUndiciResponseHeaders(data.request, data.response, _options, ignoreOutgoingRequestsMap);\n }) satisfies ChannelListener;\n\n subscribe('undici:request:create', onRequestCreated);\n subscribe('undici:request:headers', onResponseHeaders);\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * This integration handles outgoing fetch (undici) requests in light mode (without OpenTelemetry).\n * It propagates trace headers and creates breadcrumbs for responses.\n */\nexport const nativeNodeFetchIntegration = _nativeNodeFetchIntegration as (\n options?: NativeNodeFetchIntegrationOptions,\n) => Integration & {\n name: 'NodeFetch';\n setupOnce: () => void;\n};\n\nfunction onUndiciRequestCreated(\n request: UndiciRequest,\n options: { tracePropagation: boolean; ignoreOutgoingRequests?: (url: string) => boolean },\n propagationDecisionMap: LRUMap<string, boolean>,\n ignoreOutgoingRequestsMap: WeakMap<UndiciRequest, boolean>,\n): void {\n const shouldIgnore = shouldIgnoreRequest(request, options);\n ignoreOutgoingRequestsMap.set(request, shouldIgnore);\n\n if (shouldIgnore) {\n return;\n }\n\n if (options.tracePropagation) {\n addTracePropagationHeadersToFetchRequest(request, propagationDecisionMap);\n }\n}\n\nfunction onUndiciResponseHeaders(\n request: UndiciRequest,\n response: UndiciResponse,\n options: { breadcrumbs: boolean },\n ignoreOutgoingRequestsMap: WeakMap<UndiciRequest, boolean>,\n): void {\n if (!options.breadcrumbs) {\n return;\n }\n\n const shouldIgnore = ignoreOutgoingRequestsMap.get(request);\n if (shouldIgnore) {\n return;\n }\n\n addFetchRequestBreadcrumb(request, response);\n}\n\n/** Check if the given outgoing request should be ignored. */\nfunction shouldIgnoreRequest(\n request: UndiciRequest,\n options: { ignoreOutgoingRequests?: (url: string) => boolean },\n): boolean {\n // Check if tracing is suppressed (e.g. for Sentry's own transport requests)\n if (getCurrentScope().getScopeData().sdkProcessingMetadata.__SENTRY_SUPPRESS_TRACING__) {\n return true;\n }\n\n const { ignoreOutgoingRequests } = options;\n\n if (!ignoreOutgoingRequests) {\n return false;\n }\n\n const url = getAbsoluteUrl(request.origin, request.path);\n return ignoreOutgoingRequests(url);\n}\n"],"names":[],"mappings":";;;;AAWA,MAAM,gBAAA,GAAmB,WAAA;AA6BzB,MAAM,2BAAA,IAA+B,CAAC,OAAA,GAA6C,EAAC,KAAM;AACxF,EAAA,MAAM,QAAA,GAAW;AAAA,IACf,WAAA,EAAa,QAAQ,WAAA,IAAe,IAAA;AAAA,IACpC,gBAAA,EAAkB,QAAQ,gBAAA,IAAoB,IAAA;AAAA,IAC9C,wBAAwB,OAAA,CAAQ;AAAA,GAClC;AAEA,EAAA,MAAM,sBAAA,GAAyB,IAAI,MAAA,CAAwB,GAAG,CAAA;AAC9D,EAAA,MAAM,yBAAA,uBAAgC,OAAA,EAAgC;AAEtE,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,SAAA,GAAY;AACV,MAAA,MAAM,gBAAA,IAAoB,CAAC,KAAA,KAAmB;AAC5C,QAAA,MAAM,IAAA,GAAO,KAAA;AACb,QAAA,sBAAA,CAAuB,IAAA,CAAK,OAAA,EAAS,QAAA,EAAU,sBAAA,EAAwB,yBAAyB,CAAA;AAAA,MAClG,CAAA,CAAA;AAEA,MAAA,MAAM,iBAAA,IAAqB,CAAC,KAAA,KAAmB;AAC7C,QAAA,MAAM,IAAA,GAAO,KAAA;AACb,QAAA,uBAAA,CAAwB,IAAA,CAAK,OAAA,EAAS,IAAA,CAAK,QAAA,EAAU,UAAU,yBAAyB,CAAA;AAAA,MAC1F,CAAA,CAAA;AAEA,MAAA,SAAA,CAAU,yBAAyB,gBAAgB,CAAA;AACnD,MAAA,SAAA,CAAU,0BAA0B,iBAAiB,CAAA;AAAA,IACvD;AAAA,GACF;AACF,CAAA,CAAA;AAMO,MAAM,0BAAA,GAA6B;AAO1C,SAAS,sBAAA,CACP,OAAA,EACA,OAAA,EACA,sBAAA,EACA,yBAAA,EACM;AACN,EAAA,MAAM,YAAA,GAAe,mBAAA,CAAoB,OAAA,EAAS,OAAO,CAAA;AACzD,EAAA,yBAAA,CAA0B,GAAA,CAAI,SAAS,YAAY,CAAA;AAEnD,EAAA,IAAI,YAAA,EAAc;AAChB,IAAA;AAAA,EACF;AAEA,EAAA,IAAI,QAAQ,gBAAA,EAAkB;AAC5B,IAAA,wCAAA,CAAyC,SAAS,sBAAsB,CAAA;AAAA,EAC1E;AACF;AAEA,SAAS,uBAAA,CACP,OAAA,EACA,QAAA,EACA,OAAA,EACA,yBAAA,EACM;AACN,EAAA,IAAI,CAAC,QAAQ,WAAA,EAAa;AACxB,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,YAAA,GAAe,yBAAA,CAA0B,GAAA,CAAI,OAAO,CAAA;AAC1D,EAAA,IAAI,YAAA,EAAc;AAChB,IAAA;AAAA,EACF;AAEA,EAAA,yBAAA,CAA0B,SAAS,QAAQ,CAAA;AAC7C;AAGA,SAAS,mBAAA,CACP,SACA,OAAA,EACS;AAET,EAAA,IAAI,eAAA,EAAgB,CAAE,YAAA,EAAa,CAAE,sBAAsB,2BAAA,EAA6B;AACtF,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,MAAM,EAAE,wBAAuB,GAAI,OAAA;AAEnC,EAAA,IAAI,CAAC,sBAAA,EAAwB;AAC3B,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,MAAM,GAAA,GAAM,cAAA,CAAe,OAAA,CAAQ,MAAA,EAAQ,QAAQ,IAAI,CAAA;AACvD,EAAA,OAAO,uBAAuB,GAAG,CAAA;AACnC;;;;"} |
@@ -6,23 +6,17 @@ import { trace } from '@opentelemetry/api'; | ||
| const INTEGRATION_NAME = 'OtlpIntegration'; | ||
| const INTEGRATION_NAME = "OtlpIntegration"; | ||
| const _otlpIntegration = ((userOptions = {}) => { | ||
| const options = { | ||
| setupOtlpTracesExporter: userOptions.setupOtlpTracesExporter ?? true, | ||
| collectorUrl: userOptions.collectorUrl, | ||
| collectorUrl: userOptions.collectorUrl | ||
| }; | ||
| let _spanProcessor; | ||
| let _tracerProvider; | ||
| return { | ||
| name: INTEGRATION_NAME, | ||
| setup(_client) { | ||
| // Always register external propagation context so that Sentry error/log events | ||
| // are linked to the active OTel trace context. | ||
| registerExternalPropagationContext(() => { | ||
| const activeSpan = trace.getActiveSpan(); | ||
| if (!activeSpan) { | ||
| return undefined; | ||
| return void 0; | ||
| } | ||
@@ -32,6 +26,4 @@ const spanContext = activeSpan.spanContext(); | ||
| }); | ||
| debug.log(`[${INTEGRATION_NAME}] External propagation context registered.`); | ||
| }, | ||
| afterAllSetup(client) { | ||
@@ -41,9 +33,7 @@ if (options.setupOtlpTracesExporter) { | ||
| } | ||
| }, | ||
| } | ||
| }; | ||
| function setupTracesExporter(client) { | ||
| let endpoint; | ||
| let headers; | ||
| if (options.collectorUrl) { | ||
@@ -58,16 +48,12 @@ endpoint = options.collectorUrl; | ||
| } | ||
| const { protocol, host, port, path, projectId, publicKey } = dsn; | ||
| const basePath = path ? `/${path}` : ''; | ||
| const portStr = port ? `:${port}` : ''; | ||
| const basePath = path ? `/${path}` : ""; | ||
| const portStr = port ? `:${port}` : ""; | ||
| endpoint = `${protocol}://${host}${portStr}${basePath}/api/${projectId}/integration/otlp/v1/traces/`; | ||
| const sdkInfo = client.getSdkMetadata()?.sdk; | ||
| const sentryClient = sdkInfo ? `, sentry_client=${sdkInfo.name}/${sdkInfo.version}` : ''; | ||
| const sentryClient = sdkInfo ? `, sentry_client=${sdkInfo.name}/${sdkInfo.version}` : ""; | ||
| headers = { | ||
| 'X-Sentry-Auth': `Sentry sentry_version=${SENTRY_API_VERSION}, sentry_key=${publicKey}${sentryClient}`, | ||
| "X-Sentry-Auth": `Sentry sentry_version=${SENTRY_API_VERSION}, sentry_key=${publicKey}${sentryClient}` | ||
| }; | ||
| } | ||
| let exporter; | ||
@@ -77,3 +63,3 @@ try { | ||
| url: endpoint, | ||
| headers, | ||
| headers | ||
| }); | ||
@@ -84,18 +70,6 @@ } catch (e) { | ||
| } | ||
| _spanProcessor = new BatchSpanProcessor(exporter); | ||
| // Add span processor to existing global tracer provider. | ||
| // trace.getTracerProvider() returns a ProxyTracerProvider; unwrap it to get the real provider. | ||
| const globalProvider = trace.getTracerProvider(); | ||
| const delegate = | ||
| 'getDelegate' in globalProvider | ||
| ? (globalProvider ).getDelegate() | ||
| : globalProvider; | ||
| // In OTel v2, addSpanProcessor was removed. We push into the internal _spanProcessors | ||
| // array on the MultiSpanProcessor, which is how OTel's own forceFlush() accesses it. | ||
| const activeProcessor = (delegate )?._activeSpanProcessor | ||
| ; | ||
| const delegate = "getDelegate" in globalProvider ? globalProvider.getDelegate() : globalProvider; | ||
| const activeProcessor = delegate?._activeSpanProcessor; | ||
| if (activeProcessor?._spanProcessors) { | ||
@@ -105,5 +79,4 @@ activeProcessor._spanProcessors.push(_spanProcessor); | ||
| } else { | ||
| // No user-configured provider; create a minimal one and set it as global | ||
| _tracerProvider = new BasicTracerProvider({ | ||
| spanProcessors: [_spanProcessor], | ||
| spanProcessors: [_spanProcessor] | ||
| }); | ||
@@ -113,8 +86,6 @@ trace.setGlobalTracerProvider(_tracerProvider); | ||
| } | ||
| client.on('flush', () => { | ||
| client.on("flush", () => { | ||
| void _spanProcessor?.forceFlush(); | ||
| }); | ||
| client.on('close', () => { | ||
| client.on("close", () => { | ||
| void _spanProcessor?.shutdown(); | ||
@@ -124,11 +95,3 @@ void _tracerProvider?.shutdown(); | ||
| } | ||
| }) ; | ||
| /** | ||
| * OTLP integration for the Sentry light SDK. | ||
| * | ||
| * Bridges an existing OpenTelemetry setup with Sentry by: | ||
| * 1. Linking Sentry error/log events to the active OTel trace context | ||
| * 2. Exporting OTel spans to Sentry via OTLP (or to a custom collector) | ||
| */ | ||
| }); | ||
| const otlpIntegration = defineIntegration(_otlpIntegration); | ||
@@ -135,0 +98,0 @@ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"otlpIntegration.js","sources":["../../../../src/light/integrations/otlpIntegration.ts"],"sourcesContent":["import { trace } from '@opentelemetry/api';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';\nimport type { SpanExporter } from '@opentelemetry/sdk-trace-base';\nimport { BasicTracerProvider, BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';\nimport type { Client, IntegrationFn } from '@sentry/core';\nimport { debug, defineIntegration, registerExternalPropagationContext, SENTRY_API_VERSION } from '@sentry/core';\n\ninterface OtlpIntegrationOptions {\n /**\n * Whether to set up the OTLP traces exporter that sends spans to Sentry.\n * Default: true\n */\n setupOtlpTracesExporter?: boolean;\n\n /**\n * URL of your own OpenTelemetry collector.\n * When set, the exporter will send traces to this URL instead of the Sentry OTLP endpoint derived from the DSN.\n * Default: undefined (uses DSN-derived endpoint)\n */\n collectorUrl?: string;\n}\n\nconst INTEGRATION_NAME = 'OtlpIntegration';\n\nconst _otlpIntegration = ((userOptions: OtlpIntegrationOptions = {}) => {\n const options = {\n setupOtlpTracesExporter: userOptions.setupOtlpTracesExporter ?? true,\n collectorUrl: userOptions.collectorUrl,\n };\n\n let _spanProcessor: BatchSpanProcessor | undefined;\n let _tracerProvider: BasicTracerProvider | undefined;\n\n return {\n name: INTEGRATION_NAME,\n\n setup(_client: Client): void {\n // Always register external propagation context so that Sentry error/log events\n // are linked to the active OTel trace context.\n registerExternalPropagationContext(() => {\n const activeSpan = trace.getActiveSpan();\n if (!activeSpan) {\n return undefined;\n }\n const spanContext = activeSpan.spanContext();\n return { traceId: spanContext.traceId, spanId: spanContext.spanId };\n });\n\n debug.log(`[${INTEGRATION_NAME}] External propagation context registered.`);\n },\n\n afterAllSetup(client: Client): void {\n if (options.setupOtlpTracesExporter) {\n setupTracesExporter(client);\n }\n },\n };\n\n function setupTracesExporter(client: Client): void {\n let endpoint: string;\n let headers: Record<string, string> | undefined;\n\n if (options.collectorUrl) {\n endpoint = options.collectorUrl;\n debug.log(`[${INTEGRATION_NAME}] Sending traces to collector at ${endpoint}`);\n } else {\n const dsn = client.getDsn();\n if (!dsn) {\n debug.warn(`[${INTEGRATION_NAME}] No DSN found. OTLP exporter not set up.`);\n return;\n }\n\n const { protocol, host, port, path, projectId, publicKey } = dsn;\n\n const basePath = path ? `/${path}` : '';\n const portStr = port ? `:${port}` : '';\n endpoint = `${protocol}://${host}${portStr}${basePath}/api/${projectId}/integration/otlp/v1/traces/`;\n\n const sdkInfo = client.getSdkMetadata()?.sdk;\n const sentryClient = sdkInfo ? `, sentry_client=${sdkInfo.name}/${sdkInfo.version}` : '';\n headers = {\n 'X-Sentry-Auth': `Sentry sentry_version=${SENTRY_API_VERSION}, sentry_key=${publicKey}${sentryClient}`,\n };\n }\n\n let exporter: SpanExporter;\n try {\n exporter = new OTLPTraceExporter({\n url: endpoint,\n headers,\n });\n } catch (e) {\n debug.warn(`[${INTEGRATION_NAME}] Failed to create OTLPTraceExporter:`, e);\n return;\n }\n\n _spanProcessor = new BatchSpanProcessor(exporter);\n\n // Add span processor to existing global tracer provider.\n // trace.getTracerProvider() returns a ProxyTracerProvider; unwrap it to get the real provider.\n const globalProvider = trace.getTracerProvider();\n const delegate =\n 'getDelegate' in globalProvider\n ? (globalProvider as unknown as { getDelegate(): unknown }).getDelegate()\n : globalProvider;\n\n // In OTel v2, addSpanProcessor was removed. We push into the internal _spanProcessors\n // array on the MultiSpanProcessor, which is how OTel's own forceFlush() accesses it.\n const activeProcessor = (delegate as Record<string, unknown>)?._activeSpanProcessor as\n | { _spanProcessors?: unknown[] }\n | undefined;\n if (activeProcessor?._spanProcessors) {\n activeProcessor._spanProcessors.push(_spanProcessor);\n debug.log(`[${INTEGRATION_NAME}] Added span processor to existing TracerProvider.`);\n } else {\n // No user-configured provider; create a minimal one and set it as global\n _tracerProvider = new BasicTracerProvider({\n spanProcessors: [_spanProcessor],\n });\n trace.setGlobalTracerProvider(_tracerProvider);\n debug.log(`[${INTEGRATION_NAME}] Created new TracerProvider with OTLP span processor.`);\n }\n\n client.on('flush', () => {\n void _spanProcessor?.forceFlush();\n });\n\n client.on('close', () => {\n void _spanProcessor?.shutdown();\n void _tracerProvider?.shutdown();\n });\n }\n}) satisfies IntegrationFn;\n\n/**\n * OTLP integration for the Sentry light SDK.\n *\n * Bridges an existing OpenTelemetry setup with Sentry by:\n * 1. Linking Sentry error/log events to the active OTel trace context\n * 2. Exporting OTel spans to Sentry via OTLP (or to a custom collector)\n */\nexport const otlpIntegration = defineIntegration(_otlpIntegration);\n"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"otlpIntegration.js","sources":["../../../../src/light/integrations/otlpIntegration.ts"],"sourcesContent":["import { trace } from '@opentelemetry/api';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';\nimport type { SpanExporter } from '@opentelemetry/sdk-trace-base';\nimport { BasicTracerProvider, BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';\nimport type { Client, IntegrationFn } from '@sentry/core';\nimport { debug, defineIntegration, registerExternalPropagationContext, SENTRY_API_VERSION } from '@sentry/core';\n\ninterface OtlpIntegrationOptions {\n /**\n * Whether to set up the OTLP traces exporter that sends spans to Sentry.\n * Default: true\n */\n setupOtlpTracesExporter?: boolean;\n\n /**\n * URL of your own OpenTelemetry collector.\n * When set, the exporter will send traces to this URL instead of the Sentry OTLP endpoint derived from the DSN.\n * Default: undefined (uses DSN-derived endpoint)\n */\n collectorUrl?: string;\n}\n\nconst INTEGRATION_NAME = 'OtlpIntegration';\n\nconst _otlpIntegration = ((userOptions: OtlpIntegrationOptions = {}) => {\n const options = {\n setupOtlpTracesExporter: userOptions.setupOtlpTracesExporter ?? true,\n collectorUrl: userOptions.collectorUrl,\n };\n\n let _spanProcessor: BatchSpanProcessor | undefined;\n let _tracerProvider: BasicTracerProvider | undefined;\n\n return {\n name: INTEGRATION_NAME,\n\n setup(_client: Client): void {\n // Always register external propagation context so that Sentry error/log events\n // are linked to the active OTel trace context.\n registerExternalPropagationContext(() => {\n const activeSpan = trace.getActiveSpan();\n if (!activeSpan) {\n return undefined;\n }\n const spanContext = activeSpan.spanContext();\n return { traceId: spanContext.traceId, spanId: spanContext.spanId };\n });\n\n debug.log(`[${INTEGRATION_NAME}] External propagation context registered.`);\n },\n\n afterAllSetup(client: Client): void {\n if (options.setupOtlpTracesExporter) {\n setupTracesExporter(client);\n }\n },\n };\n\n function setupTracesExporter(client: Client): void {\n let endpoint: string;\n let headers: Record<string, string> | undefined;\n\n if (options.collectorUrl) {\n endpoint = options.collectorUrl;\n debug.log(`[${INTEGRATION_NAME}] Sending traces to collector at ${endpoint}`);\n } else {\n const dsn = client.getDsn();\n if (!dsn) {\n debug.warn(`[${INTEGRATION_NAME}] No DSN found. OTLP exporter not set up.`);\n return;\n }\n\n const { protocol, host, port, path, projectId, publicKey } = dsn;\n\n const basePath = path ? `/${path}` : '';\n const portStr = port ? `:${port}` : '';\n endpoint = `${protocol}://${host}${portStr}${basePath}/api/${projectId}/integration/otlp/v1/traces/`;\n\n const sdkInfo = client.getSdkMetadata()?.sdk;\n const sentryClient = sdkInfo ? `, sentry_client=${sdkInfo.name}/${sdkInfo.version}` : '';\n headers = {\n 'X-Sentry-Auth': `Sentry sentry_version=${SENTRY_API_VERSION}, sentry_key=${publicKey}${sentryClient}`,\n };\n }\n\n let exporter: SpanExporter;\n try {\n exporter = new OTLPTraceExporter({\n url: endpoint,\n headers,\n });\n } catch (e) {\n debug.warn(`[${INTEGRATION_NAME}] Failed to create OTLPTraceExporter:`, e);\n return;\n }\n\n _spanProcessor = new BatchSpanProcessor(exporter);\n\n // Add span processor to existing global tracer provider.\n // trace.getTracerProvider() returns a ProxyTracerProvider; unwrap it to get the real provider.\n const globalProvider = trace.getTracerProvider();\n const delegate =\n 'getDelegate' in globalProvider\n ? (globalProvider as unknown as { getDelegate(): unknown }).getDelegate()\n : globalProvider;\n\n // In OTel v2, addSpanProcessor was removed. We push into the internal _spanProcessors\n // array on the MultiSpanProcessor, which is how OTel's own forceFlush() accesses it.\n const activeProcessor = (delegate as Record<string, unknown>)?._activeSpanProcessor as\n | { _spanProcessors?: unknown[] }\n | undefined;\n if (activeProcessor?._spanProcessors) {\n activeProcessor._spanProcessors.push(_spanProcessor);\n debug.log(`[${INTEGRATION_NAME}] Added span processor to existing TracerProvider.`);\n } else {\n // No user-configured provider; create a minimal one and set it as global\n _tracerProvider = new BasicTracerProvider({\n spanProcessors: [_spanProcessor],\n });\n trace.setGlobalTracerProvider(_tracerProvider);\n debug.log(`[${INTEGRATION_NAME}] Created new TracerProvider with OTLP span processor.`);\n }\n\n client.on('flush', () => {\n void _spanProcessor?.forceFlush();\n });\n\n client.on('close', () => {\n void _spanProcessor?.shutdown();\n void _tracerProvider?.shutdown();\n });\n }\n}) satisfies IntegrationFn;\n\n/**\n * OTLP integration for the Sentry light SDK.\n *\n * Bridges an existing OpenTelemetry setup with Sentry by:\n * 1. Linking Sentry error/log events to the active OTel trace context\n * 2. Exporting OTel spans to Sentry via OTLP (or to a custom collector)\n */\nexport const otlpIntegration = defineIntegration(_otlpIntegration);\n"],"names":[],"mappings":"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"} |
+19
-78
@@ -23,5 +23,2 @@ import { eventFiltersIntegration, functionToStringIntegration, linkedErrorsIntegration, requestDataIntegration, debug, consoleSandbox, getCurrentScope, applySdkMetadata, envToBool, stackParserFromStackParserOptions, getIntegrationsToSetup, spanStreamingIntegration, propagationContextFromHeaders } from '@sentry/core'; | ||
| /** | ||
| * Get default integrations for the Light Node-Core SDK. | ||
| */ | ||
| function getDefaultIntegrations() { | ||
@@ -48,29 +45,13 @@ return [ | ||
| processSessionIntegration(), | ||
| modulesIntegration(), | ||
| modulesIntegration() | ||
| ]; | ||
| } | ||
| /** | ||
| * Initialize Sentry for Node in light mode (without OpenTelemetry). | ||
| */ | ||
| function init(options = {}) { | ||
| return _init(options, getDefaultIntegrations); | ||
| } | ||
| /** | ||
| * Initialize Sentry for Node in light mode, without any integrations added by default. | ||
| */ | ||
| function initWithoutDefaultIntegrations(options = {}) { | ||
| return _init(options, () => []); | ||
| } | ||
| /** | ||
| * Initialize Sentry for Node in light mode. | ||
| */ | ||
| function _init( | ||
| _options = {}, | ||
| getDefaultIntegrationsImpl, | ||
| ) { | ||
| function _init(_options = {}, getDefaultIntegrationsImpl) { | ||
| const options = getClientOptions(_options, getDefaultIntegrationsImpl); | ||
| if (options.debug === true) { | ||
@@ -80,58 +61,35 @@ if (DEBUG_BUILD) { | ||
| } else { | ||
| // use `console.warn` rather than `debug.warn` since by non-debug bundles have all `debug.x` statements stripped | ||
| consoleSandbox(() => { | ||
| // eslint-disable-next-line no-console | ||
| console.warn('[Sentry] Cannot initialize SDK with `debug` option using a non-debug bundle.'); | ||
| console.warn("[Sentry] Cannot initialize SDK with `debug` option using a non-debug bundle."); | ||
| }); | ||
| } | ||
| } | ||
| // Use AsyncLocalStorage-based context strategy instead of OpenTelemetry | ||
| setAsyncLocalStorageAsyncContextStrategy(); | ||
| const scope = getCurrentScope(); | ||
| scope.update(options.initialScope); | ||
| if (options.spotlight && !options.integrations.some(({ name }) => name === INTEGRATION_NAME)) { | ||
| options.integrations.push( | ||
| spotlightIntegration({ | ||
| sidecarUrl: typeof options.spotlight === 'string' ? options.spotlight : undefined, | ||
| }), | ||
| sidecarUrl: typeof options.spotlight === "string" ? options.spotlight : void 0 | ||
| }) | ||
| ); | ||
| } | ||
| applySdkMetadata(options, 'node-light', ['node-core']); | ||
| applySdkMetadata(options, "node-light", ["node-core"]); | ||
| const client = new LightNodeClient(options); | ||
| // The client is on the current scope, from where it generally is inherited | ||
| getCurrentScope().setClient(client); | ||
| client.init(); | ||
| debug.log(`SDK initialized from ${isCjs() ? 'CommonJS' : 'ESM'} (light mode)`); | ||
| debug.log(`SDK initialized from ${isCjs() ? "CommonJS" : "ESM"} (light mode)`); | ||
| client.startClientReportTracking(); | ||
| updateScopeFromEnvVariables(); | ||
| // Ensure we flush events when vercel functions are ended | ||
| // See: https://vercel.com/docs/functions/functions-api-reference#sigterm-signal | ||
| if (process.env.VERCEL) { | ||
| process.on('SIGTERM', async () => { | ||
| // We have 500ms for processing here, so we try to make sure to have enough time to send the events | ||
| process.on("SIGTERM", async () => { | ||
| await client.flush(200); | ||
| }); | ||
| } | ||
| return client; | ||
| } | ||
| function getClientOptions( | ||
| options, | ||
| getDefaultIntegrationsImpl, | ||
| ) { | ||
| function getClientOptions(options, getDefaultIntegrationsImpl) { | ||
| const release = getRelease(options.release); | ||
| const spotlight = getSpotlightConfig(options.spotlight); | ||
| const tracesSampleRate = getTracesSampleRate(options.tracesSampleRate); | ||
| const mergedOptions = { | ||
@@ -147,56 +105,39 @@ ...options, | ||
| spotlight, | ||
| debug: envToBool(options.debug ?? process.env.SENTRY_DEBUG), | ||
| debug: envToBool(options.debug ?? process.env.SENTRY_DEBUG) | ||
| }; | ||
| const integrations = options.integrations; | ||
| const defaultIntegrations = options.defaultIntegrations ?? getDefaultIntegrationsImpl(mergedOptions); | ||
| const resolvedIntegrations = getIntegrationsToSetup({ | ||
| defaultIntegrations, | ||
| integrations, | ||
| integrations | ||
| }); | ||
| if (mergedOptions.traceLifecycle === 'stream' && !resolvedIntegrations.some(i => i.name === 'SpanStreaming')) { | ||
| if (mergedOptions.traceLifecycle === "stream" && !resolvedIntegrations.some((i) => i.name === "SpanStreaming")) { | ||
| resolvedIntegrations.push(spanStreamingIntegration()); | ||
| } | ||
| return { | ||
| ...mergedOptions, | ||
| integrations: resolvedIntegrations, | ||
| integrations: resolvedIntegrations | ||
| }; | ||
| } | ||
| function getRelease(release) { | ||
| if (release !== undefined) { | ||
| if (release !== void 0) { | ||
| return release; | ||
| } | ||
| const detectedRelease = getSentryRelease(); | ||
| if (detectedRelease !== undefined) { | ||
| if (detectedRelease !== void 0) { | ||
| return detectedRelease; | ||
| } | ||
| return undefined; | ||
| return void 0; | ||
| } | ||
| function getTracesSampleRate(tracesSampleRate) { | ||
| if (tracesSampleRate !== undefined) { | ||
| if (tracesSampleRate !== void 0) { | ||
| return tracesSampleRate; | ||
| } | ||
| const sampleRateFromEnv = process.env.SENTRY_TRACES_SAMPLE_RATE; | ||
| if (!sampleRateFromEnv) { | ||
| return undefined; | ||
| return void 0; | ||
| } | ||
| const parsed = parseFloat(sampleRateFromEnv); | ||
| return isFinite(parsed) ? parsed : undefined; | ||
| return isFinite(parsed) ? parsed : void 0; | ||
| } | ||
| /** | ||
| * Update scope and propagation context based on environmental variables. | ||
| * | ||
| * See https://github.com/getsentry/rfcs/blob/main/text/0071-continue-trace-over-process-boundaries.md | ||
| * for more details. | ||
| */ | ||
| function updateScopeFromEnvVariables() { | ||
@@ -203,0 +144,0 @@ if (envToBool(process.env.SENTRY_USE_ENVIRONMENT) !== false) { |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"sdk.js","sources":["../../../src/light/sdk.ts"],"sourcesContent":["import type { Integration, Options } from '@sentry/core';\nimport {\n applySdkMetadata,\n consoleSandbox,\n debug,\n envToBool,\n eventFiltersIntegration,\n functionToStringIntegration,\n getCurrentScope,\n getIntegrationsToSetup,\n linkedErrorsIntegration,\n propagationContextFromHeaders,\n requestDataIntegration,\n spanStreamingIntegration,\n stackParserFromStackParserOptions,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { childProcessIntegration } from '../integrations/childProcess';\nimport { nodeContextIntegration } from '../integrations/context';\nimport { contextLinesIntegration } from '../integrations/contextlines';\nimport { localVariablesIntegration } from '../integrations/local-variables';\nimport { modulesIntegration } from '../integrations/modules';\nimport { onUncaughtExceptionIntegration } from '../integrations/onuncaughtexception';\nimport { onUnhandledRejectionIntegration } from '../integrations/onunhandledrejection';\nimport { processSessionIntegration } from '../integrations/processSession';\nimport { INTEGRATION_NAME as SPOTLIGHT_INTEGRATION_NAME, spotlightIntegration } from '../integrations/spotlight';\nimport { consoleIntegration } from '../integrations/console';\nimport { systemErrorIntegration } from '../integrations/systemError';\nimport { defaultStackParser, getSentryRelease } from '../sdk/api';\nimport { makeNodeTransport } from '../transports';\nimport type { NodeClientOptions, NodeOptions } from '../types';\nimport { isCjs } from '../utils/detection';\nimport { getSpotlightConfig } from '../utils/spotlight';\nimport { setAsyncLocalStorageAsyncContextStrategy } from './asyncLocalStorageStrategy';\nimport { LightNodeClient } from './client';\nimport { httpIntegration } from './integrations/httpIntegration';\nimport { nativeNodeFetchIntegration } from './integrations/nativeNodeFetchIntegration';\n\n/**\n * Get default integrations for the Light Node-Core SDK.\n */\nexport function getDefaultIntegrations(): Integration[] {\n return [\n // Common\n eventFiltersIntegration(),\n functionToStringIntegration(),\n linkedErrorsIntegration(),\n requestDataIntegration(),\n systemErrorIntegration(),\n // Native Wrappers\n consoleIntegration(),\n httpIntegration(),\n nativeNodeFetchIntegration(),\n // Global Handlers\n onUncaughtExceptionIntegration(),\n onUnhandledRejectionIntegration(),\n // Event Info\n contextLinesIntegration(),\n localVariablesIntegration(),\n nodeContextIntegration(),\n childProcessIntegration(),\n processSessionIntegration(),\n modulesIntegration(),\n ];\n}\n\n/**\n * Initialize Sentry for Node in light mode (without OpenTelemetry).\n */\nexport function init(options: NodeOptions | undefined = {}): LightNodeClient | undefined {\n return _init(options, getDefaultIntegrations);\n}\n\n/**\n * Initialize Sentry for Node in light mode, without any integrations added by default.\n */\nexport function initWithoutDefaultIntegrations(options: NodeOptions | undefined = {}): LightNodeClient {\n return _init(options, () => []);\n}\n\n/**\n * Initialize Sentry for Node in light mode.\n */\nfunction _init(\n _options: NodeOptions | undefined = {},\n getDefaultIntegrationsImpl: (options: Options) => Integration[],\n): LightNodeClient {\n const options = getClientOptions(_options, getDefaultIntegrationsImpl);\n\n if (options.debug === true) {\n if (DEBUG_BUILD) {\n debug.enable();\n } else {\n // use `console.warn` rather than `debug.warn` since by non-debug bundles have all `debug.x` statements stripped\n consoleSandbox(() => {\n // eslint-disable-next-line no-console\n console.warn('[Sentry] Cannot initialize SDK with `debug` option using a non-debug bundle.');\n });\n }\n }\n\n // Use AsyncLocalStorage-based context strategy instead of OpenTelemetry\n setAsyncLocalStorageAsyncContextStrategy();\n\n const scope = getCurrentScope();\n scope.update(options.initialScope);\n\n if (options.spotlight && !options.integrations.some(({ name }) => name === SPOTLIGHT_INTEGRATION_NAME)) {\n options.integrations.push(\n spotlightIntegration({\n sidecarUrl: typeof options.spotlight === 'string' ? options.spotlight : undefined,\n }),\n );\n }\n\n applySdkMetadata(options, 'node-light', ['node-core']);\n\n const client = new LightNodeClient(options);\n // The client is on the current scope, from where it generally is inherited\n getCurrentScope().setClient(client);\n\n client.init();\n\n debug.log(`SDK initialized from ${isCjs() ? 'CommonJS' : 'ESM'} (light mode)`);\n\n client.startClientReportTracking();\n\n updateScopeFromEnvVariables();\n\n // Ensure we flush events when vercel functions are ended\n // See: https://vercel.com/docs/functions/functions-api-reference#sigterm-signal\n if (process.env.VERCEL) {\n process.on('SIGTERM', async () => {\n // We have 500ms for processing here, so we try to make sure to have enough time to send the events\n await client.flush(200);\n });\n }\n\n return client;\n}\n\nfunction getClientOptions(\n options: NodeOptions,\n getDefaultIntegrationsImpl: (options: Options) => Integration[],\n): NodeClientOptions {\n const release = getRelease(options.release);\n const spotlight = getSpotlightConfig(options.spotlight);\n const tracesSampleRate = getTracesSampleRate(options.tracesSampleRate);\n\n const mergedOptions = {\n ...options,\n dsn: options.dsn ?? process.env.SENTRY_DSN,\n environment: options.environment ?? process.env.SENTRY_ENVIRONMENT,\n sendClientReports: options.sendClientReports ?? true,\n transport: options.transport ?? makeNodeTransport,\n stackParser: stackParserFromStackParserOptions(options.stackParser || defaultStackParser),\n release,\n tracesSampleRate,\n spotlight,\n debug: envToBool(options.debug ?? process.env.SENTRY_DEBUG),\n };\n\n const integrations = options.integrations;\n const defaultIntegrations = options.defaultIntegrations ?? getDefaultIntegrationsImpl(mergedOptions);\n\n const resolvedIntegrations = getIntegrationsToSetup({\n defaultIntegrations,\n integrations,\n });\n\n if (mergedOptions.traceLifecycle === 'stream' && !resolvedIntegrations.some(i => i.name === 'SpanStreaming')) {\n resolvedIntegrations.push(spanStreamingIntegration());\n }\n\n return {\n ...mergedOptions,\n integrations: resolvedIntegrations,\n };\n}\n\nfunction getRelease(release: NodeOptions['release']): string | undefined {\n if (release !== undefined) {\n return release;\n }\n\n const detectedRelease = getSentryRelease();\n if (detectedRelease !== undefined) {\n return detectedRelease;\n }\n\n return undefined;\n}\n\nfunction getTracesSampleRate(tracesSampleRate: NodeOptions['tracesSampleRate']): number | undefined {\n if (tracesSampleRate !== undefined) {\n return tracesSampleRate;\n }\n\n const sampleRateFromEnv = process.env.SENTRY_TRACES_SAMPLE_RATE;\n if (!sampleRateFromEnv) {\n return undefined;\n }\n\n const parsed = parseFloat(sampleRateFromEnv);\n return isFinite(parsed) ? parsed : undefined;\n}\n\n/**\n * Update scope and propagation context based on environmental variables.\n *\n * See https://github.com/getsentry/rfcs/blob/main/text/0071-continue-trace-over-process-boundaries.md\n * for more details.\n */\nfunction updateScopeFromEnvVariables(): void {\n if (envToBool(process.env.SENTRY_USE_ENVIRONMENT) !== false) {\n const sentryTraceEnv = process.env.SENTRY_TRACE;\n const baggageEnv = process.env.SENTRY_BAGGAGE;\n const propagationContext = propagationContextFromHeaders(sentryTraceEnv, baggageEnv);\n getCurrentScope().setPropagationContext(propagationContext);\n }\n}\n"],"names":["SPOTLIGHT_INTEGRATION_NAME"],"mappings":"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"} | ||
| {"version":3,"file":"sdk.js","sources":["../../../src/light/sdk.ts"],"sourcesContent":["import type { Integration, Options } from '@sentry/core';\nimport {\n applySdkMetadata,\n consoleSandbox,\n debug,\n envToBool,\n eventFiltersIntegration,\n functionToStringIntegration,\n getCurrentScope,\n getIntegrationsToSetup,\n linkedErrorsIntegration,\n propagationContextFromHeaders,\n requestDataIntegration,\n spanStreamingIntegration,\n stackParserFromStackParserOptions,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { childProcessIntegration } from '../integrations/childProcess';\nimport { nodeContextIntegration } from '../integrations/context';\nimport { contextLinesIntegration } from '../integrations/contextlines';\nimport { localVariablesIntegration } from '../integrations/local-variables';\nimport { modulesIntegration } from '../integrations/modules';\nimport { onUncaughtExceptionIntegration } from '../integrations/onuncaughtexception';\nimport { onUnhandledRejectionIntegration } from '../integrations/onunhandledrejection';\nimport { processSessionIntegration } from '../integrations/processSession';\nimport { INTEGRATION_NAME as SPOTLIGHT_INTEGRATION_NAME, spotlightIntegration } from '../integrations/spotlight';\nimport { consoleIntegration } from '../integrations/console';\nimport { systemErrorIntegration } from '../integrations/systemError';\nimport { defaultStackParser, getSentryRelease } from '../sdk/api';\nimport { makeNodeTransport } from '../transports';\nimport type { NodeClientOptions, NodeOptions } from '../types';\nimport { isCjs } from '../utils/detection';\nimport { getSpotlightConfig } from '../utils/spotlight';\nimport { setAsyncLocalStorageAsyncContextStrategy } from './asyncLocalStorageStrategy';\nimport { LightNodeClient } from './client';\nimport { httpIntegration } from './integrations/httpIntegration';\nimport { nativeNodeFetchIntegration } from './integrations/nativeNodeFetchIntegration';\n\n/**\n * Get default integrations for the Light Node-Core SDK.\n */\nexport function getDefaultIntegrations(): Integration[] {\n return [\n // Common\n eventFiltersIntegration(),\n functionToStringIntegration(),\n linkedErrorsIntegration(),\n requestDataIntegration(),\n systemErrorIntegration(),\n // Native Wrappers\n consoleIntegration(),\n httpIntegration(),\n nativeNodeFetchIntegration(),\n // Global Handlers\n onUncaughtExceptionIntegration(),\n onUnhandledRejectionIntegration(),\n // Event Info\n contextLinesIntegration(),\n localVariablesIntegration(),\n nodeContextIntegration(),\n childProcessIntegration(),\n processSessionIntegration(),\n modulesIntegration(),\n ];\n}\n\n/**\n * Initialize Sentry for Node in light mode (without OpenTelemetry).\n */\nexport function init(options: NodeOptions | undefined = {}): LightNodeClient | undefined {\n return _init(options, getDefaultIntegrations);\n}\n\n/**\n * Initialize Sentry for Node in light mode, without any integrations added by default.\n */\nexport function initWithoutDefaultIntegrations(options: NodeOptions | undefined = {}): LightNodeClient {\n return _init(options, () => []);\n}\n\n/**\n * Initialize Sentry for Node in light mode.\n */\nfunction _init(\n _options: NodeOptions | undefined = {},\n getDefaultIntegrationsImpl: (options: Options) => Integration[],\n): LightNodeClient {\n const options = getClientOptions(_options, getDefaultIntegrationsImpl);\n\n if (options.debug === true) {\n if (DEBUG_BUILD) {\n debug.enable();\n } else {\n // use `console.warn` rather than `debug.warn` since by non-debug bundles have all `debug.x` statements stripped\n consoleSandbox(() => {\n // eslint-disable-next-line no-console\n console.warn('[Sentry] Cannot initialize SDK with `debug` option using a non-debug bundle.');\n });\n }\n }\n\n // Use AsyncLocalStorage-based context strategy instead of OpenTelemetry\n setAsyncLocalStorageAsyncContextStrategy();\n\n const scope = getCurrentScope();\n scope.update(options.initialScope);\n\n if (options.spotlight && !options.integrations.some(({ name }) => name === SPOTLIGHT_INTEGRATION_NAME)) {\n options.integrations.push(\n spotlightIntegration({\n sidecarUrl: typeof options.spotlight === 'string' ? options.spotlight : undefined,\n }),\n );\n }\n\n applySdkMetadata(options, 'node-light', ['node-core']);\n\n const client = new LightNodeClient(options);\n // The client is on the current scope, from where it generally is inherited\n getCurrentScope().setClient(client);\n\n client.init();\n\n debug.log(`SDK initialized from ${isCjs() ? 'CommonJS' : 'ESM'} (light mode)`);\n\n client.startClientReportTracking();\n\n updateScopeFromEnvVariables();\n\n // Ensure we flush events when vercel functions are ended\n // See: https://vercel.com/docs/functions/functions-api-reference#sigterm-signal\n if (process.env.VERCEL) {\n process.on('SIGTERM', async () => {\n // We have 500ms for processing here, so we try to make sure to have enough time to send the events\n await client.flush(200);\n });\n }\n\n return client;\n}\n\nfunction getClientOptions(\n options: NodeOptions,\n getDefaultIntegrationsImpl: (options: Options) => Integration[],\n): NodeClientOptions {\n const release = getRelease(options.release);\n const spotlight = getSpotlightConfig(options.spotlight);\n const tracesSampleRate = getTracesSampleRate(options.tracesSampleRate);\n\n const mergedOptions = {\n ...options,\n dsn: options.dsn ?? process.env.SENTRY_DSN,\n environment: options.environment ?? process.env.SENTRY_ENVIRONMENT,\n sendClientReports: options.sendClientReports ?? true,\n transport: options.transport ?? makeNodeTransport,\n stackParser: stackParserFromStackParserOptions(options.stackParser || defaultStackParser),\n release,\n tracesSampleRate,\n spotlight,\n debug: envToBool(options.debug ?? process.env.SENTRY_DEBUG),\n };\n\n const integrations = options.integrations;\n const defaultIntegrations = options.defaultIntegrations ?? getDefaultIntegrationsImpl(mergedOptions);\n\n const resolvedIntegrations = getIntegrationsToSetup({\n defaultIntegrations,\n integrations,\n });\n\n if (mergedOptions.traceLifecycle === 'stream' && !resolvedIntegrations.some(i => i.name === 'SpanStreaming')) {\n resolvedIntegrations.push(spanStreamingIntegration());\n }\n\n return {\n ...mergedOptions,\n integrations: resolvedIntegrations,\n };\n}\n\nfunction getRelease(release: NodeOptions['release']): string | undefined {\n if (release !== undefined) {\n return release;\n }\n\n const detectedRelease = getSentryRelease();\n if (detectedRelease !== undefined) {\n return detectedRelease;\n }\n\n return undefined;\n}\n\nfunction getTracesSampleRate(tracesSampleRate: NodeOptions['tracesSampleRate']): number | undefined {\n if (tracesSampleRate !== undefined) {\n return tracesSampleRate;\n }\n\n const sampleRateFromEnv = process.env.SENTRY_TRACES_SAMPLE_RATE;\n if (!sampleRateFromEnv) {\n return undefined;\n }\n\n const parsed = parseFloat(sampleRateFromEnv);\n return isFinite(parsed) ? parsed : undefined;\n}\n\n/**\n * Update scope and propagation context based on environmental variables.\n *\n * See https://github.com/getsentry/rfcs/blob/main/text/0071-continue-trace-over-process-boundaries.md\n * for more details.\n */\nfunction updateScopeFromEnvVariables(): void {\n if (envToBool(process.env.SENTRY_USE_ENVIRONMENT) !== false) {\n const sentryTraceEnv = process.env.SENTRY_TRACE;\n const baggageEnv = process.env.SENTRY_BAGGAGE;\n const propagationContext = propagationContextFromHeaders(sentryTraceEnv, baggageEnv);\n getCurrentScope().setPropagationContext(propagationContext);\n }\n}\n"],"names":["SPOTLIGHT_INTEGRATION_NAME"],"mappings":"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"} |
| import { format } from 'node:util'; | ||
| import { _INTERNAL_captureLog } from '@sentry/core'; | ||
| /** | ||
| * Additional metadata to capture the log with. | ||
| */ | ||
| /** | ||
| * Capture a log with the given level. | ||
| * | ||
| * @param level - The level of the log. | ||
| * @param message - The message to log. | ||
| * @param attributes - Arbitrary structured data that stores information about the log - e.g., userId: 100. | ||
| */ | ||
| function captureLog(level, ...args) { | ||
| const [messageOrMessageTemplate, paramsOrAttributes, maybeAttributesOrMetadata, maybeMetadata] = args; | ||
| if (Array.isArray(paramsOrAttributes)) { | ||
| // type-casting here because from the type definitions we know that `maybeAttributesOrMetadata` is an attributes object (or undefined) | ||
| const attributes = { ...(maybeAttributesOrMetadata ) }; | ||
| attributes['sentry.message.template'] = messageOrMessageTemplate; | ||
| const attributes = { ...maybeAttributesOrMetadata }; | ||
| attributes["sentry.message.template"] = messageOrMessageTemplate; | ||
| paramsOrAttributes.forEach((param, index) => { | ||
@@ -30,3 +18,3 @@ attributes[`sentry.message.parameter.${index}`] = param; | ||
| // type-casting here because from the type definitions we know that `maybeAttributesOrMetadata` is a metadata object (or undefined) | ||
| (maybeAttributesOrMetadata )?.scope ?? maybeMetadata?.scope, | ||
| maybeAttributesOrMetadata?.scope ?? maybeMetadata?.scope | ||
| ); | ||
@@ -33,0 +21,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"capture.js","sources":["../../../src/logs/capture.ts"],"sourcesContent":["import { format } from 'node:util';\nimport type { Log, LogSeverityLevel, ParameterizedString, Scope } from '@sentry/core';\nimport { _INTERNAL_captureLog } from '@sentry/core';\n\n/**\n * Additional metadata to capture the log with.\n */\ninterface CaptureLogMetadata {\n scope?: Scope;\n}\n\ntype CaptureLogArgWithTemplate = [\n messageTemplate: string,\n messageParams: Array<unknown>,\n attributes?: Log['attributes'],\n metadata?: CaptureLogMetadata,\n];\n\ntype CaptureLogArgWithoutTemplate = [\n message: ParameterizedString,\n attributes?: Log['attributes'],\n metadata?: CaptureLogMetadata,\n];\n\nexport type CaptureLogArgs = CaptureLogArgWithTemplate | CaptureLogArgWithoutTemplate;\n\n/**\n * Capture a log with the given level.\n *\n * @param level - The level of the log.\n * @param message - The message to log.\n * @param attributes - Arbitrary structured data that stores information about the log - e.g., userId: 100.\n */\nexport function captureLog(level: LogSeverityLevel, ...args: CaptureLogArgs): void {\n const [messageOrMessageTemplate, paramsOrAttributes, maybeAttributesOrMetadata, maybeMetadata] = args;\n if (Array.isArray(paramsOrAttributes)) {\n // type-casting here because from the type definitions we know that `maybeAttributesOrMetadata` is an attributes object (or undefined)\n const attributes = { ...(maybeAttributesOrMetadata as Log['attributes'] | undefined) };\n attributes['sentry.message.template'] = messageOrMessageTemplate;\n paramsOrAttributes.forEach((param, index) => {\n attributes[`sentry.message.parameter.${index}`] = param;\n });\n const message = format(messageOrMessageTemplate, ...paramsOrAttributes);\n _INTERNAL_captureLog({ level, message, attributes }, maybeMetadata?.scope);\n } else {\n _INTERNAL_captureLog(\n { level, message: messageOrMessageTemplate, attributes: paramsOrAttributes },\n // type-casting here because from the type definitions we know that `maybeAttributesOrMetadata` is a metadata object (or undefined)\n (maybeAttributesOrMetadata as CaptureLogMetadata | undefined)?.scope ?? maybeMetadata?.scope,\n );\n }\n}\n"],"names":[],"mappings":";;;AAIA;AACA;AACA;;AAoBA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,UAAU,CAAC,KAAK,EAAoB,GAAG,IAAI,EAAwB;AACnF,EAAE,MAAM,CAAC,wBAAwB,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,aAAa,CAAA,GAAI,IAAI;AACvG,EAAE,IAAI,KAAK,CAAC,OAAO,CAAC,kBAAkB,CAAC,EAAE;AACzC;AACA,IAAI,MAAM,aAAa,EAAE,IAAI,yBAAA,IAA6D;AAC1F,IAAI,UAAU,CAAC,yBAAyB,CAAA,GAAI,wBAAwB;AACpE,IAAI,kBAAkB,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK;AACjD,MAAM,UAAU,CAAC,CAAC,yBAAyB,EAAE,KAAK,CAAC,CAAA,CAAA,GAAA,KAAA;AACA,IAAA,CAAA,CAAA;AACA,IAAA,MAAA,OAAA,GAAA,MAAA,CAAA,wBAAA,EAAA,GAAA,kBAAA,CAAA;AACA,IAAA,oBAAA,CAAA,EAAA,KAAA,EAAA,OAAA,EAAA,UAAA,EAAA,EAAA,aAAA,EAAA,KAAA,CAAA;AACA,EAAA,CAAA,MAAA;AACA,IAAA,oBAAA;AACA,MAAA,EAAA,KAAA,EAAA,OAAA,EAAA,wBAAA,EAAA,UAAA,EAAA,kBAAA,EAAA;AACA;AACA,MAAA,CAAA,yBAAA,IAAA,KAAA,IAAA,aAAA,EAAA,KAAA;AACA,KAAA;AACA,EAAA;AACA;;;;"} | ||
| {"version":3,"file":"capture.js","sources":["../../../src/logs/capture.ts"],"sourcesContent":["import { format } from 'node:util';\nimport type { Log, LogSeverityLevel, ParameterizedString, Scope } from '@sentry/core';\nimport { _INTERNAL_captureLog } from '@sentry/core';\n\n/**\n * Additional metadata to capture the log with.\n */\ninterface CaptureLogMetadata {\n scope?: Scope;\n}\n\ntype CaptureLogArgWithTemplate = [\n messageTemplate: string,\n messageParams: Array<unknown>,\n attributes?: Log['attributes'],\n metadata?: CaptureLogMetadata,\n];\n\ntype CaptureLogArgWithoutTemplate = [\n message: ParameterizedString,\n attributes?: Log['attributes'],\n metadata?: CaptureLogMetadata,\n];\n\nexport type CaptureLogArgs = CaptureLogArgWithTemplate | CaptureLogArgWithoutTemplate;\n\n/**\n * Capture a log with the given level.\n *\n * @param level - The level of the log.\n * @param message - The message to log.\n * @param attributes - Arbitrary structured data that stores information about the log - e.g., userId: 100.\n */\nexport function captureLog(level: LogSeverityLevel, ...args: CaptureLogArgs): void {\n const [messageOrMessageTemplate, paramsOrAttributes, maybeAttributesOrMetadata, maybeMetadata] = args;\n if (Array.isArray(paramsOrAttributes)) {\n // type-casting here because from the type definitions we know that `maybeAttributesOrMetadata` is an attributes object (or undefined)\n const attributes = { ...(maybeAttributesOrMetadata as Log['attributes'] | undefined) };\n attributes['sentry.message.template'] = messageOrMessageTemplate;\n paramsOrAttributes.forEach((param, index) => {\n attributes[`sentry.message.parameter.${index}`] = param;\n });\n const message = format(messageOrMessageTemplate, ...paramsOrAttributes);\n _INTERNAL_captureLog({ level, message, attributes }, maybeMetadata?.scope);\n } else {\n _INTERNAL_captureLog(\n { level, message: messageOrMessageTemplate, attributes: paramsOrAttributes },\n // type-casting here because from the type definitions we know that `maybeAttributesOrMetadata` is a metadata object (or undefined)\n (maybeAttributesOrMetadata as CaptureLogMetadata | undefined)?.scope ?? maybeMetadata?.scope,\n );\n }\n}\n"],"names":[],"mappings":";;;AAiCO,SAAS,UAAA,CAAW,UAA4B,IAAA,EAA4B;AACjF,EAAA,MAAM,CAAC,wBAAA,EAA0B,kBAAA,EAAoB,yBAAA,EAA2B,aAAa,CAAA,GAAI,IAAA;AACjG,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,kBAAkB,CAAA,EAAG;AAErC,IAAA,MAAM,UAAA,GAAa,EAAE,GAAI,yBAAA,EAA4D;AACrF,IAAA,UAAA,CAAW,yBAAyB,CAAA,GAAI,wBAAA;AACxC,IAAA,kBAAA,CAAmB,OAAA,CAAQ,CAAC,KAAA,EAAO,KAAA,KAAU;AAC3C,MAAA,UAAA,CAAW,CAAA,yBAAA,EAA4B,KAAK,CAAA,CAAE,CAAA,GAAI,KAAA;AAAA,IACpD,CAAC,CAAA;AACD,IAAA,MAAM,OAAA,GAAU,MAAA,CAAO,wBAAA,EAA0B,GAAG,kBAAkB,CAAA;AACtE,IAAA,oBAAA,CAAqB,EAAE,KAAA,EAAO,OAAA,EAAS,UAAA,EAAW,EAAG,eAAe,KAAK,CAAA;AAAA,EAC3E,CAAA,MAAO;AACL,IAAA,oBAAA;AAAA,MACE,EAAE,KAAA,EAAO,OAAA,EAAS,wBAAA,EAA0B,YAAY,kBAAA,EAAmB;AAAA;AAAA,MAE1E,yBAAA,EAA8D,SAAS,aAAA,EAAe;AAAA,KACzF;AAAA,EACF;AACF;;;;"} |
| import { captureLog } from './capture.js'; | ||
| export { fmt } from '@sentry/core'; | ||
| /** | ||
| * @summary Capture a log with the `trace` level. Requires the `enableLogs` option to be enabled. | ||
| * | ||
| * You can either pass a message and attributes or a message template, params and attributes. | ||
| * | ||
| * @example | ||
| * | ||
| * ``` | ||
| * Sentry.logger.trace('Starting database connection', { | ||
| * database: 'users', | ||
| * connectionId: 'conn_123' | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @example With template strings | ||
| * | ||
| * ``` | ||
| * Sentry.logger.trace('Database connection %s established for %s', | ||
| * ['successful', 'users'], | ||
| * { connectionId: 'conn_123' } | ||
| * ); | ||
| * ``` | ||
| */ | ||
| function trace(...args) { | ||
| captureLog('trace', ...args); | ||
| captureLog("trace", ...args); | ||
| } | ||
| /** | ||
| * @summary Capture a log with the `debug` level. Requires the `enableLogs` option to be enabled. | ||
| * | ||
| * You can either pass a message and attributes or a message template, params and attributes. | ||
| * | ||
| * @example | ||
| * | ||
| * ``` | ||
| * Sentry.logger.debug('Cache miss for user profile', { | ||
| * userId: 'user_123', | ||
| * cacheKey: 'profile:user_123' | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @example With template strings | ||
| * | ||
| * ``` | ||
| * Sentry.logger.debug('Cache %s for %s: %s', | ||
| * ['miss', 'user profile', 'key not found'], | ||
| * { userId: 'user_123' } | ||
| * ); | ||
| * ``` | ||
| */ | ||
| function debug(...args) { | ||
| captureLog('debug', ...args); | ||
| captureLog("debug", ...args); | ||
| } | ||
| /** | ||
| * @summary Capture a log with the `info` level. Requires the `enableLogs` option to be enabled. | ||
| * | ||
| * You can either pass a message and attributes or a message template, params and attributes. | ||
| * | ||
| * @example | ||
| * | ||
| * ``` | ||
| * Sentry.logger.info('User profile updated', { | ||
| * userId: 'user_123', | ||
| * updatedFields: ['email', 'preferences'] | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @example With template strings | ||
| * | ||
| * ``` | ||
| * Sentry.logger.info('User %s updated their %s', | ||
| * ['John Doe', 'profile settings'], | ||
| * { userId: 'user_123' } | ||
| * ); | ||
| * ``` | ||
| */ | ||
| function info(...args) { | ||
| captureLog('info', ...args); | ||
| captureLog("info", ...args); | ||
| } | ||
| /** | ||
| * @summary Capture a log with the `warn` level. Requires the `enableLogs` option to be enabled. | ||
| * | ||
| * You can either pass a message and attributes or a message template, params and attributes. | ||
| * | ||
| * @example | ||
| * | ||
| * ``` | ||
| * Sentry.logger.warn('Rate limit approaching', { | ||
| * endpoint: '/api/users', | ||
| * currentRate: '95/100', | ||
| * resetTime: '2024-03-20T10:00:00Z' | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @example With template strings | ||
| * | ||
| * ``` | ||
| * Sentry.logger.warn('Rate limit %s for %s: %s', | ||
| * ['approaching', '/api/users', '95/100 requests'], | ||
| * { resetTime: '2024-03-20T10:00:00Z' } | ||
| * ); | ||
| * ``` | ||
| */ | ||
| function warn(...args) { | ||
| captureLog('warn', ...args); | ||
| captureLog("warn", ...args); | ||
| } | ||
| /** | ||
| * @summary Capture a log with the `error` level. Requires the `enableLogs` option to be enabled. | ||
| * | ||
| * You can either pass a message and attributes or a message template, params and attributes. | ||
| * | ||
| * @example | ||
| * | ||
| * ``` | ||
| * Sentry.logger.error('Failed to process payment', { | ||
| * orderId: 'order_123', | ||
| * errorCode: 'PAYMENT_FAILED', | ||
| * amount: 99.99 | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @example With template strings | ||
| * | ||
| * ``` | ||
| * Sentry.logger.error('Payment processing failed for order %s: %s', | ||
| * ['order_123', 'insufficient funds'], | ||
| * { amount: 99.99 } | ||
| * ); | ||
| * ``` | ||
| */ | ||
| function error(...args) { | ||
| captureLog('error', ...args); | ||
| captureLog("error", ...args); | ||
| } | ||
| /** | ||
| * @summary Capture a log with the `fatal` level. Requires the `enableLogs` option to be enabled. | ||
| * | ||
| * You can either pass a message and attributes or a message template, params and attributes. | ||
| * | ||
| * @example | ||
| * | ||
| * ``` | ||
| * Sentry.logger.fatal('Database connection pool exhausted', { | ||
| * database: 'users', | ||
| * activeConnections: 100, | ||
| * maxConnections: 100 | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @example With template strings | ||
| * | ||
| * ``` | ||
| * Sentry.logger.fatal('Database %s: %s connections active', | ||
| * ['connection pool exhausted', '100/100'], | ||
| * { database: 'users' } | ||
| * ); | ||
| * ``` | ||
| */ | ||
| function fatal(...args) { | ||
| captureLog('fatal', ...args); | ||
| captureLog("fatal", ...args); | ||
| } | ||
@@ -168,0 +22,0 @@ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"exports.js","sources":["../../../src/logs/exports.ts"],"sourcesContent":["import { captureLog, type CaptureLogArgs } from './capture';\n\n/**\n * @summary Capture a log with the `trace` level. Requires the `enableLogs` option to be enabled.\n *\n * You can either pass a message and attributes or a message template, params and attributes.\n *\n * @example\n *\n * ```\n * Sentry.logger.trace('Starting database connection', {\n * database: 'users',\n * connectionId: 'conn_123'\n * });\n * ```\n *\n * @example With template strings\n *\n * ```\n * Sentry.logger.trace('Database connection %s established for %s',\n * ['successful', 'users'],\n * { connectionId: 'conn_123' }\n * );\n * ```\n */\nexport function trace(...args: CaptureLogArgs): void {\n captureLog('trace', ...args);\n}\n\n/**\n * @summary Capture a log with the `debug` level. Requires the `enableLogs` option to be enabled.\n *\n * You can either pass a message and attributes or a message template, params and attributes.\n *\n * @example\n *\n * ```\n * Sentry.logger.debug('Cache miss for user profile', {\n * userId: 'user_123',\n * cacheKey: 'profile:user_123'\n * });\n * ```\n *\n * @example With template strings\n *\n * ```\n * Sentry.logger.debug('Cache %s for %s: %s',\n * ['miss', 'user profile', 'key not found'],\n * { userId: 'user_123' }\n * );\n * ```\n */\nexport function debug(...args: CaptureLogArgs): void {\n captureLog('debug', ...args);\n}\n\n/**\n * @summary Capture a log with the `info` level. Requires the `enableLogs` option to be enabled.\n *\n * You can either pass a message and attributes or a message template, params and attributes.\n *\n * @example\n *\n * ```\n * Sentry.logger.info('User profile updated', {\n * userId: 'user_123',\n * updatedFields: ['email', 'preferences']\n * });\n * ```\n *\n * @example With template strings\n *\n * ```\n * Sentry.logger.info('User %s updated their %s',\n * ['John Doe', 'profile settings'],\n * { userId: 'user_123' }\n * );\n * ```\n */\nexport function info(...args: CaptureLogArgs): void {\n captureLog('info', ...args);\n}\n\n/**\n * @summary Capture a log with the `warn` level. Requires the `enableLogs` option to be enabled.\n *\n * You can either pass a message and attributes or a message template, params and attributes.\n *\n * @example\n *\n * ```\n * Sentry.logger.warn('Rate limit approaching', {\n * endpoint: '/api/users',\n * currentRate: '95/100',\n * resetTime: '2024-03-20T10:00:00Z'\n * });\n * ```\n *\n * @example With template strings\n *\n * ```\n * Sentry.logger.warn('Rate limit %s for %s: %s',\n * ['approaching', '/api/users', '95/100 requests'],\n * { resetTime: '2024-03-20T10:00:00Z' }\n * );\n * ```\n */\nexport function warn(...args: CaptureLogArgs): void {\n captureLog('warn', ...args);\n}\n\n/**\n * @summary Capture a log with the `error` level. Requires the `enableLogs` option to be enabled.\n *\n * You can either pass a message and attributes or a message template, params and attributes.\n *\n * @example\n *\n * ```\n * Sentry.logger.error('Failed to process payment', {\n * orderId: 'order_123',\n * errorCode: 'PAYMENT_FAILED',\n * amount: 99.99\n * });\n * ```\n *\n * @example With template strings\n *\n * ```\n * Sentry.logger.error('Payment processing failed for order %s: %s',\n * ['order_123', 'insufficient funds'],\n * { amount: 99.99 }\n * );\n * ```\n */\nexport function error(...args: CaptureLogArgs): void {\n captureLog('error', ...args);\n}\n\n/**\n * @summary Capture a log with the `fatal` level. Requires the `enableLogs` option to be enabled.\n *\n * You can either pass a message and attributes or a message template, params and attributes.\n *\n * @example\n *\n * ```\n * Sentry.logger.fatal('Database connection pool exhausted', {\n * database: 'users',\n * activeConnections: 100,\n * maxConnections: 100\n * });\n * ```\n *\n * @example With template strings\n *\n * ```\n * Sentry.logger.fatal('Database %s: %s connections active',\n * ['connection pool exhausted', '100/100'],\n * { database: 'users' }\n * );\n * ```\n */\nexport function fatal(...args: CaptureLogArgs): void {\n captureLog('fatal', ...args);\n}\n\nexport { fmt } from '@sentry/core';\n"],"names":[],"mappings":";;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,KAAK,CAAC,GAAG,IAAI,EAAwB;AACrD,EAAE,UAAU,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC;AAC9B;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,KAAK,CAAC,GAAG,IAAI,EAAwB;AACrD,EAAE,UAAU,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC;AAC9B;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,IAAI,CAAC,GAAG,IAAI,EAAwB;AACpD,EAAE,UAAU,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC;AAC7B;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,IAAI,CAAC,GAAG,IAAI,EAAwB;AACpD,EAAE,UAAU,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC;AAC7B;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,KAAK,CAAC,GAAG,IAAI,EAAwB;AACrD,EAAE,UAAU,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC;AAC9B;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,KAAK,CAAC,GAAG,IAAI,EAAwB;AACrD,EAAE,UAAU,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC;AAC9B;;;;"} | ||
| {"version":3,"file":"exports.js","sources":["../../../src/logs/exports.ts"],"sourcesContent":["import { captureLog, type CaptureLogArgs } from './capture';\n\n/**\n * @summary Capture a log with the `trace` level. Requires the `enableLogs` option to be enabled.\n *\n * You can either pass a message and attributes or a message template, params and attributes.\n *\n * @example\n *\n * ```\n * Sentry.logger.trace('Starting database connection', {\n * database: 'users',\n * connectionId: 'conn_123'\n * });\n * ```\n *\n * @example With template strings\n *\n * ```\n * Sentry.logger.trace('Database connection %s established for %s',\n * ['successful', 'users'],\n * { connectionId: 'conn_123' }\n * );\n * ```\n */\nexport function trace(...args: CaptureLogArgs): void {\n captureLog('trace', ...args);\n}\n\n/**\n * @summary Capture a log with the `debug` level. Requires the `enableLogs` option to be enabled.\n *\n * You can either pass a message and attributes or a message template, params and attributes.\n *\n * @example\n *\n * ```\n * Sentry.logger.debug('Cache miss for user profile', {\n * userId: 'user_123',\n * cacheKey: 'profile:user_123'\n * });\n * ```\n *\n * @example With template strings\n *\n * ```\n * Sentry.logger.debug('Cache %s for %s: %s',\n * ['miss', 'user profile', 'key not found'],\n * { userId: 'user_123' }\n * );\n * ```\n */\nexport function debug(...args: CaptureLogArgs): void {\n captureLog('debug', ...args);\n}\n\n/**\n * @summary Capture a log with the `info` level. Requires the `enableLogs` option to be enabled.\n *\n * You can either pass a message and attributes or a message template, params and attributes.\n *\n * @example\n *\n * ```\n * Sentry.logger.info('User profile updated', {\n * userId: 'user_123',\n * updatedFields: ['email', 'preferences']\n * });\n * ```\n *\n * @example With template strings\n *\n * ```\n * Sentry.logger.info('User %s updated their %s',\n * ['John Doe', 'profile settings'],\n * { userId: 'user_123' }\n * );\n * ```\n */\nexport function info(...args: CaptureLogArgs): void {\n captureLog('info', ...args);\n}\n\n/**\n * @summary Capture a log with the `warn` level. Requires the `enableLogs` option to be enabled.\n *\n * You can either pass a message and attributes or a message template, params and attributes.\n *\n * @example\n *\n * ```\n * Sentry.logger.warn('Rate limit approaching', {\n * endpoint: '/api/users',\n * currentRate: '95/100',\n * resetTime: '2024-03-20T10:00:00Z'\n * });\n * ```\n *\n * @example With template strings\n *\n * ```\n * Sentry.logger.warn('Rate limit %s for %s: %s',\n * ['approaching', '/api/users', '95/100 requests'],\n * { resetTime: '2024-03-20T10:00:00Z' }\n * );\n * ```\n */\nexport function warn(...args: CaptureLogArgs): void {\n captureLog('warn', ...args);\n}\n\n/**\n * @summary Capture a log with the `error` level. Requires the `enableLogs` option to be enabled.\n *\n * You can either pass a message and attributes or a message template, params and attributes.\n *\n * @example\n *\n * ```\n * Sentry.logger.error('Failed to process payment', {\n * orderId: 'order_123',\n * errorCode: 'PAYMENT_FAILED',\n * amount: 99.99\n * });\n * ```\n *\n * @example With template strings\n *\n * ```\n * Sentry.logger.error('Payment processing failed for order %s: %s',\n * ['order_123', 'insufficient funds'],\n * { amount: 99.99 }\n * );\n * ```\n */\nexport function error(...args: CaptureLogArgs): void {\n captureLog('error', ...args);\n}\n\n/**\n * @summary Capture a log with the `fatal` level. Requires the `enableLogs` option to be enabled.\n *\n * You can either pass a message and attributes or a message template, params and attributes.\n *\n * @example\n *\n * ```\n * Sentry.logger.fatal('Database connection pool exhausted', {\n * database: 'users',\n * activeConnections: 100,\n * maxConnections: 100\n * });\n * ```\n *\n * @example With template strings\n *\n * ```\n * Sentry.logger.fatal('Database %s: %s connections active',\n * ['connection pool exhausted', '100/100'],\n * { database: 'users' }\n * );\n * ```\n */\nexport function fatal(...args: CaptureLogArgs): void {\n captureLog('fatal', ...args);\n}\n\nexport { fmt } from '@sentry/core';\n"],"names":[],"mappings":";;;AAyBO,SAAS,SAAS,IAAA,EAA4B;AACnD,EAAA,UAAA,CAAW,OAAA,EAAS,GAAG,IAAI,CAAA;AAC7B;AAyBO,SAAS,SAAS,IAAA,EAA4B;AACnD,EAAA,UAAA,CAAW,OAAA,EAAS,GAAG,IAAI,CAAA;AAC7B;AAyBO,SAAS,QAAQ,IAAA,EAA4B;AAClD,EAAA,UAAA,CAAW,MAAA,EAAQ,GAAG,IAAI,CAAA;AAC5B;AA0BO,SAAS,QAAQ,IAAA,EAA4B;AAClD,EAAA,UAAA,CAAW,MAAA,EAAQ,GAAG,IAAI,CAAA;AAC5B;AA0BO,SAAS,SAAS,IAAA,EAA4B;AACnD,EAAA,UAAA,CAAW,OAAA,EAAS,GAAG,IAAI,CAAA;AAC7B;AA0BO,SAAS,SAAS,IAAA,EAA4B;AACnD,EAAA,UAAA,CAAW,OAAA,EAAS,GAAG,IAAI,CAAA;AAC7B;;;;"} |
| import { parseSemver } from '@sentry/core'; | ||
| const NODE_VERSION = parseSemver(process.versions.node) ; | ||
| const NODE_VERSION = parseSemver(process.versions.node); | ||
| const NODE_MAJOR = NODE_VERSION.major; | ||
@@ -5,0 +5,0 @@ const NODE_MINOR = NODE_VERSION.minor; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"nodeVersion.js","sources":["../../src/nodeVersion.ts"],"sourcesContent":["import { parseSemver } from '@sentry/core';\n\nexport const NODE_VERSION = parseSemver(process.versions.node) as { major: number; minor: number; patch: number };\nexport const NODE_MAJOR = NODE_VERSION.major;\nexport const NODE_MINOR = NODE_VERSION.minor;\n"],"names":[],"mappings":";;AAEO,MAAM,YAAA,GAAe,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAA;AACtD,MAAM,UAAA,GAAa,YAAY,CAAC;AAChC,MAAM,UAAA,GAAa,YAAY,CAAC;;;;"} | ||
| {"version":3,"file":"nodeVersion.js","sources":["../../src/nodeVersion.ts"],"sourcesContent":["import { parseSemver } from '@sentry/core';\n\nexport const NODE_VERSION = parseSemver(process.versions.node) as { major: number; minor: number; patch: number };\nexport const NODE_MAJOR = NODE_VERSION.major;\nexport const NODE_MINOR = NODE_VERSION.minor;\n"],"names":[],"mappings":";;AAEO,MAAM,YAAA,GAAe,WAAA,CAAY,OAAA,CAAQ,QAAA,CAAS,IAAI;AACtD,MAAM,aAAa,YAAA,CAAa;AAChC,MAAM,aAAa,YAAA,CAAa;;;;"} |
| import { SentryAsyncLocalStorageContextManager } from '@sentry/opentelemetry'; | ||
| /** | ||
| * This is a custom ContextManager for OpenTelemetry & Sentry. | ||
| * It ensures that we create a new hub per context, so that the OTEL Context & the Sentry Scopes are always in sync. | ||
| */ | ||
| const SentryContextManager = SentryAsyncLocalStorageContextManager; | ||
@@ -8,0 +4,0 @@ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"contextManager.js","sources":["../../../src/otel/contextManager.ts"],"sourcesContent":["import { SentryAsyncLocalStorageContextManager } from '@sentry/opentelemetry';\n\n/**\n * This is a custom ContextManager for OpenTelemetry & Sentry.\n * It ensures that we create a new hub per context, so that the OTEL Context & the Sentry Scopes are always in sync.\n */\nexport const SentryContextManager = SentryAsyncLocalStorageContextManager;\n"],"names":[],"mappings":";;AAEA;AACA;AACA;AACA;AACO,MAAM,oBAAA,GAAuB;;;;"} | ||
| {"version":3,"file":"contextManager.js","sources":["../../../src/otel/contextManager.ts"],"sourcesContent":["import { SentryAsyncLocalStorageContextManager } from '@sentry/opentelemetry';\n\n/**\n * This is a custom ContextManager for OpenTelemetry & Sentry.\n * It ensures that we create a new hub per context, so that the OTEL Context & the Sentry Scopes are always in sync.\n */\nexport const SentryContextManager = SentryAsyncLocalStorageContextManager;\n"],"names":[],"mappings":";;AAMO,MAAM,oBAAA,GAAuB;;;;"} |
| import { registerInstrumentations } from '@opentelemetry/instrumentation'; | ||
| /** Exported only for tests. */ | ||
| const INSTRUMENTED = {}; | ||
| /** | ||
| * Instrument an OpenTelemetry instrumentation once. | ||
| * This will skip running instrumentation again if it was already instrumented. | ||
| */ | ||
| function generateInstrumentOnce( | ||
| name, | ||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| creatorOrClass, | ||
| optionsCallback, | ||
| ) { | ||
| function generateInstrumentOnce(name, creatorOrClass, optionsCallback) { | ||
| if (optionsCallback) { | ||
| return _generateInstrumentOnceWithOptions( | ||
| name, | ||
| creatorOrClass , | ||
| optionsCallback, | ||
| creatorOrClass, | ||
| optionsCallback | ||
| ); | ||
| } | ||
| return _generateInstrumentOnce(name, creatorOrClass ); | ||
| return _generateInstrumentOnce(name, creatorOrClass); | ||
| } | ||
| // The plain version without handling of options | ||
| // Should not be used with custom options that are mutated in the creator! | ||
| function _generateInstrumentOnce( | ||
| name, | ||
| creator, | ||
| ) { | ||
| function _generateInstrumentOnce(name, creator) { | ||
| return Object.assign( | ||
| (options) => { | ||
| const instrumented = INSTRUMENTED[name] ; | ||
| const instrumented = INSTRUMENTED[name]; | ||
| if (instrumented) { | ||
| // If options are provided, ensure we update them | ||
| if (options) { | ||
@@ -43,72 +24,41 @@ instrumented.setConfig(options); | ||
| } | ||
| const instrumentation = creator(options); | ||
| INSTRUMENTED[name] = instrumentation; | ||
| registerInstrumentations({ | ||
| instrumentations: [instrumentation], | ||
| instrumentations: [instrumentation] | ||
| }); | ||
| return instrumentation; | ||
| }, | ||
| { id: name }, | ||
| { id: name } | ||
| ); | ||
| } | ||
| // This version handles options properly | ||
| function _generateInstrumentOnceWithOptions | ||
| ( | ||
| name, | ||
| instrumentationClass, | ||
| optionsCallback, | ||
| ) { | ||
| function _generateInstrumentOnceWithOptions(name, instrumentationClass, optionsCallback) { | ||
| return Object.assign( | ||
| (_options) => { | ||
| const options = optionsCallback(_options); | ||
| const instrumented = INSTRUMENTED[name] ; | ||
| const instrumented = INSTRUMENTED[name]; | ||
| if (instrumented) { | ||
| // Ensure we update options | ||
| instrumented.setConfig(options); | ||
| return instrumented; | ||
| } | ||
| const instrumentation = new instrumentationClass(options) ; | ||
| const instrumentation = new instrumentationClass(options); | ||
| INSTRUMENTED[name] = instrumentation; | ||
| registerInstrumentations({ | ||
| instrumentations: [instrumentation], | ||
| instrumentations: [instrumentation] | ||
| }); | ||
| return instrumentation; | ||
| }, | ||
| { id: name }, | ||
| { id: name } | ||
| ); | ||
| } | ||
| /** | ||
| * Ensure a given callback is called when the instrumentation is actually wrapping something. | ||
| * This can be used to ensure some logic is only called when the instrumentation is actually active. | ||
| * | ||
| * This function returns a function that can be invoked with a callback. | ||
| * This callback will either be invoked immediately | ||
| * (e.g. if the instrumentation was already wrapped, or if _wrap could not be patched), | ||
| * or once the instrumentation is actually wrapping something. | ||
| * | ||
| * Make sure to call this function right after adding the instrumentation, otherwise it may be too late! | ||
| * The returned callback can be used any time, and also multiple times. | ||
| */ | ||
| function instrumentWhenWrapped(instrumentation) { | ||
| let isWrapped = false; | ||
| let callbacks = []; | ||
| if (!hasWrap(instrumentation)) { | ||
| isWrapped = true; | ||
| } else { | ||
| const originalWrap = instrumentation['_wrap']; | ||
| instrumentation['_wrap'] = (...args) => { | ||
| const originalWrap = instrumentation["_wrap"]; | ||
| instrumentation["_wrap"] = (...args) => { | ||
| isWrapped = true; | ||
| callbacks.forEach(callback => callback()); | ||
| callbacks.forEach((callback) => callback()); | ||
| callbacks = []; | ||
@@ -118,3 +68,2 @@ return originalWrap(...args); | ||
| } | ||
| const registerCallback = (callback) => { | ||
@@ -127,10 +76,6 @@ if (isWrapped) { | ||
| }; | ||
| return registerCallback; | ||
| } | ||
| function hasWrap( | ||
| instrumentation, | ||
| ) { | ||
| return typeof (instrumentation )['_wrap'] === 'function'; | ||
| function hasWrap(instrumentation) { | ||
| return typeof instrumentation["_wrap"] === "function"; | ||
| } | ||
@@ -137,0 +82,0 @@ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"instrument.js","sources":["../../../src/otel/instrument.ts"],"sourcesContent":["import { type Instrumentation, registerInstrumentations } from '@opentelemetry/instrumentation';\n\n/** Exported only for tests. */\nexport const INSTRUMENTED: Record<string, Instrumentation> = {};\n\nexport function generateInstrumentOnce<\n Options,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n InstrumentationClass extends new (...args: any[]) => Instrumentation,\n>(\n name: string,\n instrumentationClass: InstrumentationClass,\n optionsCallback: (options: Options) => ConstructorParameters<InstrumentationClass>[0],\n): ((options: Options) => InstanceType<InstrumentationClass>) & { id: string };\nexport function generateInstrumentOnce<\n Options = unknown,\n InstrumentationInstance extends Instrumentation = Instrumentation,\n>(\n name: string,\n creator: (options?: Options) => InstrumentationInstance,\n): ((options?: Options) => InstrumentationInstance) & { id: string };\n/**\n * Instrument an OpenTelemetry instrumentation once.\n * This will skip running instrumentation again if it was already instrumented.\n */\nexport function generateInstrumentOnce<Options>(\n name: string,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n creatorOrClass: (new (...args: any[]) => Instrumentation) | ((options?: Options) => Instrumentation),\n optionsCallback?: (options: Options) => unknown,\n): ((options: Options) => Instrumentation) & { id: string } {\n if (optionsCallback) {\n return _generateInstrumentOnceWithOptions(\n name,\n creatorOrClass as new (...args: unknown[]) => Instrumentation,\n optionsCallback,\n );\n }\n\n return _generateInstrumentOnce(name, creatorOrClass as (options?: Options) => Instrumentation);\n}\n\n// The plain version without handling of options\n// Should not be used with custom options that are mutated in the creator!\nfunction _generateInstrumentOnce<Options = unknown, InstrumentationInstance extends Instrumentation = Instrumentation>(\n name: string,\n creator: (options?: Options) => InstrumentationInstance,\n): ((options?: Options) => InstrumentationInstance) & { id: string } {\n return Object.assign(\n (options?: Options) => {\n const instrumented = INSTRUMENTED[name] as InstrumentationInstance | undefined;\n if (instrumented) {\n // If options are provided, ensure we update them\n if (options) {\n instrumented.setConfig(options);\n }\n return instrumented;\n }\n\n const instrumentation = creator(options);\n INSTRUMENTED[name] = instrumentation;\n\n registerInstrumentations({\n instrumentations: [instrumentation],\n });\n\n return instrumentation;\n },\n { id: name },\n );\n}\n\n// This version handles options properly\nfunction _generateInstrumentOnceWithOptions<\n Options,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n InstrumentationClass extends new (...args: any[]) => Instrumentation,\n>(\n name: string,\n instrumentationClass: InstrumentationClass,\n optionsCallback: (options: Options) => ConstructorParameters<InstrumentationClass>[0],\n): ((options: Options) => InstanceType<InstrumentationClass>) & { id: string } {\n return Object.assign(\n (_options: Options) => {\n const options = optionsCallback(_options);\n\n const instrumented = INSTRUMENTED[name] as InstanceType<InstrumentationClass> | undefined;\n if (instrumented) {\n // Ensure we update options\n instrumented.setConfig(options);\n return instrumented;\n }\n\n const instrumentation = new instrumentationClass(options) as InstanceType<InstrumentationClass>;\n INSTRUMENTED[name] = instrumentation;\n\n registerInstrumentations({\n instrumentations: [instrumentation],\n });\n\n return instrumentation;\n },\n { id: name },\n );\n}\n\n/**\n * Ensure a given callback is called when the instrumentation is actually wrapping something.\n * This can be used to ensure some logic is only called when the instrumentation is actually active.\n *\n * This function returns a function that can be invoked with a callback.\n * This callback will either be invoked immediately\n * (e.g. if the instrumentation was already wrapped, or if _wrap could not be patched),\n * or once the instrumentation is actually wrapping something.\n *\n * Make sure to call this function right after adding the instrumentation, otherwise it may be too late!\n * The returned callback can be used any time, and also multiple times.\n */\nexport function instrumentWhenWrapped<T extends Instrumentation>(instrumentation: T): (callback: () => void) => void {\n let isWrapped = false;\n let callbacks: (() => void)[] = [];\n\n if (!hasWrap(instrumentation)) {\n isWrapped = true;\n } else {\n const originalWrap = instrumentation['_wrap'];\n\n instrumentation['_wrap'] = (...args: Parameters<typeof originalWrap>) => {\n isWrapped = true;\n callbacks.forEach(callback => callback());\n callbacks = [];\n return originalWrap(...args);\n };\n }\n\n const registerCallback = (callback: () => void): void => {\n if (isWrapped) {\n callback();\n } else {\n callbacks.push(callback);\n }\n };\n\n return registerCallback;\n}\n\nfunction hasWrap<T extends Instrumentation>(\n instrumentation: T,\n): instrumentation is T & { _wrap: (...args: unknown[]) => unknown } {\n return typeof (instrumentation as T & { _wrap?: (...args: unknown[]) => unknown })['_wrap'] === 'function';\n}\n"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"instrument.js","sources":["../../../src/otel/instrument.ts"],"sourcesContent":["import { type Instrumentation, registerInstrumentations } from '@opentelemetry/instrumentation';\n\n/** Exported only for tests. */\nexport const INSTRUMENTED: Record<string, Instrumentation> = {};\n\nexport function generateInstrumentOnce<\n Options,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n InstrumentationClass extends new (...args: any[]) => Instrumentation,\n>(\n name: string,\n instrumentationClass: InstrumentationClass,\n optionsCallback: (options: Options) => ConstructorParameters<InstrumentationClass>[0],\n): ((options: Options) => InstanceType<InstrumentationClass>) & { id: string };\nexport function generateInstrumentOnce<\n Options = unknown,\n InstrumentationInstance extends Instrumentation = Instrumentation,\n>(\n name: string,\n creator: (options?: Options) => InstrumentationInstance,\n): ((options?: Options) => InstrumentationInstance) & { id: string };\n/**\n * Instrument an OpenTelemetry instrumentation once.\n * This will skip running instrumentation again if it was already instrumented.\n */\nexport function generateInstrumentOnce<Options>(\n name: string,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n creatorOrClass: (new (...args: any[]) => Instrumentation) | ((options?: Options) => Instrumentation),\n optionsCallback?: (options: Options) => unknown,\n): ((options: Options) => Instrumentation) & { id: string } {\n if (optionsCallback) {\n return _generateInstrumentOnceWithOptions(\n name,\n creatorOrClass as new (...args: unknown[]) => Instrumentation,\n optionsCallback,\n );\n }\n\n return _generateInstrumentOnce(name, creatorOrClass as (options?: Options) => Instrumentation);\n}\n\n// The plain version without handling of options\n// Should not be used with custom options that are mutated in the creator!\nfunction _generateInstrumentOnce<Options = unknown, InstrumentationInstance extends Instrumentation = Instrumentation>(\n name: string,\n creator: (options?: Options) => InstrumentationInstance,\n): ((options?: Options) => InstrumentationInstance) & { id: string } {\n return Object.assign(\n (options?: Options) => {\n const instrumented = INSTRUMENTED[name] as InstrumentationInstance | undefined;\n if (instrumented) {\n // If options are provided, ensure we update them\n if (options) {\n instrumented.setConfig(options);\n }\n return instrumented;\n }\n\n const instrumentation = creator(options);\n INSTRUMENTED[name] = instrumentation;\n\n registerInstrumentations({\n instrumentations: [instrumentation],\n });\n\n return instrumentation;\n },\n { id: name },\n );\n}\n\n// This version handles options properly\nfunction _generateInstrumentOnceWithOptions<\n Options,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n InstrumentationClass extends new (...args: any[]) => Instrumentation,\n>(\n name: string,\n instrumentationClass: InstrumentationClass,\n optionsCallback: (options: Options) => ConstructorParameters<InstrumentationClass>[0],\n): ((options: Options) => InstanceType<InstrumentationClass>) & { id: string } {\n return Object.assign(\n (_options: Options) => {\n const options = optionsCallback(_options);\n\n const instrumented = INSTRUMENTED[name] as InstanceType<InstrumentationClass> | undefined;\n if (instrumented) {\n // Ensure we update options\n instrumented.setConfig(options);\n return instrumented;\n }\n\n const instrumentation = new instrumentationClass(options) as InstanceType<InstrumentationClass>;\n INSTRUMENTED[name] = instrumentation;\n\n registerInstrumentations({\n instrumentations: [instrumentation],\n });\n\n return instrumentation;\n },\n { id: name },\n );\n}\n\n/**\n * Ensure a given callback is called when the instrumentation is actually wrapping something.\n * This can be used to ensure some logic is only called when the instrumentation is actually active.\n *\n * This function returns a function that can be invoked with a callback.\n * This callback will either be invoked immediately\n * (e.g. if the instrumentation was already wrapped, or if _wrap could not be patched),\n * or once the instrumentation is actually wrapping something.\n *\n * Make sure to call this function right after adding the instrumentation, otherwise it may be too late!\n * The returned callback can be used any time, and also multiple times.\n */\nexport function instrumentWhenWrapped<T extends Instrumentation>(instrumentation: T): (callback: () => void) => void {\n let isWrapped = false;\n let callbacks: (() => void)[] = [];\n\n if (!hasWrap(instrumentation)) {\n isWrapped = true;\n } else {\n const originalWrap = instrumentation['_wrap'];\n\n instrumentation['_wrap'] = (...args: Parameters<typeof originalWrap>) => {\n isWrapped = true;\n callbacks.forEach(callback => callback());\n callbacks = [];\n return originalWrap(...args);\n };\n }\n\n const registerCallback = (callback: () => void): void => {\n if (isWrapped) {\n callback();\n } else {\n callbacks.push(callback);\n }\n };\n\n return registerCallback;\n}\n\nfunction hasWrap<T extends Instrumentation>(\n instrumentation: T,\n): instrumentation is T & { _wrap: (...args: unknown[]) => unknown } {\n return typeof (instrumentation as T & { _wrap?: (...args: unknown[]) => unknown })['_wrap'] === 'function';\n}\n"],"names":[],"mappings":";;AAGO,MAAM,eAAgD;AAsBtD,SAAS,sBAAA,CACd,IAAA,EAEA,cAAA,EACA,eAAA,EAC0D;AAC1D,EAAA,IAAI,eAAA,EAAiB;AACnB,IAAA,OAAO,kCAAA;AAAA,MACL,IAAA;AAAA,MACA,cAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF;AAEA,EAAA,OAAO,uBAAA,CAAwB,MAAM,cAAwD,CAAA;AAC/F;AAIA,SAAS,uBAAA,CACP,MACA,OAAA,EACmE;AACnE,EAAA,OAAO,MAAA,CAAO,MAAA;AAAA,IACZ,CAAC,OAAA,KAAsB;AACrB,MAAA,MAAM,YAAA,GAAe,aAAa,IAAI,CAAA;AACtC,MAAA,IAAI,YAAA,EAAc;AAEhB,QAAA,IAAI,OAAA,EAAS;AACX,UAAA,YAAA,CAAa,UAAU,OAAO,CAAA;AAAA,QAChC;AACA,QAAA,OAAO,YAAA;AAAA,MACT;AAEA,MAAA,MAAM,eAAA,GAAkB,QAAQ,OAAO,CAAA;AACvC,MAAA,YAAA,CAAa,IAAI,CAAA,GAAI,eAAA;AAErB,MAAA,wBAAA,CAAyB;AAAA,QACvB,gBAAA,EAAkB,CAAC,eAAe;AAAA,OACnC,CAAA;AAED,MAAA,OAAO,eAAA;AAAA,IACT,CAAA;AAAA,IACA,EAAE,IAAI,IAAA;AAAK,GACb;AACF;AAGA,SAAS,kCAAA,CAKP,IAAA,EACA,oBAAA,EACA,eAAA,EAC6E;AAC7E,EAAA,OAAO,MAAA,CAAO,MAAA;AAAA,IACZ,CAAC,QAAA,KAAsB;AACrB,MAAA,MAAM,OAAA,GAAU,gBAAgB,QAAQ,CAAA;AAExC,MAAA,MAAM,YAAA,GAAe,aAAa,IAAI,CAAA;AACtC,MAAA,IAAI,YAAA,EAAc;AAEhB,QAAA,YAAA,CAAa,UAAU,OAAO,CAAA;AAC9B,QAAA,OAAO,YAAA;AAAA,MACT;AAEA,MAAA,MAAM,eAAA,GAAkB,IAAI,oBAAA,CAAqB,OAAO,CAAA;AACxD,MAAA,YAAA,CAAa,IAAI,CAAA,GAAI,eAAA;AAErB,MAAA,wBAAA,CAAyB;AAAA,QACvB,gBAAA,EAAkB,CAAC,eAAe;AAAA,OACnC,CAAA;AAED,MAAA,OAAO,eAAA;AAAA,IACT,CAAA;AAAA,IACA,EAAE,IAAI,IAAA;AAAK,GACb;AACF;AAcO,SAAS,sBAAiD,eAAA,EAAoD;AACnH,EAAA,IAAI,SAAA,GAAY,KAAA;AAChB,EAAA,IAAI,YAA4B,EAAC;AAEjC,EAAA,IAAI,CAAC,OAAA,CAAQ,eAAe,CAAA,EAAG;AAC7B,IAAA,SAAA,GAAY,IAAA;AAAA,EACd,CAAA,MAAO;AACL,IAAA,MAAM,YAAA,GAAe,gBAAgB,OAAO,CAAA;AAE5C,IAAA,eAAA,CAAgB,OAAO,CAAA,GAAI,CAAA,GAAI,IAAA,KAA0C;AACvE,MAAA,SAAA,GAAY,IAAA;AACZ,MAAA,SAAA,CAAU,OAAA,CAAQ,CAAA,QAAA,KAAY,QAAA,EAAU,CAAA;AACxC,MAAA,SAAA,GAAY,EAAC;AACb,MAAA,OAAO,YAAA,CAAa,GAAG,IAAI,CAAA;AAAA,IAC7B,CAAA;AAAA,EACF;AAEA,EAAA,MAAM,gBAAA,GAAmB,CAAC,QAAA,KAA+B;AACvD,IAAA,IAAI,SAAA,EAAW;AACb,MAAA,QAAA,EAAS;AAAA,IACX,CAAA,MAAO;AACL,MAAA,SAAA,CAAU,KAAK,QAAQ,CAAA;AAAA,IACzB;AAAA,EACF,CAAA;AAEA,EAAA,OAAO,gBAAA;AACT;AAEA,SAAS,QACP,eAAA,EACmE;AACnE,EAAA,OAAO,OAAQ,eAAA,CAAoE,OAAO,CAAA,KAAM,UAAA;AAClG;;;;"} |
| import { diag, DiagLogLevel } from '@opentelemetry/api'; | ||
| import { debug } from '@sentry/core'; | ||
| /** | ||
| * Setup the OTEL logger to use our own debug logger. | ||
| */ | ||
| function setupOpenTelemetryLogger() { | ||
| // Disable diag, to ensure this works even if called multiple times | ||
| diag.disable(); | ||
@@ -16,5 +12,5 @@ diag.setLogger( | ||
| debug: debug.log, | ||
| verbose: debug.log, | ||
| verbose: debug.log | ||
| }, | ||
| DiagLogLevel.DEBUG, | ||
| DiagLogLevel.DEBUG | ||
| ); | ||
@@ -21,0 +17,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"logger.js","sources":["../../../src/otel/logger.ts"],"sourcesContent":["import { diag, DiagLogLevel } from '@opentelemetry/api';\nimport { debug } from '@sentry/core';\n\n/**\n * Setup the OTEL logger to use our own debug logger.\n */\nexport function setupOpenTelemetryLogger(): void {\n // Disable diag, to ensure this works even if called multiple times\n diag.disable();\n diag.setLogger(\n {\n error: debug.error,\n warn: debug.warn,\n info: debug.log,\n debug: debug.log,\n verbose: debug.log,\n },\n DiagLogLevel.DEBUG,\n );\n}\n"],"names":[],"mappings":";;;AAGA;AACA;AACA;AACO,SAAS,wBAAwB,GAAS;AACjD;AACA,EAAE,IAAI,CAAC,OAAO,EAAE;AAChB,EAAE,IAAI,CAAC,SAAS;AAChB,IAAI;AACJ,MAAM,KAAK,EAAE,KAAK,CAAC,KAAK;AACxB,MAAM,IAAI,EAAE,KAAK,CAAC,IAAI;AACtB,MAAM,IAAI,EAAE,KAAK,CAAC,GAAG;AACrB,MAAM,KAAK,EAAE,KAAK,CAAC,GAAG;AACtB,MAAM,OAAO,EAAE,KAAK,CAAC,GAAG;AACxB,KAAK;AACL,IAAI,YAAY,CAAC,KAAK;AACtB,GAAG;AACH;;;;"} | ||
| {"version":3,"file":"logger.js","sources":["../../../src/otel/logger.ts"],"sourcesContent":["import { diag, DiagLogLevel } from '@opentelemetry/api';\nimport { debug } from '@sentry/core';\n\n/**\n * Setup the OTEL logger to use our own debug logger.\n */\nexport function setupOpenTelemetryLogger(): void {\n // Disable diag, to ensure this works even if called multiple times\n diag.disable();\n diag.setLogger(\n {\n error: debug.error,\n warn: debug.warn,\n info: debug.log,\n debug: debug.log,\n verbose: debug.log,\n },\n DiagLogLevel.DEBUG,\n );\n}\n"],"names":[],"mappings":";;;AAMO,SAAS,wBAAA,GAAiC;AAE/C,EAAA,IAAA,CAAK,OAAA,EAAQ;AACb,EAAA,IAAA,CAAK,SAAA;AAAA,IACH;AAAA,MACE,OAAO,KAAA,CAAM,KAAA;AAAA,MACb,MAAM,KAAA,CAAM,IAAA;AAAA,MACZ,MAAM,KAAA,CAAM,GAAA;AAAA,MACZ,OAAO,KAAA,CAAM,GAAA;AAAA,MACb,SAAS,KAAA,CAAM;AAAA,KACjB;AAAA,IACA,YAAA,CAAa;AAAA,GACf;AACF;;;;"} |
@@ -1,1 +0,1 @@ | ||
| {"type":"module","version":"10.53.1","sideEffects":false} | ||
| {"type":"module","version":"10.54.0","sideEffects":false} |
+20
-73
| import * as http from 'node:http'; | ||
| import 'node:https'; | ||
| /** | ||
| * This code was originally forked from https://github.com/TooTallNate/proxy-agents/tree/b133295fd16f6475578b6b15bd9b4e33ecb0d0b7 | ||
| * With the following LICENSE: | ||
| * | ||
| * (The MIT License) | ||
| * | ||
| * Copyright (c) 2013 Nathan Rajlich <nathan@tootallnate.net>* | ||
| * | ||
| * Permission is hereby granted, free of charge, to any person obtaining | ||
| * a copy of this software and associated documentation files (the | ||
| * 'Software'), to deal in the Software without restriction, including | ||
| * without limitation the rights to use, copy, modify, merge, publish, | ||
| * distribute, sublicense, and/or sell copies of the Software, and to | ||
| * permit persons to whom the Software is furnished to do so, subject to | ||
| * the following conditions:* | ||
| * | ||
| * The above copyright notice and this permission notice shall be | ||
| * included in all copies or substantial portions of the Software.* | ||
| * | ||
| * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, | ||
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
| * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||
| * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | ||
| * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | ||
| * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| */ | ||
| const INTERNAL = Symbol('AgentBaseInternalState'); | ||
| class Agent extends http.Agent { | ||
| // Set by `http.Agent` - missing from `@types/node` | ||
| var _a; | ||
| const INTERNAL = /* @__PURE__ */ Symbol("AgentBaseInternalState"); | ||
| class Agent extends (_a = http.Agent, _a) { | ||
| constructor(opts) { | ||
@@ -43,3 +11,2 @@ super(opts); | ||
| } | ||
| /** | ||
@@ -50,55 +17,37 @@ * Determine whether this is an `http` or `https` request. | ||
| if (options) { | ||
| // First check the `secureEndpoint` property explicitly, since this | ||
| // means that a parent `Agent` is "passing through" to this instance. | ||
| if (typeof (options ).secureEndpoint === 'boolean') { | ||
| if (typeof options.secureEndpoint === "boolean") { | ||
| return options.secureEndpoint; | ||
| } | ||
| // If no explicit `secure` endpoint, check if `protocol` property is | ||
| // set. This will usually be the case since using a full string URL | ||
| // or `URL` instance should be the most common usage. | ||
| if (typeof options.protocol === 'string') { | ||
| return options.protocol === 'https:'; | ||
| if (typeof options.protocol === "string") { | ||
| return options.protocol === "https:"; | ||
| } | ||
| } | ||
| // Finally, if no `protocol` property was set, then fall back to | ||
| // checking the stack trace of the current call stack, and try to | ||
| // detect the "https" module. | ||
| const { stack } = new Error(); | ||
| if (typeof stack !== 'string') return false; | ||
| return stack.split('\n').some(l => l.indexOf('(https.js:') !== -1 || l.indexOf('node:https:') !== -1); | ||
| if (typeof stack !== "string") return false; | ||
| return stack.split("\n").some((l) => l.indexOf("(https.js:") !== -1 || l.indexOf("node:https:") !== -1); | ||
| } | ||
| createSocket(req, options, cb) { | ||
| const connectOpts = { | ||
| ...options, | ||
| secureEndpoint: this.isSecureEndpoint(options), | ||
| secureEndpoint: this.isSecureEndpoint(options) | ||
| }; | ||
| Promise.resolve() | ||
| .then(() => this.connect(req, connectOpts)) | ||
| .then(socket => { | ||
| if (socket instanceof http.Agent) { | ||
| // @ts-expect-error `addRequest()` isn't defined in `@types/node` | ||
| return socket.addRequest(req, connectOpts); | ||
| } | ||
| this[INTERNAL].currentSocket = socket; | ||
| // @ts-expect-error `createSocket()` isn't defined in `@types/node` | ||
| super.createSocket(req, options, cb); | ||
| }, cb); | ||
| Promise.resolve().then(() => this.connect(req, connectOpts)).then((socket) => { | ||
| if (socket instanceof http.Agent) { | ||
| return socket.addRequest(req, connectOpts); | ||
| } | ||
| this[INTERNAL].currentSocket = socket; | ||
| super.createSocket(req, options, cb); | ||
| }, cb); | ||
| } | ||
| createConnection() { | ||
| const socket = this[INTERNAL].currentSocket; | ||
| this[INTERNAL].currentSocket = undefined; | ||
| this[INTERNAL].currentSocket = void 0; | ||
| if (!socket) { | ||
| throw new Error('No socket was returned in the `connect()` function'); | ||
| throw new Error("No socket was returned in the `connect()` function"); | ||
| } | ||
| return socket; | ||
| } | ||
| get defaultPort() { | ||
| return this[INTERNAL].defaultPort ?? (this.protocol === 'https:' ? 443 : 80); | ||
| return this[INTERNAL].defaultPort ?? (this.protocol === "https:" ? 443 : 80); | ||
| } | ||
| set defaultPort(v) { | ||
@@ -109,7 +58,5 @@ if (this[INTERNAL]) { | ||
| } | ||
| get protocol() { | ||
| return this[INTERNAL].protocol ?? (this.isSecureEndpoint() ? 'https:' : 'http:'); | ||
| return this[INTERNAL].protocol ?? (this.isSecureEndpoint() ? "https:" : "http:"); | ||
| } | ||
| set protocol(v) { | ||
@@ -116,0 +63,0 @@ if (this[INTERNAL]) { |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"base.js","sources":["../../../src/proxy/base.ts"],"sourcesContent":["/**\n * This code was originally forked from https://github.com/TooTallNate/proxy-agents/tree/b133295fd16f6475578b6b15bd9b4e33ecb0d0b7\n * With the following LICENSE:\n *\n * (The MIT License)\n *\n * Copyright (c) 2013 Nathan Rajlich <nathan@tootallnate.net>*\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * 'Software'), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:*\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.*\n *\n * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/* eslint-disable @typescript-eslint/explicit-member-accessibility */\n/* eslint-disable @typescript-eslint/member-ordering */\n/* eslint-disable jsdoc/require-jsdoc */\nimport * as http from 'node:http';\nimport type * as net from 'node:net';\nimport type { Duplex } from 'node:stream';\nimport type * as tls from 'node:tls';\n\nexport * from './helpers';\n\ninterface HttpConnectOpts extends net.TcpNetConnectOpts {\n secureEndpoint: false;\n protocol?: string;\n}\n\ninterface HttpsConnectOpts extends tls.ConnectionOptions {\n secureEndpoint: true;\n protocol?: string;\n port: number;\n}\n\nexport type AgentConnectOpts = HttpConnectOpts | HttpsConnectOpts;\n\nconst INTERNAL = Symbol('AgentBaseInternalState');\n\ninterface InternalState {\n defaultPort?: number;\n protocol?: string;\n currentSocket?: Duplex;\n}\n\nexport abstract class Agent extends http.Agent {\n private [INTERNAL]: InternalState;\n\n // Set by `http.Agent` - missing from `@types/node`\n options!: Partial<net.TcpNetConnectOpts & tls.ConnectionOptions>;\n keepAlive!: boolean;\n\n constructor(opts?: http.AgentOptions) {\n super(opts);\n this[INTERNAL] = {};\n }\n\n abstract connect(\n req: http.ClientRequest,\n options: AgentConnectOpts,\n ): Promise<Duplex | http.Agent> | Duplex | http.Agent;\n\n /**\n * Determine whether this is an `http` or `https` request.\n */\n isSecureEndpoint(options?: AgentConnectOpts): boolean {\n if (options) {\n // First check the `secureEndpoint` property explicitly, since this\n // means that a parent `Agent` is \"passing through\" to this instance.\n if (typeof (options as Partial<typeof options>).secureEndpoint === 'boolean') {\n return options.secureEndpoint;\n }\n\n // If no explicit `secure` endpoint, check if `protocol` property is\n // set. This will usually be the case since using a full string URL\n // or `URL` instance should be the most common usage.\n if (typeof options.protocol === 'string') {\n return options.protocol === 'https:';\n }\n }\n\n // Finally, if no `protocol` property was set, then fall back to\n // checking the stack trace of the current call stack, and try to\n // detect the \"https\" module.\n const { stack } = new Error();\n if (typeof stack !== 'string') return false;\n return stack.split('\\n').some(l => l.indexOf('(https.js:') !== -1 || l.indexOf('node:https:') !== -1);\n }\n\n createSocket(req: http.ClientRequest, options: AgentConnectOpts, cb: (err: Error | null, s?: Duplex) => void): void {\n const connectOpts = {\n ...options,\n secureEndpoint: this.isSecureEndpoint(options),\n };\n Promise.resolve()\n .then(() => this.connect(req, connectOpts))\n .then(socket => {\n if (socket instanceof http.Agent) {\n // @ts-expect-error `addRequest()` isn't defined in `@types/node`\n return socket.addRequest(req, connectOpts);\n }\n this[INTERNAL].currentSocket = socket;\n // @ts-expect-error `createSocket()` isn't defined in `@types/node`\n super.createSocket(req, options, cb);\n }, cb);\n }\n\n createConnection(): Duplex {\n const socket = this[INTERNAL].currentSocket;\n this[INTERNAL].currentSocket = undefined;\n if (!socket) {\n throw new Error('No socket was returned in the `connect()` function');\n }\n return socket;\n }\n\n get defaultPort(): number {\n return this[INTERNAL].defaultPort ?? (this.protocol === 'https:' ? 443 : 80);\n }\n\n set defaultPort(v: number) {\n if (this[INTERNAL]) {\n this[INTERNAL].defaultPort = v;\n }\n }\n\n get protocol(): string {\n return this[INTERNAL].protocol ?? (this.isSecureEndpoint() ? 'https:' : 'http:');\n }\n\n set protocol(v: string) {\n if (this[INTERNAL]) {\n this[INTERNAL].protocol = v;\n }\n }\n}\n"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"base.js","sources":["../../../src/proxy/base.ts"],"sourcesContent":["/**\n * This code was originally forked from https://github.com/TooTallNate/proxy-agents/tree/b133295fd16f6475578b6b15bd9b4e33ecb0d0b7\n * With the following LICENSE:\n *\n * (The MIT License)\n *\n * Copyright (c) 2013 Nathan Rajlich <nathan@tootallnate.net>*\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * 'Software'), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:*\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.*\n *\n * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/* eslint-disable @typescript-eslint/explicit-member-accessibility */\n/* eslint-disable @typescript-eslint/member-ordering */\n/* eslint-disable jsdoc/require-jsdoc */\nimport * as http from 'node:http';\nimport type * as net from 'node:net';\nimport type { Duplex } from 'node:stream';\nimport type * as tls from 'node:tls';\n\nexport * from './helpers';\n\ninterface HttpConnectOpts extends net.TcpNetConnectOpts {\n secureEndpoint: false;\n protocol?: string;\n}\n\ninterface HttpsConnectOpts extends tls.ConnectionOptions {\n secureEndpoint: true;\n protocol?: string;\n port: number;\n}\n\nexport type AgentConnectOpts = HttpConnectOpts | HttpsConnectOpts;\n\nconst INTERNAL = Symbol('AgentBaseInternalState');\n\ninterface InternalState {\n defaultPort?: number;\n protocol?: string;\n currentSocket?: Duplex;\n}\n\nexport abstract class Agent extends http.Agent {\n private [INTERNAL]: InternalState;\n\n // Set by `http.Agent` - missing from `@types/node`\n options!: Partial<net.TcpNetConnectOpts & tls.ConnectionOptions>;\n keepAlive!: boolean;\n\n constructor(opts?: http.AgentOptions) {\n super(opts);\n this[INTERNAL] = {};\n }\n\n abstract connect(\n req: http.ClientRequest,\n options: AgentConnectOpts,\n ): Promise<Duplex | http.Agent> | Duplex | http.Agent;\n\n /**\n * Determine whether this is an `http` or `https` request.\n */\n isSecureEndpoint(options?: AgentConnectOpts): boolean {\n if (options) {\n // First check the `secureEndpoint` property explicitly, since this\n // means that a parent `Agent` is \"passing through\" to this instance.\n if (typeof (options as Partial<typeof options>).secureEndpoint === 'boolean') {\n return options.secureEndpoint;\n }\n\n // If no explicit `secure` endpoint, check if `protocol` property is\n // set. This will usually be the case since using a full string URL\n // or `URL` instance should be the most common usage.\n if (typeof options.protocol === 'string') {\n return options.protocol === 'https:';\n }\n }\n\n // Finally, if no `protocol` property was set, then fall back to\n // checking the stack trace of the current call stack, and try to\n // detect the \"https\" module.\n const { stack } = new Error();\n if (typeof stack !== 'string') return false;\n return stack.split('\\n').some(l => l.indexOf('(https.js:') !== -1 || l.indexOf('node:https:') !== -1);\n }\n\n createSocket(req: http.ClientRequest, options: AgentConnectOpts, cb: (err: Error | null, s?: Duplex) => void): void {\n const connectOpts = {\n ...options,\n secureEndpoint: this.isSecureEndpoint(options),\n };\n Promise.resolve()\n .then(() => this.connect(req, connectOpts))\n .then(socket => {\n if (socket instanceof http.Agent) {\n // @ts-expect-error `addRequest()` isn't defined in `@types/node`\n return socket.addRequest(req, connectOpts);\n }\n this[INTERNAL].currentSocket = socket;\n // @ts-expect-error `createSocket()` isn't defined in `@types/node`\n super.createSocket(req, options, cb);\n }, cb);\n }\n\n createConnection(): Duplex {\n const socket = this[INTERNAL].currentSocket;\n this[INTERNAL].currentSocket = undefined;\n if (!socket) {\n throw new Error('No socket was returned in the `connect()` function');\n }\n return socket;\n }\n\n get defaultPort(): number {\n return this[INTERNAL].defaultPort ?? (this.protocol === 'https:' ? 443 : 80);\n }\n\n set defaultPort(v: number) {\n if (this[INTERNAL]) {\n this[INTERNAL].defaultPort = v;\n }\n }\n\n get protocol(): string {\n return this[INTERNAL].protocol ?? (this.isSecureEndpoint() ? 'https:' : 'http:');\n }\n\n set protocol(v: string) {\n if (this[INTERNAL]) {\n this[INTERNAL].protocol = v;\n }\n }\n}\n"],"names":[],"mappings":"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"} |
+34
-95
@@ -8,39 +8,21 @@ import * as net from 'node:net'; | ||
| function debugLog(...args) { | ||
| debug.log('[https-proxy-agent]', ...args); | ||
| debug.log("[https-proxy-agent]", ...args); | ||
| } | ||
| /** | ||
| * The `HttpsProxyAgent` implements an HTTP Agent subclass that connects to | ||
| * the specified "HTTP(s) proxy server" in order to proxy HTTPS requests. | ||
| * | ||
| * Outgoing HTTP requests are first tunneled through the proxy server using the | ||
| * `CONNECT` HTTP request method to establish a connection to the proxy server, | ||
| * and then the proxy server connects to the destination target and issues the | ||
| * HTTP request from the proxy server. | ||
| * | ||
| * `https:` requests have their socket connection upgraded to TLS once | ||
| * the connection to the proxy server has been established. | ||
| */ | ||
| class HttpsProxyAgent extends Agent { | ||
| static __initStatic() {this.protocols = ['http', 'https']; } | ||
| constructor(proxy, opts) { | ||
| super(opts); | ||
| this.options = {}; | ||
| this.proxy = typeof proxy === 'string' ? new URL(proxy) : proxy; | ||
| this.proxy = typeof proxy === "string" ? new URL(proxy) : proxy; | ||
| this.proxyHeaders = opts?.headers ?? {}; | ||
| debugLog('Creating new HttpsProxyAgent instance: %o', this.proxy.href); | ||
| // Trim off the brackets from IPv6 addresses | ||
| const host = (this.proxy.hostname || this.proxy.host).replace(/^\[|\]$/g, ''); | ||
| const port = this.proxy.port ? parseInt(this.proxy.port, 10) : this.proxy.protocol === 'https:' ? 443 : 80; | ||
| debugLog("Creating new HttpsProxyAgent instance: %o", this.proxy.href); | ||
| const host = (this.proxy.hostname || this.proxy.host).replace(/^\[|\]$/g, ""); | ||
| const port = this.proxy.port ? parseInt(this.proxy.port, 10) : this.proxy.protocol === "https:" ? 443 : 80; | ||
| this.connectOpts = { | ||
| // Attempt to negotiate http/1.1 for proxy servers that support http/2 | ||
| ALPNProtocols: ['http/1.1'], | ||
| ...(opts ? omit(opts, 'headers') : null), | ||
| ALPNProtocols: ["http/1.1"], | ||
| ...opts ? omit(opts, "headers") : null, | ||
| host, | ||
| port, | ||
| port | ||
| }; | ||
| } | ||
| /** | ||
@@ -52,112 +34,69 @@ * Called when the node-core HTTP client library is creating a | ||
| const { proxy } = this; | ||
| if (!opts.host) { | ||
| throw new TypeError('No "host" provided'); | ||
| } | ||
| // Create a socket connection to the proxy server. | ||
| let socket; | ||
| if (proxy.protocol === 'https:') { | ||
| debugLog('Creating `tls.Socket`: %o', this.connectOpts); | ||
| if (proxy.protocol === "https:") { | ||
| debugLog("Creating `tls.Socket`: %o", this.connectOpts); | ||
| const servername = this.connectOpts.servername || this.connectOpts.host; | ||
| socket = tls.connect({ | ||
| ...this.connectOpts, | ||
| servername: servername && net.isIP(servername) ? undefined : servername, | ||
| servername: servername && net.isIP(servername) ? void 0 : servername | ||
| }); | ||
| } else { | ||
| debugLog('Creating `net.Socket`: %o', this.connectOpts); | ||
| debugLog("Creating `net.Socket`: %o", this.connectOpts); | ||
| socket = net.connect(this.connectOpts); | ||
| } | ||
| const headers = | ||
| typeof this.proxyHeaders === 'function' ? this.proxyHeaders() : { ...this.proxyHeaders }; | ||
| const headers = typeof this.proxyHeaders === "function" ? this.proxyHeaders() : { ...this.proxyHeaders }; | ||
| const host = net.isIPv6(opts.host) ? `[${opts.host}]` : opts.host; | ||
| let payload = `CONNECT ${host}:${opts.port} HTTP/1.1\r\n`; | ||
| // Inject the `Proxy-Authorization` header if necessary. | ||
| let payload = `CONNECT ${host}:${opts.port} HTTP/1.1\r | ||
| `; | ||
| if (proxy.username || proxy.password) { | ||
| const auth = `${decodeURIComponent(proxy.username)}:${decodeURIComponent(proxy.password)}`; | ||
| headers['Proxy-Authorization'] = `Basic ${Buffer.from(auth).toString('base64')}`; | ||
| headers["Proxy-Authorization"] = `Basic ${Buffer.from(auth).toString("base64")}`; | ||
| } | ||
| headers.Host = `${host}:${opts.port}`; | ||
| if (!headers['Proxy-Connection']) { | ||
| headers['Proxy-Connection'] = this.keepAlive ? 'Keep-Alive' : 'close'; | ||
| if (!headers["Proxy-Connection"]) { | ||
| headers["Proxy-Connection"] = this.keepAlive ? "Keep-Alive" : "close"; | ||
| } | ||
| for (const name of Object.keys(headers)) { | ||
| payload += `${name}: ${headers[name]}\r\n`; | ||
| payload += `${name}: ${headers[name]}\r | ||
| `; | ||
| } | ||
| const proxyResponsePromise = parseProxyResponse(socket); | ||
| socket.write(`${payload}\r\n`); | ||
| socket.write(`${payload}\r | ||
| `); | ||
| const { connect, buffered } = await proxyResponsePromise; | ||
| req.emit('proxyConnect', connect); | ||
| // eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
| // @ts-ignore Not EventEmitter in Node types | ||
| this.emit('proxyConnect', connect, req); | ||
| req.emit("proxyConnect", connect); | ||
| this.emit("proxyConnect", connect, req); | ||
| if (connect.statusCode === 200) { | ||
| req.once('socket', resume); | ||
| req.once("socket", resume); | ||
| if (opts.secureEndpoint) { | ||
| // The proxy is connecting to a TLS server, so upgrade | ||
| // this socket connection to a TLS connection. | ||
| debugLog('Upgrading socket connection to TLS'); | ||
| debugLog("Upgrading socket connection to TLS"); | ||
| const servername = opts.servername || opts.host; | ||
| return tls.connect({ | ||
| ...omit(opts, 'host', 'path', 'port'), | ||
| ...omit(opts, "host", "path", "port"), | ||
| socket, | ||
| servername: net.isIP(servername) ? undefined : servername, | ||
| servername: net.isIP(servername) ? void 0 : servername | ||
| }); | ||
| } | ||
| return socket; | ||
| } | ||
| // Some other status code that's not 200... need to re-play the HTTP | ||
| // header "data" events onto the socket once the HTTP machinery is | ||
| // attached so that the node core `http` can parse and handle the | ||
| // error status code. | ||
| // Close the original socket, and a new "fake" socket is returned | ||
| // instead, so that the proxy doesn't get the HTTP request | ||
| // written to it (which may contain `Authorization` headers or other | ||
| // sensitive data). | ||
| // | ||
| // See: https://hackerone.com/reports/541502 | ||
| socket.destroy(); | ||
| const fakeSocket = new net.Socket({ writable: false }); | ||
| fakeSocket.readable = true; | ||
| // Need to wait for the "socket" event to re-play the "data" events. | ||
| req.once('socket', (s) => { | ||
| debugLog('Replaying proxy buffer for failed request'); | ||
| // Replay the "buffered" Buffer onto the fake `socket`, since at | ||
| // this point the HTTP module machinery has been hooked up for | ||
| // the user. | ||
| req.once("socket", (s) => { | ||
| debugLog("Replaying proxy buffer for failed request"); | ||
| s.push(buffered); | ||
| s.push(null); | ||
| }); | ||
| return fakeSocket; | ||
| } | ||
| } HttpsProxyAgent.__initStatic(); | ||
| } | ||
| HttpsProxyAgent.protocols = ["http", "https"]; | ||
| function resume(socket) { | ||
| socket.resume(); | ||
| } | ||
| function omit( | ||
| obj, | ||
| ...keys | ||
| ) | ||
| { | ||
| const ret = {} | ||
| ; | ||
| function omit(obj, ...keys) { | ||
| const ret = {}; | ||
| let key; | ||
@@ -164,0 +103,0 @@ for (key in obj) { |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.js","sources":["../../../src/proxy/index.ts"],"sourcesContent":["/**\n * This code was originally forked from https://github.com/TooTallNate/proxy-agents/tree/b133295fd16f6475578b6b15bd9b4e33ecb0d0b7\n * With the following LICENSE:\n *\n * (The MIT License)\n *\n * Copyright (c) 2013 Nathan Rajlich <nathan@tootallnate.net>*\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * 'Software'), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:*\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.*\n *\n * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/* eslint-disable @typescript-eslint/explicit-member-accessibility */\n/* eslint-disable @typescript-eslint/no-unused-vars */\n// eslint-disable-next-line import/no-duplicates\nimport type * as http from 'node:http';\n// eslint-disable-next-line import/no-duplicates\nimport type { OutgoingHttpHeaders } from 'node:http';\nimport * as net from 'node:net';\nimport * as tls from 'node:tls';\nimport { debug } from '@sentry/core';\nimport type { AgentConnectOpts } from './base';\nimport { Agent } from './base';\nimport { parseProxyResponse } from './parse-proxy-response';\n\nfunction debugLog(...args: unknown[]): void {\n debug.log('[https-proxy-agent]', ...args);\n}\n\ntype Protocol<T> = T extends `${infer Protocol}:${infer _}` ? Protocol : never;\n\ntype ConnectOptsMap = {\n http: Omit<net.TcpNetConnectOpts, 'host' | 'port'>;\n https: Omit<tls.ConnectionOptions, 'host' | 'port'>;\n};\n\ntype ConnectOpts<T> = {\n [P in keyof ConnectOptsMap]: Protocol<T> extends P ? ConnectOptsMap[P] : never;\n}[keyof ConnectOptsMap];\n\nexport type HttpsProxyAgentOptions<T> = ConnectOpts<T> &\n http.AgentOptions & {\n headers?: OutgoingHttpHeaders | (() => OutgoingHttpHeaders);\n };\n\n/**\n * The `HttpsProxyAgent` implements an HTTP Agent subclass that connects to\n * the specified \"HTTP(s) proxy server\" in order to proxy HTTPS requests.\n *\n * Outgoing HTTP requests are first tunneled through the proxy server using the\n * `CONNECT` HTTP request method to establish a connection to the proxy server,\n * and then the proxy server connects to the destination target and issues the\n * HTTP request from the proxy server.\n *\n * `https:` requests have their socket connection upgraded to TLS once\n * the connection to the proxy server has been established.\n */\nexport class HttpsProxyAgent<Uri extends string> extends Agent {\n static protocols = ['http', 'https'] as const;\n\n readonly proxy: URL;\n proxyHeaders: OutgoingHttpHeaders | (() => OutgoingHttpHeaders);\n connectOpts: net.TcpNetConnectOpts & tls.ConnectionOptions;\n\n constructor(proxy: Uri | URL, opts?: HttpsProxyAgentOptions<Uri>) {\n super(opts);\n this.options = {};\n this.proxy = typeof proxy === 'string' ? new URL(proxy) : proxy;\n this.proxyHeaders = opts?.headers ?? {};\n debugLog('Creating new HttpsProxyAgent instance: %o', this.proxy.href);\n\n // Trim off the brackets from IPv6 addresses\n const host = (this.proxy.hostname || this.proxy.host).replace(/^\\[|\\]$/g, '');\n const port = this.proxy.port ? parseInt(this.proxy.port, 10) : this.proxy.protocol === 'https:' ? 443 : 80;\n this.connectOpts = {\n // Attempt to negotiate http/1.1 for proxy servers that support http/2\n ALPNProtocols: ['http/1.1'],\n ...(opts ? omit(opts, 'headers') : null),\n host,\n port,\n };\n }\n\n /**\n * Called when the node-core HTTP client library is creating a\n * new HTTP request.\n */\n async connect(req: http.ClientRequest, opts: AgentConnectOpts): Promise<net.Socket> {\n const { proxy } = this;\n\n if (!opts.host) {\n throw new TypeError('No \"host\" provided');\n }\n\n // Create a socket connection to the proxy server.\n let socket: net.Socket;\n if (proxy.protocol === 'https:') {\n debugLog('Creating `tls.Socket`: %o', this.connectOpts);\n const servername = this.connectOpts.servername || this.connectOpts.host;\n socket = tls.connect({\n ...this.connectOpts,\n servername: servername && net.isIP(servername) ? undefined : servername,\n });\n } else {\n debugLog('Creating `net.Socket`: %o', this.connectOpts);\n socket = net.connect(this.connectOpts);\n }\n\n const headers: OutgoingHttpHeaders =\n typeof this.proxyHeaders === 'function' ? this.proxyHeaders() : { ...this.proxyHeaders };\n const host = net.isIPv6(opts.host) ? `[${opts.host}]` : opts.host;\n let payload = `CONNECT ${host}:${opts.port} HTTP/1.1\\r\\n`;\n\n // Inject the `Proxy-Authorization` header if necessary.\n if (proxy.username || proxy.password) {\n const auth = `${decodeURIComponent(proxy.username)}:${decodeURIComponent(proxy.password)}`;\n headers['Proxy-Authorization'] = `Basic ${Buffer.from(auth).toString('base64')}`;\n }\n\n headers.Host = `${host}:${opts.port}`;\n\n if (!headers['Proxy-Connection']) {\n headers['Proxy-Connection'] = this.keepAlive ? 'Keep-Alive' : 'close';\n }\n for (const name of Object.keys(headers)) {\n payload += `${name}: ${headers[name]}\\r\\n`;\n }\n\n const proxyResponsePromise = parseProxyResponse(socket);\n\n socket.write(`${payload}\\r\\n`);\n\n const { connect, buffered } = await proxyResponsePromise;\n req.emit('proxyConnect', connect);\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore Not EventEmitter in Node types\n this.emit('proxyConnect', connect, req);\n\n if (connect.statusCode === 200) {\n req.once('socket', resume);\n\n if (opts.secureEndpoint) {\n // The proxy is connecting to a TLS server, so upgrade\n // this socket connection to a TLS connection.\n debugLog('Upgrading socket connection to TLS');\n const servername = opts.servername || opts.host;\n return tls.connect({\n ...omit(opts, 'host', 'path', 'port'),\n socket,\n servername: net.isIP(servername) ? undefined : servername,\n });\n }\n\n return socket;\n }\n\n // Some other status code that's not 200... need to re-play the HTTP\n // header \"data\" events onto the socket once the HTTP machinery is\n // attached so that the node core `http` can parse and handle the\n // error status code.\n\n // Close the original socket, and a new \"fake\" socket is returned\n // instead, so that the proxy doesn't get the HTTP request\n // written to it (which may contain `Authorization` headers or other\n // sensitive data).\n //\n // See: https://hackerone.com/reports/541502\n socket.destroy();\n\n const fakeSocket = new net.Socket({ writable: false });\n fakeSocket.readable = true;\n\n // Need to wait for the \"socket\" event to re-play the \"data\" events.\n req.once('socket', (s: net.Socket) => {\n debugLog('Replaying proxy buffer for failed request');\n // Replay the \"buffered\" Buffer onto the fake `socket`, since at\n // this point the HTTP module machinery has been hooked up for\n // the user.\n s.push(buffered);\n s.push(null);\n });\n\n return fakeSocket;\n }\n}\n\nfunction resume(socket: net.Socket | tls.TLSSocket): void {\n socket.resume();\n}\n\nfunction omit<T extends object, K extends [...(keyof T)[]]>(\n obj: T,\n ...keys: K\n): {\n [K2 in Exclude<keyof T, K[number]>]: T[K2];\n} {\n const ret = {} as {\n [K in keyof typeof obj]: (typeof obj)[K];\n };\n let key: keyof typeof obj;\n for (key in obj) {\n if (!keys.includes(key)) {\n ret[key] = obj[key];\n }\n }\n return ret;\n}\n"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"index.js","sources":["../../../src/proxy/index.ts"],"sourcesContent":["/**\n * This code was originally forked from https://github.com/TooTallNate/proxy-agents/tree/b133295fd16f6475578b6b15bd9b4e33ecb0d0b7\n * With the following LICENSE:\n *\n * (The MIT License)\n *\n * Copyright (c) 2013 Nathan Rajlich <nathan@tootallnate.net>*\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * 'Software'), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:*\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.*\n *\n * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/* eslint-disable @typescript-eslint/explicit-member-accessibility */\n/* eslint-disable @typescript-eslint/no-unused-vars */\n// eslint-disable-next-line import/no-duplicates\nimport type * as http from 'node:http';\n// eslint-disable-next-line import/no-duplicates\nimport type { OutgoingHttpHeaders } from 'node:http';\nimport * as net from 'node:net';\nimport * as tls from 'node:tls';\nimport { debug } from '@sentry/core';\nimport type { AgentConnectOpts } from './base';\nimport { Agent } from './base';\nimport { parseProxyResponse } from './parse-proxy-response';\n\nfunction debugLog(...args: unknown[]): void {\n debug.log('[https-proxy-agent]', ...args);\n}\n\ntype Protocol<T> = T extends `${infer Protocol}:${infer _}` ? Protocol : never;\n\ntype ConnectOptsMap = {\n http: Omit<net.TcpNetConnectOpts, 'host' | 'port'>;\n https: Omit<tls.ConnectionOptions, 'host' | 'port'>;\n};\n\ntype ConnectOpts<T> = {\n [P in keyof ConnectOptsMap]: Protocol<T> extends P ? ConnectOptsMap[P] : never;\n}[keyof ConnectOptsMap];\n\nexport type HttpsProxyAgentOptions<T> = ConnectOpts<T> &\n http.AgentOptions & {\n headers?: OutgoingHttpHeaders | (() => OutgoingHttpHeaders);\n };\n\n/**\n * The `HttpsProxyAgent` implements an HTTP Agent subclass that connects to\n * the specified \"HTTP(s) proxy server\" in order to proxy HTTPS requests.\n *\n * Outgoing HTTP requests are first tunneled through the proxy server using the\n * `CONNECT` HTTP request method to establish a connection to the proxy server,\n * and then the proxy server connects to the destination target and issues the\n * HTTP request from the proxy server.\n *\n * `https:` requests have their socket connection upgraded to TLS once\n * the connection to the proxy server has been established.\n */\nexport class HttpsProxyAgent<Uri extends string> extends Agent {\n static protocols = ['http', 'https'] as const;\n\n readonly proxy: URL;\n proxyHeaders: OutgoingHttpHeaders | (() => OutgoingHttpHeaders);\n connectOpts: net.TcpNetConnectOpts & tls.ConnectionOptions;\n\n constructor(proxy: Uri | URL, opts?: HttpsProxyAgentOptions<Uri>) {\n super(opts);\n this.options = {};\n this.proxy = typeof proxy === 'string' ? new URL(proxy) : proxy;\n this.proxyHeaders = opts?.headers ?? {};\n debugLog('Creating new HttpsProxyAgent instance: %o', this.proxy.href);\n\n // Trim off the brackets from IPv6 addresses\n const host = (this.proxy.hostname || this.proxy.host).replace(/^\\[|\\]$/g, '');\n const port = this.proxy.port ? parseInt(this.proxy.port, 10) : this.proxy.protocol === 'https:' ? 443 : 80;\n this.connectOpts = {\n // Attempt to negotiate http/1.1 for proxy servers that support http/2\n ALPNProtocols: ['http/1.1'],\n ...(opts ? omit(opts, 'headers') : null),\n host,\n port,\n };\n }\n\n /**\n * Called when the node-core HTTP client library is creating a\n * new HTTP request.\n */\n async connect(req: http.ClientRequest, opts: AgentConnectOpts): Promise<net.Socket> {\n const { proxy } = this;\n\n if (!opts.host) {\n throw new TypeError('No \"host\" provided');\n }\n\n // Create a socket connection to the proxy server.\n let socket: net.Socket;\n if (proxy.protocol === 'https:') {\n debugLog('Creating `tls.Socket`: %o', this.connectOpts);\n const servername = this.connectOpts.servername || this.connectOpts.host;\n socket = tls.connect({\n ...this.connectOpts,\n servername: servername && net.isIP(servername) ? undefined : servername,\n });\n } else {\n debugLog('Creating `net.Socket`: %o', this.connectOpts);\n socket = net.connect(this.connectOpts);\n }\n\n const headers: OutgoingHttpHeaders =\n typeof this.proxyHeaders === 'function' ? this.proxyHeaders() : { ...this.proxyHeaders };\n const host = net.isIPv6(opts.host) ? `[${opts.host}]` : opts.host;\n let payload = `CONNECT ${host}:${opts.port} HTTP/1.1\\r\\n`;\n\n // Inject the `Proxy-Authorization` header if necessary.\n if (proxy.username || proxy.password) {\n const auth = `${decodeURIComponent(proxy.username)}:${decodeURIComponent(proxy.password)}`;\n headers['Proxy-Authorization'] = `Basic ${Buffer.from(auth).toString('base64')}`;\n }\n\n headers.Host = `${host}:${opts.port}`;\n\n if (!headers['Proxy-Connection']) {\n headers['Proxy-Connection'] = this.keepAlive ? 'Keep-Alive' : 'close';\n }\n for (const name of Object.keys(headers)) {\n payload += `${name}: ${headers[name]}\\r\\n`;\n }\n\n const proxyResponsePromise = parseProxyResponse(socket);\n\n socket.write(`${payload}\\r\\n`);\n\n const { connect, buffered } = await proxyResponsePromise;\n req.emit('proxyConnect', connect);\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore Not EventEmitter in Node types\n this.emit('proxyConnect', connect, req);\n\n if (connect.statusCode === 200) {\n req.once('socket', resume);\n\n if (opts.secureEndpoint) {\n // The proxy is connecting to a TLS server, so upgrade\n // this socket connection to a TLS connection.\n debugLog('Upgrading socket connection to TLS');\n const servername = opts.servername || opts.host;\n return tls.connect({\n ...omit(opts, 'host', 'path', 'port'),\n socket,\n servername: net.isIP(servername) ? undefined : servername,\n });\n }\n\n return socket;\n }\n\n // Some other status code that's not 200... need to re-play the HTTP\n // header \"data\" events onto the socket once the HTTP machinery is\n // attached so that the node core `http` can parse and handle the\n // error status code.\n\n // Close the original socket, and a new \"fake\" socket is returned\n // instead, so that the proxy doesn't get the HTTP request\n // written to it (which may contain `Authorization` headers or other\n // sensitive data).\n //\n // See: https://hackerone.com/reports/541502\n socket.destroy();\n\n const fakeSocket = new net.Socket({ writable: false });\n fakeSocket.readable = true;\n\n // Need to wait for the \"socket\" event to re-play the \"data\" events.\n req.once('socket', (s: net.Socket) => {\n debugLog('Replaying proxy buffer for failed request');\n // Replay the \"buffered\" Buffer onto the fake `socket`, since at\n // this point the HTTP module machinery has been hooked up for\n // the user.\n s.push(buffered);\n s.push(null);\n });\n\n return fakeSocket;\n }\n}\n\nfunction resume(socket: net.Socket | tls.TLSSocket): void {\n socket.resume();\n}\n\nfunction omit<T extends object, K extends [...(keyof T)[]]>(\n obj: T,\n ...keys: K\n): {\n [K2 in Exclude<keyof T, K[number]>]: T[K2];\n} {\n const ret = {} as {\n [K in keyof typeof obj]: (typeof obj)[K];\n };\n let key: keyof typeof obj;\n for (key in obj) {\n if (!keys.includes(key)) {\n ret[key] = obj[key];\n }\n }\n return ret;\n}\n"],"names":[],"mappings":"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"} |
| import { debug } from '@sentry/core'; | ||
| function debugLog(...args) { | ||
| debug.log('[https-proxy-agent:parse-proxy-response]', ...args); | ||
| debug.log("[https-proxy-agent:parse-proxy-response]", ...args); | ||
| } | ||
| function parseProxyResponse(socket) { | ||
| return new Promise((resolve, reject) => { | ||
| // we need to buffer any HTTP traffic that happens with the proxy before we get | ||
| // the CONNECT response, so that if the response is anything other than an "200" | ||
| // response code, then we can re-play the "data" events on the socket once the | ||
| // HTTP parser is hooked up... | ||
| let buffersLength = 0; | ||
| const buffers = []; | ||
| function read() { | ||
| const b = socket.read(); | ||
| if (b) ondata(b); | ||
| else socket.once('readable', read); | ||
| else socket.once("readable", read); | ||
| } | ||
| function cleanup() { | ||
| socket.removeListener('end', onend); | ||
| socket.removeListener('error', onerror); | ||
| socket.removeListener('readable', read); | ||
| socket.removeListener("end", onend); | ||
| socket.removeListener("error", onerror); | ||
| socket.removeListener("readable", read); | ||
| } | ||
| function onend() { | ||
| cleanup(); | ||
| debugLog('onend'); | ||
| reject(new Error('Proxy connection ended before receiving CONNECT response')); | ||
| debugLog("onend"); | ||
| reject(new Error("Proxy connection ended before receiving CONNECT response")); | ||
| } | ||
| function onerror(err) { | ||
| cleanup(); | ||
| debugLog('onerror %o', err); | ||
| debugLog("onerror %o", err); | ||
| reject(err); | ||
| } | ||
| function ondata(b) { | ||
| buffers.push(b); | ||
| buffersLength += b.length; | ||
| const buffered = Buffer.concat(buffers, buffersLength); | ||
| const endOfHeaders = buffered.indexOf('\r\n\r\n'); | ||
| const endOfHeaders = buffered.indexOf("\r\n\r\n"); | ||
| if (endOfHeaders === -1) { | ||
| // keep buffering | ||
| debugLog('have not received end of HTTP headers yet...'); | ||
| debugLog("have not received end of HTTP headers yet..."); | ||
| read(); | ||
| return; | ||
| } | ||
| const headerParts = buffered.subarray(0, endOfHeaders).toString('ascii').split('\r\n'); | ||
| const headerParts = buffered.subarray(0, endOfHeaders).toString("ascii").split("\r\n"); | ||
| const firstLine = headerParts.shift(); | ||
| if (!firstLine) { | ||
| socket.destroy(); | ||
| return reject(new Error('No header received from proxy CONNECT response')); | ||
| return reject(new Error("No header received from proxy CONNECT response")); | ||
| } | ||
| const firstLineParts = firstLine.split(' '); | ||
| const firstLineParts = firstLine.split(" "); | ||
| const statusCode = +(firstLineParts[1] || 0); | ||
| const statusText = firstLineParts.slice(2).join(' '); | ||
| const statusText = firstLineParts.slice(2).join(" "); | ||
| const headers = {}; | ||
| for (const header of headerParts) { | ||
| if (!header) continue; | ||
| const firstColon = header.indexOf(':'); | ||
| const firstColon = header.indexOf(":"); | ||
| if (firstColon === -1) { | ||
@@ -74,3 +60,3 @@ socket.destroy(); | ||
| const current = headers[key]; | ||
| if (typeof current === 'string') { | ||
| if (typeof current === "string") { | ||
| headers[key] = [current, value]; | ||
@@ -83,3 +69,3 @@ } else if (Array.isArray(current)) { | ||
| } | ||
| debugLog('got proxy server response: %o %o', firstLine, headers); | ||
| debugLog("got proxy server response: %o %o", firstLine, headers); | ||
| cleanup(); | ||
@@ -90,11 +76,9 @@ resolve({ | ||
| statusText, | ||
| headers, | ||
| headers | ||
| }, | ||
| buffered, | ||
| buffered | ||
| }); | ||
| } | ||
| socket.on('error', onerror); | ||
| socket.on('end', onend); | ||
| socket.on("error", onerror); | ||
| socket.on("end", onend); | ||
| read(); | ||
@@ -101,0 +85,0 @@ }); |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"parse-proxy-response.js","sources":["../../../src/proxy/parse-proxy-response.ts"],"sourcesContent":["/**\n * This code was originally forked from https://github.com/TooTallNate/proxy-agents/tree/b133295fd16f6475578b6b15bd9b4e33ecb0d0b7\n * With the following LICENSE:\n *\n * (The MIT License)\n *\n * Copyright (c) 2013 Nathan Rajlich <nathan@tootallnate.net>*\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * 'Software'), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:*\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.*\n *\n * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/* eslint-disable @typescript-eslint/explicit-function-return-type */\n/* eslint-disable jsdoc/require-jsdoc */\nimport type { IncomingHttpHeaders } from 'node:http';\nimport type { Readable } from 'node:stream';\nimport { debug } from '@sentry/core';\n\nfunction debugLog(...args: unknown[]): void {\n debug.log('[https-proxy-agent:parse-proxy-response]', ...args);\n}\n\nexport interface ConnectResponse {\n statusCode: number;\n statusText: string;\n headers: IncomingHttpHeaders;\n}\n\nexport function parseProxyResponse(socket: Readable): Promise<{ connect: ConnectResponse; buffered: Buffer }> {\n return new Promise((resolve, reject) => {\n // we need to buffer any HTTP traffic that happens with the proxy before we get\n // the CONNECT response, so that if the response is anything other than an \"200\"\n // response code, then we can re-play the \"data\" events on the socket once the\n // HTTP parser is hooked up...\n let buffersLength = 0;\n const buffers: Buffer[] = [];\n\n function read() {\n const b = socket.read();\n if (b) ondata(b);\n else socket.once('readable', read);\n }\n\n function cleanup() {\n socket.removeListener('end', onend);\n socket.removeListener('error', onerror);\n socket.removeListener('readable', read);\n }\n\n function onend() {\n cleanup();\n debugLog('onend');\n reject(new Error('Proxy connection ended before receiving CONNECT response'));\n }\n\n function onerror(err: Error) {\n cleanup();\n debugLog('onerror %o', err);\n reject(err);\n }\n\n function ondata(b: Buffer) {\n buffers.push(b);\n buffersLength += b.length;\n\n const buffered = Buffer.concat(buffers, buffersLength);\n const endOfHeaders = buffered.indexOf('\\r\\n\\r\\n');\n\n if (endOfHeaders === -1) {\n // keep buffering\n debugLog('have not received end of HTTP headers yet...');\n read();\n return;\n }\n\n const headerParts = buffered.subarray(0, endOfHeaders).toString('ascii').split('\\r\\n');\n const firstLine = headerParts.shift();\n if (!firstLine) {\n socket.destroy();\n return reject(new Error('No header received from proxy CONNECT response'));\n }\n const firstLineParts = firstLine.split(' ');\n const statusCode = +(firstLineParts[1] || 0);\n const statusText = firstLineParts.slice(2).join(' ');\n const headers: IncomingHttpHeaders = {};\n for (const header of headerParts) {\n if (!header) continue;\n const firstColon = header.indexOf(':');\n if (firstColon === -1) {\n socket.destroy();\n return reject(new Error(`Invalid header from proxy CONNECT response: \"${header}\"`));\n }\n const key = header.slice(0, firstColon).toLowerCase();\n const value = header.slice(firstColon + 1).trimStart();\n const current = headers[key];\n if (typeof current === 'string') {\n headers[key] = [current, value];\n } else if (Array.isArray(current)) {\n current.push(value);\n } else {\n headers[key] = value;\n }\n }\n debugLog('got proxy server response: %o %o', firstLine, headers);\n cleanup();\n resolve({\n connect: {\n statusCode,\n statusText,\n headers,\n },\n buffered,\n });\n }\n\n socket.on('error', onerror);\n socket.on('end', onend);\n\n read();\n });\n}\n"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"parse-proxy-response.js","sources":["../../../src/proxy/parse-proxy-response.ts"],"sourcesContent":["/**\n * This code was originally forked from https://github.com/TooTallNate/proxy-agents/tree/b133295fd16f6475578b6b15bd9b4e33ecb0d0b7\n * With the following LICENSE:\n *\n * (The MIT License)\n *\n * Copyright (c) 2013 Nathan Rajlich <nathan@tootallnate.net>*\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * 'Software'), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:*\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.*\n *\n * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/* eslint-disable @typescript-eslint/explicit-function-return-type */\n/* eslint-disable jsdoc/require-jsdoc */\nimport type { IncomingHttpHeaders } from 'node:http';\nimport type { Readable } from 'node:stream';\nimport { debug } from '@sentry/core';\n\nfunction debugLog(...args: unknown[]): void {\n debug.log('[https-proxy-agent:parse-proxy-response]', ...args);\n}\n\nexport interface ConnectResponse {\n statusCode: number;\n statusText: string;\n headers: IncomingHttpHeaders;\n}\n\nexport function parseProxyResponse(socket: Readable): Promise<{ connect: ConnectResponse; buffered: Buffer }> {\n return new Promise((resolve, reject) => {\n // we need to buffer any HTTP traffic that happens with the proxy before we get\n // the CONNECT response, so that if the response is anything other than an \"200\"\n // response code, then we can re-play the \"data\" events on the socket once the\n // HTTP parser is hooked up...\n let buffersLength = 0;\n const buffers: Buffer[] = [];\n\n function read() {\n const b = socket.read();\n if (b) ondata(b);\n else socket.once('readable', read);\n }\n\n function cleanup() {\n socket.removeListener('end', onend);\n socket.removeListener('error', onerror);\n socket.removeListener('readable', read);\n }\n\n function onend() {\n cleanup();\n debugLog('onend');\n reject(new Error('Proxy connection ended before receiving CONNECT response'));\n }\n\n function onerror(err: Error) {\n cleanup();\n debugLog('onerror %o', err);\n reject(err);\n }\n\n function ondata(b: Buffer) {\n buffers.push(b);\n buffersLength += b.length;\n\n const buffered = Buffer.concat(buffers, buffersLength);\n const endOfHeaders = buffered.indexOf('\\r\\n\\r\\n');\n\n if (endOfHeaders === -1) {\n // keep buffering\n debugLog('have not received end of HTTP headers yet...');\n read();\n return;\n }\n\n const headerParts = buffered.subarray(0, endOfHeaders).toString('ascii').split('\\r\\n');\n const firstLine = headerParts.shift();\n if (!firstLine) {\n socket.destroy();\n return reject(new Error('No header received from proxy CONNECT response'));\n }\n const firstLineParts = firstLine.split(' ');\n const statusCode = +(firstLineParts[1] || 0);\n const statusText = firstLineParts.slice(2).join(' ');\n const headers: IncomingHttpHeaders = {};\n for (const header of headerParts) {\n if (!header) continue;\n const firstColon = header.indexOf(':');\n if (firstColon === -1) {\n socket.destroy();\n return reject(new Error(`Invalid header from proxy CONNECT response: \"${header}\"`));\n }\n const key = header.slice(0, firstColon).toLowerCase();\n const value = header.slice(firstColon + 1).trimStart();\n const current = headers[key];\n if (typeof current === 'string') {\n headers[key] = [current, value];\n } else if (Array.isArray(current)) {\n current.push(value);\n } else {\n headers[key] = value;\n }\n }\n debugLog('got proxy server response: %o %o', firstLine, headers);\n cleanup();\n resolve({\n connect: {\n statusCode,\n statusText,\n headers,\n },\n buffered,\n });\n }\n\n socket.on('error', onerror);\n socket.on('end', onend);\n\n read();\n });\n}\n"],"names":[],"mappings":"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"} |
+39
-97
| import { createStackParser, nodeStackLineParser, GLOBAL_OBJ } from '@sentry/core'; | ||
| import { createGetModuleFromFilename } from '../utils/module.js'; | ||
| /** | ||
| * Returns a release dynamically from environment variables. | ||
| */ | ||
| // eslint-disable-next-line complexity | ||
| function getSentryRelease(fallback) { | ||
| // Always read first as Sentry takes this as precedence | ||
| if (process.env.SENTRY_RELEASE) { | ||
| return process.env.SENTRY_RELEASE; | ||
| } | ||
| // This supports the variable that sentry-webpack-plugin injects | ||
| if (GLOBAL_OBJ.SENTRY_RELEASE?.id) { | ||
| return GLOBAL_OBJ.SENTRY_RELEASE.id; | ||
| } | ||
| // This list is in approximate alpha order, separated into 3 categories: | ||
| // 1. Git providers | ||
| // 2. CI providers with specific environment variables (has the provider name in the variable name) | ||
| // 3. CI providers with generic environment variables (checked for last to prevent possible false positives) | ||
| const possibleReleaseNameOfGitProvider = | ||
| const possibleReleaseNameOfGitProvider = ( | ||
| // GitHub Actions - https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables#default-environment-variables | ||
| process.env['GITHUB_SHA'] || | ||
| // GitLab CI - https://docs.gitlab.com/ee/ci/variables/predefined_variables.html | ||
| process.env['CI_MERGE_REQUEST_SOURCE_BRANCH_SHA'] || | ||
| process.env['CI_BUILD_REF'] || | ||
| process.env['CI_COMMIT_SHA'] || | ||
| // Bitbucket - https://support.atlassian.com/bitbucket-cloud/docs/variables-and-secrets/ | ||
| process.env['BITBUCKET_COMMIT']; | ||
| const possibleReleaseNameOfCiProvidersWithSpecificEnvVar = | ||
| process.env["GITHUB_SHA"] || // GitLab CI - https://docs.gitlab.com/ee/ci/variables/predefined_variables.html | ||
| process.env["CI_MERGE_REQUEST_SOURCE_BRANCH_SHA"] || process.env["CI_BUILD_REF"] || process.env["CI_COMMIT_SHA"] || // Bitbucket - https://support.atlassian.com/bitbucket-cloud/docs/variables-and-secrets/ | ||
| process.env["BITBUCKET_COMMIT"] | ||
| ); | ||
| const possibleReleaseNameOfCiProvidersWithSpecificEnvVar = ( | ||
| // AppVeyor - https://www.appveyor.com/docs/environment-variables/ | ||
| process.env['APPVEYOR_PULL_REQUEST_HEAD_COMMIT'] || | ||
| process.env['APPVEYOR_REPO_COMMIT'] || | ||
| // AWS CodeBuild - https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-env-vars.html | ||
| process.env['CODEBUILD_RESOLVED_SOURCE_VERSION'] || | ||
| // AWS Amplify - https://docs.aws.amazon.com/amplify/latest/userguide/environment-variables.html | ||
| process.env['AWS_COMMIT_ID'] || | ||
| // Azure Pipelines - https://docs.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml | ||
| process.env['BUILD_SOURCEVERSION'] || | ||
| // Bitrise - https://devcenter.bitrise.io/builds/available-environment-variables/ | ||
| process.env['GIT_CLONE_COMMIT_HASH'] || | ||
| // Buddy CI - https://buddy.works/docs/pipelines/environment-variables#default-environment-variables | ||
| process.env['BUDDY_EXECUTION_REVISION'] || | ||
| // Builtkite - https://buildkite.com/docs/pipelines/environment-variables | ||
| process.env['BUILDKITE_COMMIT'] || | ||
| // CircleCI - https://circleci.com/docs/variables/ | ||
| process.env['CIRCLE_SHA1'] || | ||
| // Cirrus CI - https://cirrus-ci.org/guide/writing-tasks/#environment-variables | ||
| process.env['CIRRUS_CHANGE_IN_REPO'] || | ||
| // Codefresh - https://codefresh.io/docs/docs/codefresh-yaml/variables/ | ||
| process.env['CF_REVISION'] || | ||
| // Codemagic - https://docs.codemagic.io/yaml-basic-configuration/environment-variables/ | ||
| process.env['CM_COMMIT'] || | ||
| // Cloudflare Pages - https://developers.cloudflare.com/pages/platform/build-configuration/#environment-variables | ||
| process.env['CF_PAGES_COMMIT_SHA'] || | ||
| // Drone - https://docs.drone.io/pipeline/environment/reference/ | ||
| process.env['DRONE_COMMIT_SHA'] || | ||
| // Flightcontrol - https://www.flightcontrol.dev/docs/guides/flightcontrol/environment-variables#built-in-environment-variables | ||
| process.env['FC_GIT_COMMIT_SHA'] || | ||
| // Heroku #1 https://devcenter.heroku.com/articles/heroku-ci | ||
| process.env['HEROKU_TEST_RUN_COMMIT_VERSION'] || | ||
| // Heroku #2 https://devcenter.heroku.com/articles/dyno-metadata#dyno-metadata | ||
| process.env['HEROKU_BUILD_COMMIT'] || | ||
| // Heroku #3 (deprecated by Heroku, kept for backward compatibility) | ||
| process.env['HEROKU_SLUG_COMMIT'] || | ||
| // Railway - https://docs.railway.app/reference/variables#git-variables | ||
| process.env['RAILWAY_GIT_COMMIT_SHA'] || | ||
| // Render - https://render.com/docs/environment-variables | ||
| process.env['RENDER_GIT_COMMIT'] || | ||
| // Semaphore CI - https://docs.semaphoreci.com/ci-cd-environment/environment-variables | ||
| process.env['SEMAPHORE_GIT_SHA'] || | ||
| // TravisCI - https://docs.travis-ci.com/user/environment-variables/#default-environment-variables | ||
| process.env['TRAVIS_PULL_REQUEST_SHA'] || | ||
| // Vercel - https://vercel.com/docs/v2/build-step#system-environment-variables | ||
| process.env['VERCEL_GIT_COMMIT_SHA'] || | ||
| process.env['VERCEL_GITHUB_COMMIT_SHA'] || | ||
| process.env['VERCEL_GITLAB_COMMIT_SHA'] || | ||
| process.env['VERCEL_BITBUCKET_COMMIT_SHA'] || | ||
| // Zeit (now known as Vercel) | ||
| process.env['ZEIT_GITHUB_COMMIT_SHA'] || | ||
| process.env['ZEIT_GITLAB_COMMIT_SHA'] || | ||
| process.env['ZEIT_BITBUCKET_COMMIT_SHA']; | ||
| const possibleReleaseNameOfCiProvidersWithGenericEnvVar = | ||
| process.env["APPVEYOR_PULL_REQUEST_HEAD_COMMIT"] || process.env["APPVEYOR_REPO_COMMIT"] || // AWS CodeBuild - https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-env-vars.html | ||
| process.env["CODEBUILD_RESOLVED_SOURCE_VERSION"] || // AWS Amplify - https://docs.aws.amazon.com/amplify/latest/userguide/environment-variables.html | ||
| process.env["AWS_COMMIT_ID"] || // Azure Pipelines - https://docs.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml | ||
| process.env["BUILD_SOURCEVERSION"] || // Bitrise - https://devcenter.bitrise.io/builds/available-environment-variables/ | ||
| process.env["GIT_CLONE_COMMIT_HASH"] || // Buddy CI - https://buddy.works/docs/pipelines/environment-variables#default-environment-variables | ||
| process.env["BUDDY_EXECUTION_REVISION"] || // Builtkite - https://buildkite.com/docs/pipelines/environment-variables | ||
| process.env["BUILDKITE_COMMIT"] || // CircleCI - https://circleci.com/docs/variables/ | ||
| process.env["CIRCLE_SHA1"] || // Cirrus CI - https://cirrus-ci.org/guide/writing-tasks/#environment-variables | ||
| process.env["CIRRUS_CHANGE_IN_REPO"] || // Codefresh - https://codefresh.io/docs/docs/codefresh-yaml/variables/ | ||
| process.env["CF_REVISION"] || // Codemagic - https://docs.codemagic.io/yaml-basic-configuration/environment-variables/ | ||
| process.env["CM_COMMIT"] || // Cloudflare Pages - https://developers.cloudflare.com/pages/platform/build-configuration/#environment-variables | ||
| process.env["CF_PAGES_COMMIT_SHA"] || // Drone - https://docs.drone.io/pipeline/environment/reference/ | ||
| process.env["DRONE_COMMIT_SHA"] || // Flightcontrol - https://www.flightcontrol.dev/docs/guides/flightcontrol/environment-variables#built-in-environment-variables | ||
| process.env["FC_GIT_COMMIT_SHA"] || // Heroku #1 https://devcenter.heroku.com/articles/heroku-ci | ||
| process.env["HEROKU_TEST_RUN_COMMIT_VERSION"] || // Heroku #2 https://devcenter.heroku.com/articles/dyno-metadata#dyno-metadata | ||
| process.env["HEROKU_BUILD_COMMIT"] || // Heroku #3 (deprecated by Heroku, kept for backward compatibility) | ||
| process.env["HEROKU_SLUG_COMMIT"] || // Railway - https://docs.railway.app/reference/variables#git-variables | ||
| process.env["RAILWAY_GIT_COMMIT_SHA"] || // Render - https://render.com/docs/environment-variables | ||
| process.env["RENDER_GIT_COMMIT"] || // Semaphore CI - https://docs.semaphoreci.com/ci-cd-environment/environment-variables | ||
| process.env["SEMAPHORE_GIT_SHA"] || // TravisCI - https://docs.travis-ci.com/user/environment-variables/#default-environment-variables | ||
| process.env["TRAVIS_PULL_REQUEST_SHA"] || // Vercel - https://vercel.com/docs/v2/build-step#system-environment-variables | ||
| process.env["VERCEL_GIT_COMMIT_SHA"] || process.env["VERCEL_GITHUB_COMMIT_SHA"] || process.env["VERCEL_GITLAB_COMMIT_SHA"] || process.env["VERCEL_BITBUCKET_COMMIT_SHA"] || // Zeit (now known as Vercel) | ||
| process.env["ZEIT_GITHUB_COMMIT_SHA"] || process.env["ZEIT_GITLAB_COMMIT_SHA"] || process.env["ZEIT_BITBUCKET_COMMIT_SHA"] | ||
| ); | ||
| const possibleReleaseNameOfCiProvidersWithGenericEnvVar = ( | ||
| // CloudBees CodeShip - https://docs.cloudbees.com/docs/cloudbees-codeship/latest/pro-builds-and-configuration/environment-variables | ||
| process.env['CI_COMMIT_ID'] || | ||
| // Coolify - https://coolify.io/docs/knowledge-base/environment-variables | ||
| process.env['SOURCE_COMMIT'] || | ||
| // Heroku #3 https://devcenter.heroku.com/changelog-items/630 | ||
| process.env['SOURCE_VERSION'] || | ||
| // Jenkins - https://plugins.jenkins.io/git/#environment-variables | ||
| process.env['GIT_COMMIT'] || | ||
| // Netlify - https://docs.netlify.com/configure-builds/environment-variables/#build-metadata | ||
| process.env['COMMIT_REF'] || | ||
| // TeamCity - https://www.jetbrains.com/help/teamcity/predefined-build-parameters.html | ||
| process.env['BUILD_VCS_NUMBER'] || | ||
| // Woodpecker CI - https://woodpecker-ci.org/docs/usage/environment | ||
| process.env['CI_COMMIT_SHA']; | ||
| return ( | ||
| possibleReleaseNameOfGitProvider || | ||
| possibleReleaseNameOfCiProvidersWithSpecificEnvVar || | ||
| possibleReleaseNameOfCiProvidersWithGenericEnvVar || | ||
| fallback | ||
| process.env["CI_COMMIT_ID"] || // Coolify - https://coolify.io/docs/knowledge-base/environment-variables | ||
| process.env["SOURCE_COMMIT"] || // Heroku #3 https://devcenter.heroku.com/changelog-items/630 | ||
| process.env["SOURCE_VERSION"] || // Jenkins - https://plugins.jenkins.io/git/#environment-variables | ||
| process.env["GIT_COMMIT"] || // Netlify - https://docs.netlify.com/configure-builds/environment-variables/#build-metadata | ||
| process.env["COMMIT_REF"] || // TeamCity - https://www.jetbrains.com/help/teamcity/predefined-build-parameters.html | ||
| process.env["BUILD_VCS_NUMBER"] || // Woodpecker CI - https://woodpecker-ci.org/docs/usage/environment | ||
| process.env["CI_COMMIT_SHA"] | ||
| ); | ||
| return possibleReleaseNameOfGitProvider || possibleReleaseNameOfCiProvidersWithSpecificEnvVar || possibleReleaseNameOfCiProvidersWithGenericEnvVar || fallback; | ||
| } | ||
| /** Node.js stack parser */ | ||
| const defaultStackParser = createStackParser(nodeStackLineParser(createGetModuleFromFilename())); | ||
@@ -114,0 +56,0 @@ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"api.js","sources":["../../../src/sdk/api.ts"],"sourcesContent":["// PUBLIC APIS\n\nimport type { StackParser } from '@sentry/core';\nimport { createStackParser, GLOBAL_OBJ, nodeStackLineParser } from '@sentry/core';\nimport { createGetModuleFromFilename } from '../utils/module';\n\n/**\n * Returns a release dynamically from environment variables.\n */\n// eslint-disable-next-line complexity\nexport function getSentryRelease(fallback?: string): string | undefined {\n // Always read first as Sentry takes this as precedence\n if (process.env.SENTRY_RELEASE) {\n return process.env.SENTRY_RELEASE;\n }\n\n // This supports the variable that sentry-webpack-plugin injects\n if (GLOBAL_OBJ.SENTRY_RELEASE?.id) {\n return GLOBAL_OBJ.SENTRY_RELEASE.id;\n }\n\n // This list is in approximate alpha order, separated into 3 categories:\n // 1. Git providers\n // 2. CI providers with specific environment variables (has the provider name in the variable name)\n // 3. CI providers with generic environment variables (checked for last to prevent possible false positives)\n\n const possibleReleaseNameOfGitProvider =\n // GitHub Actions - https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables#default-environment-variables\n process.env['GITHUB_SHA'] ||\n // GitLab CI - https://docs.gitlab.com/ee/ci/variables/predefined_variables.html\n process.env['CI_MERGE_REQUEST_SOURCE_BRANCH_SHA'] ||\n process.env['CI_BUILD_REF'] ||\n process.env['CI_COMMIT_SHA'] ||\n // Bitbucket - https://support.atlassian.com/bitbucket-cloud/docs/variables-and-secrets/\n process.env['BITBUCKET_COMMIT'];\n\n const possibleReleaseNameOfCiProvidersWithSpecificEnvVar =\n // AppVeyor - https://www.appveyor.com/docs/environment-variables/\n process.env['APPVEYOR_PULL_REQUEST_HEAD_COMMIT'] ||\n process.env['APPVEYOR_REPO_COMMIT'] ||\n // AWS CodeBuild - https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-env-vars.html\n process.env['CODEBUILD_RESOLVED_SOURCE_VERSION'] ||\n // AWS Amplify - https://docs.aws.amazon.com/amplify/latest/userguide/environment-variables.html\n process.env['AWS_COMMIT_ID'] ||\n // Azure Pipelines - https://docs.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml\n process.env['BUILD_SOURCEVERSION'] ||\n // Bitrise - https://devcenter.bitrise.io/builds/available-environment-variables/\n process.env['GIT_CLONE_COMMIT_HASH'] ||\n // Buddy CI - https://buddy.works/docs/pipelines/environment-variables#default-environment-variables\n process.env['BUDDY_EXECUTION_REVISION'] ||\n // Builtkite - https://buildkite.com/docs/pipelines/environment-variables\n process.env['BUILDKITE_COMMIT'] ||\n // CircleCI - https://circleci.com/docs/variables/\n process.env['CIRCLE_SHA1'] ||\n // Cirrus CI - https://cirrus-ci.org/guide/writing-tasks/#environment-variables\n process.env['CIRRUS_CHANGE_IN_REPO'] ||\n // Codefresh - https://codefresh.io/docs/docs/codefresh-yaml/variables/\n process.env['CF_REVISION'] ||\n // Codemagic - https://docs.codemagic.io/yaml-basic-configuration/environment-variables/\n process.env['CM_COMMIT'] ||\n // Cloudflare Pages - https://developers.cloudflare.com/pages/platform/build-configuration/#environment-variables\n process.env['CF_PAGES_COMMIT_SHA'] ||\n // Drone - https://docs.drone.io/pipeline/environment/reference/\n process.env['DRONE_COMMIT_SHA'] ||\n // Flightcontrol - https://www.flightcontrol.dev/docs/guides/flightcontrol/environment-variables#built-in-environment-variables\n process.env['FC_GIT_COMMIT_SHA'] ||\n // Heroku #1 https://devcenter.heroku.com/articles/heroku-ci\n process.env['HEROKU_TEST_RUN_COMMIT_VERSION'] ||\n // Heroku #2 https://devcenter.heroku.com/articles/dyno-metadata#dyno-metadata\n process.env['HEROKU_BUILD_COMMIT'] ||\n // Heroku #3 (deprecated by Heroku, kept for backward compatibility)\n process.env['HEROKU_SLUG_COMMIT'] ||\n // Railway - https://docs.railway.app/reference/variables#git-variables\n process.env['RAILWAY_GIT_COMMIT_SHA'] ||\n // Render - https://render.com/docs/environment-variables\n process.env['RENDER_GIT_COMMIT'] ||\n // Semaphore CI - https://docs.semaphoreci.com/ci-cd-environment/environment-variables\n process.env['SEMAPHORE_GIT_SHA'] ||\n // TravisCI - https://docs.travis-ci.com/user/environment-variables/#default-environment-variables\n process.env['TRAVIS_PULL_REQUEST_SHA'] ||\n // Vercel - https://vercel.com/docs/v2/build-step#system-environment-variables\n process.env['VERCEL_GIT_COMMIT_SHA'] ||\n process.env['VERCEL_GITHUB_COMMIT_SHA'] ||\n process.env['VERCEL_GITLAB_COMMIT_SHA'] ||\n process.env['VERCEL_BITBUCKET_COMMIT_SHA'] ||\n // Zeit (now known as Vercel)\n process.env['ZEIT_GITHUB_COMMIT_SHA'] ||\n process.env['ZEIT_GITLAB_COMMIT_SHA'] ||\n process.env['ZEIT_BITBUCKET_COMMIT_SHA'];\n\n const possibleReleaseNameOfCiProvidersWithGenericEnvVar =\n // CloudBees CodeShip - https://docs.cloudbees.com/docs/cloudbees-codeship/latest/pro-builds-and-configuration/environment-variables\n process.env['CI_COMMIT_ID'] ||\n // Coolify - https://coolify.io/docs/knowledge-base/environment-variables\n process.env['SOURCE_COMMIT'] ||\n // Heroku #3 https://devcenter.heroku.com/changelog-items/630\n process.env['SOURCE_VERSION'] ||\n // Jenkins - https://plugins.jenkins.io/git/#environment-variables\n process.env['GIT_COMMIT'] ||\n // Netlify - https://docs.netlify.com/configure-builds/environment-variables/#build-metadata\n process.env['COMMIT_REF'] ||\n // TeamCity - https://www.jetbrains.com/help/teamcity/predefined-build-parameters.html\n process.env['BUILD_VCS_NUMBER'] ||\n // Woodpecker CI - https://woodpecker-ci.org/docs/usage/environment\n process.env['CI_COMMIT_SHA'];\n\n return (\n possibleReleaseNameOfGitProvider ||\n possibleReleaseNameOfCiProvidersWithSpecificEnvVar ||\n possibleReleaseNameOfCiProvidersWithGenericEnvVar ||\n fallback\n );\n}\n\n/** Node.js stack parser */\nexport const defaultStackParser: StackParser = createStackParser(nodeStackLineParser(createGetModuleFromFilename()));\n"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"api.js","sources":["../../../src/sdk/api.ts"],"sourcesContent":["// PUBLIC APIS\n\nimport type { StackParser } from '@sentry/core';\nimport { createStackParser, GLOBAL_OBJ, nodeStackLineParser } from '@sentry/core';\nimport { createGetModuleFromFilename } from '../utils/module';\n\n/**\n * Returns a release dynamically from environment variables.\n */\n// eslint-disable-next-line complexity\nexport function getSentryRelease(fallback?: string): string | undefined {\n // Always read first as Sentry takes this as precedence\n if (process.env.SENTRY_RELEASE) {\n return process.env.SENTRY_RELEASE;\n }\n\n // This supports the variable that sentry-webpack-plugin injects\n if (GLOBAL_OBJ.SENTRY_RELEASE?.id) {\n return GLOBAL_OBJ.SENTRY_RELEASE.id;\n }\n\n // This list is in approximate alpha order, separated into 3 categories:\n // 1. Git providers\n // 2. CI providers with specific environment variables (has the provider name in the variable name)\n // 3. CI providers with generic environment variables (checked for last to prevent possible false positives)\n\n const possibleReleaseNameOfGitProvider =\n // GitHub Actions - https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables#default-environment-variables\n process.env['GITHUB_SHA'] ||\n // GitLab CI - https://docs.gitlab.com/ee/ci/variables/predefined_variables.html\n process.env['CI_MERGE_REQUEST_SOURCE_BRANCH_SHA'] ||\n process.env['CI_BUILD_REF'] ||\n process.env['CI_COMMIT_SHA'] ||\n // Bitbucket - https://support.atlassian.com/bitbucket-cloud/docs/variables-and-secrets/\n process.env['BITBUCKET_COMMIT'];\n\n const possibleReleaseNameOfCiProvidersWithSpecificEnvVar =\n // AppVeyor - https://www.appveyor.com/docs/environment-variables/\n process.env['APPVEYOR_PULL_REQUEST_HEAD_COMMIT'] ||\n process.env['APPVEYOR_REPO_COMMIT'] ||\n // AWS CodeBuild - https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-env-vars.html\n process.env['CODEBUILD_RESOLVED_SOURCE_VERSION'] ||\n // AWS Amplify - https://docs.aws.amazon.com/amplify/latest/userguide/environment-variables.html\n process.env['AWS_COMMIT_ID'] ||\n // Azure Pipelines - https://docs.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml\n process.env['BUILD_SOURCEVERSION'] ||\n // Bitrise - https://devcenter.bitrise.io/builds/available-environment-variables/\n process.env['GIT_CLONE_COMMIT_HASH'] ||\n // Buddy CI - https://buddy.works/docs/pipelines/environment-variables#default-environment-variables\n process.env['BUDDY_EXECUTION_REVISION'] ||\n // Builtkite - https://buildkite.com/docs/pipelines/environment-variables\n process.env['BUILDKITE_COMMIT'] ||\n // CircleCI - https://circleci.com/docs/variables/\n process.env['CIRCLE_SHA1'] ||\n // Cirrus CI - https://cirrus-ci.org/guide/writing-tasks/#environment-variables\n process.env['CIRRUS_CHANGE_IN_REPO'] ||\n // Codefresh - https://codefresh.io/docs/docs/codefresh-yaml/variables/\n process.env['CF_REVISION'] ||\n // Codemagic - https://docs.codemagic.io/yaml-basic-configuration/environment-variables/\n process.env['CM_COMMIT'] ||\n // Cloudflare Pages - https://developers.cloudflare.com/pages/platform/build-configuration/#environment-variables\n process.env['CF_PAGES_COMMIT_SHA'] ||\n // Drone - https://docs.drone.io/pipeline/environment/reference/\n process.env['DRONE_COMMIT_SHA'] ||\n // Flightcontrol - https://www.flightcontrol.dev/docs/guides/flightcontrol/environment-variables#built-in-environment-variables\n process.env['FC_GIT_COMMIT_SHA'] ||\n // Heroku #1 https://devcenter.heroku.com/articles/heroku-ci\n process.env['HEROKU_TEST_RUN_COMMIT_VERSION'] ||\n // Heroku #2 https://devcenter.heroku.com/articles/dyno-metadata#dyno-metadata\n process.env['HEROKU_BUILD_COMMIT'] ||\n // Heroku #3 (deprecated by Heroku, kept for backward compatibility)\n process.env['HEROKU_SLUG_COMMIT'] ||\n // Railway - https://docs.railway.app/reference/variables#git-variables\n process.env['RAILWAY_GIT_COMMIT_SHA'] ||\n // Render - https://render.com/docs/environment-variables\n process.env['RENDER_GIT_COMMIT'] ||\n // Semaphore CI - https://docs.semaphoreci.com/ci-cd-environment/environment-variables\n process.env['SEMAPHORE_GIT_SHA'] ||\n // TravisCI - https://docs.travis-ci.com/user/environment-variables/#default-environment-variables\n process.env['TRAVIS_PULL_REQUEST_SHA'] ||\n // Vercel - https://vercel.com/docs/v2/build-step#system-environment-variables\n process.env['VERCEL_GIT_COMMIT_SHA'] ||\n process.env['VERCEL_GITHUB_COMMIT_SHA'] ||\n process.env['VERCEL_GITLAB_COMMIT_SHA'] ||\n process.env['VERCEL_BITBUCKET_COMMIT_SHA'] ||\n // Zeit (now known as Vercel)\n process.env['ZEIT_GITHUB_COMMIT_SHA'] ||\n process.env['ZEIT_GITLAB_COMMIT_SHA'] ||\n process.env['ZEIT_BITBUCKET_COMMIT_SHA'];\n\n const possibleReleaseNameOfCiProvidersWithGenericEnvVar =\n // CloudBees CodeShip - https://docs.cloudbees.com/docs/cloudbees-codeship/latest/pro-builds-and-configuration/environment-variables\n process.env['CI_COMMIT_ID'] ||\n // Coolify - https://coolify.io/docs/knowledge-base/environment-variables\n process.env['SOURCE_COMMIT'] ||\n // Heroku #3 https://devcenter.heroku.com/changelog-items/630\n process.env['SOURCE_VERSION'] ||\n // Jenkins - https://plugins.jenkins.io/git/#environment-variables\n process.env['GIT_COMMIT'] ||\n // Netlify - https://docs.netlify.com/configure-builds/environment-variables/#build-metadata\n process.env['COMMIT_REF'] ||\n // TeamCity - https://www.jetbrains.com/help/teamcity/predefined-build-parameters.html\n process.env['BUILD_VCS_NUMBER'] ||\n // Woodpecker CI - https://woodpecker-ci.org/docs/usage/environment\n process.env['CI_COMMIT_SHA'];\n\n return (\n possibleReleaseNameOfGitProvider ||\n possibleReleaseNameOfCiProvidersWithSpecificEnvVar ||\n possibleReleaseNameOfCiProvidersWithGenericEnvVar ||\n fallback\n );\n}\n\n/** Node.js stack parser */\nexport const defaultStackParser: StackParser = createStackParser(nodeStackLineParser(createGetModuleFromFilename()));\n"],"names":[],"mappings":";;;AAUO,SAAS,iBAAiB,QAAA,EAAuC;AAEtE,EAAA,IAAI,OAAA,CAAQ,IAAI,cAAA,EAAgB;AAC9B,IAAA,OAAO,QAAQ,GAAA,CAAI,cAAA;AAAA,EACrB;AAGA,EAAA,IAAI,UAAA,CAAW,gBAAgB,EAAA,EAAI;AACjC,IAAA,OAAO,WAAW,cAAA,CAAe,EAAA;AAAA,EACnC;AAOA,EAAA,MAAM,gCAAA;AAAA;AAAA,IAEJ,OAAA,CAAQ,IAAI,YAAY,CAAA;AAAA,IAExB,OAAA,CAAQ,GAAA,CAAI,oCAAoC,CAAA,IAChD,OAAA,CAAQ,IAAI,cAAc,CAAA,IAC1B,OAAA,CAAQ,GAAA,CAAI,eAAe,CAAA;AAAA,IAE3B,OAAA,CAAQ,IAAI,kBAAkB;AAAA,GAAA;AAEhC,EAAA,MAAM,kDAAA;AAAA;AAAA,IAEJ,QAAQ,GAAA,CAAI,mCAAmC,CAAA,IAC/C,OAAA,CAAQ,IAAI,sBAAsB,CAAA;AAAA,IAElC,OAAA,CAAQ,IAAI,mCAAmC,CAAA;AAAA,IAE/C,OAAA,CAAQ,IAAI,eAAe,CAAA;AAAA,IAE3B,OAAA,CAAQ,IAAI,qBAAqB,CAAA;AAAA,IAEjC,OAAA,CAAQ,IAAI,uBAAuB,CAAA;AAAA,IAEnC,OAAA,CAAQ,IAAI,0BAA0B,CAAA;AAAA,IAEtC,OAAA,CAAQ,IAAI,kBAAkB,CAAA;AAAA,IAE9B,OAAA,CAAQ,IAAI,aAAa,CAAA;AAAA,IAEzB,OAAA,CAAQ,IAAI,uBAAuB,CAAA;AAAA,IAEnC,OAAA,CAAQ,IAAI,aAAa,CAAA;AAAA,IAEzB,OAAA,CAAQ,IAAI,WAAW,CAAA;AAAA,IAEvB,OAAA,CAAQ,IAAI,qBAAqB,CAAA;AAAA,IAEjC,OAAA,CAAQ,IAAI,kBAAkB,CAAA;AAAA,IAE9B,OAAA,CAAQ,IAAI,mBAAmB,CAAA;AAAA,IAE/B,OAAA,CAAQ,IAAI,gCAAgC,CAAA;AAAA,IAE5C,OAAA,CAAQ,IAAI,qBAAqB,CAAA;AAAA,IAEjC,OAAA,CAAQ,IAAI,oBAAoB,CAAA;AAAA,IAEhC,OAAA,CAAQ,IAAI,wBAAwB,CAAA;AAAA,IAEpC,OAAA,CAAQ,IAAI,mBAAmB,CAAA;AAAA,IAE/B,OAAA,CAAQ,IAAI,mBAAmB,CAAA;AAAA,IAE/B,OAAA,CAAQ,IAAI,yBAAyB,CAAA;AAAA,IAErC,OAAA,CAAQ,GAAA,CAAI,uBAAuB,CAAA,IACnC,QAAQ,GAAA,CAAI,0BAA0B,CAAA,IACtC,OAAA,CAAQ,GAAA,CAAI,0BAA0B,CAAA,IACtC,OAAA,CAAQ,IAAI,6BAA6B,CAAA;AAAA,IAEzC,OAAA,CAAQ,GAAA,CAAI,wBAAwB,CAAA,IACpC,OAAA,CAAQ,IAAI,wBAAwB,CAAA,IACpC,OAAA,CAAQ,GAAA,CAAI,2BAA2B;AAAA,GAAA;AAEzC,EAAA,MAAM,iDAAA;AAAA;AAAA,IAEJ,OAAA,CAAQ,IAAI,cAAc,CAAA;AAAA,IAE1B,OAAA,CAAQ,IAAI,eAAe,CAAA;AAAA,IAE3B,OAAA,CAAQ,IAAI,gBAAgB,CAAA;AAAA,IAE5B,OAAA,CAAQ,IAAI,YAAY,CAAA;AAAA,IAExB,OAAA,CAAQ,IAAI,YAAY,CAAA;AAAA,IAExB,OAAA,CAAQ,IAAI,kBAAkB,CAAA;AAAA,IAE9B,OAAA,CAAQ,IAAI,eAAe;AAAA,GAAA;AAE7B,EAAA,OACE,gCAAA,IACA,sDACA,iDAAA,IACA,QAAA;AAEJ;AAGO,MAAM,kBAAA,GAAkC,iBAAA,CAAkB,mBAAA,CAAoB,2BAAA,EAA6B,CAAC;;;;"} |
+25
-63
@@ -9,33 +9,21 @@ import * as os from 'node:os'; | ||
| const DEFAULT_CLIENT_REPORT_FLUSH_INTERVAL_MS = 60000; // 60s was chosen arbitrarily | ||
| /** A client for using Sentry with Node & OpenTelemetry. */ | ||
| const DEFAULT_CLIENT_REPORT_FLUSH_INTERVAL_MS = 6e4; | ||
| class NodeClient extends ServerRuntimeClient { | ||
| constructor(options) { | ||
| const serverName = | ||
| options.includeServerName === false | ||
| ? undefined | ||
| : options.serverName || global.process.env.SENTRY_NAME || os.hostname(); | ||
| constructor(options) { | ||
| const serverName = options.includeServerName === false ? void 0 : options.serverName || global.process.env.SENTRY_NAME || os.hostname(); | ||
| const clientOptions = { | ||
| ...options, | ||
| platform: 'node', | ||
| platform: "node", | ||
| // Use provided runtime or default to 'node' with current process version | ||
| runtime: options.runtime || { name: 'node', version: global.process.version }, | ||
| serverName, | ||
| runtime: options.runtime || { name: "node", version: global.process.version }, | ||
| serverName | ||
| }; | ||
| if (options.openTelemetryInstrumentations) { | ||
| registerInstrumentations({ | ||
| instrumentations: options.openTelemetryInstrumentations, | ||
| instrumentations: options.openTelemetryInstrumentations | ||
| }); | ||
| } | ||
| applySdkMetadata(clientOptions, 'node'); | ||
| debug.log(`Initializing Sentry: process: ${process.pid}, thread: ${isMainThread ? 'main' : `worker-${threadId}`}.`); | ||
| applySdkMetadata(clientOptions, "node"); | ||
| debug.log(`Initializing Sentry: process: ${process.pid}, thread: ${isMainThread ? "main" : `worker-${threadId}`}.`); | ||
| super(clientOptions); | ||
| if (this.getOptions().enableLogs) { | ||
@@ -45,57 +33,45 @@ this._logOnExitFlushListener = () => { | ||
| }; | ||
| if (serverName) { | ||
| this.on('beforeCaptureLog', log => { | ||
| this.on("beforeCaptureLog", (log) => { | ||
| log.attributes = { | ||
| ...log.attributes, | ||
| 'server.address': serverName, | ||
| "server.address": serverName | ||
| }; | ||
| }); | ||
| } | ||
| process.on('beforeExit', this._logOnExitFlushListener); | ||
| process.on("beforeExit", this._logOnExitFlushListener); | ||
| } | ||
| } | ||
| /** Get the OTEL tracer. */ | ||
| get tracer() { | ||
| get tracer() { | ||
| if (this._tracer) { | ||
| return this._tracer; | ||
| } | ||
| const name = '@sentry/node'; | ||
| const name = "@sentry/node"; | ||
| const version = SDK_VERSION; | ||
| const tracer = trace.getTracer(name, version); | ||
| this._tracer = tracer; | ||
| return tracer; | ||
| } | ||
| /** @inheritDoc */ | ||
| // @ts-expect-error - PromiseLike is a subset of Promise | ||
| async flush(timeout) { | ||
| async flush(timeout) { | ||
| await this.traceProvider?.forceFlush(); | ||
| if (this.getOptions().sendClientReports) { | ||
| this._flushOutcomes(); | ||
| } | ||
| return super.flush(timeout); | ||
| } | ||
| /** @inheritDoc */ | ||
| // @ts-expect-error - PromiseLike is a subset of Promise | ||
| async close(timeout) { | ||
| async close(timeout) { | ||
| if (this._clientReportInterval) { | ||
| clearInterval(this._clientReportInterval); | ||
| } | ||
| if (this._clientReportOnExitFlushListener) { | ||
| process.off('beforeExit', this._clientReportOnExitFlushListener); | ||
| process.off("beforeExit", this._clientReportOnExitFlushListener); | ||
| } | ||
| if (this._logOnExitFlushListener) { | ||
| process.off('beforeExit', this._logOnExitFlushListener); | ||
| process.off("beforeExit", this._logOnExitFlushListener); | ||
| } | ||
| const allEventsSent = await super.close(timeout); | ||
@@ -105,6 +81,4 @@ if (this.traceProvider) { | ||
| } | ||
| return allEventsSent; | ||
| } | ||
| /** | ||
@@ -125,3 +99,3 @@ * Will start tracking client reports for this client. | ||
| // collected, but it did not work, because the cleanup function never got called. | ||
| startClientReportTracking() { | ||
| startClientReportTracking() { | ||
| const clientOptions = this.getOptions(); | ||
@@ -132,31 +106,19 @@ if (clientOptions.sendClientReports) { | ||
| }; | ||
| this._clientReportInterval = setInterval(() => { | ||
| DEBUG_BUILD && debug.log('Flushing client reports based on interval.'); | ||
| DEBUG_BUILD && debug.log("Flushing client reports based on interval."); | ||
| this._flushOutcomes(); | ||
| }, clientOptions.clientReportFlushInterval ?? DEFAULT_CLIENT_REPORT_FLUSH_INTERVAL_MS) | ||
| // Unref is critical for not preventing the process from exiting because the interval is active. | ||
| .unref(); | ||
| process.on('beforeExit', this._clientReportOnExitFlushListener); | ||
| }, clientOptions.clientReportFlushInterval ?? DEFAULT_CLIENT_REPORT_FLUSH_INTERVAL_MS).unref(); | ||
| process.on("beforeExit", this._clientReportOnExitFlushListener); | ||
| } | ||
| } | ||
| /** @inheritDoc */ | ||
| _setupIntegrations() { | ||
| // Clear AI provider skip registrations before setting up integrations | ||
| // This ensures a clean state between different client initializations | ||
| // (e.g., when LangChain skips OpenAI in one client, but a subsequent client uses OpenAI standalone) | ||
| _setupIntegrations() { | ||
| _INTERNAL_clearAiProviderSkips(); | ||
| super._setupIntegrations(); | ||
| } | ||
| /** Custom implementation for OTEL, so we can handle scope-span linking. */ | ||
| _getTraceInfoFromScope( | ||
| scope, | ||
| ) { | ||
| _getTraceInfoFromScope(scope) { | ||
| if (!scope) { | ||
| return [undefined, undefined]; | ||
| return [void 0, void 0]; | ||
| } | ||
| return getTraceContextForScope(this, scope); | ||
@@ -163,0 +125,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"client.js","sources":["../../../src/sdk/client.ts"],"sourcesContent":["import * as os from 'node:os';\nimport type { Tracer } from '@opentelemetry/api';\nimport { trace } from '@opentelemetry/api';\nimport { registerInstrumentations } from '@opentelemetry/instrumentation';\nimport type { BasicTracerProvider } from '@opentelemetry/sdk-trace-base';\nimport type { DynamicSamplingContext, Scope, ServerRuntimeClientOptions, TraceContext } from '@sentry/core';\nimport {\n _INTERNAL_clearAiProviderSkips,\n _INTERNAL_flushLogsBuffer,\n applySdkMetadata,\n debug,\n SDK_VERSION,\n ServerRuntimeClient,\n} from '@sentry/core';\nimport { type AsyncLocalStorageLookup, getTraceContextForScope } from '@sentry/opentelemetry';\nimport { isMainThread, threadId } from 'worker_threads';\nimport { DEBUG_BUILD } from '../debug-build';\nimport type { NodeClientOptions } from '../types';\n\nconst DEFAULT_CLIENT_REPORT_FLUSH_INTERVAL_MS = 60_000; // 60s was chosen arbitrarily\n\n/** A client for using Sentry with Node & OpenTelemetry. */\nexport class NodeClient extends ServerRuntimeClient<NodeClientOptions> {\n public traceProvider: BasicTracerProvider | undefined;\n public asyncLocalStorageLookup: AsyncLocalStorageLookup | undefined;\n\n private _tracer: Tracer | undefined;\n private _clientReportInterval: NodeJS.Timeout | undefined;\n private _clientReportOnExitFlushListener: (() => void) | undefined;\n private _logOnExitFlushListener: (() => void) | undefined;\n\n public constructor(options: NodeClientOptions) {\n const serverName =\n options.includeServerName === false\n ? undefined\n : options.serverName || global.process.env.SENTRY_NAME || os.hostname();\n\n const clientOptions: ServerRuntimeClientOptions = {\n ...options,\n platform: 'node',\n // Use provided runtime or default to 'node' with current process version\n runtime: options.runtime || { name: 'node', version: global.process.version },\n serverName,\n };\n\n if (options.openTelemetryInstrumentations) {\n registerInstrumentations({\n instrumentations: options.openTelemetryInstrumentations,\n });\n }\n\n applySdkMetadata(clientOptions, 'node');\n\n debug.log(`Initializing Sentry: process: ${process.pid}, thread: ${isMainThread ? 'main' : `worker-${threadId}`}.`);\n\n super(clientOptions);\n\n if (this.getOptions().enableLogs) {\n this._logOnExitFlushListener = () => {\n _INTERNAL_flushLogsBuffer(this);\n };\n\n if (serverName) {\n this.on('beforeCaptureLog', log => {\n log.attributes = {\n ...log.attributes,\n 'server.address': serverName,\n };\n });\n }\n\n process.on('beforeExit', this._logOnExitFlushListener);\n }\n }\n\n /** Get the OTEL tracer. */\n public get tracer(): Tracer {\n if (this._tracer) {\n return this._tracer;\n }\n\n const name = '@sentry/node';\n const version = SDK_VERSION;\n const tracer = trace.getTracer(name, version);\n this._tracer = tracer;\n\n return tracer;\n }\n\n /** @inheritDoc */\n // @ts-expect-error - PromiseLike is a subset of Promise\n public async flush(timeout?: number): PromiseLike<boolean> {\n await this.traceProvider?.forceFlush();\n\n if (this.getOptions().sendClientReports) {\n this._flushOutcomes();\n }\n\n return super.flush(timeout);\n }\n\n /** @inheritDoc */\n // @ts-expect-error - PromiseLike is a subset of Promise\n public async close(timeout?: number | undefined): PromiseLike<boolean> {\n if (this._clientReportInterval) {\n clearInterval(this._clientReportInterval);\n }\n\n if (this._clientReportOnExitFlushListener) {\n process.off('beforeExit', this._clientReportOnExitFlushListener);\n }\n\n if (this._logOnExitFlushListener) {\n process.off('beforeExit', this._logOnExitFlushListener);\n }\n\n const allEventsSent = await super.close(timeout);\n if (this.traceProvider) {\n await this.traceProvider.shutdown();\n }\n\n return allEventsSent;\n }\n\n /**\n * Will start tracking client reports for this client.\n *\n * NOTICE: This method will create an interval that is periodically called and attach a `process.on('beforeExit')`\n * hook. To clean up these resources, call `.close()` when you no longer intend to use the client. Not doing so will\n * result in a memory leak.\n */\n // The reason client reports need to be manually activated with this method instead of just enabling them in a\n // constructor, is that if users periodically and unboundedly create new clients, we will create more and more\n // intervals and beforeExit listeners, thus leaking memory. In these situations, users are required to call\n // `client.close()` in order to dispose of the acquired resources.\n // We assume that calling this method in Sentry.init() is a sensible default, because calling Sentry.init() over and\n // over again would also result in memory leaks.\n // Note: We have experimented with using `FinalizationRegisty` to clear the interval when the client is garbage\n // collected, but it did not work, because the cleanup function never got called.\n public startClientReportTracking(): void {\n const clientOptions = this.getOptions();\n if (clientOptions.sendClientReports) {\n this._clientReportOnExitFlushListener = () => {\n this._flushOutcomes();\n };\n\n this._clientReportInterval = setInterval(() => {\n DEBUG_BUILD && debug.log('Flushing client reports based on interval.');\n this._flushOutcomes();\n }, clientOptions.clientReportFlushInterval ?? DEFAULT_CLIENT_REPORT_FLUSH_INTERVAL_MS)\n // Unref is critical for not preventing the process from exiting because the interval is active.\n .unref();\n\n process.on('beforeExit', this._clientReportOnExitFlushListener);\n }\n }\n\n /** @inheritDoc */\n protected _setupIntegrations(): void {\n // Clear AI provider skip registrations before setting up integrations\n // This ensures a clean state between different client initializations\n // (e.g., when LangChain skips OpenAI in one client, but a subsequent client uses OpenAI standalone)\n _INTERNAL_clearAiProviderSkips();\n super._setupIntegrations();\n }\n\n /** Custom implementation for OTEL, so we can handle scope-span linking. */\n protected _getTraceInfoFromScope(\n scope: Scope | undefined,\n ): [dynamicSamplingContext: Partial<DynamicSamplingContext> | undefined, traceContext: TraceContext | undefined] {\n if (!scope) {\n return [undefined, undefined];\n }\n\n return getTraceContextForScope(this, scope);\n }\n}\n"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"client.js","sources":["../../../src/sdk/client.ts"],"sourcesContent":["import * as os from 'node:os';\nimport type { Tracer } from '@opentelemetry/api';\nimport { trace } from '@opentelemetry/api';\nimport { registerInstrumentations } from '@opentelemetry/instrumentation';\nimport type { BasicTracerProvider } from '@opentelemetry/sdk-trace-base';\nimport type { DynamicSamplingContext, Scope, ServerRuntimeClientOptions, TraceContext } from '@sentry/core';\nimport {\n _INTERNAL_clearAiProviderSkips,\n _INTERNAL_flushLogsBuffer,\n applySdkMetadata,\n debug,\n SDK_VERSION,\n ServerRuntimeClient,\n} from '@sentry/core';\nimport { type AsyncLocalStorageLookup, getTraceContextForScope } from '@sentry/opentelemetry';\nimport { isMainThread, threadId } from 'worker_threads';\nimport { DEBUG_BUILD } from '../debug-build';\nimport type { NodeClientOptions } from '../types';\n\nconst DEFAULT_CLIENT_REPORT_FLUSH_INTERVAL_MS = 60_000; // 60s was chosen arbitrarily\n\n/** A client for using Sentry with Node & OpenTelemetry. */\nexport class NodeClient extends ServerRuntimeClient<NodeClientOptions> {\n public traceProvider: BasicTracerProvider | undefined;\n public asyncLocalStorageLookup: AsyncLocalStorageLookup | undefined;\n\n private _tracer: Tracer | undefined;\n private _clientReportInterval: NodeJS.Timeout | undefined;\n private _clientReportOnExitFlushListener: (() => void) | undefined;\n private _logOnExitFlushListener: (() => void) | undefined;\n\n public constructor(options: NodeClientOptions) {\n const serverName =\n options.includeServerName === false\n ? undefined\n : options.serverName || global.process.env.SENTRY_NAME || os.hostname();\n\n const clientOptions: ServerRuntimeClientOptions = {\n ...options,\n platform: 'node',\n // Use provided runtime or default to 'node' with current process version\n runtime: options.runtime || { name: 'node', version: global.process.version },\n serverName,\n };\n\n if (options.openTelemetryInstrumentations) {\n registerInstrumentations({\n instrumentations: options.openTelemetryInstrumentations,\n });\n }\n\n applySdkMetadata(clientOptions, 'node');\n\n debug.log(`Initializing Sentry: process: ${process.pid}, thread: ${isMainThread ? 'main' : `worker-${threadId}`}.`);\n\n super(clientOptions);\n\n if (this.getOptions().enableLogs) {\n this._logOnExitFlushListener = () => {\n _INTERNAL_flushLogsBuffer(this);\n };\n\n if (serverName) {\n this.on('beforeCaptureLog', log => {\n log.attributes = {\n ...log.attributes,\n 'server.address': serverName,\n };\n });\n }\n\n process.on('beforeExit', this._logOnExitFlushListener);\n }\n }\n\n /** Get the OTEL tracer. */\n public get tracer(): Tracer {\n if (this._tracer) {\n return this._tracer;\n }\n\n const name = '@sentry/node';\n const version = SDK_VERSION;\n const tracer = trace.getTracer(name, version);\n this._tracer = tracer;\n\n return tracer;\n }\n\n /** @inheritDoc */\n // @ts-expect-error - PromiseLike is a subset of Promise\n public async flush(timeout?: number): PromiseLike<boolean> {\n await this.traceProvider?.forceFlush();\n\n if (this.getOptions().sendClientReports) {\n this._flushOutcomes();\n }\n\n return super.flush(timeout);\n }\n\n /** @inheritDoc */\n // @ts-expect-error - PromiseLike is a subset of Promise\n public async close(timeout?: number | undefined): PromiseLike<boolean> {\n if (this._clientReportInterval) {\n clearInterval(this._clientReportInterval);\n }\n\n if (this._clientReportOnExitFlushListener) {\n process.off('beforeExit', this._clientReportOnExitFlushListener);\n }\n\n if (this._logOnExitFlushListener) {\n process.off('beforeExit', this._logOnExitFlushListener);\n }\n\n const allEventsSent = await super.close(timeout);\n if (this.traceProvider) {\n await this.traceProvider.shutdown();\n }\n\n return allEventsSent;\n }\n\n /**\n * Will start tracking client reports for this client.\n *\n * NOTICE: This method will create an interval that is periodically called and attach a `process.on('beforeExit')`\n * hook. To clean up these resources, call `.close()` when you no longer intend to use the client. Not doing so will\n * result in a memory leak.\n */\n // The reason client reports need to be manually activated with this method instead of just enabling them in a\n // constructor, is that if users periodically and unboundedly create new clients, we will create more and more\n // intervals and beforeExit listeners, thus leaking memory. In these situations, users are required to call\n // `client.close()` in order to dispose of the acquired resources.\n // We assume that calling this method in Sentry.init() is a sensible default, because calling Sentry.init() over and\n // over again would also result in memory leaks.\n // Note: We have experimented with using `FinalizationRegisty` to clear the interval when the client is garbage\n // collected, but it did not work, because the cleanup function never got called.\n public startClientReportTracking(): void {\n const clientOptions = this.getOptions();\n if (clientOptions.sendClientReports) {\n this._clientReportOnExitFlushListener = () => {\n this._flushOutcomes();\n };\n\n this._clientReportInterval = setInterval(() => {\n DEBUG_BUILD && debug.log('Flushing client reports based on interval.');\n this._flushOutcomes();\n }, clientOptions.clientReportFlushInterval ?? DEFAULT_CLIENT_REPORT_FLUSH_INTERVAL_MS)\n // Unref is critical for not preventing the process from exiting because the interval is active.\n .unref();\n\n process.on('beforeExit', this._clientReportOnExitFlushListener);\n }\n }\n\n /** @inheritDoc */\n protected _setupIntegrations(): void {\n // Clear AI provider skip registrations before setting up integrations\n // This ensures a clean state between different client initializations\n // (e.g., when LangChain skips OpenAI in one client, but a subsequent client uses OpenAI standalone)\n _INTERNAL_clearAiProviderSkips();\n super._setupIntegrations();\n }\n\n /** Custom implementation for OTEL, so we can handle scope-span linking. */\n protected _getTraceInfoFromScope(\n scope: Scope | undefined,\n ): [dynamicSamplingContext: Partial<DynamicSamplingContext> | undefined, traceContext: TraceContext | undefined] {\n if (!scope) {\n return [undefined, undefined];\n }\n\n return getTraceContextForScope(this, scope);\n }\n}\n"],"names":[],"mappings":"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"} |
@@ -6,8 +6,2 @@ import { GLOBAL_OBJ, debug } from '@sentry/core'; | ||
| /** | ||
| * Initialize the ESM loader - This method is private and not part of the public | ||
| * API. | ||
| * | ||
| * @ignore | ||
| */ | ||
| function initializeEsmLoader() { | ||
@@ -17,12 +11,9 @@ if (!supportsEsmLoaderHooks()) { | ||
| } | ||
| if (!GLOBAL_OBJ._sentryEsmLoaderHookRegistered) { | ||
| GLOBAL_OBJ._sentryEsmLoaderHookRegistered = true; | ||
| try { | ||
| const { addHookMessagePort } = createAddHookMessageChannel(); | ||
| // @ts-expect-error register is available in these versions | ||
| moduleModule.register('import-in-the-middle/hook.mjs', import.meta.url, { | ||
| moduleModule.register("import-in-the-middle/hook.mjs", import.meta.url, { | ||
| data: { addHookMessagePort, include: [] }, | ||
| transferList: [addHookMessagePort], | ||
| transferList: [addHookMessagePort] | ||
| }); | ||
@@ -29,0 +20,0 @@ } catch (error) { |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"esmLoader.js","sources":["../../../src/sdk/esmLoader.ts"],"sourcesContent":["import { debug, GLOBAL_OBJ } from '@sentry/core';\nimport { createAddHookMessageChannel } from 'import-in-the-middle';\nimport * as moduleModule from 'module';\nimport { supportsEsmLoaderHooks } from '../utils/detection';\n\n/**\n * Initialize the ESM loader - This method is private and not part of the public\n * API.\n *\n * @ignore\n */\nexport function initializeEsmLoader(): void {\n if (!supportsEsmLoaderHooks()) {\n return;\n }\n\n if (!GLOBAL_OBJ._sentryEsmLoaderHookRegistered) {\n GLOBAL_OBJ._sentryEsmLoaderHookRegistered = true;\n\n try {\n const { addHookMessagePort } = createAddHookMessageChannel();\n // @ts-expect-error register is available in these versions\n moduleModule.register('import-in-the-middle/hook.mjs', import.meta.url, {\n data: { addHookMessagePort, include: [] },\n transferList: [addHookMessagePort],\n });\n } catch (error) {\n debug.warn(\"Failed to register 'import-in-the-middle' hook\", error);\n }\n }\n}\n"],"names":[],"mappings":";;;;;AAKA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,mBAAmB,GAAS;AAC5C,EAAE,IAAI,CAAC,sBAAsB,EAAE,EAAE;AACjC,IAAI;AACJ,EAAE;;AAEF,EAAE,IAAI,CAAC,UAAU,CAAC,8BAA8B,EAAE;AAClD,IAAI,UAAU,CAAC,8BAAA,GAAiC,IAAI;;AAEpD,IAAI,IAAI;AACR,MAAM,MAAM,EAAE,kBAAA,KAAuB,2BAA2B,EAAE;AAClE;AACA,MAAM,YAAY,CAAC,QAAQ,CAAC,+BAA+B,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;AAC9E,QAAQ,IAAI,EAAE,EAAE,kBAAkB,EAAE,OAAO,EAAE,EAAC,EAAG;AACjD,QAAQ,YAAY,EAAE,CAAC,kBAAkB,CAAC;AAC1C,OAAO,CAAC;AACR,IAAI,CAAA,CAAE,OAAO,KAAK,EAAE;AACpB,MAAM,KAAK,CAAC,IAAI,CAAC,gDAAgD,EAAE,KAAK,CAAC;AACzE,IAAI;AACJ,EAAE;AACF;;;;"} | ||
| {"version":3,"file":"esmLoader.js","sources":["../../../src/sdk/esmLoader.ts"],"sourcesContent":["import { debug, GLOBAL_OBJ } from '@sentry/core';\nimport { createAddHookMessageChannel } from 'import-in-the-middle';\nimport * as moduleModule from 'module';\nimport { supportsEsmLoaderHooks } from '../utils/detection';\n\n/**\n * Initialize the ESM loader - This method is private and not part of the public\n * API.\n *\n * @ignore\n */\nexport function initializeEsmLoader(): void {\n if (!supportsEsmLoaderHooks()) {\n return;\n }\n\n if (!GLOBAL_OBJ._sentryEsmLoaderHookRegistered) {\n GLOBAL_OBJ._sentryEsmLoaderHookRegistered = true;\n\n try {\n const { addHookMessagePort } = createAddHookMessageChannel();\n // @ts-expect-error register is available in these versions\n moduleModule.register('import-in-the-middle/hook.mjs', import.meta.url, {\n data: { addHookMessagePort, include: [] },\n transferList: [addHookMessagePort],\n });\n } catch (error) {\n debug.warn(\"Failed to register 'import-in-the-middle' hook\", error);\n }\n }\n}\n"],"names":[],"mappings":";;;;;AAWO,SAAS,mBAAA,GAA4B;AAC1C,EAAA,IAAI,CAAC,wBAAuB,EAAG;AAC7B,IAAA;AAAA,EACF;AAEA,EAAA,IAAI,CAAC,WAAW,8BAAA,EAAgC;AAC9C,IAAA,UAAA,CAAW,8BAAA,GAAiC,IAAA;AAE5C,IAAA,IAAI;AACF,MAAA,MAAM,EAAE,kBAAA,EAAmB,GAAI,2BAAA,EAA4B;AAE3D,MAAA,YAAA,CAAa,QAAA,CAAS,+BAAA,EAAiC,MAAA,CAAA,IAAA,CAAY,GAAA,EAAK;AAAA,QACtE,IAAA,EAAM,EAAE,kBAAA,EAAoB,OAAA,EAAS,EAAC,EAAE;AAAA,QACxC,YAAA,EAAc,CAAC,kBAAkB;AAAA,OAClC,CAAA;AAAA,IACH,SAAS,KAAA,EAAO;AACd,MAAA,KAAA,CAAM,IAAA,CAAK,kDAAkD,KAAK,CAAA;AAAA,IACpE;AAAA,EACF;AACF;;;;"} |
+24
-95
@@ -24,5 +24,2 @@ import { debug, consoleSandbox, getCurrentScope, applySdkMetadata, envToBool, stackParserFromStackParserOptions, getIntegrationsToSetup, spanStreamingIntegration, propagationContextFromHeaders, inboundFiltersIntegration, functionToStringIntegration, linkedErrorsIntegration, requestDataIntegration, conversationIdIntegration, hasSpansEnabled } from '@sentry/core'; | ||
| /** | ||
| * Get default integrations for the Node-Core SDK. | ||
| */ | ||
| function getDefaultIntegrations() { | ||
@@ -52,29 +49,13 @@ return [ | ||
| processSessionIntegration(), | ||
| modulesIntegration(), | ||
| modulesIntegration() | ||
| ]; | ||
| } | ||
| /** | ||
| * Initialize Sentry for Node. | ||
| */ | ||
| function init(options = {}) { | ||
| return _init(options, getDefaultIntegrations); | ||
| } | ||
| /** | ||
| * Initialize Sentry for Node, without any integrations added by default. | ||
| */ | ||
| function initWithoutDefaultIntegrations(options = {}) { | ||
| return _init(options, () => []); | ||
| } | ||
| /** | ||
| * Initialize Sentry for Node, without performance instrumentation. | ||
| */ | ||
| function _init( | ||
| _options = {}, | ||
| getDefaultIntegrationsImpl, | ||
| ) { | ||
| function _init(_options = {}, getDefaultIntegrationsImpl) { | ||
| const options = getClientOptions(_options, getDefaultIntegrationsImpl); | ||
| if (options.debug === true) { | ||
@@ -84,59 +65,36 @@ if (DEBUG_BUILD) { | ||
| } else { | ||
| // use `console.warn` rather than `debug.warn` since by non-debug bundles have all `debug.x` statements stripped | ||
| consoleSandbox(() => { | ||
| // eslint-disable-next-line no-console | ||
| console.warn('[Sentry] Cannot initialize SDK with `debug` option using a non-debug bundle.'); | ||
| console.warn("[Sentry] Cannot initialize SDK with `debug` option using a non-debug bundle."); | ||
| }); | ||
| } | ||
| } | ||
| if (options.registerEsmLoaderHooks !== false) { | ||
| initializeEsmLoader(); | ||
| } | ||
| setOpenTelemetryContextAsyncContextStrategy(); | ||
| const scope = getCurrentScope(); | ||
| scope.update(options.initialScope); | ||
| if (options.spotlight && !options.integrations.some(({ name }) => name === INTEGRATION_NAME)) { | ||
| options.integrations.push( | ||
| spotlightIntegration({ | ||
| sidecarUrl: typeof options.spotlight === 'string' ? options.spotlight : undefined, | ||
| }), | ||
| sidecarUrl: typeof options.spotlight === "string" ? options.spotlight : void 0 | ||
| }) | ||
| ); | ||
| } | ||
| applySdkMetadata(options, 'node-core'); | ||
| applySdkMetadata(options, "node-core"); | ||
| const client = new NodeClient(options); | ||
| // The client is on the current scope, from where it generally is inherited | ||
| getCurrentScope().setClient(client); | ||
| client.init(); | ||
| debug.log(`SDK initialized from ${isCjs() ? 'CommonJS' : 'ESM'}`); | ||
| debug.log(`SDK initialized from ${isCjs() ? "CommonJS" : "ESM"}`); | ||
| client.startClientReportTracking(); | ||
| updateScopeFromEnvVariables(); | ||
| enhanceDscWithOpenTelemetryRootSpanName(client); | ||
| setupEventContextTrace(client); | ||
| // Ensure we flush events when vercel functions are ended | ||
| // See: https://vercel.com/docs/functions/functions-api-reference#sigterm-signal | ||
| if (process.env.VERCEL) { | ||
| process.on('SIGTERM', async () => { | ||
| // We have 500ms for processing here, so we try to make sure to have enough time to send the events | ||
| process.on("SIGTERM", async () => { | ||
| await client.flush(200); | ||
| }); | ||
| } | ||
| return client; | ||
| } | ||
| /** | ||
| * Validate that your OpenTelemetry setup is correct. | ||
| */ | ||
| function validateOpenTelemetrySetup() { | ||
@@ -146,36 +104,24 @@ if (!DEBUG_BUILD) { | ||
| } | ||
| const setup = openTelemetrySetupCheck(); | ||
| const required = ['SentryContextManager', 'SentryPropagator']; | ||
| const required = ["SentryContextManager", "SentryPropagator"]; | ||
| if (hasSpansEnabled()) { | ||
| required.push('SentrySpanProcessor'); | ||
| required.push("SentrySpanProcessor"); | ||
| } | ||
| for (const k of required) { | ||
| if (!setup.includes(k)) { | ||
| debug.error( | ||
| `You have to set up the ${k}. Without this, the OpenTelemetry & Sentry integration will not work properly.`, | ||
| `You have to set up the ${k}. Without this, the OpenTelemetry & Sentry integration will not work properly.` | ||
| ); | ||
| } | ||
| } | ||
| if (!setup.includes('SentrySampler')) { | ||
| if (!setup.includes("SentrySampler")) { | ||
| debug.warn( | ||
| 'You have to set up the SentrySampler. Without this, the OpenTelemetry & Sentry integration may still work, but sample rates set for the Sentry SDK will not be respected. If you use a custom sampler, make sure to use `wrapSamplingDecision`.', | ||
| "You have to set up the SentrySampler. Without this, the OpenTelemetry & Sentry integration may still work, but sample rates set for the Sentry SDK will not be respected. If you use a custom sampler, make sure to use `wrapSamplingDecision`." | ||
| ); | ||
| } | ||
| } | ||
| function getClientOptions( | ||
| options, | ||
| getDefaultIntegrationsImpl, | ||
| ) { | ||
| function getClientOptions(options, getDefaultIntegrationsImpl) { | ||
| const release = getRelease(options.release); | ||
| const spotlight = getSpotlightConfig(options.spotlight); | ||
| const tracesSampleRate = getTracesSampleRate(options.tracesSampleRate); | ||
| const mergedOptions = { | ||
@@ -191,56 +137,39 @@ ...options, | ||
| spotlight, | ||
| debug: envToBool(options.debug ?? process.env.SENTRY_DEBUG), | ||
| debug: envToBool(options.debug ?? process.env.SENTRY_DEBUG) | ||
| }; | ||
| const integrations = options.integrations; | ||
| const defaultIntegrations = options.defaultIntegrations ?? getDefaultIntegrationsImpl(mergedOptions); | ||
| const resolvedIntegrations = getIntegrationsToSetup({ | ||
| defaultIntegrations, | ||
| integrations, | ||
| integrations | ||
| }); | ||
| if (mergedOptions.traceLifecycle === 'stream' && !resolvedIntegrations.some(i => i.name === 'SpanStreaming')) { | ||
| if (mergedOptions.traceLifecycle === "stream" && !resolvedIntegrations.some((i) => i.name === "SpanStreaming")) { | ||
| resolvedIntegrations.push(spanStreamingIntegration()); | ||
| } | ||
| return { | ||
| ...mergedOptions, | ||
| integrations: resolvedIntegrations, | ||
| integrations: resolvedIntegrations | ||
| }; | ||
| } | ||
| function getRelease(release) { | ||
| if (release !== undefined) { | ||
| if (release !== void 0) { | ||
| return release; | ||
| } | ||
| const detectedRelease = getSentryRelease(); | ||
| if (detectedRelease !== undefined) { | ||
| if (detectedRelease !== void 0) { | ||
| return detectedRelease; | ||
| } | ||
| return undefined; | ||
| return void 0; | ||
| } | ||
| function getTracesSampleRate(tracesSampleRate) { | ||
| if (tracesSampleRate !== undefined) { | ||
| if (tracesSampleRate !== void 0) { | ||
| return tracesSampleRate; | ||
| } | ||
| const sampleRateFromEnv = process.env.SENTRY_TRACES_SAMPLE_RATE; | ||
| if (!sampleRateFromEnv) { | ||
| return undefined; | ||
| return void 0; | ||
| } | ||
| const parsed = parseFloat(sampleRateFromEnv); | ||
| return isFinite(parsed) ? parsed : undefined; | ||
| return isFinite(parsed) ? parsed : void 0; | ||
| } | ||
| /** | ||
| * Update scope and propagation context based on environmental variables. | ||
| * | ||
| * See https://github.com/getsentry/rfcs/blob/main/text/0071-continue-trace-over-process-boundaries.md | ||
| * for more details. | ||
| */ | ||
| function updateScopeFromEnvVariables() { | ||
@@ -247,0 +176,0 @@ if (envToBool(process.env.SENTRY_USE_ENVIRONMENT) !== false) { |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.js","sources":["../../../src/sdk/index.ts"],"sourcesContent":["import type { Integration, Options } from '@sentry/core';\nimport {\n applySdkMetadata,\n consoleSandbox,\n conversationIdIntegration,\n debug,\n envToBool,\n functionToStringIntegration,\n getCurrentScope,\n getIntegrationsToSetup,\n hasSpansEnabled,\n inboundFiltersIntegration,\n linkedErrorsIntegration,\n propagationContextFromHeaders,\n requestDataIntegration,\n spanStreamingIntegration,\n stackParserFromStackParserOptions,\n} from '@sentry/core';\nimport {\n enhanceDscWithOpenTelemetryRootSpanName,\n openTelemetrySetupCheck,\n setOpenTelemetryContextAsyncContextStrategy,\n setupEventContextTrace,\n} from '@sentry/opentelemetry';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { childProcessIntegration } from '../integrations/childProcess';\nimport { nodeContextIntegration } from '../integrations/context';\nimport { contextLinesIntegration } from '../integrations/contextlines';\nimport { httpIntegration } from '../integrations/http';\nimport { localVariablesIntegration } from '../integrations/local-variables';\nimport { modulesIntegration } from '../integrations/modules';\nimport { nativeNodeFetchIntegration } from '../integrations/node-fetch';\nimport { onUncaughtExceptionIntegration } from '../integrations/onuncaughtexception';\nimport { onUnhandledRejectionIntegration } from '../integrations/onunhandledrejection';\nimport { processSessionIntegration } from '../integrations/processSession';\nimport { INTEGRATION_NAME as SPOTLIGHT_INTEGRATION_NAME, spotlightIntegration } from '../integrations/spotlight';\nimport { consoleIntegration } from '../integrations/console';\nimport { systemErrorIntegration } from '../integrations/systemError';\nimport { makeNodeTransport } from '../transports';\nimport type { NodeClientOptions, NodeOptions } from '../types';\nimport { isCjs } from '../utils/detection';\nimport { getSpotlightConfig } from '../utils/spotlight';\nimport { defaultStackParser, getSentryRelease } from './api';\nimport { NodeClient } from './client';\nimport { initializeEsmLoader } from './esmLoader';\n\n/**\n * Get default integrations for the Node-Core SDK.\n */\nexport function getDefaultIntegrations(): Integration[] {\n return [\n // Common\n // TODO(v11): Replace with `eventFiltersIntegration` once we remove the deprecated `inboundFiltersIntegration`\n // eslint-disable-next-line deprecation/deprecation\n inboundFiltersIntegration(),\n functionToStringIntegration(),\n linkedErrorsIntegration(),\n requestDataIntegration(),\n systemErrorIntegration(),\n conversationIdIntegration(),\n // Native Wrappers\n consoleIntegration(),\n httpIntegration(),\n nativeNodeFetchIntegration(),\n // Global Handlers\n onUncaughtExceptionIntegration(),\n onUnhandledRejectionIntegration(),\n // Event Info\n contextLinesIntegration(),\n localVariablesIntegration(),\n nodeContextIntegration(),\n childProcessIntegration(),\n processSessionIntegration(),\n modulesIntegration(),\n ];\n}\n\n/**\n * Initialize Sentry for Node.\n */\nexport function init(options: NodeOptions | undefined = {}): NodeClient | undefined {\n return _init(options, getDefaultIntegrations);\n}\n\n/**\n * Initialize Sentry for Node, without any integrations added by default.\n */\nexport function initWithoutDefaultIntegrations(options: NodeOptions | undefined = {}): NodeClient {\n return _init(options, () => []);\n}\n\n/**\n * Initialize Sentry for Node, without performance instrumentation.\n */\nfunction _init(\n _options: NodeOptions | undefined = {},\n getDefaultIntegrationsImpl: (options: Options) => Integration[],\n): NodeClient {\n const options = getClientOptions(_options, getDefaultIntegrationsImpl);\n\n if (options.debug === true) {\n if (DEBUG_BUILD) {\n debug.enable();\n } else {\n // use `console.warn` rather than `debug.warn` since by non-debug bundles have all `debug.x` statements stripped\n consoleSandbox(() => {\n // eslint-disable-next-line no-console\n console.warn('[Sentry] Cannot initialize SDK with `debug` option using a non-debug bundle.');\n });\n }\n }\n\n if (options.registerEsmLoaderHooks !== false) {\n initializeEsmLoader();\n }\n\n setOpenTelemetryContextAsyncContextStrategy();\n\n const scope = getCurrentScope();\n scope.update(options.initialScope);\n\n if (options.spotlight && !options.integrations.some(({ name }) => name === SPOTLIGHT_INTEGRATION_NAME)) {\n options.integrations.push(\n spotlightIntegration({\n sidecarUrl: typeof options.spotlight === 'string' ? options.spotlight : undefined,\n }),\n );\n }\n\n applySdkMetadata(options, 'node-core');\n\n const client = new NodeClient(options);\n // The client is on the current scope, from where it generally is inherited\n getCurrentScope().setClient(client);\n\n client.init();\n\n debug.log(`SDK initialized from ${isCjs() ? 'CommonJS' : 'ESM'}`);\n\n client.startClientReportTracking();\n\n updateScopeFromEnvVariables();\n\n enhanceDscWithOpenTelemetryRootSpanName(client);\n setupEventContextTrace(client);\n\n // Ensure we flush events when vercel functions are ended\n // See: https://vercel.com/docs/functions/functions-api-reference#sigterm-signal\n if (process.env.VERCEL) {\n process.on('SIGTERM', async () => {\n // We have 500ms for processing here, so we try to make sure to have enough time to send the events\n await client.flush(200);\n });\n }\n\n return client;\n}\n\n/**\n * Validate that your OpenTelemetry setup is correct.\n */\nexport function validateOpenTelemetrySetup(): void {\n if (!DEBUG_BUILD) {\n return;\n }\n\n const setup = openTelemetrySetupCheck();\n\n const required: ReturnType<typeof openTelemetrySetupCheck> = ['SentryContextManager', 'SentryPropagator'];\n\n if (hasSpansEnabled()) {\n required.push('SentrySpanProcessor');\n }\n\n for (const k of required) {\n if (!setup.includes(k)) {\n debug.error(\n `You have to set up the ${k}. Without this, the OpenTelemetry & Sentry integration will not work properly.`,\n );\n }\n }\n\n if (!setup.includes('SentrySampler')) {\n debug.warn(\n 'You have to set up the SentrySampler. Without this, the OpenTelemetry & Sentry integration may still work, but sample rates set for the Sentry SDK will not be respected. If you use a custom sampler, make sure to use `wrapSamplingDecision`.',\n );\n }\n}\n\nfunction getClientOptions(\n options: NodeOptions,\n getDefaultIntegrationsImpl: (options: Options) => Integration[],\n): NodeClientOptions {\n const release = getRelease(options.release);\n\n const spotlight = getSpotlightConfig(options.spotlight);\n\n const tracesSampleRate = getTracesSampleRate(options.tracesSampleRate);\n\n const mergedOptions = {\n ...options,\n dsn: options.dsn ?? process.env.SENTRY_DSN,\n environment: options.environment ?? process.env.SENTRY_ENVIRONMENT,\n sendClientReports: options.sendClientReports ?? true,\n transport: options.transport ?? makeNodeTransport,\n stackParser: stackParserFromStackParserOptions(options.stackParser || defaultStackParser),\n release,\n tracesSampleRate,\n spotlight,\n debug: envToBool(options.debug ?? process.env.SENTRY_DEBUG),\n };\n\n const integrations = options.integrations;\n const defaultIntegrations = options.defaultIntegrations ?? getDefaultIntegrationsImpl(mergedOptions);\n\n const resolvedIntegrations = getIntegrationsToSetup({\n defaultIntegrations,\n integrations,\n });\n\n if (mergedOptions.traceLifecycle === 'stream' && !resolvedIntegrations.some(i => i.name === 'SpanStreaming')) {\n resolvedIntegrations.push(spanStreamingIntegration());\n }\n\n return {\n ...mergedOptions,\n integrations: resolvedIntegrations,\n };\n}\n\nfunction getRelease(release: NodeOptions['release']): string | undefined {\n if (release !== undefined) {\n return release;\n }\n\n const detectedRelease = getSentryRelease();\n if (detectedRelease !== undefined) {\n return detectedRelease;\n }\n\n return undefined;\n}\n\nfunction getTracesSampleRate(tracesSampleRate: NodeOptions['tracesSampleRate']): number | undefined {\n if (tracesSampleRate !== undefined) {\n return tracesSampleRate;\n }\n\n const sampleRateFromEnv = process.env.SENTRY_TRACES_SAMPLE_RATE;\n if (!sampleRateFromEnv) {\n return undefined;\n }\n\n const parsed = parseFloat(sampleRateFromEnv);\n return isFinite(parsed) ? parsed : undefined;\n}\n\n/**\n * Update scope and propagation context based on environmental variables.\n *\n * See https://github.com/getsentry/rfcs/blob/main/text/0071-continue-trace-over-process-boundaries.md\n * for more details.\n */\nfunction updateScopeFromEnvVariables(): void {\n if (envToBool(process.env.SENTRY_USE_ENVIRONMENT) !== false) {\n const sentryTraceEnv = process.env.SENTRY_TRACE;\n const baggageEnv = process.env.SENTRY_BAGGAGE;\n const propagationContext = propagationContextFromHeaders(sentryTraceEnv, baggageEnv);\n getCurrentScope().setPropagationContext(propagationContext);\n }\n}\n"],"names":["SPOTLIGHT_INTEGRATION_NAME"],"mappings":"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"} | ||
| {"version":3,"file":"index.js","sources":["../../../src/sdk/index.ts"],"sourcesContent":["import type { Integration, Options } from '@sentry/core';\nimport {\n applySdkMetadata,\n consoleSandbox,\n conversationIdIntegration,\n debug,\n envToBool,\n functionToStringIntegration,\n getCurrentScope,\n getIntegrationsToSetup,\n hasSpansEnabled,\n inboundFiltersIntegration,\n linkedErrorsIntegration,\n propagationContextFromHeaders,\n requestDataIntegration,\n spanStreamingIntegration,\n stackParserFromStackParserOptions,\n} from '@sentry/core';\nimport {\n enhanceDscWithOpenTelemetryRootSpanName,\n openTelemetrySetupCheck,\n setOpenTelemetryContextAsyncContextStrategy,\n setupEventContextTrace,\n} from '@sentry/opentelemetry';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { childProcessIntegration } from '../integrations/childProcess';\nimport { nodeContextIntegration } from '../integrations/context';\nimport { contextLinesIntegration } from '../integrations/contextlines';\nimport { httpIntegration } from '../integrations/http';\nimport { localVariablesIntegration } from '../integrations/local-variables';\nimport { modulesIntegration } from '../integrations/modules';\nimport { nativeNodeFetchIntegration } from '../integrations/node-fetch';\nimport { onUncaughtExceptionIntegration } from '../integrations/onuncaughtexception';\nimport { onUnhandledRejectionIntegration } from '../integrations/onunhandledrejection';\nimport { processSessionIntegration } from '../integrations/processSession';\nimport { INTEGRATION_NAME as SPOTLIGHT_INTEGRATION_NAME, spotlightIntegration } from '../integrations/spotlight';\nimport { consoleIntegration } from '../integrations/console';\nimport { systemErrorIntegration } from '../integrations/systemError';\nimport { makeNodeTransport } from '../transports';\nimport type { NodeClientOptions, NodeOptions } from '../types';\nimport { isCjs } from '../utils/detection';\nimport { getSpotlightConfig } from '../utils/spotlight';\nimport { defaultStackParser, getSentryRelease } from './api';\nimport { NodeClient } from './client';\nimport { initializeEsmLoader } from './esmLoader';\n\n/**\n * Get default integrations for the Node-Core SDK.\n */\nexport function getDefaultIntegrations(): Integration[] {\n return [\n // Common\n // TODO(v11): Replace with `eventFiltersIntegration` once we remove the deprecated `inboundFiltersIntegration`\n // eslint-disable-next-line deprecation/deprecation\n inboundFiltersIntegration(),\n functionToStringIntegration(),\n linkedErrorsIntegration(),\n requestDataIntegration(),\n systemErrorIntegration(),\n conversationIdIntegration(),\n // Native Wrappers\n consoleIntegration(),\n httpIntegration(),\n nativeNodeFetchIntegration(),\n // Global Handlers\n onUncaughtExceptionIntegration(),\n onUnhandledRejectionIntegration(),\n // Event Info\n contextLinesIntegration(),\n localVariablesIntegration(),\n nodeContextIntegration(),\n childProcessIntegration(),\n processSessionIntegration(),\n modulesIntegration(),\n ];\n}\n\n/**\n * Initialize Sentry for Node.\n */\nexport function init(options: NodeOptions | undefined = {}): NodeClient | undefined {\n return _init(options, getDefaultIntegrations);\n}\n\n/**\n * Initialize Sentry for Node, without any integrations added by default.\n */\nexport function initWithoutDefaultIntegrations(options: NodeOptions | undefined = {}): NodeClient {\n return _init(options, () => []);\n}\n\n/**\n * Initialize Sentry for Node, without performance instrumentation.\n */\nfunction _init(\n _options: NodeOptions | undefined = {},\n getDefaultIntegrationsImpl: (options: Options) => Integration[],\n): NodeClient {\n const options = getClientOptions(_options, getDefaultIntegrationsImpl);\n\n if (options.debug === true) {\n if (DEBUG_BUILD) {\n debug.enable();\n } else {\n // use `console.warn` rather than `debug.warn` since by non-debug bundles have all `debug.x` statements stripped\n consoleSandbox(() => {\n // eslint-disable-next-line no-console\n console.warn('[Sentry] Cannot initialize SDK with `debug` option using a non-debug bundle.');\n });\n }\n }\n\n if (options.registerEsmLoaderHooks !== false) {\n initializeEsmLoader();\n }\n\n setOpenTelemetryContextAsyncContextStrategy();\n\n const scope = getCurrentScope();\n scope.update(options.initialScope);\n\n if (options.spotlight && !options.integrations.some(({ name }) => name === SPOTLIGHT_INTEGRATION_NAME)) {\n options.integrations.push(\n spotlightIntegration({\n sidecarUrl: typeof options.spotlight === 'string' ? options.spotlight : undefined,\n }),\n );\n }\n\n applySdkMetadata(options, 'node-core');\n\n const client = new NodeClient(options);\n // The client is on the current scope, from where it generally is inherited\n getCurrentScope().setClient(client);\n\n client.init();\n\n debug.log(`SDK initialized from ${isCjs() ? 'CommonJS' : 'ESM'}`);\n\n client.startClientReportTracking();\n\n updateScopeFromEnvVariables();\n\n enhanceDscWithOpenTelemetryRootSpanName(client);\n setupEventContextTrace(client);\n\n // Ensure we flush events when vercel functions are ended\n // See: https://vercel.com/docs/functions/functions-api-reference#sigterm-signal\n if (process.env.VERCEL) {\n process.on('SIGTERM', async () => {\n // We have 500ms for processing here, so we try to make sure to have enough time to send the events\n await client.flush(200);\n });\n }\n\n return client;\n}\n\n/**\n * Validate that your OpenTelemetry setup is correct.\n */\nexport function validateOpenTelemetrySetup(): void {\n if (!DEBUG_BUILD) {\n return;\n }\n\n const setup = openTelemetrySetupCheck();\n\n const required: ReturnType<typeof openTelemetrySetupCheck> = ['SentryContextManager', 'SentryPropagator'];\n\n if (hasSpansEnabled()) {\n required.push('SentrySpanProcessor');\n }\n\n for (const k of required) {\n if (!setup.includes(k)) {\n debug.error(\n `You have to set up the ${k}. Without this, the OpenTelemetry & Sentry integration will not work properly.`,\n );\n }\n }\n\n if (!setup.includes('SentrySampler')) {\n debug.warn(\n 'You have to set up the SentrySampler. Without this, the OpenTelemetry & Sentry integration may still work, but sample rates set for the Sentry SDK will not be respected. If you use a custom sampler, make sure to use `wrapSamplingDecision`.',\n );\n }\n}\n\nfunction getClientOptions(\n options: NodeOptions,\n getDefaultIntegrationsImpl: (options: Options) => Integration[],\n): NodeClientOptions {\n const release = getRelease(options.release);\n\n const spotlight = getSpotlightConfig(options.spotlight);\n\n const tracesSampleRate = getTracesSampleRate(options.tracesSampleRate);\n\n const mergedOptions = {\n ...options,\n dsn: options.dsn ?? process.env.SENTRY_DSN,\n environment: options.environment ?? process.env.SENTRY_ENVIRONMENT,\n sendClientReports: options.sendClientReports ?? true,\n transport: options.transport ?? makeNodeTransport,\n stackParser: stackParserFromStackParserOptions(options.stackParser || defaultStackParser),\n release,\n tracesSampleRate,\n spotlight,\n debug: envToBool(options.debug ?? process.env.SENTRY_DEBUG),\n };\n\n const integrations = options.integrations;\n const defaultIntegrations = options.defaultIntegrations ?? getDefaultIntegrationsImpl(mergedOptions);\n\n const resolvedIntegrations = getIntegrationsToSetup({\n defaultIntegrations,\n integrations,\n });\n\n if (mergedOptions.traceLifecycle === 'stream' && !resolvedIntegrations.some(i => i.name === 'SpanStreaming')) {\n resolvedIntegrations.push(spanStreamingIntegration());\n }\n\n return {\n ...mergedOptions,\n integrations: resolvedIntegrations,\n };\n}\n\nfunction getRelease(release: NodeOptions['release']): string | undefined {\n if (release !== undefined) {\n return release;\n }\n\n const detectedRelease = getSentryRelease();\n if (detectedRelease !== undefined) {\n return detectedRelease;\n }\n\n return undefined;\n}\n\nfunction getTracesSampleRate(tracesSampleRate: NodeOptions['tracesSampleRate']): number | undefined {\n if (tracesSampleRate !== undefined) {\n return tracesSampleRate;\n }\n\n const sampleRateFromEnv = process.env.SENTRY_TRACES_SAMPLE_RATE;\n if (!sampleRateFromEnv) {\n return undefined;\n }\n\n const parsed = parseFloat(sampleRateFromEnv);\n return isFinite(parsed) ? parsed : undefined;\n}\n\n/**\n * Update scope and propagation context based on environmental variables.\n *\n * See https://github.com/getsentry/rfcs/blob/main/text/0071-continue-trace-over-process-boundaries.md\n * for more details.\n */\nfunction updateScopeFromEnvVariables(): void {\n if (envToBool(process.env.SENTRY_USE_ENVIRONMENT) !== false) {\n const sentryTraceEnv = process.env.SENTRY_TRACE;\n const baggageEnv = process.env.SENTRY_BAGGAGE;\n const propagationContext = propagationContextFromHeaders(sentryTraceEnv, baggageEnv);\n getCurrentScope().setPropagationContext(propagationContext);\n }\n}\n"],"names":["SPOTLIGHT_INTEGRATION_NAME"],"mappings":"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"} |
| import { context } from '@opentelemetry/api'; | ||
| import { getScopesFromContext } from '@sentry/opentelemetry'; | ||
| /** | ||
| * Update the active isolation scope. | ||
| * Should be used with caution! | ||
| */ | ||
| function setIsolationScope(isolationScope) { | ||
@@ -9,0 +5,0 @@ const scopes = getScopesFromContext(context.active()); |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"scope.js","sources":["../../../src/sdk/scope.ts"],"sourcesContent":["import { context } from '@opentelemetry/api';\nimport type { Scope } from '@sentry/core';\nimport { getScopesFromContext } from '@sentry/opentelemetry';\n\n/**\n * Update the active isolation scope.\n * Should be used with caution!\n */\nexport function setIsolationScope(isolationScope: Scope): void {\n const scopes = getScopesFromContext(context.active());\n if (scopes) {\n scopes.isolationScope = isolationScope;\n }\n}\n"],"names":[],"mappings":";;;AAIA;AACA;AACA;AACA;AACO,SAAS,iBAAiB,CAAC,cAAc,EAAe;AAC/D,EAAE,MAAM,MAAA,GAAS,oBAAoB,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;AACvD,EAAE,IAAI,MAAM,EAAE;AACd,IAAI,MAAM,CAAC,cAAA,GAAiB,cAAc;AAC1C,EAAE;AACF;;;;"} | ||
| {"version":3,"file":"scope.js","sources":["../../../src/sdk/scope.ts"],"sourcesContent":["import { context } from '@opentelemetry/api';\nimport type { Scope } from '@sentry/core';\nimport { getScopesFromContext } from '@sentry/opentelemetry';\n\n/**\n * Update the active isolation scope.\n * Should be used with caution!\n */\nexport function setIsolationScope(isolationScope: Scope): void {\n const scopes = getScopesFromContext(context.active());\n if (scopes) {\n scopes.isolationScope = isolationScope;\n }\n}\n"],"names":[],"mappings":";;;AAQO,SAAS,kBAAkB,cAAA,EAA6B;AAC7D,EAAA,MAAM,MAAA,GAAS,oBAAA,CAAqB,OAAA,CAAQ,MAAA,EAAQ,CAAA;AACpD,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,MAAA,CAAO,cAAA,GAAiB,cAAA;AAAA,EAC1B;AACF;;;;"} |
@@ -8,9 +8,3 @@ import * as http from 'node:http'; | ||
| // Estimated maximum size for reasonable standalone event | ||
| const GZIP_THRESHOLD = 1024 * 32; | ||
| /** | ||
| * Gets a stream from a Uint8Array or string | ||
| * Readable.from is ideal but was added in node.js v12.3.0 and v10.17.0 | ||
| */ | ||
| function streamFromBody(body) { | ||
@@ -21,12 +15,7 @@ return new Readable({ | ||
| this.push(null); | ||
| }, | ||
| } | ||
| }); | ||
| } | ||
| /** | ||
| * Creates a Transport that uses native the native 'http' and 'https' modules to send events to Sentry. | ||
| */ | ||
| function makeNodeTransport(options) { | ||
| let urlSegments; | ||
| try { | ||
@@ -36,5 +25,4 @@ urlSegments = new URL(options.url); | ||
| consoleSandbox(() => { | ||
| // eslint-disable-next-line no-console | ||
| console.warn( | ||
| '[@sentry/node]: Invalid dsn or tunnel option, will not send any events. The tunnel option must be a full URL when used.', | ||
| "[@sentry/node]: Invalid dsn or tunnel option, will not send any events. The tunnel option must be a full URL when used." | ||
| ); | ||
@@ -44,43 +32,20 @@ }); | ||
| } | ||
| const isHttps = urlSegments.protocol === 'https:'; | ||
| // Proxy prioritization: http => `options.proxy` | `process.env.http_proxy` | ||
| // Proxy prioritization: https => `options.proxy` | `process.env.https_proxy` | `process.env.http_proxy` | ||
| const isHttps = urlSegments.protocol === "https:"; | ||
| const proxy = applyNoProxyOption( | ||
| urlSegments, | ||
| options.proxy || (isHttps ? process.env.https_proxy : undefined) || process.env.http_proxy, | ||
| options.proxy || (isHttps ? process.env.https_proxy : void 0) || process.env.http_proxy | ||
| ); | ||
| const nativeHttpModule = isHttps ? https : http; | ||
| const keepAlive = options.keepAlive === undefined ? false : options.keepAlive; | ||
| // TODO(v11): Evaluate if we can set keepAlive to true. This would involve testing for memory leaks in older node | ||
| // versions(>= 8) as they had memory leaks when using it: #2555 | ||
| const agent = proxy | ||
| ? (new HttpsProxyAgent(proxy) ) | ||
| : new nativeHttpModule.Agent({ keepAlive, maxSockets: 30, timeout: 2000 }); | ||
| const keepAlive = options.keepAlive === void 0 ? false : options.keepAlive; | ||
| const agent = proxy ? new HttpsProxyAgent(proxy) : new nativeHttpModule.Agent({ keepAlive, maxSockets: 30, timeout: 2e3 }); | ||
| const requestExecutor = createRequestExecutor(options, options.httpModule ?? nativeHttpModule, agent); | ||
| return createTransport(options, requestExecutor); | ||
| } | ||
| /** | ||
| * Honors the `no_proxy` env variable with the highest priority to allow for hosts exclusion. | ||
| * | ||
| * @param transportUrl The URL the transport intends to send events to. | ||
| * @param proxy The client configured proxy. | ||
| * @returns A proxy the transport should use. | ||
| */ | ||
| function applyNoProxyOption(transportUrlSegments, proxy) { | ||
| const { no_proxy } = process.env; | ||
| const urlIsExemptFromProxy = no_proxy | ||
| ?.split(',') | ||
| .some( | ||
| exemption => transportUrlSegments.host.endsWith(exemption) || transportUrlSegments.hostname.endsWith(exemption), | ||
| ); | ||
| const urlIsExemptFromProxy = no_proxy?.split(",").some( | ||
| (exemption) => transportUrlSegments.host.endsWith(exemption) || transportUrlSegments.hostname.endsWith(exemption) | ||
| ); | ||
| if (urlIsExemptFromProxy) { | ||
| return undefined; | ||
| return void 0; | ||
| } else { | ||
@@ -90,30 +55,17 @@ return proxy; | ||
| } | ||
| /** | ||
| * Creates a RequestExecutor to be used with `createTransport`. | ||
| */ | ||
| function createRequestExecutor( | ||
| options, | ||
| httpModule, | ||
| agent, | ||
| ) { | ||
| function createRequestExecutor(options, httpModule, agent) { | ||
| const { hostname, pathname, port, protocol, search } = new URL(options.url); | ||
| return function makeRequest(request) { | ||
| return new Promise((resolve, reject) => { | ||
| // This ensures we do not generate any spans in OpenTelemetry for the transport | ||
| suppressTracing(() => { | ||
| let body = streamFromBody(request.body); | ||
| const headers = { ...options.headers }; | ||
| if (request.body.length > GZIP_THRESHOLD) { | ||
| headers['content-encoding'] = 'gzip'; | ||
| headers["content-encoding"] = "gzip"; | ||
| body = body.pipe(createGzip()); | ||
| } | ||
| const hostnameIsIPv6 = hostname.startsWith('['); | ||
| const hostnameIsIPv6 = hostname.startsWith("["); | ||
| const req = httpModule.request( | ||
| { | ||
| method: 'POST', | ||
| method: "POST", | ||
| agent, | ||
@@ -126,33 +78,22 @@ headers, | ||
| protocol, | ||
| ca: options.caCerts, | ||
| ca: options.caCerts | ||
| }, | ||
| res => { | ||
| res.on('data', () => { | ||
| // Drain socket | ||
| (res) => { | ||
| res.on("data", () => { | ||
| }); | ||
| res.on('end', () => { | ||
| // Drain socket | ||
| res.on("end", () => { | ||
| }); | ||
| res.setEncoding('utf8'); | ||
| // "Key-value pairs of header names and values. Header names are lower-cased." | ||
| // https://nodejs.org/api/http.html#http_message_headers | ||
| const retryAfterHeader = res.headers['retry-after'] ?? null; | ||
| const rateLimitsHeader = res.headers['x-sentry-rate-limits'] ?? null; | ||
| res.setEncoding("utf8"); | ||
| const retryAfterHeader = res.headers["retry-after"] ?? null; | ||
| const rateLimitsHeader = res.headers["x-sentry-rate-limits"] ?? null; | ||
| resolve({ | ||
| statusCode: res.statusCode, | ||
| headers: { | ||
| 'retry-after': retryAfterHeader, | ||
| 'x-sentry-rate-limits': Array.isArray(rateLimitsHeader) | ||
| ? rateLimitsHeader[0] || null | ||
| : rateLimitsHeader, | ||
| }, | ||
| "retry-after": retryAfterHeader, | ||
| "x-sentry-rate-limits": Array.isArray(rateLimitsHeader) ? rateLimitsHeader[0] || null : rateLimitsHeader | ||
| } | ||
| }); | ||
| }, | ||
| } | ||
| ); | ||
| req.on('error', reject); | ||
| req.on("error", reject); | ||
| body.pipe(req); | ||
@@ -159,0 +100,0 @@ }); |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"http.js","sources":["../../../src/transports/http.ts"],"sourcesContent":["import * as http from 'node:http';\nimport * as https from 'node:https';\nimport { Readable } from 'node:stream';\nimport { createGzip } from 'node:zlib';\nimport type {\n BaseTransportOptions,\n Transport,\n TransportMakeRequestResponse,\n TransportRequest,\n TransportRequestExecutor,\n} from '@sentry/core';\nimport { consoleSandbox, createTransport, suppressTracing } from '@sentry/core';\nimport { HttpsProxyAgent } from '../proxy';\nimport type { HTTPModule } from './http-module';\n\nexport interface NodeTransportOptions extends BaseTransportOptions {\n /** Set a proxy that should be used for outbound requests. */\n proxy?: string;\n /** HTTPS proxy CA certificates */\n caCerts?: string | Buffer | Array<string | Buffer>;\n /** Custom HTTP module. Defaults to the native 'http' and 'https' modules. */\n httpModule?: HTTPModule;\n /** Allow overriding connection keepAlive, defaults to false */\n keepAlive?: boolean;\n}\n\n// Estimated maximum size for reasonable standalone event\nconst GZIP_THRESHOLD = 1024 * 32;\n\n/**\n * Gets a stream from a Uint8Array or string\n * Readable.from is ideal but was added in node.js v12.3.0 and v10.17.0\n */\nfunction streamFromBody(body: Uint8Array | string): Readable {\n return new Readable({\n read() {\n this.push(body);\n this.push(null);\n },\n });\n}\n\n/**\n * Creates a Transport that uses native the native 'http' and 'https' modules to send events to Sentry.\n */\nexport function makeNodeTransport(options: NodeTransportOptions): Transport {\n let urlSegments: URL;\n\n try {\n urlSegments = new URL(options.url);\n } catch (_e) {\n consoleSandbox(() => {\n // eslint-disable-next-line no-console\n console.warn(\n '[@sentry/node]: Invalid dsn or tunnel option, will not send any events. The tunnel option must be a full URL when used.',\n );\n });\n return createTransport(options, () => Promise.resolve({}));\n }\n\n const isHttps = urlSegments.protocol === 'https:';\n\n // Proxy prioritization: http => `options.proxy` | `process.env.http_proxy`\n // Proxy prioritization: https => `options.proxy` | `process.env.https_proxy` | `process.env.http_proxy`\n const proxy = applyNoProxyOption(\n urlSegments,\n options.proxy || (isHttps ? process.env.https_proxy : undefined) || process.env.http_proxy,\n );\n\n const nativeHttpModule = isHttps ? https : http;\n const keepAlive = options.keepAlive === undefined ? false : options.keepAlive;\n\n // TODO(v11): Evaluate if we can set keepAlive to true. This would involve testing for memory leaks in older node\n // versions(>= 8) as they had memory leaks when using it: #2555\n const agent = proxy\n ? (new HttpsProxyAgent(proxy) as http.Agent)\n : new nativeHttpModule.Agent({ keepAlive, maxSockets: 30, timeout: 2000 });\n\n const requestExecutor = createRequestExecutor(options, options.httpModule ?? nativeHttpModule, agent);\n return createTransport(options, requestExecutor);\n}\n\n/**\n * Honors the `no_proxy` env variable with the highest priority to allow for hosts exclusion.\n *\n * @param transportUrl The URL the transport intends to send events to.\n * @param proxy The client configured proxy.\n * @returns A proxy the transport should use.\n */\nfunction applyNoProxyOption(transportUrlSegments: URL, proxy: string | undefined): string | undefined {\n const { no_proxy } = process.env;\n\n const urlIsExemptFromProxy = no_proxy\n ?.split(',')\n .some(\n exemption => transportUrlSegments.host.endsWith(exemption) || transportUrlSegments.hostname.endsWith(exemption),\n );\n\n if (urlIsExemptFromProxy) {\n return undefined;\n } else {\n return proxy;\n }\n}\n\n/**\n * Creates a RequestExecutor to be used with `createTransport`.\n */\nfunction createRequestExecutor(\n options: NodeTransportOptions,\n httpModule: HTTPModule,\n agent: http.Agent,\n): TransportRequestExecutor {\n const { hostname, pathname, port, protocol, search } = new URL(options.url);\n return function makeRequest(request: TransportRequest): Promise<TransportMakeRequestResponse> {\n return new Promise((resolve, reject) => {\n // This ensures we do not generate any spans in OpenTelemetry for the transport\n suppressTracing(() => {\n let body = streamFromBody(request.body);\n\n const headers: Record<string, string> = { ...options.headers };\n\n if (request.body.length > GZIP_THRESHOLD) {\n headers['content-encoding'] = 'gzip';\n body = body.pipe(createGzip());\n }\n\n const hostnameIsIPv6 = hostname.startsWith('[');\n\n const req = httpModule.request(\n {\n method: 'POST',\n agent,\n headers,\n // Remove \"[\" and \"]\" from IPv6 hostnames\n hostname: hostnameIsIPv6 ? hostname.slice(1, -1) : hostname,\n path: `${pathname}${search}`,\n port,\n protocol,\n ca: options.caCerts,\n },\n res => {\n res.on('data', () => {\n // Drain socket\n });\n\n res.on('end', () => {\n // Drain socket\n });\n\n res.setEncoding('utf8');\n\n // \"Key-value pairs of header names and values. Header names are lower-cased.\"\n // https://nodejs.org/api/http.html#http_message_headers\n const retryAfterHeader = res.headers['retry-after'] ?? null;\n const rateLimitsHeader = res.headers['x-sentry-rate-limits'] ?? null;\n\n resolve({\n statusCode: res.statusCode,\n headers: {\n 'retry-after': retryAfterHeader,\n 'x-sentry-rate-limits': Array.isArray(rateLimitsHeader)\n ? rateLimitsHeader[0] || null\n : rateLimitsHeader,\n },\n });\n },\n );\n\n req.on('error', reject);\n body.pipe(req);\n });\n });\n };\n}\n"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"http.js","sources":["../../../src/transports/http.ts"],"sourcesContent":["import * as http from 'node:http';\nimport * as https from 'node:https';\nimport { Readable } from 'node:stream';\nimport { createGzip } from 'node:zlib';\nimport type {\n BaseTransportOptions,\n Transport,\n TransportMakeRequestResponse,\n TransportRequest,\n TransportRequestExecutor,\n} from '@sentry/core';\nimport { consoleSandbox, createTransport, suppressTracing } from '@sentry/core';\nimport { HttpsProxyAgent } from '../proxy';\nimport type { HTTPModule } from './http-module';\n\nexport interface NodeTransportOptions extends BaseTransportOptions {\n /** Set a proxy that should be used for outbound requests. */\n proxy?: string;\n /** HTTPS proxy CA certificates */\n caCerts?: string | Buffer | Array<string | Buffer>;\n /** Custom HTTP module. Defaults to the native 'http' and 'https' modules. */\n httpModule?: HTTPModule;\n /** Allow overriding connection keepAlive, defaults to false */\n keepAlive?: boolean;\n}\n\n// Estimated maximum size for reasonable standalone event\nconst GZIP_THRESHOLD = 1024 * 32;\n\n/**\n * Gets a stream from a Uint8Array or string\n * Readable.from is ideal but was added in node.js v12.3.0 and v10.17.0\n */\nfunction streamFromBody(body: Uint8Array | string): Readable {\n return new Readable({\n read() {\n this.push(body);\n this.push(null);\n },\n });\n}\n\n/**\n * Creates a Transport that uses native the native 'http' and 'https' modules to send events to Sentry.\n */\nexport function makeNodeTransport(options: NodeTransportOptions): Transport {\n let urlSegments: URL;\n\n try {\n urlSegments = new URL(options.url);\n } catch (_e) {\n consoleSandbox(() => {\n // eslint-disable-next-line no-console\n console.warn(\n '[@sentry/node]: Invalid dsn or tunnel option, will not send any events. The tunnel option must be a full URL when used.',\n );\n });\n return createTransport(options, () => Promise.resolve({}));\n }\n\n const isHttps = urlSegments.protocol === 'https:';\n\n // Proxy prioritization: http => `options.proxy` | `process.env.http_proxy`\n // Proxy prioritization: https => `options.proxy` | `process.env.https_proxy` | `process.env.http_proxy`\n const proxy = applyNoProxyOption(\n urlSegments,\n options.proxy || (isHttps ? process.env.https_proxy : undefined) || process.env.http_proxy,\n );\n\n const nativeHttpModule = isHttps ? https : http;\n const keepAlive = options.keepAlive === undefined ? false : options.keepAlive;\n\n // TODO(v11): Evaluate if we can set keepAlive to true. This would involve testing for memory leaks in older node\n // versions(>= 8) as they had memory leaks when using it: #2555\n const agent = proxy\n ? (new HttpsProxyAgent(proxy) as http.Agent)\n : new nativeHttpModule.Agent({ keepAlive, maxSockets: 30, timeout: 2000 });\n\n const requestExecutor = createRequestExecutor(options, options.httpModule ?? nativeHttpModule, agent);\n return createTransport(options, requestExecutor);\n}\n\n/**\n * Honors the `no_proxy` env variable with the highest priority to allow for hosts exclusion.\n *\n * @param transportUrl The URL the transport intends to send events to.\n * @param proxy The client configured proxy.\n * @returns A proxy the transport should use.\n */\nfunction applyNoProxyOption(transportUrlSegments: URL, proxy: string | undefined): string | undefined {\n const { no_proxy } = process.env;\n\n const urlIsExemptFromProxy = no_proxy\n ?.split(',')\n .some(\n exemption => transportUrlSegments.host.endsWith(exemption) || transportUrlSegments.hostname.endsWith(exemption),\n );\n\n if (urlIsExemptFromProxy) {\n return undefined;\n } else {\n return proxy;\n }\n}\n\n/**\n * Creates a RequestExecutor to be used with `createTransport`.\n */\nfunction createRequestExecutor(\n options: NodeTransportOptions,\n httpModule: HTTPModule,\n agent: http.Agent,\n): TransportRequestExecutor {\n const { hostname, pathname, port, protocol, search } = new URL(options.url);\n return function makeRequest(request: TransportRequest): Promise<TransportMakeRequestResponse> {\n return new Promise((resolve, reject) => {\n // This ensures we do not generate any spans in OpenTelemetry for the transport\n suppressTracing(() => {\n let body = streamFromBody(request.body);\n\n const headers: Record<string, string> = { ...options.headers };\n\n if (request.body.length > GZIP_THRESHOLD) {\n headers['content-encoding'] = 'gzip';\n body = body.pipe(createGzip());\n }\n\n const hostnameIsIPv6 = hostname.startsWith('[');\n\n const req = httpModule.request(\n {\n method: 'POST',\n agent,\n headers,\n // Remove \"[\" and \"]\" from IPv6 hostnames\n hostname: hostnameIsIPv6 ? hostname.slice(1, -1) : hostname,\n path: `${pathname}${search}`,\n port,\n protocol,\n ca: options.caCerts,\n },\n res => {\n res.on('data', () => {\n // Drain socket\n });\n\n res.on('end', () => {\n // Drain socket\n });\n\n res.setEncoding('utf8');\n\n // \"Key-value pairs of header names and values. Header names are lower-cased.\"\n // https://nodejs.org/api/http.html#http_message_headers\n const retryAfterHeader = res.headers['retry-after'] ?? null;\n const rateLimitsHeader = res.headers['x-sentry-rate-limits'] ?? null;\n\n resolve({\n statusCode: res.statusCode,\n headers: {\n 'retry-after': retryAfterHeader,\n 'x-sentry-rate-limits': Array.isArray(rateLimitsHeader)\n ? rateLimitsHeader[0] || null\n : rateLimitsHeader,\n },\n });\n },\n );\n\n req.on('error', reject);\n body.pipe(req);\n });\n });\n };\n}\n"],"names":[],"mappings":"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"} |
| import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core'; | ||
| /** Adds an origin to an OTEL Span. */ | ||
| function addOriginToSpan(span, origin) { | ||
@@ -5,0 +4,0 @@ span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, origin); |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"addOriginToSpan.js","sources":["../../../src/utils/addOriginToSpan.ts"],"sourcesContent":["import type { Span } from '@opentelemetry/api';\nimport type { SpanOrigin } from '@sentry/core';\nimport { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core';\n\n/** Adds an origin to an OTEL Span. */\nexport function addOriginToSpan(span: Span, origin: SpanOrigin): void {\n span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, origin);\n}\n"],"names":[],"mappings":";;AAIA;AACO,SAAS,eAAe,CAAC,IAAI,EAAQ,MAAM,EAAoB;AACtE,EAAE,IAAI,CAAC,YAAY,CAAC,gCAAgC,EAAE,MAAM,CAAC;AAC7D;;;;"} | ||
| {"version":3,"file":"addOriginToSpan.js","sources":["../../../src/utils/addOriginToSpan.ts"],"sourcesContent":["import type { Span } from '@opentelemetry/api';\nimport type { SpanOrigin } from '@sentry/core';\nimport { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core';\n\n/** Adds an origin to an OTEL Span. */\nexport function addOriginToSpan(span: Span, origin: SpanOrigin): void {\n span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, origin);\n}\n"],"names":[],"mappings":";;AAKO,SAAS,eAAA,CAAgB,MAAY,MAAA,EAA0B;AACpE,EAAA,IAAA,CAAK,YAAA,CAAa,kCAAkC,MAAM,CAAA;AAC5D;;;;"} |
@@ -5,3 +5,3 @@ import { isCjs } from './detection.js'; | ||
| package: pkg, | ||
| 'javascript.is_cjs': isCjs(), | ||
| "javascript.is_cjs": isCjs() | ||
| }); | ||
@@ -8,0 +8,0 @@ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"createMissingInstrumentationContext.js","sources":["../../../src/utils/createMissingInstrumentationContext.ts"],"sourcesContent":["import type { MissingInstrumentationContext } from '@sentry/core';\nimport { isCjs } from './detection';\n\nexport const createMissingInstrumentationContext = (pkg: string): MissingInstrumentationContext => ({\n package: pkg,\n 'javascript.is_cjs': isCjs(),\n});\n"],"names":[],"mappings":";;MAGa,mCAAA,GAAsC,CAAC,GAAG,MAA6C;AACpG,EAAE,OAAO,EAAE,GAAG;AACd,EAAE,mBAAmB,EAAE,KAAK,EAAE;AAC9B,CAAC;;;;"} | ||
| {"version":3,"file":"createMissingInstrumentationContext.js","sources":["../../../src/utils/createMissingInstrumentationContext.ts"],"sourcesContent":["import type { MissingInstrumentationContext } from '@sentry/core';\nimport { isCjs } from './detection';\n\nexport const createMissingInstrumentationContext = (pkg: string): MissingInstrumentationContext => ({\n package: pkg,\n 'javascript.is_cjs': isCjs(),\n});\n"],"names":[],"mappings":";;AAGO,MAAM,mCAAA,GAAsC,CAAC,GAAA,MAAgD;AAAA,EAClG,OAAA,EAAS,GAAA;AAAA,EACT,qBAAqB,KAAA;AACvB,CAAA;;;;"} |
| let cachedDebuggerEnabled; | ||
| /** | ||
| * Was the debugger enabled when this function was first called? | ||
| */ | ||
| async function isDebuggerEnabled() { | ||
| if (cachedDebuggerEnabled === undefined) { | ||
| if (cachedDebuggerEnabled === void 0) { | ||
| try { | ||
| // Node can be built without inspector support | ||
| const inspector = await import('node:inspector'); | ||
@@ -16,3 +11,2 @@ cachedDebuggerEnabled = !!inspector.url(); | ||
| } | ||
| return cachedDebuggerEnabled; | ||
@@ -19,0 +13,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"debug.js","sources":["../../../src/utils/debug.ts"],"sourcesContent":["let cachedDebuggerEnabled: boolean | undefined;\n\n/**\n * Was the debugger enabled when this function was first called?\n */\nexport async function isDebuggerEnabled(): Promise<boolean> {\n if (cachedDebuggerEnabled === undefined) {\n try {\n // Node can be built without inspector support\n const inspector = await import('node:inspector');\n cachedDebuggerEnabled = !!inspector.url();\n } catch {\n cachedDebuggerEnabled = false;\n }\n }\n\n return cachedDebuggerEnabled;\n}\n"],"names":[],"mappings":"AAAA,IAAI,qBAAqB;;AAEzB;AACA;AACA;AACO,eAAe,iBAAiB,GAAqB;AAC5D,EAAE,IAAI,qBAAA,KAA0B,SAAS,EAAE;AAC3C,IAAI,IAAI;AACR;AACA,MAAM,MAAM,SAAA,GAAY,MAAM,OAAO,gBAAgB,CAAC;AACtD,MAAM,qBAAA,GAAwB,CAAC,CAAC,SAAS,CAAC,GAAG,EAAE;AAC/C,IAAI,EAAE,MAAM;AACZ,MAAM,qBAAA,GAAwB,KAAK;AACnC,IAAI;AACJ,EAAE;;AAEF,EAAE,OAAO,qBAAqB;AAC9B;;;;"} | ||
| {"version":3,"file":"debug.js","sources":["../../../src/utils/debug.ts"],"sourcesContent":["let cachedDebuggerEnabled: boolean | undefined;\n\n/**\n * Was the debugger enabled when this function was first called?\n */\nexport async function isDebuggerEnabled(): Promise<boolean> {\n if (cachedDebuggerEnabled === undefined) {\n try {\n // Node can be built without inspector support\n const inspector = await import('node:inspector');\n cachedDebuggerEnabled = !!inspector.url();\n } catch {\n cachedDebuggerEnabled = false;\n }\n }\n\n return cachedDebuggerEnabled;\n}\n"],"names":[],"mappings":"AAAA,IAAI,qBAAA;AAKJ,eAAsB,iBAAA,GAAsC;AAC1D,EAAA,IAAI,0BAA0B,MAAA,EAAW;AACvC,IAAA,IAAI;AAEF,MAAA,MAAM,SAAA,GAAY,MAAM,OAAO,gBAAgB,CAAA;AAC/C,MAAA,qBAAA,GAAwB,CAAC,CAAC,SAAA,CAAU,GAAA,EAAI;AAAA,IAC1C,CAAA,CAAA,MAAQ;AACN,MAAA,qBAAA,GAAwB,KAAA;AAAA,IAC1B;AAAA,EACF;AAEA,EAAA,OAAO,qBAAA;AACT;;;;"} |
| import { consoleSandbox } from '@sentry/core'; | ||
| import { NODE_MAJOR, NODE_MINOR } from '../nodeVersion.js'; | ||
| /** Detect CommonJS. */ | ||
| function isCjs() { | ||
| try { | ||
| // oxlint-disable-next-line typescript/prefer-optional-chain | ||
| return typeof module !== 'undefined' && typeof module.exports !== 'undefined'; | ||
| return typeof module !== "undefined" && typeof module.exports !== "undefined"; | ||
| } catch { | ||
@@ -13,8 +11,3 @@ return false; | ||
| } | ||
| let hasWarnedAboutNodeVersion; | ||
| /** | ||
| * Check if the current Node.js version supports module.register | ||
| */ | ||
| function supportsEsmLoaderHooks() { | ||
@@ -24,18 +17,13 @@ if (isCjs()) { | ||
| } | ||
| if (NODE_MAJOR >= 21 || (NODE_MAJOR === 20 && NODE_MINOR >= 6) || (NODE_MAJOR === 18 && NODE_MINOR >= 19)) { | ||
| if (NODE_MAJOR >= 21 || NODE_MAJOR === 20 && NODE_MINOR >= 6 || NODE_MAJOR === 18 && NODE_MINOR >= 19) { | ||
| return true; | ||
| } | ||
| if (!hasWarnedAboutNodeVersion) { | ||
| hasWarnedAboutNodeVersion = true; | ||
| consoleSandbox(() => { | ||
| // eslint-disable-next-line no-console | ||
| console.warn( | ||
| `[Sentry] You are using Node.js v${process.versions.node} in ESM mode ("import syntax"). The Sentry Node.js SDK is not compatible with ESM in Node.js versions before 18.19.0 or before 20.6.0. Please either build your application with CommonJS ("require() syntax"), or upgrade your Node.js version.`, | ||
| `[Sentry] You are using Node.js v${process.versions.node} in ESM mode ("import syntax"). The Sentry Node.js SDK is not compatible with ESM in Node.js versions before 18.19.0 or before 20.6.0. Please either build your application with CommonJS ("require() syntax"), or upgrade your Node.js version.` | ||
| ); | ||
| }); | ||
| } | ||
| return false; | ||
@@ -42,0 +30,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"detection.js","sources":["../../../src/utils/detection.ts"],"sourcesContent":["import { consoleSandbox } from '@sentry/core';\nimport { NODE_MAJOR, NODE_MINOR } from '../nodeVersion';\n\n/** Detect CommonJS. */\nexport function isCjs(): boolean {\n try {\n // oxlint-disable-next-line typescript/prefer-optional-chain\n return typeof module !== 'undefined' && typeof module.exports !== 'undefined';\n } catch {\n return false;\n }\n}\n\nlet hasWarnedAboutNodeVersion: boolean | undefined;\n\n/**\n * Check if the current Node.js version supports module.register\n */\nexport function supportsEsmLoaderHooks(): boolean {\n if (isCjs()) {\n return false;\n }\n\n if (NODE_MAJOR >= 21 || (NODE_MAJOR === 20 && NODE_MINOR >= 6) || (NODE_MAJOR === 18 && NODE_MINOR >= 19)) {\n return true;\n }\n\n if (!hasWarnedAboutNodeVersion) {\n hasWarnedAboutNodeVersion = true;\n\n consoleSandbox(() => {\n // eslint-disable-next-line no-console\n console.warn(\n `[Sentry] You are using Node.js v${process.versions.node} in ESM mode (\"import syntax\"). The Sentry Node.js SDK is not compatible with ESM in Node.js versions before 18.19.0 or before 20.6.0. Please either build your application with CommonJS (\"require() syntax\"), or upgrade your Node.js version.`,\n );\n });\n }\n\n return false;\n}\n"],"names":[],"mappings":";;;AAGA;AACO,SAAS,KAAK,GAAY;AACjC,EAAE,IAAI;AACN;AACA,IAAI,OAAO,OAAO,MAAA,KAAW,WAAA,IAAe,OAAO,MAAM,CAAC,OAAA,KAAY,WAAW;AACjF,EAAE,EAAE,MAAM;AACV,IAAI,OAAO,KAAK;AAChB,EAAE;AACF;;AAEA,IAAI,yBAAyB;;AAE7B;AACA;AACA;AACO,SAAS,sBAAsB,GAAY;AAClD,EAAE,IAAI,KAAK,EAAE,EAAE;AACf,IAAI,OAAO,KAAK;AAChB,EAAE;;AAEF,EAAE,IAAI,UAAA,IAAc,EAAA,KAAO,UAAA,KAAe,EAAA,IAAM,UAAA,IAAc,CAAC,CAAA,KAAM,UAAA,KAAe,EAAA,IAAM,UAAA,IAAc,EAAE,CAAC,EAAE;AAC7G,IAAI,OAAO,IAAI;AACf,EAAE;;AAEF,EAAE,IAAI,CAAC,yBAAyB,EAAE;AAClC,IAAI,yBAAA,GAA4B,IAAI;;AAEpC,IAAI,cAAc,CAAC,MAAM;AACzB;AACA,MAAM,OAAO,CAAC,IAAI;AAClB,QAAQ,CAAC,gCAAgC,EAAE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,gPAAgP,CAAC;AAClT,OAAO;AACP,IAAI,CAAC,CAAC;AACN,EAAE;;AAEF,EAAE,OAAO,KAAK;AACd;;;;"} | ||
| {"version":3,"file":"detection.js","sources":["../../../src/utils/detection.ts"],"sourcesContent":["import { consoleSandbox } from '@sentry/core';\nimport { NODE_MAJOR, NODE_MINOR } from '../nodeVersion';\n\n/** Detect CommonJS. */\nexport function isCjs(): boolean {\n try {\n // oxlint-disable-next-line typescript/prefer-optional-chain\n return typeof module !== 'undefined' && typeof module.exports !== 'undefined';\n } catch {\n return false;\n }\n}\n\nlet hasWarnedAboutNodeVersion: boolean | undefined;\n\n/**\n * Check if the current Node.js version supports module.register\n */\nexport function supportsEsmLoaderHooks(): boolean {\n if (isCjs()) {\n return false;\n }\n\n if (NODE_MAJOR >= 21 || (NODE_MAJOR === 20 && NODE_MINOR >= 6) || (NODE_MAJOR === 18 && NODE_MINOR >= 19)) {\n return true;\n }\n\n if (!hasWarnedAboutNodeVersion) {\n hasWarnedAboutNodeVersion = true;\n\n consoleSandbox(() => {\n // eslint-disable-next-line no-console\n console.warn(\n `[Sentry] You are using Node.js v${process.versions.node} in ESM mode (\"import syntax\"). The Sentry Node.js SDK is not compatible with ESM in Node.js versions before 18.19.0 or before 20.6.0. Please either build your application with CommonJS (\"require() syntax\"), or upgrade your Node.js version.`,\n );\n });\n }\n\n return false;\n}\n"],"names":[],"mappings":";;;AAIO,SAAS,KAAA,GAAiB;AAC/B,EAAA,IAAI;AAEF,IAAA,OAAO,OAAO,MAAA,KAAW,WAAA,IAAe,OAAO,OAAO,OAAA,KAAY,WAAA;AAAA,EACpE,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,KAAA;AAAA,EACT;AACF;AAEA,IAAI,yBAAA;AAKG,SAAS,sBAAA,GAAkC;AAChD,EAAA,IAAI,OAAM,EAAG;AACX,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,IAAI,UAAA,IAAc,MAAO,UAAA,KAAe,EAAA,IAAM,cAAc,CAAA,IAAO,UAAA,KAAe,EAAA,IAAM,UAAA,IAAc,EAAA,EAAK;AACzG,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,IAAI,CAAC,yBAAA,EAA2B;AAC9B,IAAA,yBAAA,GAA4B,IAAA;AAE5B,IAAA,cAAA,CAAe,MAAM;AAEnB,MAAA,OAAA,CAAQ,IAAA;AAAA,QACN,CAAA,gCAAA,EAAmC,OAAA,CAAQ,QAAA,CAAS,IAAI,CAAA,gPAAA;AAAA,OAC1D;AAAA,IACF,CAAC,CAAA;AAAA,EACH;AAEA,EAAA,OAAO,KAAA;AACT;;;;"} |
@@ -6,34 +6,17 @@ import { isWrapped } from '@opentelemetry/instrumentation'; | ||
| /** | ||
| * Checks and warns if a framework isn't wrapped by opentelemetry. | ||
| */ | ||
| function ensureIsWrapped( | ||
| maybeWrappedFunction, | ||
| name, | ||
| ) { | ||
| function ensureIsWrapped(maybeWrappedFunction, name) { | ||
| const clientOptions = getClient()?.getOptions(); | ||
| if ( | ||
| !clientOptions?.disableInstrumentationWarnings && | ||
| !( | ||
| isWrapped(maybeWrappedFunction) || | ||
| typeof getOriginalFunction(maybeWrappedFunction ) === 'function' | ||
| ) && | ||
| isEnabled() && | ||
| hasSpansEnabled(clientOptions) | ||
| ) { | ||
| if (!clientOptions?.disableInstrumentationWarnings && !(isWrapped(maybeWrappedFunction) || typeof getOriginalFunction(maybeWrappedFunction) === "function") && isEnabled() && hasSpansEnabled(clientOptions)) { | ||
| consoleSandbox(() => { | ||
| if (isCjs()) { | ||
| // eslint-disable-next-line no-console | ||
| console.warn( | ||
| `[Sentry] ${name} is not instrumented. This is likely because you required/imported ${name} before calling \`Sentry.init()\`.`, | ||
| `[Sentry] ${name} is not instrumented. This is likely because you required/imported ${name} before calling \`Sentry.init()\`.` | ||
| ); | ||
| } else { | ||
| // eslint-disable-next-line no-console | ||
| console.warn( | ||
| `[Sentry] ${name} is not instrumented. Please make sure to initialize Sentry in a separate file that you \`--import\` when running node, see: https://docs.sentry.io/platforms/javascript/guides/${name}/install/esm/.`, | ||
| `[Sentry] ${name} is not instrumented. Please make sure to initialize Sentry in a separate file that you \`--import\` when running node, see: https://docs.sentry.io/platforms/javascript/guides/${name}/install/esm/.` | ||
| ); | ||
| } | ||
| }); | ||
| getGlobalScope().setContext('missing_instrumentation', createMissingInstrumentationContext(name)); | ||
| getGlobalScope().setContext("missing_instrumentation", createMissingInstrumentationContext(name)); | ||
| } | ||
@@ -40,0 +23,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"ensureIsWrapped.js","sources":["../../../src/utils/ensureIsWrapped.ts"],"sourcesContent":["import { isWrapped } from '@opentelemetry/instrumentation';\nimport {\n consoleSandbox,\n getClient,\n getOriginalFunction,\n getGlobalScope,\n hasSpansEnabled,\n isEnabled,\n type WrappedFunction,\n} from '@sentry/core';\nimport type { NodeClient } from '../sdk/client';\nimport { createMissingInstrumentationContext } from './createMissingInstrumentationContext';\nimport { isCjs } from './detection';\n\n/**\n * Checks and warns if a framework isn't wrapped by opentelemetry.\n */\nexport function ensureIsWrapped(\n maybeWrappedFunction: unknown,\n name: 'express' | 'connect' | 'fastify' | 'hapi' | 'koa' | 'hono',\n): void {\n const clientOptions = getClient<NodeClient>()?.getOptions();\n if (\n !clientOptions?.disableInstrumentationWarnings &&\n !(\n isWrapped(maybeWrappedFunction) ||\n typeof getOriginalFunction(maybeWrappedFunction as WrappedFunction) === 'function'\n ) &&\n isEnabled() &&\n hasSpansEnabled(clientOptions)\n ) {\n consoleSandbox(() => {\n if (isCjs()) {\n // eslint-disable-next-line no-console\n console.warn(\n `[Sentry] ${name} is not instrumented. This is likely because you required/imported ${name} before calling \\`Sentry.init()\\`.`,\n );\n } else {\n // eslint-disable-next-line no-console\n console.warn(\n `[Sentry] ${name} is not instrumented. Please make sure to initialize Sentry in a separate file that you \\`--import\\` when running node, see: https://docs.sentry.io/platforms/javascript/guides/${name}/install/esm/.`,\n );\n }\n });\n\n getGlobalScope().setContext('missing_instrumentation', createMissingInstrumentationContext(name));\n }\n}\n"],"names":[],"mappings":";;;;;AAcA;AACA;AACA;AACO,SAAS,eAAe;AAC/B,EAAE,oBAAoB;AACtB,EAAE,IAAI;AACN,EAAQ;AACR,EAAE,MAAM,gBAAgB,SAAS,EAAc,EAAE,UAAU,EAAE;AAC7D,EAAE;AACF,IAAI,CAAC,aAAa,EAAE,8BAAA;AACpB,IAAI;AACJ,MAAM,SAAS,CAAC,oBAAoB,CAAA;AACpC,MAAM,OAAO,mBAAmB,CAAC,oBAAA,OAA6C;AAC9E,KAAI;AACJ,IAAI,SAAS,EAAC;AACd,IAAI,eAAe,CAAC,aAAa;AACjC,IAAI;AACJ,IAAI,cAAc,CAAC,MAAM;AACzB,MAAM,IAAI,KAAK,EAAE,EAAE;AACnB;AACA,QAAQ,OAAO,CAAC,IAAI;AACpB,UAAU,CAAC,SAAS,EAAE,IAAI,CAAC,mEAAmE,EAAE,IAAI,CAAC,kCAAkC,CAAC;AACxI,SAAS;AACT,MAAM,OAAO;AACb;AACA,QAAQ,OAAO,CAAC,IAAI;AACpB,UAAU,CAAC,SAAS,EAAE,IAAI,CAAC,gLAAgL,EAAE,IAAI,CAAC,cAAc,CAAC;AACjO,SAAS;AACT,MAAM;AACN,IAAI,CAAC,CAAC;;AAEN,IAAI,cAAc,EAAE,CAAC,UAAU,CAAC,yBAAyB,EAAE,mCAAmC,CAAC,IAAI,CAAC,CAAC;AACrG,EAAE;AACF;;;;"} | ||
| {"version":3,"file":"ensureIsWrapped.js","sources":["../../../src/utils/ensureIsWrapped.ts"],"sourcesContent":["import { isWrapped } from '@opentelemetry/instrumentation';\nimport {\n consoleSandbox,\n getClient,\n getOriginalFunction,\n getGlobalScope,\n hasSpansEnabled,\n isEnabled,\n type WrappedFunction,\n} from '@sentry/core';\nimport type { NodeClient } from '../sdk/client';\nimport { createMissingInstrumentationContext } from './createMissingInstrumentationContext';\nimport { isCjs } from './detection';\n\n/**\n * Checks and warns if a framework isn't wrapped by opentelemetry.\n */\nexport function ensureIsWrapped(\n maybeWrappedFunction: unknown,\n name: 'express' | 'connect' | 'fastify' | 'hapi' | 'koa' | 'hono',\n): void {\n const clientOptions = getClient<NodeClient>()?.getOptions();\n if (\n !clientOptions?.disableInstrumentationWarnings &&\n !(\n isWrapped(maybeWrappedFunction) ||\n typeof getOriginalFunction(maybeWrappedFunction as WrappedFunction) === 'function'\n ) &&\n isEnabled() &&\n hasSpansEnabled(clientOptions)\n ) {\n consoleSandbox(() => {\n if (isCjs()) {\n // eslint-disable-next-line no-console\n console.warn(\n `[Sentry] ${name} is not instrumented. This is likely because you required/imported ${name} before calling \\`Sentry.init()\\`.`,\n );\n } else {\n // eslint-disable-next-line no-console\n console.warn(\n `[Sentry] ${name} is not instrumented. Please make sure to initialize Sentry in a separate file that you \\`--import\\` when running node, see: https://docs.sentry.io/platforms/javascript/guides/${name}/install/esm/.`,\n );\n }\n });\n\n getGlobalScope().setContext('missing_instrumentation', createMissingInstrumentationContext(name));\n }\n}\n"],"names":[],"mappings":";;;;;AAiBO,SAAS,eAAA,CACd,sBACA,IAAA,EACM;AACN,EAAA,MAAM,aAAA,GAAgB,SAAA,EAAsB,EAAG,UAAA,EAAW;AAC1D,EAAA,IACE,CAAC,aAAA,EAAe,8BAAA,IAChB,EACE,SAAA,CAAU,oBAAoB,CAAA,IAC9B,OAAO,mBAAA,CAAoB,oBAAuC,MAAM,UAAA,CAAA,IAE1E,SAAA,EAAU,IACV,eAAA,CAAgB,aAAa,CAAA,EAC7B;AACA,IAAA,cAAA,CAAe,MAAM;AACnB,MAAA,IAAI,OAAM,EAAG;AAEX,QAAA,OAAA,CAAQ,IAAA;AAAA,UACN,CAAA,SAAA,EAAY,IAAI,CAAA,mEAAA,EAAsE,IAAI,CAAA,kCAAA;AAAA,SAC5F;AAAA,MACF,CAAA,MAAO;AAEL,QAAA,OAAA,CAAQ,IAAA;AAAA,UACN,CAAA,SAAA,EAAY,IAAI,CAAA,gLAAA,EAAmL,IAAI,CAAA,cAAA;AAAA,SACzM;AAAA,MACF;AAAA,IACF,CAAC,CAAA;AAED,IAAA,cAAA,EAAe,CAAE,UAAA,CAAW,yBAAA,EAA2B,mCAAA,CAAoC,IAAI,CAAC,CAAA;AAAA,EAClG;AACF;;;;"} |
| import { consoleSandbox, getClient, debug } from '@sentry/core'; | ||
| import { DEBUG_BUILD } from '../debug-build.js'; | ||
| const DEFAULT_SHUTDOWN_TIMEOUT = 2000; | ||
| /** | ||
| * @hidden | ||
| */ | ||
| const DEFAULT_SHUTDOWN_TIMEOUT = 2e3; | ||
| function logAndExitProcess(error) { | ||
| consoleSandbox(() => { | ||
| // eslint-disable-next-line no-console | ||
| console.error(error); | ||
| }); | ||
| const client = getClient(); | ||
| if (client === undefined) { | ||
| DEBUG_BUILD && debug.warn('No NodeClient was defined, we are exiting the process now.'); | ||
| if (client === void 0) { | ||
| DEBUG_BUILD && debug.warn("No NodeClient was defined, we are exiting the process now."); | ||
| global.process.exit(1); | ||
| return; | ||
| } | ||
| const options = client.getOptions(); | ||
| const timeout = | ||
| options?.shutdownTimeout && options.shutdownTimeout > 0 ? options.shutdownTimeout : DEFAULT_SHUTDOWN_TIMEOUT; | ||
| const timeout = options?.shutdownTimeout && options.shutdownTimeout > 0 ? options.shutdownTimeout : DEFAULT_SHUTDOWN_TIMEOUT; | ||
| client.close(timeout).then( | ||
| (result) => { | ||
| if (!result) { | ||
| DEBUG_BUILD && debug.warn('We reached the timeout for emptying the request buffer, still exiting now!'); | ||
| DEBUG_BUILD && debug.warn("We reached the timeout for emptying the request buffer, still exiting now!"); | ||
| } | ||
| global.process.exit(1); | ||
| }, | ||
| error => { | ||
| DEBUG_BUILD && debug.error(error); | ||
| }, | ||
| (error2) => { | ||
| DEBUG_BUILD && debug.error(error2); | ||
| } | ||
| ); | ||
@@ -37,0 +28,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"errorhandling.js","sources":["../../../src/utils/errorhandling.ts"],"sourcesContent":["import { consoleSandbox, debug, getClient } from '@sentry/core';\nimport { DEBUG_BUILD } from '../debug-build';\nimport type { NodeClient } from '../sdk/client';\n\nconst DEFAULT_SHUTDOWN_TIMEOUT = 2000;\n\n/**\n * @hidden\n */\nexport function logAndExitProcess(error: unknown): void {\n consoleSandbox(() => {\n // eslint-disable-next-line no-console\n console.error(error);\n });\n\n const client = getClient<NodeClient>();\n\n if (client === undefined) {\n DEBUG_BUILD && debug.warn('No NodeClient was defined, we are exiting the process now.');\n global.process.exit(1);\n return;\n }\n\n const options = client.getOptions();\n const timeout =\n options?.shutdownTimeout && options.shutdownTimeout > 0 ? options.shutdownTimeout : DEFAULT_SHUTDOWN_TIMEOUT;\n client.close(timeout).then(\n (result: boolean) => {\n if (!result) {\n DEBUG_BUILD && debug.warn('We reached the timeout for emptying the request buffer, still exiting now!');\n }\n global.process.exit(1);\n },\n error => {\n DEBUG_BUILD && debug.error(error);\n },\n );\n}\n"],"names":[],"mappings":";;;AAIA,MAAM,wBAAA,GAA2B,IAAI;;AAErC;AACA;AACA;AACO,SAAS,iBAAiB,CAAC,KAAK,EAAiB;AACxD,EAAE,cAAc,CAAC,MAAM;AACvB;AACA,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;AACxB,EAAE,CAAC,CAAC;;AAEJ,EAAE,MAAM,MAAA,GAAS,SAAS,EAAc;;AAExC,EAAE,IAAI,MAAA,KAAW,SAAS,EAAE;AAC5B,IAAI,eAAe,KAAK,CAAC,IAAI,CAAC,4DAA4D,CAAC;AAC3F,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;AAC1B,IAAI;AACJ,EAAE;;AAEF,EAAE,MAAM,OAAA,GAAU,MAAM,CAAC,UAAU,EAAE;AACrC,EAAE,MAAM,OAAA;AACR,IAAI,OAAO,EAAE,eAAA,IAAmB,OAAO,CAAC,eAAA,GAAkB,CAAA,GAAI,OAAO,CAAC,eAAA,GAAkB,wBAAwB;AAChH,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI;AAC5B,IAAI,CAAC,MAAM,KAAc;AACzB,MAAM,IAAI,CAAC,MAAM,EAAE;AACnB,QAAQ,eAAe,KAAK,CAAC,IAAI,CAAC,4EAA4E,CAAC;AAC/G,MAAM;AACN,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;AAC5B,IAAI,CAAC;AACL,IAAI,SAAS;AACb,MAAM,eAAe,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC;AACvC,IAAI,CAAC;AACL,GAAG;AACH;;;;"} | ||
| {"version":3,"file":"errorhandling.js","sources":["../../../src/utils/errorhandling.ts"],"sourcesContent":["import { consoleSandbox, debug, getClient } from '@sentry/core';\nimport { DEBUG_BUILD } from '../debug-build';\nimport type { NodeClient } from '../sdk/client';\n\nconst DEFAULT_SHUTDOWN_TIMEOUT = 2000;\n\n/**\n * @hidden\n */\nexport function logAndExitProcess(error: unknown): void {\n consoleSandbox(() => {\n // eslint-disable-next-line no-console\n console.error(error);\n });\n\n const client = getClient<NodeClient>();\n\n if (client === undefined) {\n DEBUG_BUILD && debug.warn('No NodeClient was defined, we are exiting the process now.');\n global.process.exit(1);\n return;\n }\n\n const options = client.getOptions();\n const timeout =\n options?.shutdownTimeout && options.shutdownTimeout > 0 ? options.shutdownTimeout : DEFAULT_SHUTDOWN_TIMEOUT;\n client.close(timeout).then(\n (result: boolean) => {\n if (!result) {\n DEBUG_BUILD && debug.warn('We reached the timeout for emptying the request buffer, still exiting now!');\n }\n global.process.exit(1);\n },\n error => {\n DEBUG_BUILD && debug.error(error);\n },\n );\n}\n"],"names":["error"],"mappings":";;;AAIA,MAAM,wBAAA,GAA2B,GAAA;AAK1B,SAAS,kBAAkB,KAAA,EAAsB;AACtD,EAAA,cAAA,CAAe,MAAM;AAEnB,IAAA,OAAA,CAAQ,MAAM,KAAK,CAAA;AAAA,EACrB,CAAC,CAAA;AAED,EAAA,MAAM,SAAS,SAAA,EAAsB;AAErC,EAAA,IAAI,WAAW,MAAA,EAAW;AACxB,IAAA,WAAA,IAAe,KAAA,CAAM,KAAK,4DAA4D,CAAA;AACtF,IAAA,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAC,CAAA;AACrB,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,OAAA,GAAU,OAAO,UAAA,EAAW;AAClC,EAAA,MAAM,UACJ,OAAA,EAAS,eAAA,IAAmB,QAAQ,eAAA,GAAkB,CAAA,GAAI,QAAQ,eAAA,GAAkB,wBAAA;AACtF,EAAA,MAAA,CAAO,KAAA,CAAM,OAAO,CAAA,CAAE,IAAA;AAAA,IACpB,CAAC,MAAA,KAAoB;AACnB,MAAA,IAAI,CAAC,MAAA,EAAQ;AACX,QAAA,WAAA,IAAe,KAAA,CAAM,KAAK,4EAA4E,CAAA;AAAA,MACxG;AACA,MAAA,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,IACvB,CAAA;AAAA,IACA,CAAAA,MAAAA,KAAS;AACP,MAAA,WAAA,IAAe,KAAA,CAAM,MAAMA,MAAK,CAAA;AAAA,IAClC;AAAA,GACF;AACF;;;;"} |
| import { posix, sep } from 'node:path'; | ||
| import { dirname } from '@sentry/core'; | ||
| /** normalizes Windows paths */ | ||
| function normalizeWindowsPath(path) { | ||
| return path | ||
| .replace(/^[A-Z]:/, '') // remove Windows-style prefix | ||
| .replace(/\\/g, '/'); // replace all `\` instances with `/` | ||
| return path.replace(/^[A-Z]:/, "").replace(/\\/g, "/"); | ||
| } | ||
| /** Creates a function that gets the module name from a filename */ | ||
| function createGetModuleFromFilename( | ||
| basePath = process.argv[1] ? dirname(process.argv[1]) : process.cwd(), | ||
| isWindows = sep === '\\', | ||
| ) { | ||
| function createGetModuleFromFilename(basePath = process.argv[1] ? dirname(process.argv[1]) : process.cwd(), isWindows = sep === "\\") { | ||
| const normalizedBase = isWindows ? normalizeWindowsPath(basePath) : basePath; | ||
| return (filename) => { | ||
@@ -22,33 +13,19 @@ if (!filename) { | ||
| } | ||
| const normalizedFilename = isWindows ? normalizeWindowsPath(filename) : filename; | ||
| // eslint-disable-next-line prefer-const | ||
| let { dir, base: file, ext } = posix.parse(normalizedFilename); | ||
| if (ext === '.js' || ext === '.mjs' || ext === '.cjs') { | ||
| if (ext === ".js" || ext === ".mjs" || ext === ".cjs") { | ||
| file = file.slice(0, ext.length * -1); | ||
| } | ||
| // The file name might be URI-encoded which we want to decode to | ||
| // the original file name. | ||
| const decodedFile = decodeURIComponent(file); | ||
| if (!dir) { | ||
| // No dirname whatsoever | ||
| dir = '.'; | ||
| dir = "."; | ||
| } | ||
| const n = dir.lastIndexOf('/node_modules'); | ||
| const n = dir.lastIndexOf("/node_modules"); | ||
| if (n > -1) { | ||
| return `${dir.slice(n + 14).replace(/\//g, '.')}:${decodedFile}`; | ||
| return `${dir.slice(n + 14).replace(/\//g, ".")}:${decodedFile}`; | ||
| } | ||
| // Let's see if it's a part of the main module | ||
| // To be a part of main module, it has to share the same base | ||
| if (dir.startsWith(normalizedBase)) { | ||
| const moduleName = dir.slice(normalizedBase.length + 1).replace(/\//g, '.'); | ||
| const moduleName = dir.slice(normalizedBase.length + 1).replace(/\//g, "."); | ||
| return moduleName ? `${moduleName}:${decodedFile}` : decodedFile; | ||
| } | ||
| return decodedFile; | ||
@@ -55,0 +32,0 @@ }; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"module.js","sources":["../../../src/utils/module.ts"],"sourcesContent":["import { posix, sep } from 'node:path';\nimport { dirname } from '@sentry/core';\n\n/** normalizes Windows paths */\nfunction normalizeWindowsPath(path: string): string {\n return path\n .replace(/^[A-Z]:/, '') // remove Windows-style prefix\n .replace(/\\\\/g, '/'); // replace all `\\` instances with `/`\n}\n\n/** Creates a function that gets the module name from a filename */\nexport function createGetModuleFromFilename(\n basePath: string = process.argv[1] ? dirname(process.argv[1]) : process.cwd(),\n isWindows: boolean = sep === '\\\\',\n): (filename: string | undefined) => string | undefined {\n const normalizedBase = isWindows ? normalizeWindowsPath(basePath) : basePath;\n\n return (filename: string | undefined) => {\n if (!filename) {\n return;\n }\n\n const normalizedFilename = isWindows ? normalizeWindowsPath(filename) : filename;\n\n // eslint-disable-next-line prefer-const\n let { dir, base: file, ext } = posix.parse(normalizedFilename);\n\n if (ext === '.js' || ext === '.mjs' || ext === '.cjs') {\n file = file.slice(0, ext.length * -1);\n }\n\n // The file name might be URI-encoded which we want to decode to\n // the original file name.\n const decodedFile = decodeURIComponent(file);\n\n if (!dir) {\n // No dirname whatsoever\n dir = '.';\n }\n\n const n = dir.lastIndexOf('/node_modules');\n if (n > -1) {\n return `${dir.slice(n + 14).replace(/\\//g, '.')}:${decodedFile}`;\n }\n\n // Let's see if it's a part of the main module\n // To be a part of main module, it has to share the same base\n if (dir.startsWith(normalizedBase)) {\n const moduleName = dir.slice(normalizedBase.length + 1).replace(/\\//g, '.');\n return moduleName ? `${moduleName}:${decodedFile}` : decodedFile;\n }\n\n return decodedFile;\n };\n}\n"],"names":[],"mappings":";;;AAGA;AACA,SAAS,oBAAoB,CAAC,IAAI,EAAkB;AACpD,EAAE,OAAO;AACT,KAAK,OAAO,CAAC,SAAS,EAAE,EAAE,CAAA;AAC1B,KAAK,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;AACxB;;AAEA;AACO,SAAS,2BAA2B;AAC3C,EAAE,QAAQ,GAAW,OAAO,CAAC,IAAI,CAAC,CAAC,CAAA,GAAI,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA,GAAI,OAAO,CAAC,GAAG,EAAE;AAC/E,EAAE,SAAS,GAAY,GAAA,KAAQ,IAAI;AACnC,EAAwD;AACxD,EAAE,MAAM,cAAA,GAAiB,SAAA,GAAY,oBAAoB,CAAC,QAAQ,CAAA,GAAI,QAAQ;;AAE9E,EAAE,OAAO,CAAC,QAAQ,KAAyB;AAC3C,IAAI,IAAI,CAAC,QAAQ,EAAE;AACnB,MAAM;AACN,IAAI;;AAEJ,IAAI,MAAM,kBAAA,GAAqB,SAAA,GAAY,oBAAoB,CAAC,QAAQ,CAAA,GAAI,QAAQ;;AAEpF;AACA,IAAI,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,GAAA,EAAI,GAAI,KAAK,CAAC,KAAK,CAAC,kBAAkB,CAAC;;AAElE,IAAI,IAAI,GAAA,KAAQ,KAAA,IAAS,GAAA,KAAQ,MAAA,IAAU,GAAA,KAAQ,MAAM,EAAE;AAC3D,MAAM,IAAA,GAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,MAAA,GAAS,EAAE,CAAC;AAC3C,IAAI;;AAEJ;AACA;AACA,IAAI,MAAM,WAAA,GAAc,kBAAkB,CAAC,IAAI,CAAC;;AAEhD,IAAI,IAAI,CAAC,GAAG,EAAE;AACd;AACA,MAAM,GAAA,GAAM,GAAG;AACf,IAAI;;AAEJ,IAAI,MAAM,IAAI,GAAG,CAAC,WAAW,CAAC,eAAe,CAAC;AAC9C,IAAI,IAAI,CAAA,GAAI,EAAE,EAAE;AAChB,MAAM,OAAO,CAAC,EAAA,GAAA,CAAA,KAAA,CAAA,CAAA,GAAA,EAAA,CAAA,CAAA,OAAA,CAAA,KAAA,EAAA,GAAA,CAAA,CAAA,CAAA,EAAA,WAAA,CAAA,CAAA;AACA,IAAA;;AAEA;AACA;AACA,IAAA,IAAA,GAAA,CAAA,UAAA,CAAA,cAAA,CAAA,EAAA;AACA,MAAA,MAAA,UAAA,GAAA,GAAA,CAAA,KAAA,CAAA,cAAA,CAAA,MAAA,GAAA,CAAA,CAAA,CAAA,OAAA,CAAA,KAAA,EAAA,GAAA,CAAA;AACA,MAAA,OAAA,UAAA,GAAA,CAAA,EAAA,UAAA,CAAA,CAAA,EAAA,WAAA,CAAA,CAAA,GAAA,WAAA;AACA,IAAA;;AAEA,IAAA,OAAA,WAAA;AACA,EAAA,CAAA;AACA;;;;"} | ||
| {"version":3,"file":"module.js","sources":["../../../src/utils/module.ts"],"sourcesContent":["import { posix, sep } from 'node:path';\nimport { dirname } from '@sentry/core';\n\n/** normalizes Windows paths */\nfunction normalizeWindowsPath(path: string): string {\n return path\n .replace(/^[A-Z]:/, '') // remove Windows-style prefix\n .replace(/\\\\/g, '/'); // replace all `\\` instances with `/`\n}\n\n/** Creates a function that gets the module name from a filename */\nexport function createGetModuleFromFilename(\n basePath: string = process.argv[1] ? dirname(process.argv[1]) : process.cwd(),\n isWindows: boolean = sep === '\\\\',\n): (filename: string | undefined) => string | undefined {\n const normalizedBase = isWindows ? normalizeWindowsPath(basePath) : basePath;\n\n return (filename: string | undefined) => {\n if (!filename) {\n return;\n }\n\n const normalizedFilename = isWindows ? normalizeWindowsPath(filename) : filename;\n\n // eslint-disable-next-line prefer-const\n let { dir, base: file, ext } = posix.parse(normalizedFilename);\n\n if (ext === '.js' || ext === '.mjs' || ext === '.cjs') {\n file = file.slice(0, ext.length * -1);\n }\n\n // The file name might be URI-encoded which we want to decode to\n // the original file name.\n const decodedFile = decodeURIComponent(file);\n\n if (!dir) {\n // No dirname whatsoever\n dir = '.';\n }\n\n const n = dir.lastIndexOf('/node_modules');\n if (n > -1) {\n return `${dir.slice(n + 14).replace(/\\//g, '.')}:${decodedFile}`;\n }\n\n // Let's see if it's a part of the main module\n // To be a part of main module, it has to share the same base\n if (dir.startsWith(normalizedBase)) {\n const moduleName = dir.slice(normalizedBase.length + 1).replace(/\\//g, '.');\n return moduleName ? `${moduleName}:${decodedFile}` : decodedFile;\n }\n\n return decodedFile;\n };\n}\n"],"names":[],"mappings":";;;AAIA,SAAS,qBAAqB,IAAA,EAAsB;AAClD,EAAA,OAAO,KACJ,OAAA,CAAQ,SAAA,EAAW,EAAE,CAAA,CACrB,OAAA,CAAQ,OAAO,GAAG,CAAA;AACvB;AAGO,SAAS,4BACd,QAAA,GAAmB,OAAA,CAAQ,IAAA,CAAK,CAAC,IAAI,OAAA,CAAQ,OAAA,CAAQ,IAAA,CAAK,CAAC,CAAC,CAAA,GAAI,OAAA,CAAQ,KAAI,EAC5E,SAAA,GAAqB,QAAQ,IAAA,EACyB;AACtD,EAAA,MAAM,cAAA,GAAiB,SAAA,GAAY,oBAAA,CAAqB,QAAQ,CAAA,GAAI,QAAA;AAEpE,EAAA,OAAO,CAAC,QAAA,KAAiC;AACvC,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,kBAAA,GAAqB,SAAA,GAAY,oBAAA,CAAqB,QAAQ,CAAA,GAAI,QAAA;AAGxE,IAAA,IAAI,EAAE,KAAK,IAAA,EAAM,IAAA,EAAM,KAAI,GAAI,KAAA,CAAM,MAAM,kBAAkB,CAAA;AAE7D,IAAA,IAAI,GAAA,KAAQ,KAAA,IAAS,GAAA,KAAQ,MAAA,IAAU,QAAQ,MAAA,EAAQ;AACrD,MAAA,IAAA,GAAO,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,GAAA,CAAI,SAAS,EAAE,CAAA;AAAA,IACtC;AAIA,IAAA,MAAM,WAAA,GAAc,mBAAmB,IAAI,CAAA;AAE3C,IAAA,IAAI,CAAC,GAAA,EAAK;AAER,MAAA,GAAA,GAAM,GAAA;AAAA,IACR;AAEA,IAAA,MAAM,CAAA,GAAI,GAAA,CAAI,WAAA,CAAY,eAAe,CAAA;AACzC,IAAA,IAAI,IAAI,EAAA,EAAI;AACV,MAAA,OAAO,CAAA,EAAG,GAAA,CAAI,KAAA,CAAM,CAAA,GAAI,EAAE,CAAA,CAAE,OAAA,CAAQ,KAAA,EAAO,GAAG,CAAC,CAAA,CAAA,EAAI,WAAW,CAAA,CAAA;AAAA,IAChE;AAIA,IAAA,IAAI,GAAA,CAAI,UAAA,CAAW,cAAc,CAAA,EAAG;AAClC,MAAA,MAAM,UAAA,GAAa,IAAI,KAAA,CAAM,cAAA,CAAe,SAAS,CAAC,CAAA,CAAE,OAAA,CAAQ,KAAA,EAAO,GAAG,CAAA;AAC1E,MAAA,OAAO,UAAA,GAAa,CAAA,EAAG,UAAU,CAAA,CAAA,EAAI,WAAW,CAAA,CAAA,GAAK,WAAA;AAAA,IACvD;AAEA,IAAA,OAAO,WAAA;AAAA,EACT,CAAA;AACF;;;;"} |
| import { getClient, shouldPropagateTraceForUrl, getTraceData, mergeBaggageHeaders, getBreadcrumbLogLevelFromHttpStatusCode, addBreadcrumb, debug, parseUrl, getSanitizedUrlString } from '@sentry/core'; | ||
| const SENTRY_TRACE_HEADER = 'sentry-trace'; | ||
| const SENTRY_BAGGAGE_HEADER = 'baggage'; | ||
| const W3C_TRACEPARENT_HEADER = 'traceparent'; | ||
| /** | ||
| * Add trace propagation headers to an outgoing fetch/undici request. | ||
| * | ||
| * Checks if the request URL matches trace propagation targets, | ||
| * then injects sentry-trace, traceparent, and baggage headers. | ||
| */ | ||
| // eslint-disable-next-line complexity | ||
| function addTracePropagationHeadersToFetchRequest( | ||
| request, | ||
| propagationDecisionMap, | ||
| ) { | ||
| const SENTRY_TRACE_HEADER = "sentry-trace"; | ||
| const SENTRY_BAGGAGE_HEADER = "baggage"; | ||
| const W3C_TRACEPARENT_HEADER = "traceparent"; | ||
| function addTracePropagationHeadersToFetchRequest(request, propagationDecisionMap) { | ||
| const url = getAbsoluteUrl(request.origin, request.path); | ||
| // Manually add the trace headers, if it applies | ||
| // Note: We do not use `propagation.inject()` here, because our propagator relies on an active span | ||
| // Which we do not have in this case | ||
| // The propagator _may_ overwrite this, but this should be fine as it is the same data | ||
| const { tracePropagationTargets, propagateTraceparent } = getClient()?.getOptions() || {}; | ||
| const addedHeaders = shouldPropagateTraceForUrl(url, tracePropagationTargets, propagationDecisionMap) | ||
| ? getTraceData({ propagateTraceparent }) | ||
| : undefined; | ||
| const addedHeaders = shouldPropagateTraceForUrl(url, tracePropagationTargets, propagationDecisionMap) ? getTraceData({ propagateTraceparent }) : void 0; | ||
| if (!addedHeaders) { | ||
| return; | ||
| } | ||
| const { 'sentry-trace': sentryTrace, baggage, traceparent } = addedHeaders; | ||
| // Undici can expose headers either as a raw string (v5-style) or as a flat array of pairs (v6-style). | ||
| // In the array form, even indices are header names and odd indices are values; in undici v6 a value | ||
| // may be `string | string[]` when a header has multiple values. The helpers below (_deduplicateArrayHeader, | ||
| // push, etc.) expect each value slot to be a single string, so we normalize array headers first. | ||
| const requestHeaders = Array.isArray(request.headers) | ||
| ? normalizeUndiciHeaderPairs(request.headers) | ||
| : stringToArrayHeaders(request.headers); | ||
| // OTel's UndiciInstrumentation calls propagation.inject() which unconditionally | ||
| // appends headers to the request. When the user also sets headers via getTraceData(), | ||
| // this results in duplicate sentry-trace and baggage (and optionally traceparent) entries. | ||
| // We clean these up before applying our own logic. | ||
| const { "sentry-trace": sentryTrace, baggage, traceparent } = addedHeaders; | ||
| const requestHeaders = Array.isArray(request.headers) ? normalizeUndiciHeaderPairs(request.headers) : stringToArrayHeaders(request.headers); | ||
| _deduplicateArrayHeader(requestHeaders, SENTRY_TRACE_HEADER); | ||
@@ -51,10 +20,3 @@ _deduplicateArrayHeader(requestHeaders, SENTRY_BAGGAGE_HEADER); | ||
| } | ||
| // We do not want to overwrite existing headers here | ||
| // If the core UndiciInstrumentation is registered, it will already have set the headers | ||
| // We do not want to add any then | ||
| const hasExistingSentryTraceHeader = _findExistingHeaderIndex(requestHeaders, SENTRY_TRACE_HEADER) !== -1; | ||
| // We do not want to set any headers if we already have an existing sentry-trace header. | ||
| // sentry-trace is still the source of truth, otherwise we risk mixing up baggage and sentry-trace values. | ||
| if (!hasExistingSentryTraceHeader) { | ||
@@ -64,8 +26,5 @@ if (sentryTrace) { | ||
| } | ||
| if (traceparent && _findExistingHeaderIndex(requestHeaders, 'traceparent') === -1) { | ||
| requestHeaders.push('traceparent', traceparent); | ||
| if (traceparent && _findExistingHeaderIndex(requestHeaders, "traceparent") === -1) { | ||
| requestHeaders.push("traceparent", traceparent); | ||
| } | ||
| // For baggage, we make sure to merge this into a possibly existing header | ||
| const existingBaggageIndex = _findExistingHeaderIndex(requestHeaders, SENTRY_BAGGAGE_HEADER); | ||
@@ -75,3 +34,2 @@ if (baggage && existingBaggageIndex === -1) { | ||
| } else if (baggage) { | ||
| // headers in format [key_0, value_0, key_1, value_1, ...], hence the +1 here | ||
| const existingBaggageValue = requestHeaders[existingBaggageIndex + 1]; | ||
@@ -84,6 +42,3 @@ const merged = mergeBaggageHeaders(existingBaggageValue, baggage); | ||
| } | ||
| if (Array.isArray(request.headers)) { | ||
| // Replace contents in place so we keep the same array reference undici/fetch still holds. | ||
| // `requestHeaders` is already normalized (string pairs only); splice writes them back. | ||
| request.headers.splice(0, request.headers.length, ...requestHeaders); | ||
@@ -94,10 +49,2 @@ } else { | ||
| } | ||
| /** | ||
| * Convert undici’s header array into `[name, value, name, value, ...]` where every value is a string. | ||
| * | ||
| * Undici v6 uses this shape: `[k1, v1, k2, v2, ...]`. Types allow each `v` to be `string | string[]` when | ||
| * that header has multiple values. Sentry’s dedupe/merge helpers expect one string per value slot, so | ||
| * multi-value arrays are joined with `', '`. Missing value slots become `''`. | ||
| */ | ||
| function normalizeUndiciHeaderPairs(headers) { | ||
@@ -108,7 +55,5 @@ const out = []; | ||
| if (i % 2 === 0) { | ||
| // Header name (should always be a string; coerce defensively). | ||
| out.push(typeof entry === 'string' ? entry : String(entry)); | ||
| out.push(typeof entry === "string" ? entry : String(entry)); | ||
| } else { | ||
| // Header value: flatten `string[]` to a single string for downstream string-only helpers. | ||
| out.push(Array.isArray(entry) ? entry.join(', ') : (entry ?? '')); | ||
| out.push(Array.isArray(entry) ? entry.join(", ") : entry ?? ""); | ||
| } | ||
@@ -118,9 +63,8 @@ } | ||
| } | ||
| function stringToArrayHeaders(requestHeaders) { | ||
| const headersArray = requestHeaders.split('\r\n'); | ||
| const headersArray = requestHeaders.split("\r\n"); | ||
| const headers = []; | ||
| for (const header of headersArray) { | ||
| try { | ||
| const colonIndex = header.indexOf(':'); | ||
| const colonIndex = header.indexOf(":"); | ||
| if (colonIndex === -1) { | ||
@@ -140,6 +84,4 @@ continue; | ||
| } | ||
| function arrayToStringHeaders(headers) { | ||
| const headerPairs = []; | ||
| for (let i = 0; i < headers.length; i += 2) { | ||
@@ -149,3 +91,2 @@ const key = headers[i]; | ||
| if (!key || value == null) { | ||
| // skip falsy keys but only null/undefined values | ||
| continue; | ||
@@ -155,16 +96,7 @@ } | ||
| } | ||
| if (!headerPairs.length) { | ||
| return ''; | ||
| return ""; | ||
| } | ||
| return headerPairs.join('\r\n').concat('\r\n'); | ||
| return headerPairs.join("\r\n").concat("\r\n"); | ||
| } | ||
| /** | ||
| * For a given header name, if there are multiple entries in the [key, value, key, value, ...] array, | ||
| * keep the first entry and remove the rest. | ||
| * For baggage, values are merged to preserve all entries but to dedupe sentry- values, and always | ||
| * keep the first occurrence of them | ||
| */ | ||
| function _deduplicateArrayHeader(headers, headerName) { | ||
@@ -176,3 +108,2 @@ let firstIndex = -1; | ||
| } | ||
| if (firstIndex === -1) { | ||
@@ -182,8 +113,4 @@ firstIndex = i; | ||
| } | ||
| const firstHeaderValue = headers[firstIndex + 1]; | ||
| if (headerName === SENTRY_BAGGAGE_HEADER && firstHeaderValue) { | ||
| // mergeBaggageHeaders always takes sentry- values from the new baggage (2nd param) and merges | ||
| // it with the existing one (1st param). Here, we want to keep the first header's existing | ||
| // sentry- values in favor of the new ones. Hence we swap the parameters. | ||
| const merged = mergeBaggageHeaders(headers[i + 1], firstHeaderValue); | ||
@@ -198,37 +125,26 @@ if (merged) { | ||
| } | ||
| /** | ||
| * Find the index of an existing header in an array of headers. | ||
| * Only take even indices, because headers are in format [key_0, value_0, key_1, value_1, ...] | ||
| * otherwise we could match a header _value_ with @param name | ||
| */ | ||
| function _findExistingHeaderIndex(headers, name) { | ||
| return headers.findIndex((header, i) => i % 2 === 0 && header === name); | ||
| } | ||
| /** Add a breadcrumb for an outgoing fetch/undici request. */ | ||
| function addFetchRequestBreadcrumb(request, response) { | ||
| const data = getBreadcrumbData(request); | ||
| const statusCode = response.statusCode; | ||
| const level = getBreadcrumbLogLevelFromHttpStatusCode(statusCode); | ||
| addBreadcrumb( | ||
| { | ||
| category: 'http', | ||
| category: "http", | ||
| data: { | ||
| status_code: statusCode, | ||
| ...data, | ||
| ...data | ||
| }, | ||
| type: 'http', | ||
| level, | ||
| type: "http", | ||
| level | ||
| }, | ||
| { | ||
| event: 'response', | ||
| event: "response", | ||
| request, | ||
| response, | ||
| }, | ||
| response | ||
| } | ||
| ); | ||
| } | ||
| function getBreadcrumbData(request) { | ||
@@ -238,15 +154,12 @@ try { | ||
| const parsedUrl = parseUrl(url); | ||
| const data = { | ||
| url: getSanitizedUrlString(parsedUrl), | ||
| 'http.method': request.method || 'GET', | ||
| "http.method": request.method || "GET" | ||
| }; | ||
| if (parsedUrl.search) { | ||
| data['http.query'] = parsedUrl.search; | ||
| data["http.query"] = parsedUrl.search; | ||
| } | ||
| if (parsedUrl.hash) { | ||
| data['http.fragment'] = parsedUrl.hash; | ||
| data["http.fragment"] = parsedUrl.hash; | ||
| } | ||
| return data; | ||
@@ -257,5 +170,3 @@ } catch { | ||
| } | ||
| /** Get the absolute URL from an origin and path. */ | ||
| function getAbsoluteUrl(origin, path = '/') { | ||
| function getAbsoluteUrl(origin, path = "/") { | ||
| try { | ||
@@ -265,13 +176,9 @@ const url = new URL(path, origin); | ||
| } catch { | ||
| // fallback: Construct it on our own | ||
| const url = `${origin}`; | ||
| if (url.endsWith('/') && path.startsWith('/')) { | ||
| if (url.endsWith("/") && path.startsWith("/")) { | ||
| return `${url}${path.slice(1)}`; | ||
| } | ||
| if (!url.endsWith('/') && !path.startsWith('/')) { | ||
| if (!url.endsWith("/") && !path.startsWith("/")) { | ||
| return `${url}/${path}`; | ||
| } | ||
| return `${url}${path}`; | ||
@@ -278,0 +185,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"outgoingFetchRequest.js","sources":["../../../src/utils/outgoingFetchRequest.ts"],"sourcesContent":["import type { LRUMap, SanitizedRequestData } from '@sentry/core';\nimport {\n addBreadcrumb,\n getBreadcrumbLogLevelFromHttpStatusCode,\n getClient,\n getSanitizedUrlString,\n getTraceData,\n parseUrl,\n shouldPropagateTraceForUrl,\n mergeBaggageHeaders,\n} from '@sentry/core';\nimport type { UndiciRequest, UndiciResponse } from '../integrations/node-fetch/types';\nimport { debug } from '@sentry/core';\nconst SENTRY_TRACE_HEADER = 'sentry-trace';\nconst SENTRY_BAGGAGE_HEADER = 'baggage';\nconst W3C_TRACEPARENT_HEADER = 'traceparent';\n/**\n * Add trace propagation headers to an outgoing fetch/undici request.\n *\n * Checks if the request URL matches trace propagation targets,\n * then injects sentry-trace, traceparent, and baggage headers.\n */\n// eslint-disable-next-line complexity\nexport function addTracePropagationHeadersToFetchRequest(\n request: UndiciRequest,\n propagationDecisionMap: LRUMap<string, boolean>,\n): void {\n const url = getAbsoluteUrl(request.origin, request.path);\n\n // Manually add the trace headers, if it applies\n // Note: We do not use `propagation.inject()` here, because our propagator relies on an active span\n // Which we do not have in this case\n // The propagator _may_ overwrite this, but this should be fine as it is the same data\n const { tracePropagationTargets, propagateTraceparent } = getClient()?.getOptions() || {};\n const addedHeaders = shouldPropagateTraceForUrl(url, tracePropagationTargets, propagationDecisionMap)\n ? getTraceData({ propagateTraceparent })\n : undefined;\n\n if (!addedHeaders) {\n return;\n }\n\n const { 'sentry-trace': sentryTrace, baggage, traceparent } = addedHeaders;\n\n // Undici can expose headers either as a raw string (v5-style) or as a flat array of pairs (v6-style).\n // In the array form, even indices are header names and odd indices are values; in undici v6 a value\n // may be `string | string[]` when a header has multiple values. The helpers below (_deduplicateArrayHeader,\n // push, etc.) expect each value slot to be a single string, so we normalize array headers first.\n const requestHeaders: string[] = Array.isArray(request.headers)\n ? normalizeUndiciHeaderPairs(request.headers)\n : stringToArrayHeaders(request.headers);\n\n // OTel's UndiciInstrumentation calls propagation.inject() which unconditionally\n // appends headers to the request. When the user also sets headers via getTraceData(),\n // this results in duplicate sentry-trace and baggage (and optionally traceparent) entries.\n // We clean these up before applying our own logic.\n _deduplicateArrayHeader(requestHeaders, SENTRY_TRACE_HEADER);\n _deduplicateArrayHeader(requestHeaders, SENTRY_BAGGAGE_HEADER);\n if (propagateTraceparent) {\n _deduplicateArrayHeader(requestHeaders, W3C_TRACEPARENT_HEADER);\n }\n\n // We do not want to overwrite existing headers here\n // If the core UndiciInstrumentation is registered, it will already have set the headers\n // We do not want to add any then\n const hasExistingSentryTraceHeader = _findExistingHeaderIndex(requestHeaders, SENTRY_TRACE_HEADER) !== -1;\n\n // We do not want to set any headers if we already have an existing sentry-trace header.\n // sentry-trace is still the source of truth, otherwise we risk mixing up baggage and sentry-trace values.\n if (!hasExistingSentryTraceHeader) {\n if (sentryTrace) {\n requestHeaders.push(SENTRY_TRACE_HEADER, sentryTrace);\n }\n\n if (traceparent && _findExistingHeaderIndex(requestHeaders, 'traceparent') === -1) {\n requestHeaders.push('traceparent', traceparent);\n }\n\n // For baggage, we make sure to merge this into a possibly existing header\n const existingBaggageIndex = _findExistingHeaderIndex(requestHeaders, SENTRY_BAGGAGE_HEADER);\n if (baggage && existingBaggageIndex === -1) {\n requestHeaders.push(SENTRY_BAGGAGE_HEADER, baggage);\n } else if (baggage) {\n // headers in format [key_0, value_0, key_1, value_1, ...], hence the +1 here\n const existingBaggageValue = requestHeaders[existingBaggageIndex + 1];\n const merged = mergeBaggageHeaders(existingBaggageValue, baggage);\n if (merged) {\n requestHeaders[existingBaggageIndex + 1] = merged;\n }\n }\n }\n\n if (Array.isArray(request.headers)) {\n // Replace contents in place so we keep the same array reference undici/fetch still holds.\n // `requestHeaders` is already normalized (string pairs only); splice writes them back.\n request.headers.splice(0, request.headers.length, ...requestHeaders);\n } else {\n request.headers = arrayToStringHeaders(requestHeaders);\n }\n}\n\n/**\n * Convert undici’s header array into `[name, value, name, value, ...]` where every value is a string.\n *\n * Undici v6 uses this shape: `[k1, v1, k2, v2, ...]`. Types allow each `v` to be `string | string[]` when\n * that header has multiple values. Sentry’s dedupe/merge helpers expect one string per value slot, so\n * multi-value arrays are joined with `', '`. Missing value slots become `''`.\n */\nfunction normalizeUndiciHeaderPairs(headers: (string | string[])[]): string[] {\n const out: string[] = [];\n for (let i = 0; i < headers.length; i++) {\n const entry = headers[i];\n if (i % 2 === 0) {\n // Header name (should always be a string; coerce defensively).\n out.push(typeof entry === 'string' ? entry : String(entry));\n } else {\n // Header value: flatten `string[]` to a single string for downstream string-only helpers.\n out.push(Array.isArray(entry) ? entry.join(', ') : (entry ?? ''));\n }\n }\n return out;\n}\n\nfunction stringToArrayHeaders(requestHeaders: string): string[] {\n const headersArray = requestHeaders.split('\\r\\n');\n const headers: string[] = [];\n for (const header of headersArray) {\n try {\n const colonIndex = header.indexOf(':');\n if (colonIndex === -1) {\n continue;\n }\n const key = header.slice(0, colonIndex).trim();\n const value = header.slice(colonIndex + 1).trim();\n if (key) {\n headers.push(key, value);\n }\n } catch {\n debug.warn(`Failed to convert string request header to array header: ${header}`);\n }\n }\n return headers;\n}\n\nfunction arrayToStringHeaders(headers: string[]): string {\n const headerPairs: string[] = [];\n\n for (let i = 0; i < headers.length; i += 2) {\n const key = headers[i];\n const value = headers[i + 1];\n if (!key || value == null) {\n // skip falsy keys but only null/undefined values\n continue;\n }\n headerPairs.push(`${key}: ${value}`);\n }\n\n if (!headerPairs.length) {\n return '';\n }\n\n return headerPairs.join('\\r\\n').concat('\\r\\n');\n}\n\n/**\n * For a given header name, if there are multiple entries in the [key, value, key, value, ...] array,\n * keep the first entry and remove the rest.\n * For baggage, values are merged to preserve all entries but to dedupe sentry- values, and always\n * keep the first occurrence of them\n */\nfunction _deduplicateArrayHeader(headers: string[], headerName: string): void {\n let firstIndex = -1;\n for (let i = 0; i < headers.length; i += 2) {\n if (headers[i] !== headerName) {\n continue;\n }\n\n if (firstIndex === -1) {\n firstIndex = i;\n continue;\n }\n\n const firstHeaderValue = headers[firstIndex + 1];\n if (headerName === SENTRY_BAGGAGE_HEADER && firstHeaderValue) {\n // mergeBaggageHeaders always takes sentry- values from the new baggage (2nd param) and merges\n // it with the existing one (1st param). Here, we want to keep the first header's existing\n // sentry- values in favor of the new ones. Hence we swap the parameters.\n const merged = mergeBaggageHeaders(headers[i + 1], firstHeaderValue);\n if (merged) {\n headers[firstIndex + 1] = merged;\n }\n }\n headers.splice(i, 2);\n i -= 2;\n }\n}\n\n/**\n * Find the index of an existing header in an array of headers.\n * Only take even indices, because headers are in format [key_0, value_0, key_1, value_1, ...]\n * otherwise we could match a header _value_ with @param name\n */\nfunction _findExistingHeaderIndex(headers: string[], name: string): number {\n return headers.findIndex((header, i) => i % 2 === 0 && header === name);\n}\n\n/** Add a breadcrumb for an outgoing fetch/undici request. */\nexport function addFetchRequestBreadcrumb(request: UndiciRequest, response: UndiciResponse): void {\n const data = getBreadcrumbData(request);\n\n const statusCode = response.statusCode;\n const level = getBreadcrumbLogLevelFromHttpStatusCode(statusCode);\n\n addBreadcrumb(\n {\n category: 'http',\n data: {\n status_code: statusCode,\n ...data,\n },\n type: 'http',\n level,\n },\n {\n event: 'response',\n request,\n response,\n },\n );\n}\n\nfunction getBreadcrumbData(request: UndiciRequest): Partial<SanitizedRequestData> {\n try {\n const url = getAbsoluteUrl(request.origin, request.path);\n const parsedUrl = parseUrl(url);\n\n const data: Partial<SanitizedRequestData> = {\n url: getSanitizedUrlString(parsedUrl),\n 'http.method': request.method || 'GET',\n };\n\n if (parsedUrl.search) {\n data['http.query'] = parsedUrl.search;\n }\n if (parsedUrl.hash) {\n data['http.fragment'] = parsedUrl.hash;\n }\n\n return data;\n } catch {\n return {};\n }\n}\n\n/** Get the absolute URL from an origin and path. */\nexport function getAbsoluteUrl(origin: string, path: string = '/'): string {\n try {\n const url = new URL(path, origin);\n return url.toString();\n } catch {\n // fallback: Construct it on our own\n const url = `${origin}`;\n\n if (url.endsWith('/') && path.startsWith('/')) {\n return `${url}${path.slice(1)}`;\n }\n\n if (!url.endsWith('/') && !path.startsWith('/')) {\n return `${url}/${path}`;\n }\n\n return `${url}${path}`;\n }\n}\n"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"outgoingFetchRequest.js","sources":["../../../src/utils/outgoingFetchRequest.ts"],"sourcesContent":["import type { LRUMap, SanitizedRequestData } from '@sentry/core';\nimport {\n addBreadcrumb,\n getBreadcrumbLogLevelFromHttpStatusCode,\n getClient,\n getSanitizedUrlString,\n getTraceData,\n parseUrl,\n shouldPropagateTraceForUrl,\n mergeBaggageHeaders,\n} from '@sentry/core';\nimport type { UndiciRequest, UndiciResponse } from '../integrations/node-fetch/types';\nimport { debug } from '@sentry/core';\nconst SENTRY_TRACE_HEADER = 'sentry-trace';\nconst SENTRY_BAGGAGE_HEADER = 'baggage';\nconst W3C_TRACEPARENT_HEADER = 'traceparent';\n/**\n * Add trace propagation headers to an outgoing fetch/undici request.\n *\n * Checks if the request URL matches trace propagation targets,\n * then injects sentry-trace, traceparent, and baggage headers.\n */\n// eslint-disable-next-line complexity\nexport function addTracePropagationHeadersToFetchRequest(\n request: UndiciRequest,\n propagationDecisionMap: LRUMap<string, boolean>,\n): void {\n const url = getAbsoluteUrl(request.origin, request.path);\n\n // Manually add the trace headers, if it applies\n // Note: We do not use `propagation.inject()` here, because our propagator relies on an active span\n // Which we do not have in this case\n // The propagator _may_ overwrite this, but this should be fine as it is the same data\n const { tracePropagationTargets, propagateTraceparent } = getClient()?.getOptions() || {};\n const addedHeaders = shouldPropagateTraceForUrl(url, tracePropagationTargets, propagationDecisionMap)\n ? getTraceData({ propagateTraceparent })\n : undefined;\n\n if (!addedHeaders) {\n return;\n }\n\n const { 'sentry-trace': sentryTrace, baggage, traceparent } = addedHeaders;\n\n // Undici can expose headers either as a raw string (v5-style) or as a flat array of pairs (v6-style).\n // In the array form, even indices are header names and odd indices are values; in undici v6 a value\n // may be `string | string[]` when a header has multiple values. The helpers below (_deduplicateArrayHeader,\n // push, etc.) expect each value slot to be a single string, so we normalize array headers first.\n const requestHeaders: string[] = Array.isArray(request.headers)\n ? normalizeUndiciHeaderPairs(request.headers)\n : stringToArrayHeaders(request.headers);\n\n // OTel's UndiciInstrumentation calls propagation.inject() which unconditionally\n // appends headers to the request. When the user also sets headers via getTraceData(),\n // this results in duplicate sentry-trace and baggage (and optionally traceparent) entries.\n // We clean these up before applying our own logic.\n _deduplicateArrayHeader(requestHeaders, SENTRY_TRACE_HEADER);\n _deduplicateArrayHeader(requestHeaders, SENTRY_BAGGAGE_HEADER);\n if (propagateTraceparent) {\n _deduplicateArrayHeader(requestHeaders, W3C_TRACEPARENT_HEADER);\n }\n\n // We do not want to overwrite existing headers here\n // If the core UndiciInstrumentation is registered, it will already have set the headers\n // We do not want to add any then\n const hasExistingSentryTraceHeader = _findExistingHeaderIndex(requestHeaders, SENTRY_TRACE_HEADER) !== -1;\n\n // We do not want to set any headers if we already have an existing sentry-trace header.\n // sentry-trace is still the source of truth, otherwise we risk mixing up baggage and sentry-trace values.\n if (!hasExistingSentryTraceHeader) {\n if (sentryTrace) {\n requestHeaders.push(SENTRY_TRACE_HEADER, sentryTrace);\n }\n\n if (traceparent && _findExistingHeaderIndex(requestHeaders, 'traceparent') === -1) {\n requestHeaders.push('traceparent', traceparent);\n }\n\n // For baggage, we make sure to merge this into a possibly existing header\n const existingBaggageIndex = _findExistingHeaderIndex(requestHeaders, SENTRY_BAGGAGE_HEADER);\n if (baggage && existingBaggageIndex === -1) {\n requestHeaders.push(SENTRY_BAGGAGE_HEADER, baggage);\n } else if (baggage) {\n // headers in format [key_0, value_0, key_1, value_1, ...], hence the +1 here\n const existingBaggageValue = requestHeaders[existingBaggageIndex + 1];\n const merged = mergeBaggageHeaders(existingBaggageValue, baggage);\n if (merged) {\n requestHeaders[existingBaggageIndex + 1] = merged;\n }\n }\n }\n\n if (Array.isArray(request.headers)) {\n // Replace contents in place so we keep the same array reference undici/fetch still holds.\n // `requestHeaders` is already normalized (string pairs only); splice writes them back.\n request.headers.splice(0, request.headers.length, ...requestHeaders);\n } else {\n request.headers = arrayToStringHeaders(requestHeaders);\n }\n}\n\n/**\n * Convert undici’s header array into `[name, value, name, value, ...]` where every value is a string.\n *\n * Undici v6 uses this shape: `[k1, v1, k2, v2, ...]`. Types allow each `v` to be `string | string[]` when\n * that header has multiple values. Sentry’s dedupe/merge helpers expect one string per value slot, so\n * multi-value arrays are joined with `', '`. Missing value slots become `''`.\n */\nfunction normalizeUndiciHeaderPairs(headers: (string | string[])[]): string[] {\n const out: string[] = [];\n for (let i = 0; i < headers.length; i++) {\n const entry = headers[i];\n if (i % 2 === 0) {\n // Header name (should always be a string; coerce defensively).\n out.push(typeof entry === 'string' ? entry : String(entry));\n } else {\n // Header value: flatten `string[]` to a single string for downstream string-only helpers.\n out.push(Array.isArray(entry) ? entry.join(', ') : (entry ?? ''));\n }\n }\n return out;\n}\n\nfunction stringToArrayHeaders(requestHeaders: string): string[] {\n const headersArray = requestHeaders.split('\\r\\n');\n const headers: string[] = [];\n for (const header of headersArray) {\n try {\n const colonIndex = header.indexOf(':');\n if (colonIndex === -1) {\n continue;\n }\n const key = header.slice(0, colonIndex).trim();\n const value = header.slice(colonIndex + 1).trim();\n if (key) {\n headers.push(key, value);\n }\n } catch {\n debug.warn(`Failed to convert string request header to array header: ${header}`);\n }\n }\n return headers;\n}\n\nfunction arrayToStringHeaders(headers: string[]): string {\n const headerPairs: string[] = [];\n\n for (let i = 0; i < headers.length; i += 2) {\n const key = headers[i];\n const value = headers[i + 1];\n if (!key || value == null) {\n // skip falsy keys but only null/undefined values\n continue;\n }\n headerPairs.push(`${key}: ${value}`);\n }\n\n if (!headerPairs.length) {\n return '';\n }\n\n return headerPairs.join('\\r\\n').concat('\\r\\n');\n}\n\n/**\n * For a given header name, if there are multiple entries in the [key, value, key, value, ...] array,\n * keep the first entry and remove the rest.\n * For baggage, values are merged to preserve all entries but to dedupe sentry- values, and always\n * keep the first occurrence of them\n */\nfunction _deduplicateArrayHeader(headers: string[], headerName: string): void {\n let firstIndex = -1;\n for (let i = 0; i < headers.length; i += 2) {\n if (headers[i] !== headerName) {\n continue;\n }\n\n if (firstIndex === -1) {\n firstIndex = i;\n continue;\n }\n\n const firstHeaderValue = headers[firstIndex + 1];\n if (headerName === SENTRY_BAGGAGE_HEADER && firstHeaderValue) {\n // mergeBaggageHeaders always takes sentry- values from the new baggage (2nd param) and merges\n // it with the existing one (1st param). Here, we want to keep the first header's existing\n // sentry- values in favor of the new ones. Hence we swap the parameters.\n const merged = mergeBaggageHeaders(headers[i + 1], firstHeaderValue);\n if (merged) {\n headers[firstIndex + 1] = merged;\n }\n }\n headers.splice(i, 2);\n i -= 2;\n }\n}\n\n/**\n * Find the index of an existing header in an array of headers.\n * Only take even indices, because headers are in format [key_0, value_0, key_1, value_1, ...]\n * otherwise we could match a header _value_ with @param name\n */\nfunction _findExistingHeaderIndex(headers: string[], name: string): number {\n return headers.findIndex((header, i) => i % 2 === 0 && header === name);\n}\n\n/** Add a breadcrumb for an outgoing fetch/undici request. */\nexport function addFetchRequestBreadcrumb(request: UndiciRequest, response: UndiciResponse): void {\n const data = getBreadcrumbData(request);\n\n const statusCode = response.statusCode;\n const level = getBreadcrumbLogLevelFromHttpStatusCode(statusCode);\n\n addBreadcrumb(\n {\n category: 'http',\n data: {\n status_code: statusCode,\n ...data,\n },\n type: 'http',\n level,\n },\n {\n event: 'response',\n request,\n response,\n },\n );\n}\n\nfunction getBreadcrumbData(request: UndiciRequest): Partial<SanitizedRequestData> {\n try {\n const url = getAbsoluteUrl(request.origin, request.path);\n const parsedUrl = parseUrl(url);\n\n const data: Partial<SanitizedRequestData> = {\n url: getSanitizedUrlString(parsedUrl),\n 'http.method': request.method || 'GET',\n };\n\n if (parsedUrl.search) {\n data['http.query'] = parsedUrl.search;\n }\n if (parsedUrl.hash) {\n data['http.fragment'] = parsedUrl.hash;\n }\n\n return data;\n } catch {\n return {};\n }\n}\n\n/** Get the absolute URL from an origin and path. */\nexport function getAbsoluteUrl(origin: string, path: string = '/'): string {\n try {\n const url = new URL(path, origin);\n return url.toString();\n } catch {\n // fallback: Construct it on our own\n const url = `${origin}`;\n\n if (url.endsWith('/') && path.startsWith('/')) {\n return `${url}${path.slice(1)}`;\n }\n\n if (!url.endsWith('/') && !path.startsWith('/')) {\n return `${url}/${path}`;\n }\n\n return `${url}${path}`;\n }\n}\n"],"names":[],"mappings":"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"} |
| import { envToBool } from '@sentry/core'; | ||
| /** | ||
| * Parse the spotlight option with proper precedence: | ||
| * - `false` or explicit string from options: use as-is | ||
| * - `true`: enable spotlight, but prefer a custom URL from the env var if set | ||
| * - `undefined`: defer entirely to the env var (bool or URL) | ||
| */ | ||
| function getSpotlightConfig(optionsSpotlight) { | ||
@@ -13,14 +7,8 @@ if (optionsSpotlight === false) { | ||
| } | ||
| if (typeof optionsSpotlight === 'string') { | ||
| if (typeof optionsSpotlight === "string") { | ||
| return optionsSpotlight; | ||
| } | ||
| // optionsSpotlight is true or undefined | ||
| const envBool = envToBool(process.env.SENTRY_SPOTLIGHT, { strict: true }); | ||
| const envUrl = envBool === null && process.env.SENTRY_SPOTLIGHT ? process.env.SENTRY_SPOTLIGHT : undefined; | ||
| return optionsSpotlight === true | ||
| ? (envUrl ?? true) // true: use env URL if present, otherwise true | ||
| : (envBool ?? envUrl); // undefined: use env var (bool or URL) | ||
| const envUrl = envBool === null && process.env.SENTRY_SPOTLIGHT ? process.env.SENTRY_SPOTLIGHT : void 0; | ||
| return optionsSpotlight === true ? envUrl ?? true : envBool ?? envUrl; | ||
| } | ||
@@ -27,0 +15,0 @@ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"spotlight.js","sources":["../../../src/utils/spotlight.ts"],"sourcesContent":["import { envToBool } from '@sentry/core';\n\n/**\n * Parse the spotlight option with proper precedence:\n * - `false` or explicit string from options: use as-is\n * - `true`: enable spotlight, but prefer a custom URL from the env var if set\n * - `undefined`: defer entirely to the env var (bool or URL)\n */\nexport function getSpotlightConfig(optionsSpotlight: boolean | string | undefined): boolean | string | undefined {\n if (optionsSpotlight === false) {\n return false;\n }\n\n if (typeof optionsSpotlight === 'string') {\n return optionsSpotlight;\n }\n\n // optionsSpotlight is true or undefined\n const envBool = envToBool(process.env.SENTRY_SPOTLIGHT, { strict: true });\n const envUrl = envBool === null && process.env.SENTRY_SPOTLIGHT ? process.env.SENTRY_SPOTLIGHT : 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":[],"mappings":";;AAEA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,kBAAkB,CAAC,gBAAgB,EAA8D;AACjH,EAAE,IAAI,gBAAA,KAAqB,KAAK,EAAE;AAClC,IAAI,OAAO,KAAK;AAChB,EAAE;;AAEF,EAAE,IAAI,OAAO,gBAAA,KAAqB,QAAQ,EAAE;AAC5C,IAAI,OAAO,gBAAgB;AAC3B,EAAE;;AAEF;AACA,EAAE,MAAM,OAAA,GAAU,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,EAAE,MAAM,EAAE,IAAA,EAAM,CAAC;AAC3E,EAAE,MAAM,MAAA,GAAS,YAAY,IAAA,IAAQ,OAAO,CAAC,GAAG,CAAC,gBAAA,GAAmB,OAAO,CAAC,GAAG,CAAC,gBAAA,GAAmB,SAAS;;AAE5G,EAAE,OAAO,qBAAqB;AAC9B,OAAO,MAAA,IAAU,IAAI;AACrB,OAAO,OAAA,IAAW,MAAM,CAAC,CAAA;AACzB;;;;"} | ||
| {"version":3,"file":"spotlight.js","sources":["../../../src/utils/spotlight.ts"],"sourcesContent":["import { envToBool } from '@sentry/core';\n\n/**\n * Parse the spotlight option with proper precedence:\n * - `false` or explicit string from options: use as-is\n * - `true`: enable spotlight, but prefer a custom URL from the env var if set\n * - `undefined`: defer entirely to the env var (bool or URL)\n */\nexport function getSpotlightConfig(optionsSpotlight: boolean | string | undefined): boolean | string | undefined {\n if (optionsSpotlight === false) {\n return false;\n }\n\n if (typeof optionsSpotlight === 'string') {\n return optionsSpotlight;\n }\n\n // optionsSpotlight is true or undefined\n const envBool = envToBool(process.env.SENTRY_SPOTLIGHT, { strict: true });\n const envUrl = envBool === null && process.env.SENTRY_SPOTLIGHT ? process.env.SENTRY_SPOTLIGHT : 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":[],"mappings":";;AAQO,SAAS,mBAAmB,gBAAA,EAA8E;AAC/G,EAAA,IAAI,qBAAqB,KAAA,EAAO;AAC9B,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,IAAI,OAAO,qBAAqB,QAAA,EAAU;AACxC,IAAA,OAAO,gBAAA;AAAA,EACT;AAGA,EAAA,MAAM,OAAA,GAAU,UAAU,OAAA,CAAQ,GAAA,CAAI,kBAAkB,EAAE,MAAA,EAAQ,MAAM,CAAA;AACxE,EAAA,MAAM,MAAA,GAAS,YAAY,IAAA,IAAQ,OAAA,CAAQ,IAAI,gBAAA,GAAmB,OAAA,CAAQ,IAAI,gBAAA,GAAmB,MAAA;AAEjG,EAAA,OAAO,gBAAA,KAAqB,IAAA,GACvB,MAAA,IAAU,IAAA,GACV,OAAA,IAAW,MAAA;AAClB;;;;"} |
@@ -1,4 +0,5 @@ | ||
| import { EventEmitter } from 'node:events'; | ||
| import { RequestOptions } from 'node:http'; | ||
| import { Client, HttpIncomingMessage, Integration, Scope } from '@sentry/core'; | ||
| import { HttpIncomingMessage, Integration } from '@sentry/core'; | ||
| import { recordRequestSession } from '@sentry/core'; | ||
| export { recordRequestSession }; | ||
| interface WeakRefImpl<T> { | ||
@@ -66,17 +67,2 @@ deref(): T | undefined; | ||
| }; | ||
| /** | ||
| * Starts a session and tracks it in the context of a given isolation scope. | ||
| * When the passed response is finished, the session is put into a task and is | ||
| * aggregated with other sessions that may happen in a certain time window | ||
| * (sessionFlushingDelayMs). | ||
| * | ||
| * The sessions are always aggregated by the client that is on the current scope | ||
| * at the time of ending the response (if there is one). | ||
| */ | ||
| export declare function recordRequestSession(client: Client, { requestIsolationScope, response, sessionFlushingDelayMS, }: { | ||
| requestIsolationScope: Scope; | ||
| response: EventEmitter; | ||
| sessionFlushingDelayMS?: number; | ||
| }): void; | ||
| export {}; | ||
| //# sourceMappingURL=httpServerIntegration.d.ts.map |
@@ -1,4 +0,5 @@ | ||
| import type { EventEmitter } from 'node:events'; | ||
| import type { RequestOptions } from 'node:http'; | ||
| import type { Client, HttpIncomingMessage, Integration, Scope } from '@sentry/core'; | ||
| import type { HttpIncomingMessage, Integration } from '@sentry/core'; | ||
| import { recordRequestSession } from '@sentry/core'; | ||
| export { recordRequestSession }; | ||
| interface WeakRefImpl<T> { | ||
@@ -66,17 +67,2 @@ deref(): T | undefined; | ||
| }; | ||
| /** | ||
| * Starts a session and tracks it in the context of a given isolation scope. | ||
| * When the passed response is finished, the session is put into a task and is | ||
| * aggregated with other sessions that may happen in a certain time window | ||
| * (sessionFlushingDelayMs). | ||
| * | ||
| * The sessions are always aggregated by the client that is on the current scope | ||
| * at the time of ending the response (if there is one). | ||
| */ | ||
| export declare function recordRequestSession(client: Client, { requestIsolationScope, response, sessionFlushingDelayMS, }: { | ||
| requestIsolationScope: Scope; | ||
| response: EventEmitter; | ||
| sessionFlushingDelayMS?: number; | ||
| }): void; | ||
| export {}; | ||
| //# sourceMappingURL=httpServerIntegration.d.ts.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"httpServerIntegration.d.ts","sourceRoot":"","sources":["../../../../src/integrations/http/httpServerIntegration.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAmB,cAAc,EAA0B,MAAM,WAAW,CAAC;AAGzF,OAAO,KAAK,EAAqB,MAAM,EAAE,mBAAmB,EAAE,WAAW,EAAiB,KAAK,EAAE,MAAM,cAAc,CAAC;AAqBtH,UAAU,WAAW,CAAC,CAAC;IACrB,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CACxB;AAED,KAAK,iBAAiB,GAAG,CAAC,IAAI,EAAE,MAAM,OAAO,KAAK,OAAO,CAAC;AAC1D,KAAK,oCAAoC,GAAG,mBAAmB,GAAG;IAChE,kBAAkB,CAAC,EAAE,WAAW,CAAC,iBAAiB,CAAC,CAAC;CACrD,CAAC;AAgBF,MAAM,WAAW,4BAA4B;IAC3C;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAEhC;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,KAAK,OAAO,CAAC;IAEtE;;;;;;;;;;;;;OAaG;IACH,kBAAkB,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;CAC7D;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,oCAAoC,EAAE,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAErH;AA+BD;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,EAA6B,CAC7D,OAAO,CAAC,EAAE,4BAA4B,KACnC,WAAW,GAAG;IACjB,IAAI,EAAE,YAAY,CAAC;IACnB,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB,CAAC;AAoHF;;;;;;;;GAQG;AAEH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,EACE,qBAAqB,EACrB,QAAQ,EACR,sBAAsB,GACvB,EAAE;IACD,qBAAqB,EAAE,KAAK,CAAC;IAC7B,QAAQ,EAAE,YAAY,CAAC;IACvB,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC,GACA,IAAI,CAoDN"} | ||
| {"version":3,"file":"httpServerIntegration.d.ts","sourceRoot":"","sources":["../../../../src/integrations/http/httpServerIntegration.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEhD,OAAO,KAAK,EAAE,mBAAmB,EAAsB,WAAW,EAAiB,MAAM,cAAc,CAAC;AACxG,OAAO,EAML,oBAAoB,EACrB,MAAM,cAAc,CAAC;AAKtB,OAAO,EAAE,oBAAoB,EAAE,CAAC;AAMhC,UAAU,WAAW,CAAC,CAAC;IACrB,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CACxB;AAED,KAAK,iBAAiB,GAAG,CAAC,IAAI,EAAE,MAAM,OAAO,KAAK,OAAO,CAAC;AAC1D,KAAK,oCAAoC,GAAG,mBAAmB,GAAG;IAChE,kBAAkB,CAAC,EAAE,WAAW,CAAC,iBAAiB,CAAC,CAAC;CACrD,CAAC;AAEF,MAAM,WAAW,4BAA4B;IAC3C;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAEhC;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,KAAK,OAAO,CAAC;IAEtE;;;;;;;;;;;;;OAaG;IACH,kBAAkB,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;CAC7D;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,oCAAoC,EAAE,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAErH;AAmFD;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,EAA6B,CAC7D,OAAO,CAAC,EAAE,4BAA4B,KACnC,WAAW,GAAG;IACjB,IAAI,EAAE,YAAY,CAAC;IACnB,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB,CAAC"} |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"httpIntegration.d.ts","sourceRoot":"","sources":["../../../../src/light/integrations/httpIntegration.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,KAAK,EAA0C,WAAW,EAAiB,MAAM,cAAc,CAAC;AAkCvG,MAAM,WAAW,sBAAsB;IACrC;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,KAAK,OAAO,CAAC;IAEtE;;;;;;;;;;;;;OAaG;IACH,kBAAkB,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAE5D;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;;;;;;OAOG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAE3B;;;;;;OAMG;IACH,sBAAsB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,KAAK,OAAO,CAAC;CAC5E;AAqFD;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAuB,CAAC,OAAO,CAAC,EAAE,sBAAsB,KAAK,WAAW,GAAG;IACrG,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB,CAAC"} | ||
| {"version":3,"file":"httpIntegration.d.ts","sourceRoot":"","sources":["../../../../src/light/integrations/httpIntegration.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,KAAK,EAA0C,WAAW,EAAiB,MAAM,cAAc,CAAC;AAuBvG,MAAM,WAAW,sBAAsB;IACrC;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,KAAK,OAAO,CAAC;IAEtE;;;;;;;;;;;;;OAaG;IACH,kBAAkB,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAE5D;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;;;;;;OAOG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAE3B;;;;;;OAMG;IACH,sBAAsB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,KAAK,OAAO,CAAC;CAC5E;AAqFD;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAuB,CAAC,OAAO,CAAC,EAAE,sBAAsB,KAAK,WAAW,GAAG;IACrG,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB,CAAC"} |
+3
-3
| { | ||
| "name": "@sentry/node-core", | ||
| "version": "10.53.1", | ||
| "version": "10.54.0", | ||
| "description": "Sentry Node-Core SDK", | ||
@@ -108,4 +108,4 @@ "repository": "git://github.com/getsentry/sentry-javascript.git", | ||
| "dependencies": { | ||
| "@sentry/core": "10.53.1", | ||
| "@sentry/opentelemetry": "10.53.1", | ||
| "@sentry/core": "10.54.0", | ||
| "@sentry/opentelemetry": "10.54.0", | ||
| "import-in-the-middle": "^3.0.0" | ||
@@ -112,0 +112,0 @@ }, |
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const core = require('@sentry/core'); | ||
| const debugBuild = require('../debug-build.js'); | ||
| /** | ||
| * This method patches the request object to capture the body. | ||
| * Instead of actually consuming the streamed body ourselves, which has potential side effects, | ||
| * we monkey patch `req.on('data')` to intercept the body chunks. | ||
| * This way, we only read the body if the user also consumes the body, ensuring we do not change any behavior in unexpected ways. | ||
| */ | ||
| function patchRequestToCaptureBody( | ||
| req, | ||
| isolationScope, | ||
| maxIncomingRequestBodySize, | ||
| integrationName, | ||
| ) { | ||
| let bodyByteLength = 0; | ||
| const chunks = []; | ||
| debugBuild.DEBUG_BUILD && core.debug.log(integrationName, 'Patching request.on'); | ||
| /** | ||
| * We need to keep track of the original callbacks, in order to be able to remove listeners again. | ||
| * Since `off` depends on having the exact same function reference passed in, we need to be able to map | ||
| * original listeners to our wrapped ones. | ||
| */ | ||
| const callbackMap = new WeakMap(); | ||
| const maxBodySize = core.getMaxBodyByteLength(maxIncomingRequestBodySize); | ||
| try { | ||
| // eslint-disable-next-line @typescript-eslint/unbound-method | ||
| req.on = new Proxy(req.on, { | ||
| apply: (target, thisArg, args) => { | ||
| const [event, listener, ...restArgs] = args; | ||
| if (event === 'data') { | ||
| debugBuild.DEBUG_BUILD && | ||
| core.debug.log(integrationName, `Handling request.on("data") with maximum body size of ${maxBodySize}b`); | ||
| const callback = new Proxy(listener, { | ||
| apply: (target, thisArg, args) => { | ||
| try { | ||
| const chunk = args[0] ; | ||
| const bufferifiedChunk = Buffer.from(chunk); | ||
| if (bodyByteLength < maxBodySize) { | ||
| chunks.push(bufferifiedChunk); | ||
| bodyByteLength += bufferifiedChunk.byteLength; | ||
| } else if (debugBuild.DEBUG_BUILD) { | ||
| core.debug.log( | ||
| integrationName, | ||
| `Dropping request body chunk because maximum body length of ${maxBodySize}b is exceeded.`, | ||
| ); | ||
| } | ||
| } catch (_err) { | ||
| debugBuild.DEBUG_BUILD && core.debug.error(integrationName, 'Encountered error while storing body chunk.'); | ||
| } | ||
| return Reflect.apply(target, thisArg, args); | ||
| }, | ||
| }); | ||
| callbackMap.set(listener, callback); | ||
| return Reflect.apply(target, thisArg, [event, callback, ...restArgs]); | ||
| } | ||
| return Reflect.apply(target, thisArg, args); | ||
| }, | ||
| }); | ||
| // Ensure we also remove callbacks correctly | ||
| // eslint-disable-next-line @typescript-eslint/unbound-method | ||
| req.off = new Proxy(req.off, { | ||
| apply: (target, thisArg, args) => { | ||
| const [, listener] = args; | ||
| const callback = callbackMap.get(listener); | ||
| if (callback) { | ||
| callbackMap.delete(listener); | ||
| const modifiedArgs = args.slice(); | ||
| modifiedArgs[1] = callback; | ||
| return Reflect.apply(target, thisArg, modifiedArgs); | ||
| } | ||
| return Reflect.apply(target, thisArg, args); | ||
| }, | ||
| }); | ||
| req.on('end', () => { | ||
| try { | ||
| const body = Buffer.concat(chunks).toString('utf-8'); | ||
| if (body) { | ||
| // Using Buffer.byteLength here, because the body may contain characters that are not 1 byte long | ||
| const bodyByteLength = Buffer.byteLength(body, 'utf-8'); | ||
| const truncatedBody = | ||
| bodyByteLength > maxBodySize | ||
| ? `${Buffer.from(body) | ||
| .subarray(0, maxBodySize - 3) | ||
| .toString('utf-8')}...` | ||
| : body; | ||
| isolationScope.setSDKProcessingMetadata({ normalizedRequest: { data: truncatedBody } }); | ||
| } | ||
| } catch (error) { | ||
| if (debugBuild.DEBUG_BUILD) { | ||
| core.debug.error(integrationName, 'Error building captured request body', error); | ||
| } | ||
| } | ||
| }); | ||
| } catch (error) { | ||
| if (debugBuild.DEBUG_BUILD) { | ||
| core.debug.error(integrationName, 'Error patching request to capture body', error); | ||
| } | ||
| } | ||
| } | ||
| exports.patchRequestToCaptureBody = patchRequestToCaptureBody; | ||
| //# sourceMappingURL=captureRequestBody.js.map |
| {"version":3,"file":"captureRequestBody.js","sources":["../../../src/utils/captureRequestBody.ts"],"sourcesContent":["import type { IncomingMessage } from 'node:http';\nimport type { Scope } from '@sentry/core';\nimport { debug, getMaxBodyByteLength, type MaxRequestBodySize } from '@sentry/core';\nimport { DEBUG_BUILD } from '../debug-build';\n\n/**\n * This method patches the request object to capture the body.\n * Instead of actually consuming the streamed body ourselves, which has potential side effects,\n * we monkey patch `req.on('data')` to intercept the body chunks.\n * This way, we only read the body if the user also consumes the body, ensuring we do not change any behavior in unexpected ways.\n */\nexport function patchRequestToCaptureBody(\n req: IncomingMessage,\n isolationScope: Scope,\n maxIncomingRequestBodySize: Exclude<MaxRequestBodySize, 'none'>,\n integrationName: string,\n): void {\n let bodyByteLength = 0;\n const chunks: Buffer[] = [];\n\n DEBUG_BUILD && debug.log(integrationName, 'Patching request.on');\n\n /**\n * We need to keep track of the original callbacks, in order to be able to remove listeners again.\n * Since `off` depends on having the exact same function reference passed in, we need to be able to map\n * original listeners to our wrapped ones.\n */\n const callbackMap = new WeakMap();\n\n const maxBodySize = getMaxBodyByteLength(maxIncomingRequestBodySize);\n\n try {\n // eslint-disable-next-line @typescript-eslint/unbound-method\n req.on = new Proxy(req.on, {\n apply: (target, thisArg, args: Parameters<typeof req.on>) => {\n const [event, listener, ...restArgs] = args;\n\n if (event === 'data') {\n DEBUG_BUILD &&\n debug.log(integrationName, `Handling request.on(\"data\") with maximum body size of ${maxBodySize}b`);\n\n const callback = new Proxy(listener, {\n apply: (target, thisArg, args: Parameters<typeof listener>) => {\n try {\n const chunk = args[0] as Buffer | string;\n const bufferifiedChunk = Buffer.from(chunk);\n\n if (bodyByteLength < maxBodySize) {\n chunks.push(bufferifiedChunk);\n bodyByteLength += bufferifiedChunk.byteLength;\n } else if (DEBUG_BUILD) {\n debug.log(\n integrationName,\n `Dropping request body chunk because maximum body length of ${maxBodySize}b is exceeded.`,\n );\n }\n } catch (_err) {\n DEBUG_BUILD && debug.error(integrationName, 'Encountered error while storing body chunk.');\n }\n\n return Reflect.apply(target, thisArg, args);\n },\n });\n\n callbackMap.set(listener, callback);\n\n return Reflect.apply(target, thisArg, [event, callback, ...restArgs]);\n }\n\n return Reflect.apply(target, thisArg, args);\n },\n });\n\n // Ensure we also remove callbacks correctly\n // eslint-disable-next-line @typescript-eslint/unbound-method\n req.off = new Proxy(req.off, {\n apply: (target, thisArg, args: Parameters<typeof req.off>) => {\n const [, listener] = args;\n\n const callback = callbackMap.get(listener);\n if (callback) {\n callbackMap.delete(listener);\n\n const modifiedArgs = args.slice();\n modifiedArgs[1] = callback;\n return Reflect.apply(target, thisArg, modifiedArgs);\n }\n\n return Reflect.apply(target, thisArg, args);\n },\n });\n\n req.on('end', () => {\n try {\n const body = Buffer.concat(chunks).toString('utf-8');\n if (body) {\n // Using Buffer.byteLength here, because the body may contain characters that are not 1 byte long\n const bodyByteLength = Buffer.byteLength(body, 'utf-8');\n const truncatedBody =\n bodyByteLength > maxBodySize\n ? `${Buffer.from(body)\n .subarray(0, maxBodySize - 3)\n .toString('utf-8')}...`\n : body;\n\n isolationScope.setSDKProcessingMetadata({ normalizedRequest: { data: truncatedBody } });\n }\n } catch (error) {\n if (DEBUG_BUILD) {\n debug.error(integrationName, 'Error building captured request body', error);\n }\n }\n });\n } catch (error) {\n if (DEBUG_BUILD) {\n debug.error(integrationName, 'Error patching request to capture body', error);\n }\n }\n}\n"],"names":["DEBUG_BUILD","debug","getMaxBodyByteLength"],"mappings":"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"} |
| import { debug, getMaxBodyByteLength } from '@sentry/core'; | ||
| import { DEBUG_BUILD } from '../debug-build.js'; | ||
| /** | ||
| * This method patches the request object to capture the body. | ||
| * Instead of actually consuming the streamed body ourselves, which has potential side effects, | ||
| * we monkey patch `req.on('data')` to intercept the body chunks. | ||
| * This way, we only read the body if the user also consumes the body, ensuring we do not change any behavior in unexpected ways. | ||
| */ | ||
| function patchRequestToCaptureBody( | ||
| req, | ||
| isolationScope, | ||
| maxIncomingRequestBodySize, | ||
| integrationName, | ||
| ) { | ||
| let bodyByteLength = 0; | ||
| const chunks = []; | ||
| DEBUG_BUILD && debug.log(integrationName, 'Patching request.on'); | ||
| /** | ||
| * We need to keep track of the original callbacks, in order to be able to remove listeners again. | ||
| * Since `off` depends on having the exact same function reference passed in, we need to be able to map | ||
| * original listeners to our wrapped ones. | ||
| */ | ||
| const callbackMap = new WeakMap(); | ||
| const maxBodySize = getMaxBodyByteLength(maxIncomingRequestBodySize); | ||
| try { | ||
| // eslint-disable-next-line @typescript-eslint/unbound-method | ||
| req.on = new Proxy(req.on, { | ||
| apply: (target, thisArg, args) => { | ||
| const [event, listener, ...restArgs] = args; | ||
| if (event === 'data') { | ||
| DEBUG_BUILD && | ||
| debug.log(integrationName, `Handling request.on("data") with maximum body size of ${maxBodySize}b`); | ||
| const callback = new Proxy(listener, { | ||
| apply: (target, thisArg, args) => { | ||
| try { | ||
| const chunk = args[0] ; | ||
| const bufferifiedChunk = Buffer.from(chunk); | ||
| if (bodyByteLength < maxBodySize) { | ||
| chunks.push(bufferifiedChunk); | ||
| bodyByteLength += bufferifiedChunk.byteLength; | ||
| } else if (DEBUG_BUILD) { | ||
| debug.log( | ||
| integrationName, | ||
| `Dropping request body chunk because maximum body length of ${maxBodySize}b is exceeded.`, | ||
| ); | ||
| } | ||
| } catch (_err) { | ||
| DEBUG_BUILD && debug.error(integrationName, 'Encountered error while storing body chunk.'); | ||
| } | ||
| return Reflect.apply(target, thisArg, args); | ||
| }, | ||
| }); | ||
| callbackMap.set(listener, callback); | ||
| return Reflect.apply(target, thisArg, [event, callback, ...restArgs]); | ||
| } | ||
| return Reflect.apply(target, thisArg, args); | ||
| }, | ||
| }); | ||
| // Ensure we also remove callbacks correctly | ||
| // eslint-disable-next-line @typescript-eslint/unbound-method | ||
| req.off = new Proxy(req.off, { | ||
| apply: (target, thisArg, args) => { | ||
| const [, listener] = args; | ||
| const callback = callbackMap.get(listener); | ||
| if (callback) { | ||
| callbackMap.delete(listener); | ||
| const modifiedArgs = args.slice(); | ||
| modifiedArgs[1] = callback; | ||
| return Reflect.apply(target, thisArg, modifiedArgs); | ||
| } | ||
| return Reflect.apply(target, thisArg, args); | ||
| }, | ||
| }); | ||
| req.on('end', () => { | ||
| try { | ||
| const body = Buffer.concat(chunks).toString('utf-8'); | ||
| if (body) { | ||
| // Using Buffer.byteLength here, because the body may contain characters that are not 1 byte long | ||
| const bodyByteLength = Buffer.byteLength(body, 'utf-8'); | ||
| const truncatedBody = | ||
| bodyByteLength > maxBodySize | ||
| ? `${Buffer.from(body) | ||
| .subarray(0, maxBodySize - 3) | ||
| .toString('utf-8')}...` | ||
| : body; | ||
| isolationScope.setSDKProcessingMetadata({ normalizedRequest: { data: truncatedBody } }); | ||
| } | ||
| } catch (error) { | ||
| if (DEBUG_BUILD) { | ||
| debug.error(integrationName, 'Error building captured request body', error); | ||
| } | ||
| } | ||
| }); | ||
| } catch (error) { | ||
| if (DEBUG_BUILD) { | ||
| debug.error(integrationName, 'Error patching request to capture body', error); | ||
| } | ||
| } | ||
| } | ||
| export { patchRequestToCaptureBody }; | ||
| //# sourceMappingURL=captureRequestBody.js.map |
| {"version":3,"file":"captureRequestBody.js","sources":["../../../src/utils/captureRequestBody.ts"],"sourcesContent":["import type { IncomingMessage } from 'node:http';\nimport type { Scope } from '@sentry/core';\nimport { debug, getMaxBodyByteLength, type MaxRequestBodySize } from '@sentry/core';\nimport { DEBUG_BUILD } from '../debug-build';\n\n/**\n * This method patches the request object to capture the body.\n * Instead of actually consuming the streamed body ourselves, which has potential side effects,\n * we monkey patch `req.on('data')` to intercept the body chunks.\n * This way, we only read the body if the user also consumes the body, ensuring we do not change any behavior in unexpected ways.\n */\nexport function patchRequestToCaptureBody(\n req: IncomingMessage,\n isolationScope: Scope,\n maxIncomingRequestBodySize: Exclude<MaxRequestBodySize, 'none'>,\n integrationName: string,\n): void {\n let bodyByteLength = 0;\n const chunks: Buffer[] = [];\n\n DEBUG_BUILD && debug.log(integrationName, 'Patching request.on');\n\n /**\n * We need to keep track of the original callbacks, in order to be able to remove listeners again.\n * Since `off` depends on having the exact same function reference passed in, we need to be able to map\n * original listeners to our wrapped ones.\n */\n const callbackMap = new WeakMap();\n\n const maxBodySize = getMaxBodyByteLength(maxIncomingRequestBodySize);\n\n try {\n // eslint-disable-next-line @typescript-eslint/unbound-method\n req.on = new Proxy(req.on, {\n apply: (target, thisArg, args: Parameters<typeof req.on>) => {\n const [event, listener, ...restArgs] = args;\n\n if (event === 'data') {\n DEBUG_BUILD &&\n debug.log(integrationName, `Handling request.on(\"data\") with maximum body size of ${maxBodySize}b`);\n\n const callback = new Proxy(listener, {\n apply: (target, thisArg, args: Parameters<typeof listener>) => {\n try {\n const chunk = args[0] as Buffer | string;\n const bufferifiedChunk = Buffer.from(chunk);\n\n if (bodyByteLength < maxBodySize) {\n chunks.push(bufferifiedChunk);\n bodyByteLength += bufferifiedChunk.byteLength;\n } else if (DEBUG_BUILD) {\n debug.log(\n integrationName,\n `Dropping request body chunk because maximum body length of ${maxBodySize}b is exceeded.`,\n );\n }\n } catch (_err) {\n DEBUG_BUILD && debug.error(integrationName, 'Encountered error while storing body chunk.');\n }\n\n return Reflect.apply(target, thisArg, args);\n },\n });\n\n callbackMap.set(listener, callback);\n\n return Reflect.apply(target, thisArg, [event, callback, ...restArgs]);\n }\n\n return Reflect.apply(target, thisArg, args);\n },\n });\n\n // Ensure we also remove callbacks correctly\n // eslint-disable-next-line @typescript-eslint/unbound-method\n req.off = new Proxy(req.off, {\n apply: (target, thisArg, args: Parameters<typeof req.off>) => {\n const [, listener] = args;\n\n const callback = callbackMap.get(listener);\n if (callback) {\n callbackMap.delete(listener);\n\n const modifiedArgs = args.slice();\n modifiedArgs[1] = callback;\n return Reflect.apply(target, thisArg, modifiedArgs);\n }\n\n return Reflect.apply(target, thisArg, args);\n },\n });\n\n req.on('end', () => {\n try {\n const body = Buffer.concat(chunks).toString('utf-8');\n if (body) {\n // Using Buffer.byteLength here, because the body may contain characters that are not 1 byte long\n const bodyByteLength = Buffer.byteLength(body, 'utf-8');\n const truncatedBody =\n bodyByteLength > maxBodySize\n ? `${Buffer.from(body)\n .subarray(0, maxBodySize - 3)\n .toString('utf-8')}...`\n : body;\n\n isolationScope.setSDKProcessingMetadata({ normalizedRequest: { data: truncatedBody } });\n }\n } catch (error) {\n if (DEBUG_BUILD) {\n debug.error(integrationName, 'Error building captured request body', error);\n }\n }\n });\n } catch (error) {\n if (DEBUG_BUILD) {\n debug.error(integrationName, 'Error patching request to capture body', error);\n }\n }\n}\n"],"names":[],"mappings":"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"} |
| import { IncomingMessage } from 'node:http'; | ||
| import { Scope } from '@sentry/core'; | ||
| import { MaxRequestBodySize } from '@sentry/core'; | ||
| /** | ||
| * This method patches the request object to capture the body. | ||
| * Instead of actually consuming the streamed body ourselves, which has potential side effects, | ||
| * we monkey patch `req.on('data')` to intercept the body chunks. | ||
| * This way, we only read the body if the user also consumes the body, ensuring we do not change any behavior in unexpected ways. | ||
| */ | ||
| export declare function patchRequestToCaptureBody(req: IncomingMessage, isolationScope: Scope, maxIncomingRequestBodySize: Exclude<MaxRequestBodySize, 'none'>, integrationName: string): void; | ||
| //# sourceMappingURL=captureRequestBody.d.ts.map |
| import type { IncomingMessage } from 'node:http'; | ||
| import type { Scope } from '@sentry/core'; | ||
| import { type MaxRequestBodySize } from '@sentry/core'; | ||
| /** | ||
| * This method patches the request object to capture the body. | ||
| * Instead of actually consuming the streamed body ourselves, which has potential side effects, | ||
| * we monkey patch `req.on('data')` to intercept the body chunks. | ||
| * This way, we only read the body if the user also consumes the body, ensuring we do not change any behavior in unexpected ways. | ||
| */ | ||
| export declare function patchRequestToCaptureBody(req: IncomingMessage, isolationScope: Scope, maxIncomingRequestBodySize: Exclude<MaxRequestBodySize, 'none'>, integrationName: string): void; | ||
| //# sourceMappingURL=captureRequestBody.d.ts.map |
| {"version":3,"file":"captureRequestBody.d.ts","sourceRoot":"","sources":["../../../src/utils/captureRequestBody.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAA+B,KAAK,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAGpF;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,eAAe,EACpB,cAAc,EAAE,KAAK,EACrB,0BAA0B,EAAE,OAAO,CAAC,kBAAkB,EAAE,MAAM,CAAC,EAC/D,eAAe,EAAE,MAAM,GACtB,IAAI,CAsGN"} |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 40 instances in 1 package
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 40 instances in 1 package
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
1660652
-12.32%489
-1.41%14855
-19.77%+ Added
+ Added
- Removed
- Removed
Updated