@posthog/browser-common 0.9.3 → 0.9.4
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/client.d.ts +4 -2
- package/dist/config.js +1 -1
- package/dist/config.mjs +1 -1
- package/dist/console-logs.d.ts +15 -0
- package/dist/console-logs.js +317 -0
- package/dist/console-logs.mjs +276 -0
- package/dist/extension-tokens.d.ts +4 -0
- package/dist/extension-tokens.js +40 -0
- package/dist/extension-tokens.mjs +2 -0
- package/dist/extension.d.ts +4 -0
- package/dist/feature-flags.js +1 -1
- package/dist/feature-flags.mjs +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +15 -7
- package/dist/index.mjs +1 -0
- package/dist/logs-config.d.ts +24 -0
- package/dist/logs-config.js +80 -0
- package/dist/logs-config.mjs +42 -0
- package/dist/logs-types.d.ts +31 -0
- package/dist/logs-types.js +46 -0
- package/dist/logs-types.mjs +8 -0
- package/dist/logs.d.ts +85 -0
- package/dist/logs.js +469 -0
- package/dist/logs.mjs +428 -0
- package/dist/token.d.ts +1 -1
- package/dist/utils/console-original.d.ts +6 -0
- package/dist/utils/console-original.js +43 -0
- package/dist/utils/console-original.mjs +5 -0
- package/dist/utils/patch.d.ts +3 -0
- package/dist/utils/patch.js +90 -0
- package/dist/utils/patch.mjs +52 -0
- package/dist/utils/sanitize-url.d.ts +11 -0
- package/dist/utils/sanitize-url.js +55 -0
- package/dist/utils/sanitize-url.mjs +17 -0
- package/package.json +42 -2
package/dist/client.d.ts
CHANGED
|
@@ -19,6 +19,8 @@ export interface SessionContext {
|
|
|
19
19
|
readonly windowId: string;
|
|
20
20
|
/** When the session started, as a Unix timestamp in milliseconds. */
|
|
21
21
|
readonly sessionStartTimestamp: number;
|
|
22
|
+
/** Most recent session activity, as a Unix timestamp in milliseconds. */
|
|
23
|
+
readonly lastActivityTimestamp: number;
|
|
22
24
|
}
|
|
23
25
|
/** A captured event, as observed by `onEvent`. */
|
|
24
26
|
export interface CapturedEventInfo {
|
|
@@ -98,8 +100,8 @@ export interface Client {
|
|
|
98
100
|
readonly initialPersonProperties: DeepReadonly<Record<string, unknown>>;
|
|
99
101
|
/** Active group memberships attached to events as `$groups`. */
|
|
100
102
|
readonly groups: DeepReadonly<Record<string, string>>;
|
|
101
|
-
/** The current session,
|
|
102
|
-
readonly session: SessionContext;
|
|
103
|
+
/** The current session, if one exists. Reading it need not create one. */
|
|
104
|
+
readonly session: SessionContext | undefined;
|
|
103
105
|
/** Whether the host currently permits data capture. */
|
|
104
106
|
readonly canCapture: boolean;
|
|
105
107
|
/** Synchronously admits an analytics event to the client's normal pipeline. */
|
package/dist/config.js
CHANGED
package/dist/config.mjs
CHANGED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { BufferedConsoleEntry } from './logs-types';
|
|
2
|
+
import type { CaptureLogOptions } from '@posthog/types';
|
|
3
|
+
import type { LogSdkContext } from '@posthog/core';
|
|
4
|
+
export interface ConsoleLogsSink {
|
|
5
|
+
captureConsoleLog(options: CaptureLogOptions): void;
|
|
6
|
+
captureBufferedConsoleLog?(options: CaptureLogOptions, context: LogSdkContext, occurredAtMs: number): void;
|
|
7
|
+
}
|
|
8
|
+
export interface ConsoleLogsHost {
|
|
9
|
+
console: Console;
|
|
10
|
+
hostname: string;
|
|
11
|
+
getCapturingLogs(): ConsoleLogsSink | undefined;
|
|
12
|
+
}
|
|
13
|
+
export declare const initializeLogs: (host: ConsoleLogsHost) => (() => void);
|
|
14
|
+
/** Replays console calls the main bundle recorded before this script loaded. */
|
|
15
|
+
export declare const replayConsoleBuffer: (host: ConsoleLogsHost, entries: BufferedConsoleEntry[]) => void;
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.d = (exports1, getters, values)=>{
|
|
5
|
+
var define = (defs, kind)=>{
|
|
6
|
+
for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
[kind]: defs[key]
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
define(getters, "get");
|
|
12
|
+
define(values, "value");
|
|
13
|
+
};
|
|
14
|
+
})();
|
|
15
|
+
(()=>{
|
|
16
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
17
|
+
})();
|
|
18
|
+
(()=>{
|
|
19
|
+
__webpack_require__.r = (exports1)=>{
|
|
20
|
+
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
21
|
+
value: 'Module'
|
|
22
|
+
});
|
|
23
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
24
|
+
value: true
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
})();
|
|
28
|
+
var __webpack_exports__ = {};
|
|
29
|
+
__webpack_require__.r(__webpack_exports__);
|
|
30
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
31
|
+
initializeLogs: ()=>initializeLogs,
|
|
32
|
+
replayConsoleBuffer: ()=>replayConsoleBuffer
|
|
33
|
+
});
|
|
34
|
+
const patch_js_namespaceObject = require("./utils/patch.js");
|
|
35
|
+
const console_original_js_namespaceObject = require("./utils/console-original.js");
|
|
36
|
+
const core_namespaceObject = require("@posthog/core");
|
|
37
|
+
const LOG_BODY_SIZE_LIMIT = 10000;
|
|
38
|
+
const LOG_ATTRIBUTES_LIMIT = 50;
|
|
39
|
+
const appendWithLimit = (parts, text, budget)=>{
|
|
40
|
+
if (budget.remaining <= 0) {
|
|
41
|
+
budget.truncated = true;
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
if (text.length <= budget.remaining) {
|
|
45
|
+
parts.push(text);
|
|
46
|
+
budget.remaining -= text.length;
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
parts.push(text.slice(0, budget.remaining));
|
|
50
|
+
budget.remaining = 0;
|
|
51
|
+
budget.truncated = true;
|
|
52
|
+
return false;
|
|
53
|
+
};
|
|
54
|
+
const stringifyStringWithLimit = (value, parts, budget)=>{
|
|
55
|
+
const serialized = JSON.stringify(value);
|
|
56
|
+
if (serialized.length <= budget.remaining) return appendWithLimit(parts, serialized, budget);
|
|
57
|
+
budget.truncated = true;
|
|
58
|
+
if (budget.remaining < 2) return false;
|
|
59
|
+
let low = 0;
|
|
60
|
+
let high = Math.min(value.length, budget.remaining - 2);
|
|
61
|
+
while(low < high){
|
|
62
|
+
const mid = Math.ceil((low + high) / 2);
|
|
63
|
+
if (JSON.stringify(value.slice(0, mid)).length <= budget.remaining) low = mid;
|
|
64
|
+
else high = mid - 1;
|
|
65
|
+
}
|
|
66
|
+
return appendWithLimit(parts, JSON.stringify(value.slice(0, low)), budget);
|
|
67
|
+
};
|
|
68
|
+
const isJSONSerializablePrimitive = (value)=>void 0 !== value && 'function' != typeof value && 'symbol' != typeof value && 'bigint' != typeof value;
|
|
69
|
+
const isNumberOrBoolean = (value)=>{
|
|
70
|
+
try {
|
|
71
|
+
return (0, core_namespaceObject.isNumber)(value) || (0, core_namespaceObject.isBoolean)(value);
|
|
72
|
+
} catch {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
const collectAttributeValue = (key, value, collector)=>{
|
|
77
|
+
if (collector.truncated) return;
|
|
78
|
+
collector.keysRemaining -= 1;
|
|
79
|
+
collector.sizeRemaining -= String(value).length + key.length;
|
|
80
|
+
if (collector.keysRemaining <= 0 || collector.sizeRemaining <= 0) {
|
|
81
|
+
collector.truncated = true;
|
|
82
|
+
collector.result['attributes_truncated'] = true;
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
collector.result[key] = value;
|
|
86
|
+
};
|
|
87
|
+
const collectFlattenedAttributes = (value, key, collector)=>{
|
|
88
|
+
if (collector.truncated) return;
|
|
89
|
+
if ((0, core_namespaceObject.isObject)(value)) {
|
|
90
|
+
if (collector.seen.has(value)) return void collectAttributeValue(key || 'circular', '[Circular]', collector);
|
|
91
|
+
collector.seen.add(value);
|
|
92
|
+
try {
|
|
93
|
+
for(const childKey in value)try {
|
|
94
|
+
if (!Object.prototype.hasOwnProperty.call(value, childKey)) continue;
|
|
95
|
+
const childValue = value[childKey];
|
|
96
|
+
collectFlattenedAttributes(childValue, key ? `${key}.${childKey}` : childKey, collector);
|
|
97
|
+
if (collector.truncated) break;
|
|
98
|
+
} catch {
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
} catch {}
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
collectAttributeValue(key, value, collector);
|
|
105
|
+
};
|
|
106
|
+
const stringifyValueWithLimit = (value, parts, budget, seen, inArray = false, attributeCollector)=>{
|
|
107
|
+
if (!isJSONSerializablePrimitive(value)) return inArray ? appendWithLimit(parts, 'null', budget) : true;
|
|
108
|
+
if ((0, core_namespaceObject.isNull)(value) || isNumberOrBoolean(value)) return appendWithLimit(parts, JSON.stringify(value), budget);
|
|
109
|
+
if ('string' == typeof value) return stringifyStringWithLimit(value, parts, budget);
|
|
110
|
+
if (!(0, core_namespaceObject.isObject)(value) && !(0, core_namespaceObject.isArray)(value)) return appendWithLimit(parts, JSON.stringify(value), budget);
|
|
111
|
+
if (seen.has(value)) return stringifyStringWithLimit('[Circular]', parts, budget);
|
|
112
|
+
seen.add(value);
|
|
113
|
+
try {
|
|
114
|
+
const toJSON = value.toJSON;
|
|
115
|
+
if ((0, core_namespaceObject.isFunction)(toJSON)) return stringifyValueWithLimit(toJSON.call(value), parts, budget, seen, inArray);
|
|
116
|
+
} catch {}
|
|
117
|
+
try {
|
|
118
|
+
const objectTag = Object.prototype.toString.call(value);
|
|
119
|
+
if ('[object String]' === objectTag) return stringifyStringWithLimit(String(value.valueOf()), parts, budget);
|
|
120
|
+
if ('[object Number]' === objectTag || '[object Boolean]' === objectTag) return appendWithLimit(parts, JSON.stringify(value.valueOf()), budget);
|
|
121
|
+
} catch {}
|
|
122
|
+
if (value instanceof Error) {
|
|
123
|
+
const errorObject = {};
|
|
124
|
+
try {
|
|
125
|
+
for(const key in value)if (Object.prototype.hasOwnProperty.call(value, key)) errorObject[key] = value[key];
|
|
126
|
+
} catch {}
|
|
127
|
+
try {
|
|
128
|
+
errorObject.name = value.name;
|
|
129
|
+
} catch {}
|
|
130
|
+
try {
|
|
131
|
+
errorObject.message = value.message;
|
|
132
|
+
} catch {}
|
|
133
|
+
try {
|
|
134
|
+
errorObject.stack = value.stack;
|
|
135
|
+
} catch {}
|
|
136
|
+
return stringifyValueWithLimit(errorObject, parts, budget, seen, inArray, attributeCollector);
|
|
137
|
+
}
|
|
138
|
+
if ((0, core_namespaceObject.isArray)(value)) {
|
|
139
|
+
if (!appendWithLimit(parts, '[', budget)) return false;
|
|
140
|
+
for(let i = 0; i < value.length; i++){
|
|
141
|
+
if (i > 0 && !appendWithLimit(parts, ',', budget)) return false;
|
|
142
|
+
let item;
|
|
143
|
+
try {
|
|
144
|
+
item = value[i];
|
|
145
|
+
} catch {
|
|
146
|
+
item = void 0;
|
|
147
|
+
}
|
|
148
|
+
if (!stringifyValueWithLimit(item, parts, budget, seen, true)) return false;
|
|
149
|
+
}
|
|
150
|
+
return appendWithLimit(parts, ']', budget);
|
|
151
|
+
}
|
|
152
|
+
if (!appendWithLimit(parts, '{', budget)) return false;
|
|
153
|
+
let isFirst = true;
|
|
154
|
+
try {
|
|
155
|
+
for(const key in value){
|
|
156
|
+
if (!Object.prototype.hasOwnProperty.call(value, key)) continue;
|
|
157
|
+
if (budget.remaining <= 0) {
|
|
158
|
+
budget.truncated = true;
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
let propertyValue;
|
|
162
|
+
try {
|
|
163
|
+
propertyValue = value[key];
|
|
164
|
+
} catch {
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
if (attributeCollector) try {
|
|
168
|
+
collectFlattenedAttributes(propertyValue, key, attributeCollector);
|
|
169
|
+
} catch {}
|
|
170
|
+
if (!isJSONSerializablePrimitive(propertyValue)) continue;
|
|
171
|
+
const propertyPrefix = `${isFirst ? '' : ','}${JSON.stringify(key)}:`;
|
|
172
|
+
if (propertyPrefix.length >= budget.remaining) {
|
|
173
|
+
budget.truncated = true;
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
const partsBeforeProperty = parts.length;
|
|
177
|
+
const remainingBeforeProperty = budget.remaining;
|
|
178
|
+
const truncatedBeforeProperty = budget.truncated;
|
|
179
|
+
if (!appendWithLimit(parts, propertyPrefix, budget)) return false;
|
|
180
|
+
const partsBeforeValue = parts.length;
|
|
181
|
+
const serialized = stringifyValueWithLimit(propertyValue, parts, budget, seen, false);
|
|
182
|
+
const truncatedAfterValue = budget.truncated;
|
|
183
|
+
if (parts.length === partsBeforeValue) {
|
|
184
|
+
parts.length = partsBeforeProperty;
|
|
185
|
+
budget.remaining = remainingBeforeProperty;
|
|
186
|
+
budget.truncated = serialized ? truncatedBeforeProperty : truncatedAfterValue;
|
|
187
|
+
if (!serialized) return false;
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
isFirst = false;
|
|
191
|
+
if (!serialized) return false;
|
|
192
|
+
}
|
|
193
|
+
} catch {}
|
|
194
|
+
return appendWithLimit(parts, '}', budget);
|
|
195
|
+
};
|
|
196
|
+
const stringifyArgsSafely = (args, sizeLimit)=>{
|
|
197
|
+
const parts = [];
|
|
198
|
+
const budget = {
|
|
199
|
+
remaining: sizeLimit,
|
|
200
|
+
truncated: false
|
|
201
|
+
};
|
|
202
|
+
const attributeCollector = (0, core_namespaceObject.isObject)(args[0]) ? {
|
|
203
|
+
result: {},
|
|
204
|
+
keysRemaining: LOG_ATTRIBUTES_LIMIT,
|
|
205
|
+
sizeRemaining: LOG_BODY_SIZE_LIMIT,
|
|
206
|
+
truncated: false,
|
|
207
|
+
seen: new WeakSet([
|
|
208
|
+
args[0]
|
|
209
|
+
])
|
|
210
|
+
} : void 0;
|
|
211
|
+
for(let i = 0; i < args.length; i++){
|
|
212
|
+
if (i > 0 && !appendWithLimit(parts, ' ', budget)) break;
|
|
213
|
+
if (!stringifyValueWithLimit(args[i], parts, budget, new WeakSet(), false, 0 === i ? attributeCollector : void 0)) break;
|
|
214
|
+
}
|
|
215
|
+
return {
|
|
216
|
+
body: parts.join('') + (budget.truncated ? '...' : ''),
|
|
217
|
+
truncated: budget.truncated,
|
|
218
|
+
attributes: attributeCollector?.result || {}
|
|
219
|
+
};
|
|
220
|
+
};
|
|
221
|
+
const LEVEL_MAP = {
|
|
222
|
+
debug: 'debug',
|
|
223
|
+
log: 'info',
|
|
224
|
+
warn: 'warn',
|
|
225
|
+
error: 'error',
|
|
226
|
+
info: 'info'
|
|
227
|
+
};
|
|
228
|
+
const initializeLogs = (host)=>{
|
|
229
|
+
const console = host.console;
|
|
230
|
+
let currentHost = host;
|
|
231
|
+
const restoreConsoleMethods = [];
|
|
232
|
+
const attributes = {
|
|
233
|
+
host: host.hostname
|
|
234
|
+
};
|
|
235
|
+
let isCapturingLog = false;
|
|
236
|
+
const dispose = ()=>{
|
|
237
|
+
currentHost = void 0;
|
|
238
|
+
for (const restore of restoreConsoleMethods)try {
|
|
239
|
+
restore();
|
|
240
|
+
} catch {}
|
|
241
|
+
};
|
|
242
|
+
try {
|
|
243
|
+
for (const level of Object.keys(LEVEL_MAP)){
|
|
244
|
+
const logWrapper = (next)=>(...args)=>{
|
|
245
|
+
let acquiredGuard = false;
|
|
246
|
+
try {
|
|
247
|
+
const activeHost = currentHost;
|
|
248
|
+
if (args.length > 0 && !isCapturingLog && activeHost) {
|
|
249
|
+
const logs = activeHost.getCapturingLogs();
|
|
250
|
+
if (logs) {
|
|
251
|
+
isCapturingLog = true;
|
|
252
|
+
acquiredGuard = true;
|
|
253
|
+
const { body, truncated, attributes: flattenedAttributes } = stringifyArgsSafely(args, LOG_BODY_SIZE_LIMIT);
|
|
254
|
+
const logAttributes = {
|
|
255
|
+
...attributes,
|
|
256
|
+
...truncated ? {
|
|
257
|
+
body_truncated: 'true'
|
|
258
|
+
} : {}
|
|
259
|
+
};
|
|
260
|
+
const options = {
|
|
261
|
+
level: LEVEL_MAP[level],
|
|
262
|
+
body,
|
|
263
|
+
attributes: {
|
|
264
|
+
'log.source': `console.${level}`,
|
|
265
|
+
...logAttributes,
|
|
266
|
+
...flattenedAttributes
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
logs.captureConsoleLog(options);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
} catch {} finally{
|
|
273
|
+
if (acquiredGuard) isCapturingLog = false;
|
|
274
|
+
next.apply(console, args);
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
const consoleBeforePatch = console[level];
|
|
278
|
+
restoreConsoleMethods.push((0, patch_js_namespaceObject.patch)(console, level, (next)=>{
|
|
279
|
+
const wrapped = logWrapper(next);
|
|
280
|
+
wrapped.__rrweb_original__ = (0, console_original_js_namespaceObject.originalConsoleMethod)(consoleBeforePatch);
|
|
281
|
+
return wrapped;
|
|
282
|
+
}));
|
|
283
|
+
}
|
|
284
|
+
} catch (error) {
|
|
285
|
+
dispose();
|
|
286
|
+
throw error;
|
|
287
|
+
}
|
|
288
|
+
return dispose;
|
|
289
|
+
};
|
|
290
|
+
const replayConsoleBuffer = (host, entries)=>{
|
|
291
|
+
const logs = host.getCapturingLogs();
|
|
292
|
+
if (!logs) return;
|
|
293
|
+
for (const entry of entries)try {
|
|
294
|
+
const { body, truncated, attributes: flattenedAttributes } = stringifyArgsSafely(entry.args, LOG_BODY_SIZE_LIMIT);
|
|
295
|
+
logs.captureBufferedConsoleLog?.({
|
|
296
|
+
level: LEVEL_MAP[entry.level],
|
|
297
|
+
body,
|
|
298
|
+
attributes: {
|
|
299
|
+
'log.source': `console.${entry.level}`,
|
|
300
|
+
host: host.hostname,
|
|
301
|
+
...truncated ? {
|
|
302
|
+
body_truncated: 'true'
|
|
303
|
+
} : {},
|
|
304
|
+
...flattenedAttributes
|
|
305
|
+
}
|
|
306
|
+
}, entry.context, entry.occurredAtMs);
|
|
307
|
+
} catch {}
|
|
308
|
+
};
|
|
309
|
+
exports.initializeLogs = __webpack_exports__.initializeLogs;
|
|
310
|
+
exports.replayConsoleBuffer = __webpack_exports__.replayConsoleBuffer;
|
|
311
|
+
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
312
|
+
"initializeLogs",
|
|
313
|
+
"replayConsoleBuffer"
|
|
314
|
+
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
315
|
+
Object.defineProperty(exports, '__esModule', {
|
|
316
|
+
value: true
|
|
317
|
+
});
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { patch } from "./utils/patch.mjs";
|
|
2
|
+
import { originalConsoleMethod } from "./utils/console-original.mjs";
|
|
3
|
+
import { isArray, isBoolean, isFunction, isNull, isNumber, isObject } from "@posthog/core";
|
|
4
|
+
const LOG_BODY_SIZE_LIMIT = 10000;
|
|
5
|
+
const LOG_ATTRIBUTES_LIMIT = 50;
|
|
6
|
+
const appendWithLimit = (parts, text, budget)=>{
|
|
7
|
+
if (budget.remaining <= 0) {
|
|
8
|
+
budget.truncated = true;
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
if (text.length <= budget.remaining) {
|
|
12
|
+
parts.push(text);
|
|
13
|
+
budget.remaining -= text.length;
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
parts.push(text.slice(0, budget.remaining));
|
|
17
|
+
budget.remaining = 0;
|
|
18
|
+
budget.truncated = true;
|
|
19
|
+
return false;
|
|
20
|
+
};
|
|
21
|
+
const stringifyStringWithLimit = (value, parts, budget)=>{
|
|
22
|
+
const serialized = JSON.stringify(value);
|
|
23
|
+
if (serialized.length <= budget.remaining) return appendWithLimit(parts, serialized, budget);
|
|
24
|
+
budget.truncated = true;
|
|
25
|
+
if (budget.remaining < 2) return false;
|
|
26
|
+
let low = 0;
|
|
27
|
+
let high = Math.min(value.length, budget.remaining - 2);
|
|
28
|
+
while(low < high){
|
|
29
|
+
const mid = Math.ceil((low + high) / 2);
|
|
30
|
+
if (JSON.stringify(value.slice(0, mid)).length <= budget.remaining) low = mid;
|
|
31
|
+
else high = mid - 1;
|
|
32
|
+
}
|
|
33
|
+
return appendWithLimit(parts, JSON.stringify(value.slice(0, low)), budget);
|
|
34
|
+
};
|
|
35
|
+
const isJSONSerializablePrimitive = (value)=>void 0 !== value && 'function' != typeof value && 'symbol' != typeof value && 'bigint' != typeof value;
|
|
36
|
+
const isNumberOrBoolean = (value)=>{
|
|
37
|
+
try {
|
|
38
|
+
return isNumber(value) || isBoolean(value);
|
|
39
|
+
} catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
const collectAttributeValue = (key, value, collector)=>{
|
|
44
|
+
if (collector.truncated) return;
|
|
45
|
+
collector.keysRemaining -= 1;
|
|
46
|
+
collector.sizeRemaining -= String(value).length + key.length;
|
|
47
|
+
if (collector.keysRemaining <= 0 || collector.sizeRemaining <= 0) {
|
|
48
|
+
collector.truncated = true;
|
|
49
|
+
collector.result['attributes_truncated'] = true;
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
collector.result[key] = value;
|
|
53
|
+
};
|
|
54
|
+
const collectFlattenedAttributes = (value, key, collector)=>{
|
|
55
|
+
if (collector.truncated) return;
|
|
56
|
+
if (isObject(value)) {
|
|
57
|
+
if (collector.seen.has(value)) return void collectAttributeValue(key || 'circular', '[Circular]', collector);
|
|
58
|
+
collector.seen.add(value);
|
|
59
|
+
try {
|
|
60
|
+
for(const childKey in value)try {
|
|
61
|
+
if (!Object.prototype.hasOwnProperty.call(value, childKey)) continue;
|
|
62
|
+
const childValue = value[childKey];
|
|
63
|
+
collectFlattenedAttributes(childValue, key ? `${key}.${childKey}` : childKey, collector);
|
|
64
|
+
if (collector.truncated) break;
|
|
65
|
+
} catch {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
} catch {}
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
collectAttributeValue(key, value, collector);
|
|
72
|
+
};
|
|
73
|
+
const stringifyValueWithLimit = (value, parts, budget, seen, inArray = false, attributeCollector)=>{
|
|
74
|
+
if (!isJSONSerializablePrimitive(value)) return inArray ? appendWithLimit(parts, 'null', budget) : true;
|
|
75
|
+
if (isNull(value) || isNumberOrBoolean(value)) return appendWithLimit(parts, JSON.stringify(value), budget);
|
|
76
|
+
if ('string' == typeof value) return stringifyStringWithLimit(value, parts, budget);
|
|
77
|
+
if (!isObject(value) && !isArray(value)) return appendWithLimit(parts, JSON.stringify(value), budget);
|
|
78
|
+
if (seen.has(value)) return stringifyStringWithLimit('[Circular]', parts, budget);
|
|
79
|
+
seen.add(value);
|
|
80
|
+
try {
|
|
81
|
+
const toJSON = value.toJSON;
|
|
82
|
+
if (isFunction(toJSON)) return stringifyValueWithLimit(toJSON.call(value), parts, budget, seen, inArray);
|
|
83
|
+
} catch {}
|
|
84
|
+
try {
|
|
85
|
+
const objectTag = Object.prototype.toString.call(value);
|
|
86
|
+
if ('[object String]' === objectTag) return stringifyStringWithLimit(String(value.valueOf()), parts, budget);
|
|
87
|
+
if ('[object Number]' === objectTag || '[object Boolean]' === objectTag) return appendWithLimit(parts, JSON.stringify(value.valueOf()), budget);
|
|
88
|
+
} catch {}
|
|
89
|
+
if (value instanceof Error) {
|
|
90
|
+
const errorObject = {};
|
|
91
|
+
try {
|
|
92
|
+
for(const key in value)if (Object.prototype.hasOwnProperty.call(value, key)) errorObject[key] = value[key];
|
|
93
|
+
} catch {}
|
|
94
|
+
try {
|
|
95
|
+
errorObject.name = value.name;
|
|
96
|
+
} catch {}
|
|
97
|
+
try {
|
|
98
|
+
errorObject.message = value.message;
|
|
99
|
+
} catch {}
|
|
100
|
+
try {
|
|
101
|
+
errorObject.stack = value.stack;
|
|
102
|
+
} catch {}
|
|
103
|
+
return stringifyValueWithLimit(errorObject, parts, budget, seen, inArray, attributeCollector);
|
|
104
|
+
}
|
|
105
|
+
if (isArray(value)) {
|
|
106
|
+
if (!appendWithLimit(parts, '[', budget)) return false;
|
|
107
|
+
for(let i = 0; i < value.length; i++){
|
|
108
|
+
if (i > 0 && !appendWithLimit(parts, ',', budget)) return false;
|
|
109
|
+
let item;
|
|
110
|
+
try {
|
|
111
|
+
item = value[i];
|
|
112
|
+
} catch {
|
|
113
|
+
item = void 0;
|
|
114
|
+
}
|
|
115
|
+
if (!stringifyValueWithLimit(item, parts, budget, seen, true)) return false;
|
|
116
|
+
}
|
|
117
|
+
return appendWithLimit(parts, ']', budget);
|
|
118
|
+
}
|
|
119
|
+
if (!appendWithLimit(parts, '{', budget)) return false;
|
|
120
|
+
let isFirst = true;
|
|
121
|
+
try {
|
|
122
|
+
for(const key in value){
|
|
123
|
+
if (!Object.prototype.hasOwnProperty.call(value, key)) continue;
|
|
124
|
+
if (budget.remaining <= 0) {
|
|
125
|
+
budget.truncated = true;
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
let propertyValue;
|
|
129
|
+
try {
|
|
130
|
+
propertyValue = value[key];
|
|
131
|
+
} catch {
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (attributeCollector) try {
|
|
135
|
+
collectFlattenedAttributes(propertyValue, key, attributeCollector);
|
|
136
|
+
} catch {}
|
|
137
|
+
if (!isJSONSerializablePrimitive(propertyValue)) continue;
|
|
138
|
+
const propertyPrefix = `${isFirst ? '' : ','}${JSON.stringify(key)}:`;
|
|
139
|
+
if (propertyPrefix.length >= budget.remaining) {
|
|
140
|
+
budget.truncated = true;
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
const partsBeforeProperty = parts.length;
|
|
144
|
+
const remainingBeforeProperty = budget.remaining;
|
|
145
|
+
const truncatedBeforeProperty = budget.truncated;
|
|
146
|
+
if (!appendWithLimit(parts, propertyPrefix, budget)) return false;
|
|
147
|
+
const partsBeforeValue = parts.length;
|
|
148
|
+
const serialized = stringifyValueWithLimit(propertyValue, parts, budget, seen, false);
|
|
149
|
+
const truncatedAfterValue = budget.truncated;
|
|
150
|
+
if (parts.length === partsBeforeValue) {
|
|
151
|
+
parts.length = partsBeforeProperty;
|
|
152
|
+
budget.remaining = remainingBeforeProperty;
|
|
153
|
+
budget.truncated = serialized ? truncatedBeforeProperty : truncatedAfterValue;
|
|
154
|
+
if (!serialized) return false;
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
isFirst = false;
|
|
158
|
+
if (!serialized) return false;
|
|
159
|
+
}
|
|
160
|
+
} catch {}
|
|
161
|
+
return appendWithLimit(parts, '}', budget);
|
|
162
|
+
};
|
|
163
|
+
const stringifyArgsSafely = (args, sizeLimit)=>{
|
|
164
|
+
const parts = [];
|
|
165
|
+
const budget = {
|
|
166
|
+
remaining: sizeLimit,
|
|
167
|
+
truncated: false
|
|
168
|
+
};
|
|
169
|
+
const attributeCollector = isObject(args[0]) ? {
|
|
170
|
+
result: {},
|
|
171
|
+
keysRemaining: LOG_ATTRIBUTES_LIMIT,
|
|
172
|
+
sizeRemaining: LOG_BODY_SIZE_LIMIT,
|
|
173
|
+
truncated: false,
|
|
174
|
+
seen: new WeakSet([
|
|
175
|
+
args[0]
|
|
176
|
+
])
|
|
177
|
+
} : void 0;
|
|
178
|
+
for(let i = 0; i < args.length; i++){
|
|
179
|
+
if (i > 0 && !appendWithLimit(parts, ' ', budget)) break;
|
|
180
|
+
if (!stringifyValueWithLimit(args[i], parts, budget, new WeakSet(), false, 0 === i ? attributeCollector : void 0)) break;
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
body: parts.join('') + (budget.truncated ? '...' : ''),
|
|
184
|
+
truncated: budget.truncated,
|
|
185
|
+
attributes: attributeCollector?.result || {}
|
|
186
|
+
};
|
|
187
|
+
};
|
|
188
|
+
const LEVEL_MAP = {
|
|
189
|
+
debug: 'debug',
|
|
190
|
+
log: 'info',
|
|
191
|
+
warn: 'warn',
|
|
192
|
+
error: 'error',
|
|
193
|
+
info: 'info'
|
|
194
|
+
};
|
|
195
|
+
const initializeLogs = (host)=>{
|
|
196
|
+
const console = host.console;
|
|
197
|
+
let currentHost = host;
|
|
198
|
+
const restoreConsoleMethods = [];
|
|
199
|
+
const attributes = {
|
|
200
|
+
host: host.hostname
|
|
201
|
+
};
|
|
202
|
+
let isCapturingLog = false;
|
|
203
|
+
const dispose = ()=>{
|
|
204
|
+
currentHost = void 0;
|
|
205
|
+
for (const restore of restoreConsoleMethods)try {
|
|
206
|
+
restore();
|
|
207
|
+
} catch {}
|
|
208
|
+
};
|
|
209
|
+
try {
|
|
210
|
+
for (const level of Object.keys(LEVEL_MAP)){
|
|
211
|
+
const logWrapper = (next)=>(...args)=>{
|
|
212
|
+
let acquiredGuard = false;
|
|
213
|
+
try {
|
|
214
|
+
const activeHost = currentHost;
|
|
215
|
+
if (args.length > 0 && !isCapturingLog && activeHost) {
|
|
216
|
+
const logs = activeHost.getCapturingLogs();
|
|
217
|
+
if (logs) {
|
|
218
|
+
isCapturingLog = true;
|
|
219
|
+
acquiredGuard = true;
|
|
220
|
+
const { body, truncated, attributes: flattenedAttributes } = stringifyArgsSafely(args, LOG_BODY_SIZE_LIMIT);
|
|
221
|
+
const logAttributes = {
|
|
222
|
+
...attributes,
|
|
223
|
+
...truncated ? {
|
|
224
|
+
body_truncated: 'true'
|
|
225
|
+
} : {}
|
|
226
|
+
};
|
|
227
|
+
const options = {
|
|
228
|
+
level: LEVEL_MAP[level],
|
|
229
|
+
body,
|
|
230
|
+
attributes: {
|
|
231
|
+
'log.source': `console.${level}`,
|
|
232
|
+
...logAttributes,
|
|
233
|
+
...flattenedAttributes
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
logs.captureConsoleLog(options);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
} catch {} finally{
|
|
240
|
+
if (acquiredGuard) isCapturingLog = false;
|
|
241
|
+
next.apply(console, args);
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
const consoleBeforePatch = console[level];
|
|
245
|
+
restoreConsoleMethods.push(patch(console, level, (next)=>{
|
|
246
|
+
const wrapped = logWrapper(next);
|
|
247
|
+
wrapped.__rrweb_original__ = originalConsoleMethod(consoleBeforePatch);
|
|
248
|
+
return wrapped;
|
|
249
|
+
}));
|
|
250
|
+
}
|
|
251
|
+
} catch (error) {
|
|
252
|
+
dispose();
|
|
253
|
+
throw error;
|
|
254
|
+
}
|
|
255
|
+
return dispose;
|
|
256
|
+
};
|
|
257
|
+
const replayConsoleBuffer = (host, entries)=>{
|
|
258
|
+
const logs = host.getCapturingLogs();
|
|
259
|
+
if (!logs) return;
|
|
260
|
+
for (const entry of entries)try {
|
|
261
|
+
const { body, truncated, attributes: flattenedAttributes } = stringifyArgsSafely(entry.args, LOG_BODY_SIZE_LIMIT);
|
|
262
|
+
logs.captureBufferedConsoleLog?.({
|
|
263
|
+
level: LEVEL_MAP[entry.level],
|
|
264
|
+
body,
|
|
265
|
+
attributes: {
|
|
266
|
+
'log.source': `console.${entry.level}`,
|
|
267
|
+
host: host.hostname,
|
|
268
|
+
...truncated ? {
|
|
269
|
+
body_truncated: 'true'
|
|
270
|
+
} : {},
|
|
271
|
+
...flattenedAttributes
|
|
272
|
+
}
|
|
273
|
+
}, entry.context, entry.occurredAtMs);
|
|
274
|
+
} catch {}
|
|
275
|
+
};
|
|
276
|
+
export { initializeLogs, replayConsoleBuffer };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { PostHogFeatureFlags } from './feature-flags';
|
|
2
|
+
import type { ExtensionToken } from './token';
|
|
3
|
+
/** Resolve the common feature-flags API without importing its implementation. */
|
|
4
|
+
export declare const FeatureFlagsCommonExtension: ExtensionToken<PostHogFeatureFlags>;
|