@statsig/statsig-node-core 0.12.1 → 0.12.2-beta.2511192113

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Utility functions for console capture payload parsing and processing
3
+ */
4
+ export declare function safeStringify(val: unknown, maxKeysCount: number, maxDepth: number, maxLength: number): string;
5
+ export declare function shouldNotStringify(val: object, maxKeysCount: number, maxDepth: number): boolean;
6
+ export declare function isPlainObject(obj: unknown): boolean;
7
+ export declare function isObjectTooDeep(obj: unknown, maxDepth: number, seen?: WeakSet<object>): boolean;
8
+ export declare function getStackTrace(): string | null;
9
+ export declare function truncateString(str: string, maxLength: number): string;
10
+ export declare function simpleStringify(val: object, maxLength: number): string;
11
+ export declare function wrapFunctionWithRestore(targetObject: Record<string, unknown>, functionName: string, wrapperFactory: (original: (...args: unknown[]) => unknown) => (...args: unknown[]) => unknown): () => void;
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ /**
3
+ * Utility functions for console capture payload parsing and processing
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.safeStringify = safeStringify;
7
+ exports.shouldNotStringify = shouldNotStringify;
8
+ exports.isPlainObject = isPlainObject;
9
+ exports.isObjectTooDeep = isObjectTooDeep;
10
+ exports.getStackTrace = getStackTrace;
11
+ exports.truncateString = truncateString;
12
+ exports.simpleStringify = simpleStringify;
13
+ exports.wrapFunctionWithRestore = wrapFunctionWithRestore;
14
+ function safeStringify(val, maxKeysCount, maxDepth, maxLength) {
15
+ try {
16
+ if (shouldNotStringify(val, maxKeysCount, maxDepth)) {
17
+ return simpleStringify(val, maxLength);
18
+ }
19
+ if (typeof val === 'string') {
20
+ return truncateString(val, maxLength);
21
+ }
22
+ if (typeof val === 'object' && val !== null) {
23
+ return truncateString(JSON.stringify(val), maxLength);
24
+ }
25
+ return truncateString(String(val), maxLength);
26
+ }
27
+ catch (_a) {
28
+ return truncateString('[Unserializable]', maxLength);
29
+ }
30
+ }
31
+ function shouldNotStringify(val, maxKeysCount, maxDepth) {
32
+ if (isPlainObject(val)) {
33
+ if (Object.keys(val).length > maxKeysCount) {
34
+ return true;
35
+ }
36
+ if (isObjectTooDeep(val, maxDepth)) {
37
+ return true;
38
+ }
39
+ return false;
40
+ }
41
+ if (typeof val === 'function') {
42
+ return true;
43
+ }
44
+ return false;
45
+ }
46
+ function isPlainObject(obj) {
47
+ return Object.prototype.toString.call(obj) === '[object Object]';
48
+ }
49
+ function isObjectTooDeep(obj, maxDepth, seen = new WeakSet()) {
50
+ if (maxDepth <= 0) {
51
+ return true;
52
+ }
53
+ if (typeof obj !== 'object' || obj === null) {
54
+ return false; // primitives are never "too deep"
55
+ }
56
+ if (seen.has(obj)) {
57
+ return false; // cycle detected
58
+ }
59
+ seen.add(obj);
60
+ return Object.values(obj).some((value) => isObjectTooDeep(value, maxDepth - 1, seen));
61
+ }
62
+ function getStackTrace() {
63
+ const stack = new Error().stack;
64
+ return stack ? stack.split('\n').slice(2, 5).join('\n') : null;
65
+ }
66
+ function truncateString(str, maxLength) {
67
+ if (str.length <= maxLength) {
68
+ return str;
69
+ }
70
+ return str.slice(0, maxLength) + '...';
71
+ }
72
+ function simpleStringify(val, maxLength) {
73
+ return truncateString(val.toString(), maxLength);
74
+ }
75
+ function wrapFunctionWithRestore(targetObject, functionName, wrapperFactory) {
76
+ const originalFunction = targetObject[functionName];
77
+ if (typeof originalFunction !== 'function') {
78
+ return () => {
79
+ // noop
80
+ };
81
+ }
82
+ try {
83
+ const wrappedFunction = wrapperFactory(originalFunction);
84
+ Object.defineProperty(wrappedFunction, '__statsig_original__', {
85
+ enumerable: false,
86
+ value: originalFunction,
87
+ });
88
+ targetObject[functionName] = wrappedFunction;
89
+ // Restore function
90
+ return () => {
91
+ targetObject[functionName] = originalFunction;
92
+ };
93
+ }
94
+ catch (_a) {
95
+ return () => {
96
+ // noop
97
+ };
98
+ }
99
+ }
@@ -0,0 +1,3 @@
1
+ import { ConsoleCaptureOptions } from './statsig-generated';
2
+ export declare function startStatsigConsoleCapture(sdkKey: string, options?: ConsoleCaptureOptions): void;
3
+ export declare function stopStatsigConsoleCapture(): void;
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.startStatsigConsoleCapture = startStatsigConsoleCapture;
4
+ exports.stopStatsigConsoleCapture = stopStatsigConsoleCapture;
5
+ const console_captrue_utils_1 = require("./console_captrue_utils");
6
+ const _1 = require(".");
7
+ const CAPTURE_LEVELS = [
8
+ 'log',
9
+ 'warn',
10
+ 'error',
11
+ 'debug',
12
+ 'info',
13
+ 'trace',
14
+ ];
15
+ const originalConsoleFns = {};
16
+ const originalConsoleErrorFn = typeof console !== 'undefined' ? console.error : undefined;
17
+ const MAX_KEYS = 100;
18
+ const MAX_DEPTH = 10;
19
+ const MAX_LENGTH = 4096;
20
+ function captureLog(level, args, sdkKey, maxKeys, maxDepth, maxLength) {
21
+ const message = args.map((a) => (0, console_captrue_utils_1.safeStringify)(a, maxKeys, maxDepth, maxLength));
22
+ if (level.toLowerCase() === 'error' &&
23
+ typeof args[0] === 'string' &&
24
+ args[0].startsWith('Trace')) {
25
+ level = 'trace'; // node use error for console.trace
26
+ }
27
+ const stackTrace = (0, console_captrue_utils_1.getStackTrace)();
28
+ (0, _1.statsigCaptureLogLine)(level, message, sdkKey, stackTrace);
29
+ }
30
+ function startStatsigConsoleCapture(sdkKey, options) {
31
+ stopStatsigConsoleCapture();
32
+ for (const level of CAPTURE_LEVELS) {
33
+ const originalFn = console[level];
34
+ if (!originalFn || typeof originalFn !== 'function') {
35
+ continue;
36
+ }
37
+ let isCapturing = false;
38
+ const restoreFn = (0, console_captrue_utils_1.wrapFunctionWithRestore)(console, level, (originalFn) => {
39
+ return (...args) => {
40
+ var _a, _b, _c;
41
+ originalFn(...args);
42
+ if (isCapturing)
43
+ return;
44
+ isCapturing = true;
45
+ const maxKeys = Math.min((_a = options === null || options === void 0 ? void 0 : options.maxKeys) !== null && _a !== void 0 ? _a : MAX_KEYS, MAX_KEYS);
46
+ const maxDepth = Math.min((_b = options === null || options === void 0 ? void 0 : options.maxDepth) !== null && _b !== void 0 ? _b : MAX_DEPTH, MAX_DEPTH);
47
+ const maxLength = Math.min((_c = options === null || options === void 0 ? void 0 : options.maxLength) !== null && _c !== void 0 ? _c : MAX_LENGTH, MAX_LENGTH);
48
+ try {
49
+ captureLog(level, args, sdkKey, maxKeys, maxDepth, maxLength);
50
+ }
51
+ catch (err) {
52
+ if (originalConsoleErrorFn &&
53
+ typeof originalConsoleErrorFn === 'function') {
54
+ originalConsoleErrorFn('Statsig log capture failed:', err);
55
+ }
56
+ }
57
+ finally {
58
+ isCapturing = false;
59
+ }
60
+ };
61
+ });
62
+ originalConsoleFns[level] = restoreFn;
63
+ }
64
+ }
65
+ function stopStatsigConsoleCapture() {
66
+ for (const level of CAPTURE_LEVELS) {
67
+ const restoreFn = originalConsoleFns[level];
68
+ if (restoreFn && typeof restoreFn === 'function') {
69
+ restoreFn();
70
+ delete originalConsoleFns[level];
71
+ }
72
+ }
73
+ for (const key of Object.keys(originalConsoleFns)) {
74
+ delete originalConsoleFns[key];
75
+ }
76
+ }
package/error_boundary.js CHANGED
@@ -35,7 +35,7 @@ class ErrorBoundary {
35
35
  }
36
36
  static _onError(tag, error) {
37
37
  _tryConvertInvalidArgError(error);
38
- console.error(tag, error);
38
+ console.error('Statsig::' + tag, error);
39
39
  }
40
40
  }
41
41
  exports.ErrorBoundary = ErrorBoundary;
package/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { DynamicConfig, Experiment, Layer, ParameterStore, StatsigNapiInternal, StatsigOptions, StatsigUser } from './statsig-generated';
1
+ import { DynamicConfig, Experiment, Layer, ParameterStore, StatsigNapiInternal, StatsigOptions, StatsigResult, StatsigUser } from './statsig-generated';
2
2
  export * from './statsig-generated';
3
3
  export { StatsigUser, Experiment, DynamicConfig, Layer, ParameterStore };
4
4
  export declare class Statsig extends StatsigNapiInternal {
@@ -9,4 +9,6 @@ export declare class Statsig extends StatsigNapiInternal {
9
9
  static removeSharedInstance(): void;
10
10
  private static _createErrorInstance;
11
11
  constructor(sdkKey: string, options?: StatsigOptions);
12
+ stopConsoleCapture(): void;
13
+ shutdown(timeout_ms?: number): Promise<StatsigResult>;
12
14
  }
package/index.js CHANGED
@@ -27,30 +27,36 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
29
  exports.Statsig = exports.ParameterStore = exports.Layer = exports.DynamicConfig = exports.Experiment = exports.StatsigUser = void 0;
30
- // @ts-nocheck
30
+ const console_capture_1 = require("./console_capture");
31
+ const https_proxy_agent_1 = require("https-proxy-agent");
32
+ const node_fetch_1 = __importDefault(require("node-fetch"));
33
+ const error_boundary_1 = require("./error_boundary");
31
34
  const statsig_generated_1 = require("./statsig-generated");
32
35
  Object.defineProperty(exports, "DynamicConfig", { enumerable: true, get: function () { return statsig_generated_1.DynamicConfig; } });
33
36
  Object.defineProperty(exports, "Experiment", { enumerable: true, get: function () { return statsig_generated_1.Experiment; } });
34
37
  Object.defineProperty(exports, "Layer", { enumerable: true, get: function () { return statsig_generated_1.Layer; } });
35
38
  Object.defineProperty(exports, "ParameterStore", { enumerable: true, get: function () { return statsig_generated_1.ParameterStore; } });
36
39
  Object.defineProperty(exports, "StatsigUser", { enumerable: true, get: function () { return statsig_generated_1.StatsigUser; } });
37
- const error_boundary_1 = require("./error_boundary");
38
- const https_proxy_agent_1 = require("https-proxy-agent");
39
- const node_fetch_1 = __importDefault(require("node-fetch"));
40
40
  __exportStar(require("./statsig-generated"), exports);
41
- statsig_generated_1.StatsigUser.prototype[Symbol.for('nodejs.util.inspect.custom')] = function () {
41
+ const inspectSym = Symbol.for('nodejs.util.inspect.custom');
42
+ // @ts-expect-error - prototype assignment
43
+ statsig_generated_1.StatsigUser.prototype[inspectSym] = function () {
42
44
  return this.toJSON();
43
45
  };
44
- statsig_generated_1.Experiment.prototype[Symbol.for('nodejs.util.inspect.custom')] = function () {
46
+ // @ts-expect-error - prototype assignment
47
+ statsig_generated_1.Experiment.prototype[inspectSym] = function () {
45
48
  return this.toJSON();
46
49
  };
47
- statsig_generated_1.DynamicConfig.prototype[Symbol.for('nodejs.util.inspect.custom')] = function () {
50
+ // @ts-expect-error - prototype assignment
51
+ statsig_generated_1.DynamicConfig.prototype[inspectSym] = function () {
48
52
  return this.toJSON();
49
53
  };
50
- statsig_generated_1.Layer.prototype[Symbol.for('nodejs.util.inspect.custom')] = function () {
54
+ // @ts-expect-error - prototype assignment
55
+ statsig_generated_1.Layer.prototype[inspectSym] = function () {
51
56
  return this.toJSON();
52
57
  };
53
- statsig_generated_1.ParameterStore.prototype[Symbol.for('nodejs.util.inspect.custom')] = function () {
58
+ // @ts-expect-error - prototype assignment
59
+ statsig_generated_1.ParameterStore.prototype[inspectSym] = function () {
54
60
  return this.toJSON();
55
61
  };
56
62
  function createProxyAgent(options) {
@@ -78,9 +84,11 @@ function createFetchFunc(options) {
78
84
  agent: proxyAgent,
79
85
  });
80
86
  const data = yield res.arrayBuffer();
87
+ const resHeaders = Object.fromEntries(res.headers.entries());
81
88
  return {
82
89
  status: res.status,
83
90
  data: Array.from(new Uint8Array(data)),
91
+ headers: resHeaders,
84
92
  };
85
93
  }
86
94
  catch (err) {
@@ -120,9 +128,25 @@ class Statsig extends statsig_generated_1.StatsigNapiInternal {
120
128
  return dummyInstance;
121
129
  }
122
130
  constructor(sdkKey, options) {
131
+ var _a;
123
132
  const fetchFunc = createFetchFunc(options);
124
133
  super(fetchFunc, sdkKey, options);
125
134
  error_boundary_1.ErrorBoundary.wrap(this);
135
+ if ((_a = options === null || options === void 0 ? void 0 : options.consoleCaptureOptions) === null || _a === void 0 ? void 0 : _a.enabled) {
136
+ (0, console_capture_1.startStatsigConsoleCapture)(sdkKey, options.consoleCaptureOptions);
137
+ }
138
+ }
139
+ stopConsoleCapture() {
140
+ (0, console_capture_1.stopStatsigConsoleCapture)();
141
+ }
142
+ shutdown(timeout_ms) {
143
+ const _super = Object.create(null, {
144
+ shutdown: { get: () => super.shutdown }
145
+ });
146
+ return __awaiter(this, void 0, void 0, function* () {
147
+ (0, console_capture_1.stopStatsigConsoleCapture)();
148
+ return _super.shutdown.call(this, timeout_ms);
149
+ });
126
150
  }
127
151
  }
128
152
  exports.Statsig = Statsig;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@statsig/statsig-node-core",
3
- "version": "0.12.1",
3
+ "version": "0.12.2-beta.2511192113",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "test": "jest --colors"
@@ -45,6 +45,7 @@
45
45
  "aarch64-apple-darwin",
46
46
  "aarch64-unknown-linux-gnu",
47
47
  "aarch64-unknown-linux-musl",
48
+ "aarch64-pc-windows-msvc",
48
49
  "i686-pc-windows-msvc",
49
50
  "x86_64-apple-darwin",
50
51
  "x86_64-pc-windows-msvc",
@@ -53,14 +54,13 @@
53
54
  ]
54
55
  },
55
56
  "optionalDependencies": {
56
- "@statsig/statsig-node-core-linux-x64-musl": "0.12.1",
57
- "@statsig/statsig-node-core-linux-x64-gnu": "0.12.1",
58
- "@statsig/statsig-node-core-win32-x64-msvc": "0.12.1",
59
- "@statsig/statsig-node-core-darwin-x64": "0.12.1",
60
- "@statsig/statsig-node-core-win32-ia32-msvc": "0.12.1",
61
- "@statsig/statsig-node-core-linux-arm64-musl": "0.12.1",
62
- "@statsig/statsig-node-core-linux-arm64-gnu": "0.12.1",
63
- "@statsig/statsig-node-core-win32-arm64-msvc": "0.12.1",
64
- "@statsig/statsig-node-core-darwin-arm64": "0.12.1"
57
+ "@statsig/statsig-node-core-linux-x64-musl": "0.12.2-beta.2511192113",
58
+ "@statsig/statsig-node-core-linux-x64-gnu": "0.12.2-beta.2511192113",
59
+ "@statsig/statsig-node-core-win32-x64-msvc": "0.12.2-beta.2511192113",
60
+ "@statsig/statsig-node-core-darwin-x64": "0.12.2-beta.2511192113",
61
+ "@statsig/statsig-node-core-win32-ia32-msvc": "0.12.2-beta.2511192113",
62
+ "@statsig/statsig-node-core-linux-arm64-musl": "0.12.2-beta.2511192113",
63
+ "@statsig/statsig-node-core-linux-arm64-gnu": "0.12.2-beta.2511192113",
64
+ "@statsig/statsig-node-core-darwin-arm64": "0.12.2-beta.2511192113"
65
65
  }
66
66
  }
@@ -52,6 +52,7 @@ export declare class ParameterStore {
52
52
  name: string
53
53
  getValue<T>(paramName: string, fallback?: T): T
54
54
  getEvaluationDetails(): EvaluationDetails
55
+ toJSON(): Record<string, any>
55
56
  }
56
57
 
57
58
  export declare class StatsigNapiInternal {
@@ -149,6 +150,15 @@ export interface ClientInitResponseOptions {
149
150
  removeDefaultValueGates?: boolean
150
151
  }
151
152
 
153
+ export interface ConsoleCaptureOptions {
154
+ enabled: boolean
155
+ logLevels?: Array<'trace' | 'debug' | 'log' | 'info' | 'warn' | 'error'>
156
+ user?: StatsigUser
157
+ maxKeys?: number
158
+ maxDepth?: number
159
+ maxLength?: number
160
+ }
161
+
152
162
  export interface DataStore {
153
163
  initialize?: () => Promise<void>
154
164
  shutdown?: () => Promise<void>
@@ -198,6 +208,7 @@ export interface LayerEvaluationOptions {
198
208
  export interface NapiNetworkFuncResult {
199
209
  status: number
200
210
  data?: Array<number>
211
+ headers?: Record<string, string>
201
212
  error?: string
202
213
  }
203
214
 
@@ -260,6 +271,8 @@ export interface SpecAdapterConfig {
260
271
  domainName?: string
261
272
  }
262
273
 
274
+ export declare function statsigCaptureLogLine(level: string, payload: Array<string>, sdkKey: string, stackTrace?: string | undefined | null): void
275
+
263
276
  export interface StatsigOptions {
264
277
  dataStore?: DataStore
265
278
  disableAllLogging?: boolean
@@ -288,7 +301,9 @@ export interface StatsigOptions {
288
301
  waitForCountryLookupInit?: boolean
289
302
  waitForUserAgentInit?: boolean
290
303
  proxyConfig?: ProxyConfig
304
+ consoleCaptureOptions?: ConsoleCaptureOptions
291
305
  useThirdPartyUaParser?: boolean
306
+ experimentalFlags?: Set<string>
292
307
  }
293
308
 
294
309
  export interface StatsigResult {
@@ -375,3 +375,4 @@ module.exports.__internal__testObservabilityClient = nativeBinding.__internal__t
375
375
  module.exports.__internal__testOutputLogger = nativeBinding.__internal__testOutputLogger
376
376
  module.exports.__internal__testPersistentStorage = nativeBinding.__internal__testPersistentStorage
377
377
  module.exports.OverrideAdapterType = nativeBinding.OverrideAdapterType
378
+ module.exports.statsigCaptureLogLine = nativeBinding.statsigCaptureLogLine