@posthog/core 1.48.5 → 1.48.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/error-tracking/coercers/error-event-coercer.d.ts.map +1 -1
- package/dist/error-tracking/coercers/error-event-coercer.js +5 -5
- package/dist/error-tracking/coercers/error-event-coercer.mjs +5 -5
- package/dist/logs/index.js +1 -1
- package/dist/logs/index.mjs +1 -1
- package/dist/logs/logs-utils.d.ts +4 -13
- package/dist/logs/logs-utils.d.ts.map +1 -1
- package/dist/logs/logs-utils.js +160 -26
- package/dist/logs/logs-utils.mjs +160 -26
- package/dist/metrics/index.js +1 -1
- package/dist/metrics/index.mjs +1 -1
- package/dist/posthog-core.d.ts.map +1 -1
- package/dist/posthog-core.js +1 -0
- package/dist/posthog-core.mjs +1 -0
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +14 -24
- package/dist/utils/index.mjs +2 -2
- package/dist/utils/json-utils.d.ts +15 -0
- package/dist/utils/json-utils.d.ts.map +1 -1
- package/dist/utils/json-utils.js +24 -0
- package/dist/utils/json-utils.mjs +1 -1
- package/package.json +2 -2
- package/src/error-tracking/coercers/error-event-coercer.spec.ts +33 -0
- package/src/error-tracking/coercers/error-event-coercer.ts +13 -6
- package/src/logs/index.ts +1 -1
- package/src/logs/logs-utils.spec.ts +314 -9
- package/src/logs/logs-utils.ts +215 -27
- package/src/metrics/index.spec.ts +15 -1
- package/src/metrics/index.ts +1 -1
- package/src/posthog-core.ts +1 -0
- package/src/utils/index.ts +3 -1
- package/src/utils/json-utils.ts +15 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAEpC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,yBAAyB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAEpC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,yBAAyB,CAAA;AAGvC,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAC9C,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,cAAc,CAAA;AAC5B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,UAAU,CAAA;AACxB,cAAc,oBAAoB,CAAA;AAElC,eAAO,MAAM,aAAa,SAAS,CAAA;AAEnC,eAAO,MAAM,UAAU,QAAoE,CAAA;AAE3F,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAE3D;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,MAAM,GAAG,MAAM,CAE9E;AAED,wBAAgB,MAAM,CAAC,WAAW,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAI9D;AASD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,GAAG,SAAS,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,SAAS,MAAM,GAAG,MAAM,GAAG,SAAS,CAMxG;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAA;CACtC;AAED,wBAAsB,SAAS,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,CAAC,CAAC,CAqB5F;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,GAAG,CAOnE;AAED,wBAAsB,eAAe,CAAC,CAAC,EACrC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,SAAS,EAAE,MAAM,EACjB,SAAS,CAAC,EAAE,MAAM,IAAI,GACrB,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAoBnB;AAGD,eAAO,MAAM,SAAS,GAAI,KAAK,GAAG,KAAG,GAAG,IAAI,OAAO,CAAC,GAAG,CAEtD,CAAA;AAED,wBAAgB,QAAQ,IAAI,SAAS,GAAG,SAAS,CAEhD;AAED,wBAAgB,UAAU,CAAC,CAAC,EAC1B,QAAQ,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,GAC1C,OAAO,CAAC,CAAC;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAE,CAAC,EAAE,CAAC,CAStF"}
|
package/dist/utils/index.js
CHANGED
|
@@ -89,13 +89,15 @@ var __webpack_exports__ = {};
|
|
|
89
89
|
removeTrailingSlash: ()=>removeTrailingSlash,
|
|
90
90
|
retriable: ()=>retriable,
|
|
91
91
|
safeSetTimeout: ()=>safeSetTimeout,
|
|
92
|
-
stripUrlHash: ()=>stripUrlHash
|
|
92
|
+
stripUrlHash: ()=>stripUrlHash,
|
|
93
|
+
toJsonSafeValue: ()=>_json_utils__WEBPACK_IMPORTED_MODULE_3__.toJsonSafeValue
|
|
93
94
|
});
|
|
94
95
|
var _bot_detection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./bot-detection");
|
|
95
96
|
var __WEBPACK_REEXPORT_OBJECT__ = {};
|
|
96
97
|
for(var __WEBPACK_IMPORT_KEY__ in _bot_detection__WEBPACK_IMPORTED_MODULE_0__)if ([
|
|
97
98
|
"removeTrailingSlash",
|
|
98
99
|
"isValidUUID",
|
|
100
|
+
"toJsonSafeValue",
|
|
99
101
|
"currentISOTime",
|
|
100
102
|
"safeSetTimeout",
|
|
101
103
|
"UUID_REGEX",
|
|
@@ -119,6 +121,7 @@ var __webpack_exports__ = {};
|
|
|
119
121
|
for(var __WEBPACK_IMPORT_KEY__ in _browser_utils__WEBPACK_IMPORTED_MODULE_1__)if ([
|
|
120
122
|
"removeTrailingSlash",
|
|
121
123
|
"isValidUUID",
|
|
124
|
+
"toJsonSafeValue",
|
|
122
125
|
"currentISOTime",
|
|
123
126
|
"safeSetTimeout",
|
|
124
127
|
"UUID_REGEX",
|
|
@@ -142,6 +145,7 @@ var __webpack_exports__ = {};
|
|
|
142
145
|
for(var __WEBPACK_IMPORT_KEY__ in _bucketed_rate_limiter__WEBPACK_IMPORTED_MODULE_2__)if ([
|
|
143
146
|
"removeTrailingSlash",
|
|
144
147
|
"isValidUUID",
|
|
148
|
+
"toJsonSafeValue",
|
|
145
149
|
"currentISOTime",
|
|
146
150
|
"safeSetTimeout",
|
|
147
151
|
"UUID_REGEX",
|
|
@@ -161,33 +165,12 @@ var __webpack_exports__ = {};
|
|
|
161
165
|
}).bind(0, __WEBPACK_IMPORT_KEY__);
|
|
162
166
|
__webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__);
|
|
163
167
|
var _json_utils__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__("./json-utils");
|
|
164
|
-
var __WEBPACK_REEXPORT_OBJECT__ = {};
|
|
165
|
-
for(var __WEBPACK_IMPORT_KEY__ in _json_utils__WEBPACK_IMPORTED_MODULE_3__)if ([
|
|
166
|
-
"removeTrailingSlash",
|
|
167
|
-
"isValidUUID",
|
|
168
|
-
"currentISOTime",
|
|
169
|
-
"safeSetTimeout",
|
|
170
|
-
"UUID_REGEX",
|
|
171
|
-
"assert",
|
|
172
|
-
"allSettled",
|
|
173
|
-
"retriable",
|
|
174
|
-
"default",
|
|
175
|
-
"stripUrlHash",
|
|
176
|
-
"STRING_FORMAT",
|
|
177
|
-
"currentTimestamp",
|
|
178
|
-
"raceWithTimeout",
|
|
179
|
-
"getFetch",
|
|
180
|
-
"getEventUuid",
|
|
181
|
-
"isPromise"
|
|
182
|
-
].indexOf(__WEBPACK_IMPORT_KEY__) < 0) __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = (function(key) {
|
|
183
|
-
return _json_utils__WEBPACK_IMPORTED_MODULE_3__[key];
|
|
184
|
-
}).bind(0, __WEBPACK_IMPORT_KEY__);
|
|
185
|
-
__webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__);
|
|
186
168
|
var _number_utils__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__("./number-utils");
|
|
187
169
|
var __WEBPACK_REEXPORT_OBJECT__ = {};
|
|
188
170
|
for(var __WEBPACK_IMPORT_KEY__ in _number_utils__WEBPACK_IMPORTED_MODULE_4__)if ([
|
|
189
171
|
"removeTrailingSlash",
|
|
190
172
|
"isValidUUID",
|
|
173
|
+
"toJsonSafeValue",
|
|
191
174
|
"currentISOTime",
|
|
192
175
|
"safeSetTimeout",
|
|
193
176
|
"UUID_REGEX",
|
|
@@ -211,6 +194,7 @@ var __webpack_exports__ = {};
|
|
|
211
194
|
for(var __WEBPACK_IMPORT_KEY__ in _string_utils__WEBPACK_IMPORTED_MODULE_5__)if ([
|
|
212
195
|
"removeTrailingSlash",
|
|
213
196
|
"isValidUUID",
|
|
197
|
+
"toJsonSafeValue",
|
|
214
198
|
"currentISOTime",
|
|
215
199
|
"safeSetTimeout",
|
|
216
200
|
"UUID_REGEX",
|
|
@@ -234,6 +218,7 @@ var __webpack_exports__ = {};
|
|
|
234
218
|
for(var __WEBPACK_IMPORT_KEY__ in _type_utils__WEBPACK_IMPORTED_MODULE_6__)if ([
|
|
235
219
|
"removeTrailingSlash",
|
|
236
220
|
"isValidUUID",
|
|
221
|
+
"toJsonSafeValue",
|
|
237
222
|
"currentISOTime",
|
|
238
223
|
"safeSetTimeout",
|
|
239
224
|
"UUID_REGEX",
|
|
@@ -257,6 +242,7 @@ var __webpack_exports__ = {};
|
|
|
257
242
|
for(var __WEBPACK_IMPORT_KEY__ in _promise_queue__WEBPACK_IMPORTED_MODULE_7__)if ([
|
|
258
243
|
"removeTrailingSlash",
|
|
259
244
|
"isValidUUID",
|
|
245
|
+
"toJsonSafeValue",
|
|
260
246
|
"currentISOTime",
|
|
261
247
|
"safeSetTimeout",
|
|
262
248
|
"UUID_REGEX",
|
|
@@ -280,6 +266,7 @@ var __webpack_exports__ = {};
|
|
|
280
266
|
for(var __WEBPACK_IMPORT_KEY__ in _logger__WEBPACK_IMPORTED_MODULE_8__)if ([
|
|
281
267
|
"removeTrailingSlash",
|
|
282
268
|
"isValidUUID",
|
|
269
|
+
"toJsonSafeValue",
|
|
283
270
|
"currentISOTime",
|
|
284
271
|
"safeSetTimeout",
|
|
285
272
|
"UUID_REGEX",
|
|
@@ -303,6 +290,7 @@ var __webpack_exports__ = {};
|
|
|
303
290
|
for(var __WEBPACK_IMPORT_KEY__ in _user_agent_utils__WEBPACK_IMPORTED_MODULE_9__)if ([
|
|
304
291
|
"removeTrailingSlash",
|
|
305
292
|
"isValidUUID",
|
|
293
|
+
"toJsonSafeValue",
|
|
306
294
|
"currentISOTime",
|
|
307
295
|
"safeSetTimeout",
|
|
308
296
|
"UUID_REGEX",
|
|
@@ -417,6 +405,7 @@ exports.removeTrailingSlash = __webpack_exports__.removeTrailingSlash;
|
|
|
417
405
|
exports.retriable = __webpack_exports__.retriable;
|
|
418
406
|
exports.safeSetTimeout = __webpack_exports__.safeSetTimeout;
|
|
419
407
|
exports.stripUrlHash = __webpack_exports__.stripUrlHash;
|
|
408
|
+
exports.toJsonSafeValue = __webpack_exports__.toJsonSafeValue;
|
|
420
409
|
for(var __webpack_i__ in __webpack_exports__)if (-1 === [
|
|
421
410
|
"STRING_FORMAT",
|
|
422
411
|
"UUID_REGEX",
|
|
@@ -432,7 +421,8 @@ for(var __webpack_i__ in __webpack_exports__)if (-1 === [
|
|
|
432
421
|
"removeTrailingSlash",
|
|
433
422
|
"retriable",
|
|
434
423
|
"safeSetTimeout",
|
|
435
|
-
"stripUrlHash"
|
|
424
|
+
"stripUrlHash",
|
|
425
|
+
"toJsonSafeValue"
|
|
436
426
|
].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
|
|
437
427
|
Object.defineProperty(exports, '__esModule', {
|
|
438
428
|
value: true
|
package/dist/utils/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { toJsonSafeValue } from "./json-utils.mjs";
|
|
1
2
|
export * from "./bot-detection.mjs";
|
|
2
3
|
export * from "./browser-utils.mjs";
|
|
3
4
|
export * from "./bucketed-rate-limiter.mjs";
|
|
4
|
-
export * from "./json-utils.mjs";
|
|
5
5
|
export * from "./number-utils.mjs";
|
|
6
6
|
export * from "./string-utils.mjs";
|
|
7
7
|
export * from "./type-utils.mjs";
|
|
@@ -88,4 +88,4 @@ function allSettled(promises) {
|
|
|
88
88
|
reason
|
|
89
89
|
}))));
|
|
90
90
|
}
|
|
91
|
-
export { STRING_FORMAT, UUID_REGEX, allSettled, assert, currentISOTime, currentTimestamp, getEventUuid, getFetch, isPromise, isValidUUID, raceWithTimeout, removeTrailingSlash, retriable, safeSetTimeout, stripUrlHash };
|
|
91
|
+
export { STRING_FORMAT, UUID_REGEX, allSettled, assert, currentISOTime, currentTimestamp, getEventUuid, getFetch, isPromise, isValidUUID, raceWithTimeout, removeTrailingSlash, retriable, safeSetTimeout, stripUrlHash, toJsonSafeValue };
|
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
/** Traversal budgets and markers, shared with the OTLP attribute encoder. Not
|
|
2
|
+
* public API — `utils/index.ts` re-exports `toJsonSafeValue` only. */
|
|
3
|
+
export declare const MAX_JSON_SAFE_VALUE_DEPTH = 20;
|
|
4
|
+
export declare const MAX_JSON_SAFE_VALUE_ITEMS = 1000;
|
|
5
|
+
export declare const MAX_JSON_SAFE_VALUE_NODES = 10000;
|
|
6
|
+
export declare const CIRCULAR_VALUE = "[Circular]";
|
|
7
|
+
export declare const TRUNCATED_VALUE = "[Truncated]";
|
|
8
|
+
export declare const UNSERIALIZABLE_VALUE = "[Unserializable]";
|
|
9
|
+
export declare const FUNCTION_VALUE = "[Function]";
|
|
10
|
+
/**
|
|
11
|
+
* Replaces unpaired surrogates with U+FFFD. A lone surrogate survives
|
|
12
|
+
* `JSON.stringify` as a `\uD800`-style escape, which strict JSON parsers
|
|
13
|
+
* reject — for OTLP that means the whole request is refused.
|
|
14
|
+
*/
|
|
15
|
+
export declare function sanitizeString(value: string): string;
|
|
1
16
|
/**
|
|
2
17
|
* Converts an arbitrary value, sanitizing `toJSON` results when available.
|
|
3
18
|
* The limits keep pathological values from causing unbounded recursion or traversal.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json-utils.d.ts","sourceRoot":"","sources":["../../src/utils/json-utils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"json-utils.d.ts","sourceRoot":"","sources":["../../src/utils/json-utils.ts"],"names":[],"mappings":"AAAA;sEACsE;AACtE,eAAO,MAAM,yBAAyB,KAAK,CAAA;AAC3C,eAAO,MAAM,yBAAyB,OAAQ,CAAA;AAC9C,eAAO,MAAM,yBAAyB,QAAS,CAAA;AAC/C,eAAO,MAAM,cAAc,eAAe,CAAA;AAC1C,eAAO,MAAM,eAAe,gBAAgB,CAAA;AAC5C,eAAO,MAAM,oBAAoB,qBAAqB,CAAA;AACtD,eAAO,MAAM,cAAc,eAAe,CAAA;AAW1C;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAiBpD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CA2GvD"}
|
package/dist/utils/json-utils.js
CHANGED
|
@@ -24,6 +24,14 @@ var __webpack_require__ = {};
|
|
|
24
24
|
var __webpack_exports__ = {};
|
|
25
25
|
__webpack_require__.r(__webpack_exports__);
|
|
26
26
|
__webpack_require__.d(__webpack_exports__, {
|
|
27
|
+
CIRCULAR_VALUE: ()=>CIRCULAR_VALUE,
|
|
28
|
+
FUNCTION_VALUE: ()=>FUNCTION_VALUE,
|
|
29
|
+
MAX_JSON_SAFE_VALUE_DEPTH: ()=>MAX_JSON_SAFE_VALUE_DEPTH,
|
|
30
|
+
MAX_JSON_SAFE_VALUE_ITEMS: ()=>MAX_JSON_SAFE_VALUE_ITEMS,
|
|
31
|
+
MAX_JSON_SAFE_VALUE_NODES: ()=>MAX_JSON_SAFE_VALUE_NODES,
|
|
32
|
+
TRUNCATED_VALUE: ()=>TRUNCATED_VALUE,
|
|
33
|
+
UNSERIALIZABLE_VALUE: ()=>UNSERIALIZABLE_VALUE,
|
|
34
|
+
sanitizeString: ()=>sanitizeString,
|
|
27
35
|
toJsonSafeValue: ()=>toJsonSafeValue
|
|
28
36
|
});
|
|
29
37
|
const MAX_JSON_SAFE_VALUE_DEPTH = 20;
|
|
@@ -119,8 +127,24 @@ function toJsonSafeValue(value) {
|
|
|
119
127
|
};
|
|
120
128
|
return convert(value, 0);
|
|
121
129
|
}
|
|
130
|
+
exports.CIRCULAR_VALUE = __webpack_exports__.CIRCULAR_VALUE;
|
|
131
|
+
exports.FUNCTION_VALUE = __webpack_exports__.FUNCTION_VALUE;
|
|
132
|
+
exports.MAX_JSON_SAFE_VALUE_DEPTH = __webpack_exports__.MAX_JSON_SAFE_VALUE_DEPTH;
|
|
133
|
+
exports.MAX_JSON_SAFE_VALUE_ITEMS = __webpack_exports__.MAX_JSON_SAFE_VALUE_ITEMS;
|
|
134
|
+
exports.MAX_JSON_SAFE_VALUE_NODES = __webpack_exports__.MAX_JSON_SAFE_VALUE_NODES;
|
|
135
|
+
exports.TRUNCATED_VALUE = __webpack_exports__.TRUNCATED_VALUE;
|
|
136
|
+
exports.UNSERIALIZABLE_VALUE = __webpack_exports__.UNSERIALIZABLE_VALUE;
|
|
137
|
+
exports.sanitizeString = __webpack_exports__.sanitizeString;
|
|
122
138
|
exports.toJsonSafeValue = __webpack_exports__.toJsonSafeValue;
|
|
123
139
|
for(var __webpack_i__ in __webpack_exports__)if (-1 === [
|
|
140
|
+
"CIRCULAR_VALUE",
|
|
141
|
+
"FUNCTION_VALUE",
|
|
142
|
+
"MAX_JSON_SAFE_VALUE_DEPTH",
|
|
143
|
+
"MAX_JSON_SAFE_VALUE_ITEMS",
|
|
144
|
+
"MAX_JSON_SAFE_VALUE_NODES",
|
|
145
|
+
"TRUNCATED_VALUE",
|
|
146
|
+
"UNSERIALIZABLE_VALUE",
|
|
147
|
+
"sanitizeString",
|
|
124
148
|
"toJsonSafeValue"
|
|
125
149
|
].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
|
|
126
150
|
Object.defineProperty(exports, '__esModule', {
|
|
@@ -91,4 +91,4 @@ function toJsonSafeValue(value) {
|
|
|
91
91
|
};
|
|
92
92
|
return convert(value, 0);
|
|
93
93
|
}
|
|
94
|
-
export { toJsonSafeValue };
|
|
94
|
+
export { CIRCULAR_VALUE, FUNCTION_VALUE, MAX_JSON_SAFE_VALUE_DEPTH, MAX_JSON_SAFE_VALUE_ITEMS, MAX_JSON_SAFE_VALUE_NODES, TRUNCATED_VALUE, UNSERIALIZABLE_VALUE, sanitizeString, toJsonSafeValue };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@posthog/core",
|
|
3
|
-
"version": "1.48.
|
|
3
|
+
"version": "1.48.7",
|
|
4
4
|
"bugs": {
|
|
5
5
|
"url": "https://github.com/PostHog/posthog-js/issues"
|
|
6
6
|
},
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
}
|
|
71
71
|
},
|
|
72
72
|
"dependencies": {
|
|
73
|
-
"@posthog/types": "^1.405.
|
|
73
|
+
"@posthog/types": "^1.405.1"
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
76
|
"@rslib/core": "0.10.6",
|
|
@@ -88,6 +88,39 @@ describe('ErrorEventCoercer', () => {
|
|
|
88
88
|
})
|
|
89
89
|
})
|
|
90
90
|
|
|
91
|
+
it.each([
|
|
92
|
+
{ name: 'a zeroed position', lineno: 0, colno: 0 },
|
|
93
|
+
{ name: 'no position at all', lineno: undefined, colno: undefined },
|
|
94
|
+
{ name: 'a column but no line', lineno: 0, colno: 13 },
|
|
95
|
+
])('reports no stack trace for $name', ({ lineno, colno }) => {
|
|
96
|
+
const exception = buildException(
|
|
97
|
+
new FakeErrorEvent({
|
|
98
|
+
message: 'ResizeObserver loop completed with undelivered notifications.',
|
|
99
|
+
filename: 'https://example.com/dashboard/1',
|
|
100
|
+
lineno,
|
|
101
|
+
colno,
|
|
102
|
+
})
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
expect(exception.stacktrace).toBeUndefined()
|
|
106
|
+
expect(exception).toMatchObject({ value: 'ResizeObserver loop completed with undelivered notifications.' })
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('keeps a frame when only the column is zero', () => {
|
|
110
|
+
const exception = buildException(
|
|
111
|
+
new FakeErrorEvent({
|
|
112
|
+
message: 'Uncaught TypeError: x is not a function',
|
|
113
|
+
filename: 'https://example.com/app.js',
|
|
114
|
+
lineno: 42,
|
|
115
|
+
colno: 0,
|
|
116
|
+
})
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
expect(exception.stacktrace?.frames).toEqual([
|
|
120
|
+
expect.objectContaining({ filename: 'https://example.com/app.js', lineno: 42, colno: 0 }),
|
|
121
|
+
])
|
|
122
|
+
})
|
|
123
|
+
|
|
91
124
|
it('does not parse frame-shaped lines from a multiline message', () => {
|
|
92
125
|
const exception = buildException(
|
|
93
126
|
new FakeErrorEvent({
|
|
@@ -46,12 +46,19 @@ export class ErrorEventCoercer implements ErrorTrackingCoercer<ErrorEventLike> {
|
|
|
46
46
|
|
|
47
47
|
private _buildLocationStack(err: ErrorEventLike): string | undefined {
|
|
48
48
|
const location = err as ErrorEventLike & ErrorEventLocation
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
return `Error\n at ${location.filename}:${lineno}:${colno}`
|
|
49
|
+
const lineno = location.lineno ?? 0
|
|
50
|
+
const colno = location.colno ?? 0
|
|
51
|
+
if (!isString(location.filename) || location.filename.length === 0) {
|
|
52
|
+
return undefined
|
|
54
53
|
}
|
|
55
|
-
|
|
54
|
+
// Stack lines are 1-indexed, so a zero line means the report carries no code position. The
|
|
55
|
+
// browser pairs that with the document URL as `filename`, and a frame built from the two names
|
|
56
|
+
// a page rather than a script, which no source map can resolve. Leaving the exception frameless
|
|
57
|
+
// says the same thing without inviting a lookup that cannot succeed.
|
|
58
|
+
if (lineno === 0) {
|
|
59
|
+
return undefined
|
|
60
|
+
}
|
|
61
|
+
// The message stays in `value`, which prevents multiline messages from being parsed as extra frames.
|
|
62
|
+
return `Error\n at ${location.filename}:${lineno}:${colno}`
|
|
56
63
|
}
|
|
57
64
|
}
|
package/src/logs/index.ts
CHANGED
|
@@ -119,7 +119,7 @@ export class PostHogLogs {
|
|
|
119
119
|
// Build before deferring so attributes reflect state at capture time, not
|
|
120
120
|
// at drain time (identity/session changes between capture and drain must
|
|
121
121
|
// not corrupt recorded attributes).
|
|
122
|
-
const record = buildOtlpLogRecord(filtered, this._getContext())
|
|
122
|
+
const record = buildOtlpLogRecord(filtered, this._getContext(), this._logger)
|
|
123
123
|
const entry: BufferedLogEntry = { record }
|
|
124
124
|
|
|
125
125
|
this._onReady(() => this._enqueue(entry))
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CaptureLogOptions, LogSeverityLevel } from '@posthog/types'
|
|
1
|
+
import type { CaptureLogOptions, LogAttributeValue, LogSeverityLevel } from '@posthog/types'
|
|
2
2
|
import type { LogSdkContext } from './types'
|
|
3
3
|
import {
|
|
4
4
|
buildOtlpLogRecord,
|
|
@@ -69,10 +69,36 @@ describe('logs-utils', () => {
|
|
|
69
69
|
expect(toOtlpAnyValue('hello')).toEqual({ stringValue: 'hello' })
|
|
70
70
|
})
|
|
71
71
|
|
|
72
|
-
it('converts integers', () => {
|
|
73
|
-
expect(toOtlpAnyValue(42)).toEqual({ intValue: 42 })
|
|
74
|
-
expect(toOtlpAnyValue(0)).toEqual({ intValue: 0 })
|
|
75
|
-
expect(toOtlpAnyValue(-7)).toEqual({ intValue: -7 })
|
|
72
|
+
it('converts integers to decimal strings', () => {
|
|
73
|
+
expect(toOtlpAnyValue(42)).toEqual({ intValue: '42' })
|
|
74
|
+
expect(toOtlpAnyValue(0)).toEqual({ intValue: '0' })
|
|
75
|
+
expect(toOtlpAnyValue(-7)).toEqual({ intValue: '-7' })
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
// Spec: outside int64 it is a stringValue, never an intValue.
|
|
79
|
+
it('converts integers outside int64 to stringValue', () => {
|
|
80
|
+
expect(toOtlpAnyValue(2 ** 63)).toEqual({ stringValue: '9223372036854775808' })
|
|
81
|
+
expect(toOtlpAnyValue(-(2 ** 64))).toEqual({ stringValue: '-18446744073709551616' })
|
|
82
|
+
expect(toOtlpAnyValue(1e21)).toEqual({ stringValue: '1000000000000000000000' })
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it('logs a debug line when an integer falls outside int64', () => {
|
|
86
|
+
const logger = { debug: jest.fn(), info: jest.fn(), warn: jest.fn(), error: jest.fn(), critical: jest.fn() }
|
|
87
|
+
toOtlpAnyValue(2 ** 63, logger as any)
|
|
88
|
+
expect(logger.debug).toHaveBeenCalledWith(expect.stringContaining('outside the int64 range'))
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('keeps int64 min as intValue', () => {
|
|
92
|
+
// In range, but `String` renders it 192 below int64 min, so the decimal
|
|
93
|
+
// has to come from BigInt.
|
|
94
|
+
expect(toOtlpAnyValue(-(2 ** 63))).toEqual({ intValue: '-9223372036854775808' })
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
it('keeps large in-range integers exact', () => {
|
|
98
|
+
expect(toOtlpAnyValue(Number.MAX_SAFE_INTEGER)).toEqual({ intValue: '9007199254740991' })
|
|
99
|
+
// The largest double below 2^63 — no double exists between the two.
|
|
100
|
+
expect(toOtlpAnyValue(9223372036854774784)).toEqual({ intValue: '9223372036854774784' })
|
|
101
|
+
expect(toOtlpAnyValue(2 ** 62)).toEqual({ intValue: '4611686018427387904' })
|
|
76
102
|
})
|
|
77
103
|
|
|
78
104
|
it('converts floats to doubleValue', () => {
|
|
@@ -108,16 +134,295 @@ describe('logs-utils', () => {
|
|
|
108
134
|
it('converts mixed primitive arrays recursively', () => {
|
|
109
135
|
expect(toOtlpAnyValue([1, 'x', true])).toEqual({
|
|
110
136
|
arrayValue: {
|
|
111
|
-
values: [{ intValue: 1 }, { stringValue: 'x' }, { boolValue: true }],
|
|
137
|
+
values: [{ intValue: '1' }, { stringValue: 'x' }, { boolValue: true }],
|
|
112
138
|
},
|
|
113
139
|
})
|
|
114
140
|
})
|
|
115
141
|
|
|
116
|
-
it('
|
|
142
|
+
it('converts plain objects to kvlistValue', () => {
|
|
117
143
|
expect(toOtlpAnyValue({ a: 1, b: 'two' })).toEqual({
|
|
118
|
-
|
|
144
|
+
kvlistValue: {
|
|
145
|
+
values: [
|
|
146
|
+
{ key: 'a', value: { intValue: '1' } },
|
|
147
|
+
{ key: 'b', value: { stringValue: 'two' } },
|
|
148
|
+
],
|
|
149
|
+
},
|
|
150
|
+
})
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
it('converts nested objects recursively', () => {
|
|
154
|
+
expect(toOtlpAnyValue({ outer: { inner: 1 } })).toEqual({
|
|
155
|
+
kvlistValue: {
|
|
156
|
+
values: [
|
|
157
|
+
{
|
|
158
|
+
key: 'outer',
|
|
159
|
+
value: { kvlistValue: { values: [{ key: 'inner', value: { intValue: '1' } }] } },
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
},
|
|
163
|
+
})
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
it('drops null and undefined keys inside objects', () => {
|
|
167
|
+
expect(toOtlpAnyValue({ kept: 1, gone: null, alsoGone: undefined })).toEqual({
|
|
168
|
+
kvlistValue: { values: [{ key: 'kept', value: { intValue: '1' } }] },
|
|
169
|
+
})
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
// Not in LogAttributeValue, but reachable at runtime from untyped callers.
|
|
173
|
+
it('encodes Dates as ISO strings', () => {
|
|
174
|
+
expect(toOtlpAnyValue(new Date('2026-08-20T10:00:00.000Z') as unknown as LogAttributeValue)).toEqual({
|
|
175
|
+
stringValue: '2026-08-20T10:00:00.000Z',
|
|
176
|
+
})
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
it('marks circular references instead of recursing', () => {
|
|
180
|
+
const cyclic: Record<string, unknown> = { name: 'root' }
|
|
181
|
+
cyclic.self = cyclic
|
|
182
|
+
expect(toOtlpAnyValue(cyclic)).toEqual({
|
|
183
|
+
kvlistValue: {
|
|
184
|
+
values: [
|
|
185
|
+
{ key: 'name', value: { stringValue: 'root' } },
|
|
186
|
+
{ key: 'self', value: { stringValue: '[Circular]' } },
|
|
187
|
+
],
|
|
188
|
+
},
|
|
189
|
+
})
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
// An escaping error would surface in the caller's application code.
|
|
193
|
+
it('does not throw on an object nested past the depth cap', () => {
|
|
194
|
+
let deep: Record<string, unknown> = { end: true }
|
|
195
|
+
for (let i = 0; i < 25000; i++) {
|
|
196
|
+
deep = { next: deep }
|
|
197
|
+
}
|
|
198
|
+
expect(() => toOtlpAnyValue(deep)).not.toThrow()
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
it('truncates at exactly 20 levels instead of recursing', () => {
|
|
202
|
+
let deep: Record<string, unknown> = { end: true }
|
|
203
|
+
for (let i = 0; i < 25; i++) {
|
|
204
|
+
deep = { next: deep }
|
|
205
|
+
}
|
|
206
|
+
const encoded = JSON.stringify(toOtlpAnyValue(deep))
|
|
207
|
+
expect(encoded).toContain('[Truncated]')
|
|
208
|
+
expect(encoded.split('"next"').length - 1).toBe(20)
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
it('marks a throwing getter without losing the rest of the object', () => {
|
|
212
|
+
const attrs = {
|
|
213
|
+
ok: 1,
|
|
214
|
+
get bad(): number {
|
|
215
|
+
throw new Error('getter blew up')
|
|
216
|
+
},
|
|
217
|
+
}
|
|
218
|
+
expect(() => toOtlpKeyValueList(attrs)).not.toThrow()
|
|
219
|
+
expect(toOtlpKeyValueList(attrs)).toEqual([
|
|
220
|
+
{ key: 'ok', value: { intValue: '1' } },
|
|
221
|
+
{ key: 'bad', value: { stringValue: '[Unserializable]' } },
|
|
222
|
+
])
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
// for...in walks the prototype chain once own keys are exhausted.
|
|
226
|
+
it('ignores inherited enumerable properties', () => {
|
|
227
|
+
const inherited: Record<string, unknown> = Object.create({ fromPrototype: 'leaked' })
|
|
228
|
+
inherited.own = 1
|
|
229
|
+
expect(toOtlpAnyValue(inherited)).toEqual({
|
|
230
|
+
kvlistValue: { values: [{ key: 'own', value: { intValue: '1' } }] },
|
|
119
231
|
})
|
|
120
232
|
})
|
|
233
|
+
|
|
234
|
+
// `String(fn)` would put the function's source text on the wire.
|
|
235
|
+
it('marks function and symbol values instead of stringifying them', () => {
|
|
236
|
+
expect(toOtlpAnyValue({ handler: () => 1, retries: 2 } as unknown as LogAttributeValue)).toEqual({
|
|
237
|
+
kvlistValue: {
|
|
238
|
+
values: [
|
|
239
|
+
{ key: 'handler', value: { stringValue: '[Function]' } },
|
|
240
|
+
{ key: 'retries', value: { intValue: '2' } },
|
|
241
|
+
],
|
|
242
|
+
},
|
|
243
|
+
})
|
|
244
|
+
expect(toOtlpAnyValue({ sym: Symbol('x') } as unknown as LogAttributeValue)).toEqual({
|
|
245
|
+
kvlistValue: { values: [{ key: 'sym', value: { stringValue: 'Symbol(x)' } }] },
|
|
246
|
+
})
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
// dayjs, Decimal, ORM documents.
|
|
250
|
+
it('honours toJSON', () => {
|
|
251
|
+
const wrapped = { toJSON: () => ({ amount: 5 }) }
|
|
252
|
+
expect(toOtlpAnyValue(wrapped as unknown as LogAttributeValue)).toEqual({
|
|
253
|
+
kvlistValue: { values: [{ key: 'amount', value: { intValue: '5' } }] },
|
|
254
|
+
})
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
it('falls back to the plain walk when toJSON throws', () => {
|
|
258
|
+
const wrapped = {
|
|
259
|
+
kept: 1,
|
|
260
|
+
toJSON: () => {
|
|
261
|
+
throw new Error('nope')
|
|
262
|
+
},
|
|
263
|
+
}
|
|
264
|
+
expect(toOtlpAnyValue(wrapped as unknown as LogAttributeValue)).toEqual({
|
|
265
|
+
kvlistValue: {
|
|
266
|
+
values: [
|
|
267
|
+
{ key: 'kept', value: { intValue: '1' } },
|
|
268
|
+
{ key: 'toJSON', value: { stringValue: '[Function]' } },
|
|
269
|
+
],
|
|
270
|
+
},
|
|
271
|
+
})
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
// A toJSON returning its own object is a cycle like any other.
|
|
275
|
+
it('marks a cycle that runs through toJSON', () => {
|
|
276
|
+
const cyclic: Record<string, unknown> = {}
|
|
277
|
+
cyclic.toJSON = () => ({ inner: cyclic })
|
|
278
|
+
expect(toOtlpAnyValue(cyclic)).toEqual({
|
|
279
|
+
kvlistValue: { values: [{ key: 'inner', value: { stringValue: '[Circular]' } }] },
|
|
280
|
+
})
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
// Both `null` and `{}` here are rejected for the whole request; iOS and
|
|
284
|
+
// Android drop them too.
|
|
285
|
+
it('drops holes and nullish elements from arrays', () => {
|
|
286
|
+
// eslint-disable-next-line no-sparse-arrays
|
|
287
|
+
expect(toOtlpAnyValue([1, , 3])).toEqual({
|
|
288
|
+
arrayValue: { values: [{ intValue: '1' }, { intValue: '3' }] },
|
|
289
|
+
})
|
|
290
|
+
expect(toOtlpAnyValue([1, null, undefined, 3])).toEqual({
|
|
291
|
+
arrayValue: { values: [{ intValue: '1' }, { intValue: '3' }] },
|
|
292
|
+
})
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
it('stops encoding array items once the node budget is spent', () => {
|
|
296
|
+
const row: Record<string, number> = {}
|
|
297
|
+
for (let i = 0; i < 20; i++) {
|
|
298
|
+
row[`k${i}`] = i
|
|
299
|
+
}
|
|
300
|
+
const wide = Array.from({ length: 1000 }, () => ({ ...row }))
|
|
301
|
+
const values = toOtlpAnyValue(wide).arrayValue!.values
|
|
302
|
+
expect(values[values.length - 1]).toEqual({ stringValue: '[Truncated]' })
|
|
303
|
+
// One marker, not one per unencodable item.
|
|
304
|
+
expect(values.filter((v) => v.stringValue === '[Truncated]')).toHaveLength(1)
|
|
305
|
+
})
|
|
306
|
+
|
|
307
|
+
it('caps a shared object graph instead of expanding it', () => {
|
|
308
|
+
let graph: Record<string, unknown> = { leaf: true }
|
|
309
|
+
for (let i = 0; i < 20; i++) {
|
|
310
|
+
graph = { a: graph, b: graph }
|
|
311
|
+
}
|
|
312
|
+
const encoded = JSON.stringify(toOtlpAnyValue(graph))
|
|
313
|
+
expect(encoded).toContain('[Truncated]')
|
|
314
|
+
expect(encoded.length).toBeLessThan(1_000_000)
|
|
315
|
+
})
|
|
316
|
+
|
|
317
|
+
it('caps a very wide object without inventing an attribute key', () => {
|
|
318
|
+
const wide: Record<string, number> = {}
|
|
319
|
+
for (let i = 0; i < 5000; i++) {
|
|
320
|
+
wide[`k${i}`] = i
|
|
321
|
+
}
|
|
322
|
+
const logger = { debug: jest.fn(), info: jest.fn(), warn: jest.fn(), error: jest.fn(), critical: jest.fn() }
|
|
323
|
+
const values = toOtlpAnyValue(wide, logger as any).kvlistValue!.values
|
|
324
|
+
expect(values).toHaveLength(1000)
|
|
325
|
+
expect(values.every((v) => v.key.startsWith('k'))).toBe(true)
|
|
326
|
+
expect(logger.debug).toHaveBeenCalledWith(expect.stringContaining('truncated'))
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
// Why the encoder does not delegate to toJsonSafeValue: that maps them to null.
|
|
330
|
+
it('keeps non-finite floats as strings inside nested objects', () => {
|
|
331
|
+
expect(toOtlpAnyValue({ nested: { ratio: NaN } })).toEqual({
|
|
332
|
+
kvlistValue: {
|
|
333
|
+
values: [
|
|
334
|
+
{
|
|
335
|
+
key: 'nested',
|
|
336
|
+
value: { kvlistValue: { values: [{ key: 'ratio', value: { stringValue: 'NaN' } }] } },
|
|
337
|
+
},
|
|
338
|
+
],
|
|
339
|
+
},
|
|
340
|
+
})
|
|
341
|
+
})
|
|
342
|
+
|
|
343
|
+
// A lone surrogate survives JSON.stringify as a \uD800 escape, which the
|
|
344
|
+
// server rejects for the whole request.
|
|
345
|
+
it('replaces unpaired surrogates in values and keys', () => {
|
|
346
|
+
expect(toOtlpAnyValue('ok\ud83d')).toEqual({ stringValue: 'ok\ufffd' })
|
|
347
|
+
expect(toOtlpAnyValue({ nested: 'ok\ud83d' })).toEqual({
|
|
348
|
+
kvlistValue: { values: [{ key: 'nested', value: { stringValue: 'ok\ufffd' } }] },
|
|
349
|
+
})
|
|
350
|
+
expect(toOtlpKeyValueList({ 'key\ud83d': 1 })).toEqual([{ key: 'key\ufffd', value: { intValue: '1' } }])
|
|
351
|
+
})
|
|
352
|
+
|
|
353
|
+
it('encodes empty containers with an explicit values array', () => {
|
|
354
|
+
expect(toOtlpAnyValue({})).toEqual({ kvlistValue: { values: [] } })
|
|
355
|
+
expect(toOtlpAnyValue([])).toEqual({ arrayValue: { values: [] } })
|
|
356
|
+
})
|
|
357
|
+
|
|
358
|
+
it('keeps a Date whose toISOString is overridden out of the wire format', () => {
|
|
359
|
+
const broken = new Date('2026-08-20T10:00:00.000Z')
|
|
360
|
+
|
|
361
|
+
;(broken as any).toISOString = () => ({})
|
|
362
|
+
expect(typeof toOtlpAnyValue(broken as unknown as LogAttributeValue).stringValue).toBe('string')
|
|
363
|
+
})
|
|
364
|
+
|
|
365
|
+
it('encodes sibling references to one object twice, not as circular', () => {
|
|
366
|
+
const shared = { id: 1 }
|
|
367
|
+
expect(toOtlpAnyValue({ a: shared, b: shared })).toEqual({
|
|
368
|
+
kvlistValue: {
|
|
369
|
+
values: [
|
|
370
|
+
{ key: 'a', value: { kvlistValue: { values: [{ key: 'id', value: { intValue: '1' } }] } } },
|
|
371
|
+
{ key: 'b', value: { kvlistValue: { values: [{ key: 'id', value: { intValue: '1' } }] } } },
|
|
372
|
+
],
|
|
373
|
+
},
|
|
374
|
+
})
|
|
375
|
+
})
|
|
376
|
+
})
|
|
377
|
+
|
|
378
|
+
describe('buildOtlpLogRecord attribute reads', () => {
|
|
379
|
+
// Reading `options.attributes` happens before the encoder's per-key guard.
|
|
380
|
+
it('marks an attribute whose getter throws without dropping the record', () => {
|
|
381
|
+
const attributes = {
|
|
382
|
+
ok: 1,
|
|
383
|
+
get bad(): number {
|
|
384
|
+
throw new Error('disposed store')
|
|
385
|
+
},
|
|
386
|
+
}
|
|
387
|
+
const record = buildOtlpLogRecord({ body: 'x', attributes }, {})
|
|
388
|
+
expect(record.attributes).toContainEqual({ key: 'ok', value: { intValue: '1' } })
|
|
389
|
+
expect(record.attributes).toContainEqual({ key: 'bad', value: { stringValue: '[Unserializable]' } })
|
|
390
|
+
})
|
|
391
|
+
|
|
392
|
+
// Plain assignment hits the prototype setter and the attribute vanishes.
|
|
393
|
+
it('keeps an attribute literally named __proto__', () => {
|
|
394
|
+
const attributes = JSON.parse('{"__proto__": {"a": 1}, "normal": "n"}')
|
|
395
|
+
const record = buildOtlpLogRecord({ body: 'x', attributes }, {})
|
|
396
|
+
expect(record.attributes.map((a) => a.key)).toContain('__proto__')
|
|
397
|
+
})
|
|
398
|
+
|
|
399
|
+
it('coerces a non-string body', () => {
|
|
400
|
+
// A non-string stringValue is refused for the whole request.
|
|
401
|
+
const record = buildOtlpLogRecord({ body: 12345 as unknown as string }, {})
|
|
402
|
+
expect(record.body).toEqual({ stringValue: '12345' })
|
|
403
|
+
})
|
|
404
|
+
|
|
405
|
+
// `String()` throws on these, and buildOtlpLogRecord is called straight from
|
|
406
|
+
// captureLog — beforeSend can also hand back any body.
|
|
407
|
+
it('marks a body that cannot be coerced to a string', () => {
|
|
408
|
+
const throwing = {
|
|
409
|
+
toString() {
|
|
410
|
+
throw new Error('boom')
|
|
411
|
+
},
|
|
412
|
+
} as unknown as string
|
|
413
|
+
expect(() => buildOtlpLogRecord({ body: throwing }, {})).not.toThrow()
|
|
414
|
+
expect(buildOtlpLogRecord({ body: throwing }, {}).body).toEqual({ stringValue: '[Unserializable]' })
|
|
415
|
+
expect(buildOtlpLogRecord({ body: Object.create(null) }, {}).body).toEqual({
|
|
416
|
+
stringValue: '[Unserializable]',
|
|
417
|
+
})
|
|
418
|
+
})
|
|
419
|
+
|
|
420
|
+
it('keeps the record when the attributes object itself cannot be read', () => {
|
|
421
|
+
const revocable = Proxy.revocable({ a: 1 }, {})
|
|
422
|
+
revocable.revoke()
|
|
423
|
+
|
|
424
|
+
expect(buildOtlpLogRecord({ body: 'x', attributes: revocable.proxy }, {}).body).toEqual({ stringValue: 'x' })
|
|
425
|
+
})
|
|
121
426
|
})
|
|
122
427
|
|
|
123
428
|
describe('toOtlpKeyValueList', () => {
|
|
@@ -130,7 +435,7 @@ describe('logs-utils', () => {
|
|
|
130
435
|
})
|
|
131
436
|
).toEqual([
|
|
132
437
|
{ key: 'name', value: { stringValue: 'test' } },
|
|
133
|
-
{ key: 'count', value: { intValue: 5 } },
|
|
438
|
+
{ key: 'count', value: { intValue: '5' } },
|
|
134
439
|
{ key: 'active', value: { boolValue: true } },
|
|
135
440
|
])
|
|
136
441
|
})
|