posthog-node
Advanced tools
| import { ErrorTracking as CoreErrorTracking } from '@posthog/core'; | ||
| export declare function addUncaughtExceptionListener(captureFn: (exception: Error, hint: CoreErrorTracking.EventHint) => void, onFatalFn: (exception: Error) => void): void; | ||
| export declare function addUnhandledRejectionListener(captureFn: (exception: unknown, hint: CoreErrorTracking.EventHint) => void): void; | ||
| type UnhandledRejectionMode = 'throw' | 'strict' | 'warn' | 'warn-with-error-code' | 'none'; | ||
| export declare function getUnhandledRejectionMode(execArgv?: string[], nodeOptions?: string | undefined): UnhandledRejectionMode; | ||
| export declare function addUncaughtExceptionListener(captureFn: (exception: Error, hint: CoreErrorTracking.EventHint) => void, onFatalFn: (exception: Error) => void, mode?: UnhandledRejectionMode): void; | ||
| export declare function addUnhandledRejectionListener(captureFn: (exception: unknown, hint: CoreErrorTracking.EventHint) => void, mode?: UnhandledRejectionMode): void; | ||
| export {}; | ||
| //# sourceMappingURL=autocapture.d.ts.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"autocapture.d.ts","sourceRoot":"","sources":["../../../src/extensions/error-tracking/autocapture.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,IAAI,iBAAiB,EAAE,MAAM,eAAe,CAAA;AA6ClE,wBAAgB,4BAA4B,CAC1C,SAAS,EAAE,CAAC,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,iBAAiB,CAAC,SAAS,KAAK,IAAI,EACxE,SAAS,EAAE,CAAC,SAAS,EAAE,KAAK,KAAK,IAAI,GACpC,IAAI,CAEN;AAED,wBAAgB,6BAA6B,CAC3C,SAAS,EAAE,CAAC,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,iBAAiB,CAAC,SAAS,KAAK,IAAI,GACzE,IAAI,CASN"} | ||
| {"version":3,"file":"autocapture.d.ts","sourceRoot":"","sources":["../../../src/extensions/error-tracking/autocapture.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,IAAI,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAGlE,KAAK,sBAAsB,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,sBAAsB,GAAG,MAAM,CAAA;AA+D3F,wBAAgB,yBAAyB,CACvC,QAAQ,GAAE,MAAM,EAAsB,EACtC,WAAW,GAAE,MAAM,GAAG,SAAgC,GACrD,sBAAsB,CAKxB;AA6CD,wBAAgB,4BAA4B,CAC1C,SAAS,EAAE,CAAC,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,iBAAiB,CAAC,SAAS,KAAK,IAAI,EACxE,SAAS,EAAE,CAAC,SAAS,EAAE,KAAK,KAAK,IAAI,EACrC,IAAI,GAAE,sBAAyD,GAC9D,IAAI,CAYN;AAED,wBAAgB,6BAA6B,CAC3C,SAAS,EAAE,CAAC,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,iBAAiB,CAAC,SAAS,KAAK,IAAI,EAC1E,IAAI,GAAE,sBAAyD,GAC9D,IAAI,CAiBN"} |
@@ -28,16 +28,66 @@ "use strict"; | ||
| addUncaughtExceptionListener: ()=>addUncaughtExceptionListener, | ||
| addUnhandledRejectionListener: ()=>addUnhandledRejectionListener | ||
| addUnhandledRejectionListener: ()=>addUnhandledRejectionListener, | ||
| getUnhandledRejectionMode: ()=>getUnhandledRejectionMode | ||
| }); | ||
| const UNHANDLED_REJECTION_OPTION_NAMES = [ | ||
| '--unhandled-rejections', | ||
| '--unhandled_rejections' | ||
| ]; | ||
| const UNHANDLED_REJECTION_MODES = new Set([ | ||
| 'throw', | ||
| 'strict', | ||
| 'warn', | ||
| 'warn-with-error-code', | ||
| 'none' | ||
| ]); | ||
| const STARTUP_EXEC_ARGV = [ | ||
| ...globalThis.process?.execArgv ?? [] | ||
| ]; | ||
| const STARTUP_NODE_OPTIONS = globalThis.process?.env?.NODE_OPTIONS; | ||
| function splitNodeOptions(nodeOptions) { | ||
| const args = []; | ||
| let current = ''; | ||
| let isInString = false; | ||
| for(let index = 0; index < nodeOptions.length; index++){ | ||
| const character = nodeOptions[index]; | ||
| if ('\\' === character && isInString && index + 1 < nodeOptions.length) current += nodeOptions[++index]; | ||
| else if (' ' !== character || isInString) if ('"' === character) isInString = !isInString; | ||
| else current += character; | ||
| else if (current) { | ||
| args.push(current); | ||
| current = ''; | ||
| } | ||
| } | ||
| if (current) args.push(current); | ||
| return args; | ||
| } | ||
| function findUnhandledRejectionMode(args) { | ||
| let mode; | ||
| for(let index = 0; index < args.length; index++){ | ||
| const argument = args[index]; | ||
| const optionName = UNHANDLED_REJECTION_OPTION_NAMES.find((name)=>argument === name || argument.startsWith(`${name}=`)); | ||
| if (!optionName) continue; | ||
| const value = argument === optionName ? args[++index] : argument.slice(optionName.length + 1); | ||
| if (UNHANDLED_REJECTION_MODES.has(value)) mode = value; | ||
| } | ||
| return mode; | ||
| } | ||
| function getUnhandledRejectionMode(execArgv = STARTUP_EXEC_ARGV, nodeOptions = STARTUP_NODE_OPTIONS) { | ||
| return findUnhandledRejectionMode(execArgv) ?? findUnhandledRejectionMode(splitNodeOptions(nodeOptions ?? '')) ?? 'throw'; | ||
| } | ||
| const STARTUP_UNHANDLED_REJECTION_MODE = getUnhandledRejectionMode(); | ||
| function captureUncaughtException(captureFn, error, origin) { | ||
| captureFn(error, { | ||
| mechanism: { | ||
| type: 'unhandledRejection' === origin ? 'onunhandledrejection' : 'onuncaughtexception', | ||
| handled: false | ||
| } | ||
| }); | ||
| } | ||
| function makeUncaughtExceptionHandler(captureFn, onFatalFn) { | ||
| let calledFatalError = false; | ||
| return Object.assign((error)=>{ | ||
| return Object.assign((error, origin)=>{ | ||
| const userProvidedListenersCount = global.process.listeners('uncaughtException').filter((listener)=>'domainUncaughtExceptionClear' !== listener.name && true !== listener._posthogErrorHandler).length; | ||
| const processWouldExit = 0 === userProvidedListenersCount; | ||
| captureFn(error, { | ||
| mechanism: { | ||
| type: 'onuncaughtexception', | ||
| handled: false | ||
| } | ||
| }); | ||
| if (!calledFatalError && processWouldExit) { | ||
| captureUncaughtException(captureFn, error, origin); | ||
| if (!calledFatalError && 0 === userProvidedListenersCount) { | ||
| calledFatalError = true; | ||
@@ -50,7 +100,13 @@ onFatalFn(error); | ||
| } | ||
| function addUncaughtExceptionListener(captureFn, onFatalFn) { | ||
| globalThis.process?.on('uncaughtException', makeUncaughtExceptionHandler(captureFn, onFatalFn)); | ||
| function addUncaughtExceptionListener(captureFn, onFatalFn, mode = STARTUP_UNHANDLED_REJECTION_MODE) { | ||
| const process = globalThis.process; | ||
| if (!process) return; | ||
| if ('strict' === mode) return void process.on('uncaughtExceptionMonitor', (error, origin)=>captureUncaughtException(captureFn, error, origin)); | ||
| process.on('uncaughtException', makeUncaughtExceptionHandler(captureFn, onFatalFn)); | ||
| } | ||
| function addUnhandledRejectionListener(captureFn) { | ||
| globalThis.process?.on('unhandledRejection', (reason)=>captureFn(reason, { | ||
| function addUnhandledRejectionListener(captureFn, mode = STARTUP_UNHANDLED_REJECTION_MODE) { | ||
| const process = globalThis.process; | ||
| if (!process || 'throw' === mode || 'strict' === mode || 'warn-with-error-code' === mode) return; | ||
| process.on('unhandledRejection', (reason)=>{ | ||
| captureFn(reason, { | ||
| mechanism: { | ||
@@ -60,9 +116,12 @@ type: 'onunhandledrejection', | ||
| } | ||
| })); | ||
| }); | ||
| }); | ||
| } | ||
| exports.addUncaughtExceptionListener = __webpack_exports__.addUncaughtExceptionListener; | ||
| exports.addUnhandledRejectionListener = __webpack_exports__.addUnhandledRejectionListener; | ||
| exports.getUnhandledRejectionMode = __webpack_exports__.getUnhandledRejectionMode; | ||
| for(var __webpack_i__ in __webpack_exports__)if (-1 === [ | ||
| "addUncaughtExceptionListener", | ||
| "addUnhandledRejectionListener" | ||
| "addUnhandledRejectionListener", | ||
| "getUnhandledRejectionMode" | ||
| ].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__]; | ||
@@ -69,0 +128,0 @@ Object.defineProperty(exports, '__esModule', { |
@@ -0,13 +1,62 @@ | ||
| const UNHANDLED_REJECTION_OPTION_NAMES = [ | ||
| '--unhandled-rejections', | ||
| '--unhandled_rejections' | ||
| ]; | ||
| const UNHANDLED_REJECTION_MODES = new Set([ | ||
| 'throw', | ||
| 'strict', | ||
| 'warn', | ||
| 'warn-with-error-code', | ||
| 'none' | ||
| ]); | ||
| const STARTUP_EXEC_ARGV = [ | ||
| ...globalThis.process?.execArgv ?? [] | ||
| ]; | ||
| const STARTUP_NODE_OPTIONS = globalThis.process?.env?.NODE_OPTIONS; | ||
| function splitNodeOptions(nodeOptions) { | ||
| const args = []; | ||
| let current = ''; | ||
| let isInString = false; | ||
| for(let index = 0; index < nodeOptions.length; index++){ | ||
| const character = nodeOptions[index]; | ||
| if ('\\' === character && isInString && index + 1 < nodeOptions.length) current += nodeOptions[++index]; | ||
| else if (' ' !== character || isInString) if ('"' === character) isInString = !isInString; | ||
| else current += character; | ||
| else if (current) { | ||
| args.push(current); | ||
| current = ''; | ||
| } | ||
| } | ||
| if (current) args.push(current); | ||
| return args; | ||
| } | ||
| function findUnhandledRejectionMode(args) { | ||
| let mode; | ||
| for(let index = 0; index < args.length; index++){ | ||
| const argument = args[index]; | ||
| const optionName = UNHANDLED_REJECTION_OPTION_NAMES.find((name)=>argument === name || argument.startsWith(`${name}=`)); | ||
| if (!optionName) continue; | ||
| const value = argument === optionName ? args[++index] : argument.slice(optionName.length + 1); | ||
| if (UNHANDLED_REJECTION_MODES.has(value)) mode = value; | ||
| } | ||
| return mode; | ||
| } | ||
| function getUnhandledRejectionMode(execArgv = STARTUP_EXEC_ARGV, nodeOptions = STARTUP_NODE_OPTIONS) { | ||
| return findUnhandledRejectionMode(execArgv) ?? findUnhandledRejectionMode(splitNodeOptions(nodeOptions ?? '')) ?? 'throw'; | ||
| } | ||
| const STARTUP_UNHANDLED_REJECTION_MODE = getUnhandledRejectionMode(); | ||
| function captureUncaughtException(captureFn, error, origin) { | ||
| captureFn(error, { | ||
| mechanism: { | ||
| type: 'unhandledRejection' === origin ? 'onunhandledrejection' : 'onuncaughtexception', | ||
| handled: false | ||
| } | ||
| }); | ||
| } | ||
| function makeUncaughtExceptionHandler(captureFn, onFatalFn) { | ||
| let calledFatalError = false; | ||
| return Object.assign((error)=>{ | ||
| return Object.assign((error, origin)=>{ | ||
| const userProvidedListenersCount = global.process.listeners('uncaughtException').filter((listener)=>'domainUncaughtExceptionClear' !== listener.name && true !== listener._posthogErrorHandler).length; | ||
| const processWouldExit = 0 === userProvidedListenersCount; | ||
| captureFn(error, { | ||
| mechanism: { | ||
| type: 'onuncaughtexception', | ||
| handled: false | ||
| } | ||
| }); | ||
| if (!calledFatalError && processWouldExit) { | ||
| captureUncaughtException(captureFn, error, origin); | ||
| if (!calledFatalError && 0 === userProvidedListenersCount) { | ||
| calledFatalError = true; | ||
@@ -20,7 +69,13 @@ onFatalFn(error); | ||
| } | ||
| function addUncaughtExceptionListener(captureFn, onFatalFn) { | ||
| globalThis.process?.on('uncaughtException', makeUncaughtExceptionHandler(captureFn, onFatalFn)); | ||
| function addUncaughtExceptionListener(captureFn, onFatalFn, mode = STARTUP_UNHANDLED_REJECTION_MODE) { | ||
| const process = globalThis.process; | ||
| if (!process) return; | ||
| if ('strict' === mode) return void process.on('uncaughtExceptionMonitor', (error, origin)=>captureUncaughtException(captureFn, error, origin)); | ||
| process.on('uncaughtException', makeUncaughtExceptionHandler(captureFn, onFatalFn)); | ||
| } | ||
| function addUnhandledRejectionListener(captureFn) { | ||
| globalThis.process?.on('unhandledRejection', (reason)=>captureFn(reason, { | ||
| function addUnhandledRejectionListener(captureFn, mode = STARTUP_UNHANDLED_REJECTION_MODE) { | ||
| const process = globalThis.process; | ||
| if (!process || 'throw' === mode || 'strict' === mode || 'warn-with-error-code' === mode) return; | ||
| process.on('unhandledRejection', (reason)=>{ | ||
| captureFn(reason, { | ||
| mechanism: { | ||
@@ -30,4 +85,5 @@ type: 'onunhandledrejection', | ||
| } | ||
| })); | ||
| }); | ||
| }); | ||
| } | ||
| export { addUncaughtExceptionListener, addUnhandledRejectionListener }; | ||
| export { addUncaughtExceptionListener, addUnhandledRejectionListener, getUnhandledRejectionMode }; |
@@ -1,2 +0,2 @@ | ||
| export declare const version = "5.47.8"; | ||
| export declare const version = "5.47.9"; | ||
| //# sourceMappingURL=version.d.ts.map |
+1
-1
@@ -29,3 +29,3 @@ "use strict"; | ||
| }); | ||
| const version = '5.47.8'; | ||
| const version = '5.47.9'; | ||
| exports.version = __webpack_exports__.version; | ||
@@ -32,0 +32,0 @@ for(var __webpack_i__ in __webpack_exports__)if (-1 === [ |
+1
-1
@@ -1,2 +0,2 @@ | ||
| const version = '5.47.8'; | ||
| const version = '5.47.9'; | ||
| export { version }; |
+1
-1
| { | ||
| "name": "posthog-node", | ||
| "version": "5.47.8", | ||
| "version": "5.47.9", | ||
| "bugs": { | ||
@@ -5,0 +5,0 @@ "url": "https://github.com/PostHog/posthog-js/issues" |
@@ -7,4 +7,91 @@ // Portions of this file are derived from getsentry/sentry-javascript | ||
| type ErrorHandler = { _posthogErrorHandler: boolean } & ((error: Error) => void) | ||
| type ErrorHandler = { _posthogErrorHandler: boolean } & NodeJS.UncaughtExceptionListener | ||
| type UnhandledRejectionMode = 'throw' | 'strict' | 'warn' | 'warn-with-error-code' | 'none' | ||
| const UNHANDLED_REJECTION_OPTION_NAMES = ['--unhandled-rejections', '--unhandled_rejections'] | ||
| const UNHANDLED_REJECTION_MODES = new Set<UnhandledRejectionMode>([ | ||
| 'throw', | ||
| 'strict', | ||
| 'warn', | ||
| 'warn-with-error-code', | ||
| 'none', | ||
| ]) | ||
| const STARTUP_EXEC_ARGV = [...(globalThis.process?.execArgv ?? [])] | ||
| const STARTUP_NODE_OPTIONS = globalThis.process?.env?.NODE_OPTIONS | ||
| function splitNodeOptions(nodeOptions: string): string[] { | ||
| const args: string[] = [] | ||
| let current = '' | ||
| let isInString = false | ||
| for (let index = 0; index < nodeOptions.length; index++) { | ||
| const character = nodeOptions[index] | ||
| if (character === '\\' && isInString && index + 1 < nodeOptions.length) { | ||
| current += nodeOptions[++index] | ||
| } else if (character === ' ' && !isInString) { | ||
| if (current) { | ||
| args.push(current) | ||
| current = '' | ||
| } | ||
| } else if (character === '"') { | ||
| isInString = !isInString | ||
| } else { | ||
| current += character | ||
| } | ||
| } | ||
| if (current) { | ||
| args.push(current) | ||
| } | ||
| return args | ||
| } | ||
| function findUnhandledRejectionMode(args: string[]): UnhandledRejectionMode | undefined { | ||
| let mode: UnhandledRejectionMode | undefined | ||
| for (let index = 0; index < args.length; index++) { | ||
| const argument = args[index] | ||
| const optionName = UNHANDLED_REJECTION_OPTION_NAMES.find( | ||
| (name) => argument === name || argument.startsWith(`${name}=`) | ||
| ) | ||
| if (!optionName) { | ||
| continue | ||
| } | ||
| const value = argument === optionName ? args[++index] : argument.slice(optionName.length + 1) | ||
| if (UNHANDLED_REJECTION_MODES.has(value as UnhandledRejectionMode)) { | ||
| mode = value as UnhandledRejectionMode | ||
| } | ||
| } | ||
| return mode | ||
| } | ||
| export function getUnhandledRejectionMode( | ||
| execArgv: string[] = STARTUP_EXEC_ARGV, | ||
| nodeOptions: string | undefined = STARTUP_NODE_OPTIONS | ||
| ): UnhandledRejectionMode { | ||
| // Command-line arguments take precedence over NODE_OPTIONS, and the last occurrence wins within each source. | ||
| return ( | ||
| findUnhandledRejectionMode(execArgv) ?? findUnhandledRejectionMode(splitNodeOptions(nodeOptions ?? '')) ?? 'throw' | ||
| ) | ||
| } | ||
| const STARTUP_UNHANDLED_REJECTION_MODE = getUnhandledRejectionMode() | ||
| function captureUncaughtException( | ||
| captureFn: (exception: Error, hint: CoreErrorTracking.EventHint) => void, | ||
| error: Error, | ||
| origin: NodeJS.UncaughtExceptionOrigin | ||
| ): void { | ||
| captureFn(error, { | ||
| mechanism: { | ||
| type: origin === 'unhandledRejection' ? 'onunhandledrejection' : 'onuncaughtexception', | ||
| handled: false, | ||
| }, | ||
| }) | ||
| } | ||
| function makeUncaughtExceptionHandler( | ||
@@ -17,28 +104,15 @@ captureFn: (exception: Error, hint: CoreErrorTracking.EventHint) => void, | ||
| return Object.assign( | ||
| (error: Error): void => { | ||
| // 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. | ||
| (error: Error, origin: NodeJS.UncaughtExceptionOrigin): void => { | ||
| // Like Sentry, we only consider listeners that are currently registered. We intentionally do not reconstruct | ||
| // participation by once listeners or listeners added or removed during dispatch. | ||
| const userProvidedListenersCount = global.process.listeners('uncaughtException').filter((listener) => { | ||
| // There are 2 listeners we ignore: | ||
| return ( | ||
| // as soon as we're using domains this listener is attached by node itself | ||
| listener.name !== 'domainUncaughtExceptionClear' && | ||
| // the handler we register in this integration | ||
| (listener as ErrorHandler)._posthogErrorHandler !== true | ||
| (listener as Partial<ErrorHandler>)._posthogErrorHandler !== true | ||
| ) | ||
| }).length | ||
| const processWouldExit = userProvidedListenersCount === 0 | ||
| captureUncaughtException(captureFn, error, origin) | ||
| captureFn(error, { | ||
| mechanism: { | ||
| type: 'onuncaughtexception', | ||
| handled: false, | ||
| }, | ||
| }) | ||
| if (!calledFatalError && processWouldExit) { | ||
| if (!calledFatalError && userProvidedListenersCount === 0) { | ||
| calledFatalError = true | ||
@@ -54,12 +128,32 @@ onFatalFn(error) | ||
| captureFn: (exception: Error, hint: CoreErrorTracking.EventHint) => void, | ||
| onFatalFn: (exception: Error) => void | ||
| onFatalFn: (exception: Error) => void, | ||
| mode: UnhandledRejectionMode = STARTUP_UNHANDLED_REJECTION_MODE | ||
| ): void { | ||
| globalThis.process?.on('uncaughtException', makeUncaughtExceptionHandler(captureFn, onFatalFn)) | ||
| const process = globalThis.process | ||
| if (!process) { | ||
| return | ||
| } | ||
| if (mode === 'strict') { | ||
| process.on('uncaughtExceptionMonitor', (error, origin) => captureUncaughtException(captureFn, error, origin)) | ||
| return | ||
| } | ||
| process.on('uncaughtException', makeUncaughtExceptionHandler(captureFn, onFatalFn)) | ||
| } | ||
| export function addUnhandledRejectionListener( | ||
| captureFn: (exception: unknown, hint: CoreErrorTracking.EventHint) => void | ||
| captureFn: (exception: unknown, hint: CoreErrorTracking.EventHint) => void, | ||
| mode: UnhandledRejectionMode = STARTUP_UNHANDLED_REJECTION_MODE | ||
| ): void { | ||
| globalThis.process?.on('unhandledRejection', (reason: unknown) => { | ||
| return captureFn(reason, { | ||
| const process = globalThis.process | ||
| // Installing an unhandledRejection listener changes Node's behavior in these modes. We intentionally skip capture | ||
| // rather than reproducing Node's fatal handling or warn-with-error-code warning and exit-code semantics in the SDK. | ||
| if (!process || mode === 'throw' || mode === 'strict' || mode === 'warn-with-error-code') { | ||
| return | ||
| } | ||
| process.on('unhandledRejection', (reason: unknown) => { | ||
| captureFn(reason, { | ||
| mechanism: { | ||
@@ -66,0 +160,0 @@ type: 'onunhandledrejection', |
+1
-1
@@ -1,1 +0,1 @@ | ||
| export const version = '5.47.8' | ||
| export const version = '5.47.9' |
802119
1.05%17712
1.12%