@newtalaria/browser 0.1.9 → 0.1.12
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/README.md +4 -2
- package/dist/client.d.ts +6 -3
- package/dist/client.d.ts.map +1 -1
- package/dist/index.js +299 -53
- package/dist/index.js.map +4 -4
- package/dist/replay/fit_segment.d.ts +9 -1
- package/dist/replay/fit_segment.d.ts.map +1 -1
- package/dist/replay/paint_base_size.d.ts +2 -1
- package/dist/replay/paint_base_size.d.ts.map +1 -1
- package/dist/replay/segment_buffer.d.ts +15 -3
- package/dist/replay/segment_buffer.d.ts.map +1 -1
- package/dist/sdk_meta.d.ts +4 -0
- package/dist/sdk_meta.d.ts.map +1 -0
- package/dist/talaria.browser.iife.js +299 -53
- package/dist/talaria.browser.iife.js.map +4 -4
- package/dist/utils/browser_context.d.ts +28 -0
- package/dist/utils/browser_context.d.ts.map +1 -0
- package/dist/utils/browser_extension_noise.d.ts +11 -0
- package/dist/utils/browser_extension_noise.d.ts.map +1 -0
- package/package.json +1 -1
|
@@ -23,11 +23,19 @@ export declare function planOversizedRetry(events: RrwebEvent[]): {
|
|
|
23
23
|
left: RrwebEvent[];
|
|
24
24
|
right: RrwebEvent[];
|
|
25
25
|
};
|
|
26
|
+
/** Leading Meta+FullSnapshot (2) or FullSnapshot alone (1); else 0. */
|
|
27
|
+
export declare function paintBaseEventCount(events: RrwebEvent[]): number;
|
|
26
28
|
/**
|
|
27
29
|
* Binary-search the largest prefix that gzips under the pack target.
|
|
28
30
|
* Remainder is returned for re-queue; null means every event was dropped.
|
|
31
|
+
*
|
|
32
|
+
* When `atomicMinCount` is set (segment-0 paint base), never return a prefix
|
|
33
|
+
* shorter than that count unless the atomic unit itself exceeds `hardCap`
|
|
34
|
+
* (then null — caller should abort as oversized, not ship Meta-only).
|
|
29
35
|
*/
|
|
30
|
-
export declare function fitCompressedPrefix(events: RrwebEvent[], compress: CompressEvents, targetBytes?: number, hardCap?: number
|
|
36
|
+
export declare function fitCompressedPrefix(events: RrwebEvent[], compress: CompressEvents, targetBytes?: number, hardCap?: number, opts?: {
|
|
37
|
+
atomicMinCount?: number;
|
|
38
|
+
}): Promise<{
|
|
31
39
|
events: RrwebEvent[];
|
|
32
40
|
gzip: Uint8Array;
|
|
33
41
|
remainder: RrwebEvent[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fit_segment.d.ts","sourceRoot":"","sources":["../../src/replay/fit_segment.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"fit_segment.d.ts","sourceRoot":"","sources":["../../src/replay/fit_segment.ts"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,UAAU,EAChB,MAAM,qBAAqB,CAAC;AAE7B,MAAM,MAAM,cAAc,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;AAE3E;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,iBAAiB,EAAE,MAAM,EACzB,OAAO,EAAE,MAAM,EACf,OAAO,SAA4B,GAClC,MAAM,CAER;AAED,2EAA2E;AAC3E,wBAAgB,0BAA0B,CAAC,IAAI,EAAE;IAC/C,YAAY,EAAE,MAAM,CAAC;IACrB,uBAAuB,EAAE,MAAM,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAOV;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,UAAU,EAAE,GACnB;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,UAAU,EAAE,CAAC;IAAC,KAAK,EAAE,UAAU,EAAE,CAAA;CAAE,CAUpF;AAED,uEAAuE;AACvE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,CAYhE;AAED;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,UAAU,EAAE,EACpB,QAAQ,EAAE,cAAc,EACxB,WAAW,SAAkC,EAC7C,OAAO,SAA+B,EACtC,IAAI,CAAC,EAAE;IAAE,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GACjC,OAAO,CAAC;IACT,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,IAAI,EAAE,UAAU,CAAC;IACjB,SAAS,EAAE,UAAU,EAAE,CAAC;CACzB,GAAG,IAAI,CAAC,CA2ER"}
|
|
@@ -8,7 +8,8 @@ export type PaintBaseSizeDetails = {
|
|
|
8
8
|
fullSnapshotEstimatedBytes?: number;
|
|
9
9
|
/** Actual gzip size of FullSnapshot alone, when measured. */
|
|
10
10
|
fullSnapshotCompressedBytes?: number;
|
|
11
|
-
|
|
11
|
+
/** Soft estimated take window (not the server uncompressed hard cap). */
|
|
12
|
+
softTakeEstimatedBytes: number;
|
|
12
13
|
targetCompressedSegmentBytes: number;
|
|
13
14
|
maxCompressedSegmentBytes: number;
|
|
14
15
|
} & Record<string, unknown>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paint_base_size.d.ts","sourceRoot":"","sources":["../../src/replay/paint_base_size.ts"],"names":[],"mappings":"AACA,OAAO,EAML,KAAK,UAAU,EAChB,MAAM,qBAAqB,CAAC;AAE7B,gEAAgE;AAChE,MAAM,MAAM,oBAAoB,GAAG;IACjC,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,0DAA0D;IAC1D,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,4DAA4D;IAC5D,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC,6DAA6D;IAC7D,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,
|
|
1
|
+
{"version":3,"file":"paint_base_size.d.ts","sourceRoot":"","sources":["../../src/replay/paint_base_size.ts"],"names":[],"mappings":"AACA,OAAO,EAML,KAAK,UAAU,EAChB,MAAM,qBAAqB,CAAC;AAE7B,gEAAgE;AAChE,MAAM,MAAM,oBAAoB,GAAG;IACjC,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,0DAA0D;IAC1D,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,4DAA4D;IAC5D,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC,6DAA6D;IAC7D,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,yEAAyE;IACzE,sBAAsB,EAAE,MAAM,CAAC;IAC/B,4BAA4B,EAAE,MAAM,CAAC;IACrC,yBAAyB,EAAE,MAAM,CAAC;CACnC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE5B,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,UAAU,EAAE,EACpB,IAAI,CAAC,EAAE;IAAE,2BAA2B,CAAC,EAAE,MAAM,CAAA;CAAE,GAC9C,oBAAoB,CAsBtB"}
|
|
@@ -5,8 +5,9 @@ export interface RrwebEvent {
|
|
|
5
5
|
timestamp: number;
|
|
6
6
|
}
|
|
7
7
|
/**
|
|
8
|
-
* Soft
|
|
9
|
-
*
|
|
8
|
+
* Soft estimated-JSON take size before gzip fitting.
|
|
9
|
+
* Not a hard server limit — packing uses MAX_COMPRESSED_SEGMENT_BYTES.
|
|
10
|
+
* Paint base (Meta+FullSnapshot) is taken atomically and may exceed this.
|
|
10
11
|
*/
|
|
11
12
|
export declare const SEGMENT_SIZE_BYTES = 800000;
|
|
12
13
|
/**
|
|
@@ -66,12 +67,23 @@ export declare class SegmentBuffer {
|
|
|
66
67
|
*/
|
|
67
68
|
trimRing(now?: number, maxBytes?: number): void;
|
|
68
69
|
shouldFlushBySize(): boolean;
|
|
70
|
+
/**
|
|
71
|
+
* How many leading events form the rrweb paint base (Meta+FullSnapshot or
|
|
72
|
+
* FullSnapshot alone). Returns 0 if the buffer does not start with a paint base.
|
|
73
|
+
*/
|
|
74
|
+
paintBaseLength(): number;
|
|
69
75
|
/**
|
|
70
76
|
* Take a prefix of events whose estimated JSON size is ~maxBytes.
|
|
71
77
|
* Always returns at least one event when the buffer is non-empty (caller
|
|
72
78
|
* must handle a single oversized event).
|
|
79
|
+
*
|
|
80
|
+
* When `keepPaintBaseTogether` is true (segment 0), Meta immediately followed
|
|
81
|
+
* by FullSnapshot is taken as one unit even if estimated size exceeds
|
|
82
|
+
* maxBytes — gzip fitting / hard caps decide whether it can ship.
|
|
73
83
|
*/
|
|
74
|
-
takeByEstimatedBytes(maxBytes: number
|
|
84
|
+
takeByEstimatedBytes(maxBytes: number, opts?: {
|
|
85
|
+
keepPaintBaseTogether?: boolean;
|
|
86
|
+
}): RrwebEvent[];
|
|
75
87
|
takeAll(): RrwebEvent[];
|
|
76
88
|
/** Re-queue events at the front (failed upload retry / bisect remainder). */
|
|
77
89
|
prepend(events: RrwebEvent[]): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"segment_buffer.d.ts","sourceRoot":"","sources":["../../src/replay/segment_buffer.ts"],"names":[],"mappings":"AAEA,oDAAoD;AACpD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAED
|
|
1
|
+
{"version":3,"file":"segment_buffer.d.ts","sourceRoot":"","sources":["../../src/replay/segment_buffer.ts"],"names":[],"mappings":"AAEA,oDAAoD;AACpD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,SAAU,CAAC;AAC1C;;;GAGG;AACH,eAAO,MAAM,4BAA4B,QAAc,CAAC;AACxD,iFAAiF;AACjF,eAAO,MAAM,+BAA+B,QACF,CAAC;AAC3C;;;GAGG;AACH,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAC3C;;GAEG;AACH,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAC1C,uEAAuE;AACvE,eAAO,MAAM,+BAA+B,QAAkB,CAAC;AAC/D,8DAA8D;AAC9D,eAAO,MAAM,yBAAyB,OAAQ,CAAC;AAC/C,+BAA+B;AAC/B,eAAO,MAAM,gBAAgB,OAAQ,CAAC;AACtC,oEAAoE;AACpE,eAAO,MAAM,cAAc,QAAS,CAAC;AACrC,6DAA6D;AAC7D,eAAO,MAAM,qBAAqB,UAAY,CAAC;AAC/C,2DAA2D;AAC3D,eAAO,MAAM,qBAAqB,QAAS,CAAC;AAC5C;;;GAGG;AACH,eAAO,MAAM,sBAAsB,QAAgB,CAAC;AAEpD,mCAAmC;AACnC,eAAO,MAAM,mBAAmB,IAAI,CAAC;AACrC,2BAA2B;AAC3B,eAAO,MAAM,UAAU,IAAI,CAAC;AAE5B,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,YAAY,CAAK;IAEzB,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,IAAI,cAAc,IAAI,MAAM,CAE3B;IAED,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAK7B,eAAe,IAAI,OAAO;IAI1B,4DAA4D;IAC5D,gBAAgB,IAAI,UAAU,GAAG,SAAS;IAI1C;;;OAGG;IACH,kBAAkB,IAAI,OAAO;IAc7B;;;OAGG;IACH,QAAQ,CAAC,GAAG,SAAa,EAAE,QAAQ,SAAwB,GAAG,IAAI;IAiBlE,iBAAiB,IAAI,OAAO;IAI5B;;;OAGG;IACH,eAAe,IAAI,MAAM;IAczB;;;;;;;;OAQG;IACH,oBAAoB,CAClB,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE;QAAE,qBAAqB,CAAC,EAAE,OAAO,CAAA;KAAE,GACzC,UAAU,EAAE;IAqCf,OAAO,IAAI,UAAU,EAAE;IAOvB,6EAA6E;IAC7E,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,IAAI;IAMnC,KAAK,IAAI,IAAI;IAKb,SAAS,IAAI;QAAE,SAAS,EAAE,IAAI,CAAC;QAAC,OAAO,EAAE,IAAI,CAAA;KAAE,GAAG,IAAI;IAUtD,OAAO,CAAC,cAAc;CAGvB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sdk_meta.d.ts","sourceRoot":"","sources":["../src/sdk_meta.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,eAAO,MAAM,QAAQ,wBAAwB,CAAC;AAC9C,eAAO,MAAM,WAAW,WAAW,CAAC"}
|
|
@@ -17,6 +17,21 @@
|
|
|
17
17
|
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
// src/utils/browser_extension_noise.ts
|
|
21
|
+
var EXTENSION_URL = /(?:chrome|moz|safari|safari-web|ms-browser)-extension:\/\//i;
|
|
22
|
+
var WEBKIT_MASKED = /webkit-masked-url:\/\/hidden\//i;
|
|
23
|
+
function isBrowserExtensionNoise(opts) {
|
|
24
|
+
const filename = opts.filename ?? "";
|
|
25
|
+
if (EXTENSION_URL.test(filename) || WEBKIT_MASKED.test(filename)) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
const stack = opts.stack ?? "";
|
|
29
|
+
if (EXTENSION_URL.test(stack) || WEBKIT_MASKED.test(stack)) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
|
|
20
35
|
// src/utils/opaque_script_error.ts
|
|
21
36
|
function isOpaqueCrossOriginScriptError(event) {
|
|
22
37
|
const msg = (event.message ?? "").trim();
|
|
@@ -25,6 +40,137 @@
|
|
|
25
40
|
return true;
|
|
26
41
|
}
|
|
27
42
|
|
|
43
|
+
// src/utils/browser_context.ts
|
|
44
|
+
function parseBrowserContext(ua = typeof navigator !== "undefined" ? navigator.userAgent : "", language = typeof navigator !== "undefined" ? navigator.language : "") {
|
|
45
|
+
const userAgent = ua || "";
|
|
46
|
+
const lang = language || "";
|
|
47
|
+
let name = "unknown";
|
|
48
|
+
let version = "";
|
|
49
|
+
const rules = [
|
|
50
|
+
{ name: "Edge", re: /Edg(?:e|A|iOS)?\/([\d.]+)/ },
|
|
51
|
+
{ name: "Opera", re: /OPR\/([\d.]+)/ },
|
|
52
|
+
{ name: "Samsung Internet", re: /SamsungBrowser\/([\d.]+)/ },
|
|
53
|
+
{ name: "Firefox", re: /Firefox\/([\d.]+)/ },
|
|
54
|
+
{ name: "Chrome", re: /(?:Chrome|CriOS)\/([\d.]+)/ },
|
|
55
|
+
{ name: "Safari", re: /Version\/([\d.]+).*Safari/ }
|
|
56
|
+
];
|
|
57
|
+
for (const rule2 of rules) {
|
|
58
|
+
const m = userAgent.match(rule2.re);
|
|
59
|
+
if (m) {
|
|
60
|
+
name = rule2.name;
|
|
61
|
+
version = m[1] ?? "";
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (name === "unknown" && /iPhone|iPad|iPod/.test(userAgent) && /AppleWebKit/.test(userAgent)) {
|
|
66
|
+
name = "Safari";
|
|
67
|
+
const m = userAgent.match(/OS ([\d_]+)/);
|
|
68
|
+
if (m?.[1]) version = m[1].replace(/_/g, ".");
|
|
69
|
+
}
|
|
70
|
+
let os = "unknown";
|
|
71
|
+
let osVersion = "";
|
|
72
|
+
const win = userAgent.match(/Windows NT ([\d.]+)/);
|
|
73
|
+
if (win) {
|
|
74
|
+
os = "Windows";
|
|
75
|
+
osVersion = win[1] ?? "";
|
|
76
|
+
} else {
|
|
77
|
+
const android = userAgent.match(/Android ([\d.]+)/);
|
|
78
|
+
if (android) {
|
|
79
|
+
os = "Android";
|
|
80
|
+
osVersion = android[1] ?? "";
|
|
81
|
+
} else {
|
|
82
|
+
const ios = userAgent.match(/(?:iPhone|iPad|iPod).*OS ([\d_]+)/);
|
|
83
|
+
if (ios) {
|
|
84
|
+
os = "iOS";
|
|
85
|
+
osVersion = (ios[1] ?? "").replace(/_/g, ".");
|
|
86
|
+
} else {
|
|
87
|
+
const mac = userAgent.match(/Mac OS X ([\d_]+)/);
|
|
88
|
+
if (mac) {
|
|
89
|
+
os = "macOS";
|
|
90
|
+
osVersion = (mac[1] ?? "").replace(/_/g, ".");
|
|
91
|
+
} else if (/CrOS/.test(userAgent)) {
|
|
92
|
+
os = "Chrome OS";
|
|
93
|
+
} else if (/Linux/.test(userAgent)) {
|
|
94
|
+
os = "Linux";
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
let device = "desktop";
|
|
100
|
+
if (/iPad|Tablet|Android(?!.*Mobile)/i.test(userAgent)) {
|
|
101
|
+
device = "tablet";
|
|
102
|
+
} else if (/Mobi|iPhone|iPod|Android.*Mobile/i.test(userAgent)) {
|
|
103
|
+
device = "mobile";
|
|
104
|
+
} else if (!userAgent) {
|
|
105
|
+
device = "unknown";
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
name,
|
|
109
|
+
version,
|
|
110
|
+
os,
|
|
111
|
+
osVersion,
|
|
112
|
+
device,
|
|
113
|
+
language: lang,
|
|
114
|
+
userAgent
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function browserContextTags(ctx) {
|
|
118
|
+
return {
|
|
119
|
+
"browser.name": ctx.name,
|
|
120
|
+
"browser.version": ctx.version || "unknown",
|
|
121
|
+
"os.name": ctx.os,
|
|
122
|
+
"os.version": ctx.osVersion || "unknown",
|
|
123
|
+
device: ctx.device
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
async function collectBrowserContext() {
|
|
127
|
+
const base = parseBrowserContext();
|
|
128
|
+
const nav = typeof navigator !== "undefined" ? navigator : void 0;
|
|
129
|
+
const uad = nav?.userAgentData;
|
|
130
|
+
if (!uad) return base;
|
|
131
|
+
if (uad.mobile === true) base.device = "mobile";
|
|
132
|
+
if (uad.platform) {
|
|
133
|
+
const p = uad.platform;
|
|
134
|
+
if (/Windows/i.test(p)) base.os = "Windows";
|
|
135
|
+
else if (/macOS|Mac OS/i.test(p)) base.os = "macOS";
|
|
136
|
+
else if (/Android/i.test(p)) base.os = "Android";
|
|
137
|
+
else if (/iOS|iPhone/i.test(p)) base.os = "iOS";
|
|
138
|
+
else if (/Linux/i.test(p)) base.os = "Linux";
|
|
139
|
+
else if (/Chrome OS|Chromium OS/i.test(p)) base.os = "Chrome OS";
|
|
140
|
+
else base.os = p;
|
|
141
|
+
}
|
|
142
|
+
const brands = uad.brands ?? [];
|
|
143
|
+
const interesting = brands.find(
|
|
144
|
+
(b) => !/Not.?A.?Brand/i.test(b.brand) && !/Chromium/i.test(b.brand)
|
|
145
|
+
);
|
|
146
|
+
if (interesting) {
|
|
147
|
+
base.name = interesting.brand.replace(/^Google /, "");
|
|
148
|
+
base.version = interesting.version;
|
|
149
|
+
}
|
|
150
|
+
try {
|
|
151
|
+
if (typeof uad.getHighEntropyValues === "function") {
|
|
152
|
+
const hi = await uad.getHighEntropyValues([
|
|
153
|
+
"platformVersion",
|
|
154
|
+
"fullVersionList"
|
|
155
|
+
]);
|
|
156
|
+
if (hi.platformVersion) base.osVersion = hi.platformVersion;
|
|
157
|
+
const full = hi.fullVersionList?.find(
|
|
158
|
+
(b) => !/Not.?A.?Brand/i.test(b.brand) && !/Chromium/i.test(b.brand)
|
|
159
|
+
);
|
|
160
|
+
if (full) {
|
|
161
|
+
base.name = full.brand.replace(/^Google /, "");
|
|
162
|
+
base.version = full.version;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
} catch {
|
|
166
|
+
}
|
|
167
|
+
return base;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// src/sdk_meta.ts
|
|
171
|
+
var SDK_NAME = "@newtalaria/browser";
|
|
172
|
+
var SDK_VERSION = "0.1.12";
|
|
173
|
+
|
|
28
174
|
// src/transport/serverpod.ts
|
|
29
175
|
var ServerpodTransport = class {
|
|
30
176
|
constructor(options) {
|
|
@@ -206,15 +352,44 @@
|
|
|
206
352
|
shouldFlushBySize() {
|
|
207
353
|
return this.pendingBytes >= SEGMENT_SIZE_BYTES;
|
|
208
354
|
}
|
|
355
|
+
/**
|
|
356
|
+
* How many leading events form the rrweb paint base (Meta+FullSnapshot or
|
|
357
|
+
* FullSnapshot alone). Returns 0 if the buffer does not start with a paint base.
|
|
358
|
+
*/
|
|
359
|
+
paintBaseLength() {
|
|
360
|
+
if (this.events.length === 0) return 0;
|
|
361
|
+
if (this.events[0].type === RRWEB_META && this.events[1]?.type === RRWEB_FULL_SNAPSHOT) {
|
|
362
|
+
return 2;
|
|
363
|
+
}
|
|
364
|
+
if (this.events[0].type === RRWEB_FULL_SNAPSHOT) {
|
|
365
|
+
return 1;
|
|
366
|
+
}
|
|
367
|
+
return 0;
|
|
368
|
+
}
|
|
209
369
|
/**
|
|
210
370
|
* Take a prefix of events whose estimated JSON size is ~maxBytes.
|
|
211
371
|
* Always returns at least one event when the buffer is non-empty (caller
|
|
212
372
|
* must handle a single oversized event).
|
|
373
|
+
*
|
|
374
|
+
* When `keepPaintBaseTogether` is true (segment 0), Meta immediately followed
|
|
375
|
+
* by FullSnapshot is taken as one unit even if estimated size exceeds
|
|
376
|
+
* maxBytes — gzip fitting / hard caps decide whether it can ship.
|
|
213
377
|
*/
|
|
214
|
-
takeByEstimatedBytes(maxBytes) {
|
|
378
|
+
takeByEstimatedBytes(maxBytes, opts) {
|
|
215
379
|
if (this.events.length === 0) return [];
|
|
216
380
|
const batch = [];
|
|
217
381
|
let batchBytes = 0;
|
|
382
|
+
if (opts?.keepPaintBaseTogether) {
|
|
383
|
+
const paintLen = this.paintBaseLength();
|
|
384
|
+
if (paintLen > 0) {
|
|
385
|
+
for (let i = 0; i < paintLen; i++) {
|
|
386
|
+
const ev = this.events.shift();
|
|
387
|
+
batch.push(ev);
|
|
388
|
+
batchBytes += estimateJsonBytes(ev);
|
|
389
|
+
}
|
|
390
|
+
this.recomputeBytes();
|
|
391
|
+
}
|
|
392
|
+
}
|
|
218
393
|
while (this.events.length > 0) {
|
|
219
394
|
const next = this.events[0];
|
|
220
395
|
const nextBytes = estimateJsonBytes(next);
|
|
@@ -339,8 +514,53 @@
|
|
|
339
514
|
right: events.slice(mid)
|
|
340
515
|
};
|
|
341
516
|
}
|
|
342
|
-
|
|
517
|
+
function paintBaseEventCount(events) {
|
|
518
|
+
if (events.length === 0) return 0;
|
|
519
|
+
if (events[0].type === RRWEB_META && events[1]?.type === RRWEB_FULL_SNAPSHOT) {
|
|
520
|
+
return 2;
|
|
521
|
+
}
|
|
522
|
+
if (events[0].type === RRWEB_FULL_SNAPSHOT) {
|
|
523
|
+
return 1;
|
|
524
|
+
}
|
|
525
|
+
return 0;
|
|
526
|
+
}
|
|
527
|
+
async function fitCompressedPrefix(events, compress, targetBytes = TARGET_COMPRESSED_SEGMENT_BYTES, hardCap = MAX_COMPRESSED_SEGMENT_BYTES, opts) {
|
|
343
528
|
if (events.length === 0) return null;
|
|
529
|
+
const atomicMin = opts?.atomicMinCount ?? 0;
|
|
530
|
+
if (atomicMin > 0) {
|
|
531
|
+
if (events.length < atomicMin) return null;
|
|
532
|
+
const paint = events.slice(0, atomicMin);
|
|
533
|
+
const paintGzip = await compress(paint);
|
|
534
|
+
if (paintGzip.length > hardCap) {
|
|
535
|
+
return null;
|
|
536
|
+
}
|
|
537
|
+
let lo = atomicMin;
|
|
538
|
+
let hi = events.length;
|
|
539
|
+
let best = {
|
|
540
|
+
events: paint,
|
|
541
|
+
gzip: paintGzip
|
|
542
|
+
};
|
|
543
|
+
while (lo <= hi) {
|
|
544
|
+
const mid = Math.floor((lo + hi) / 2);
|
|
545
|
+
if (mid < atomicMin) {
|
|
546
|
+
lo = atomicMin;
|
|
547
|
+
continue;
|
|
548
|
+
}
|
|
549
|
+
const prefix = events.slice(0, mid);
|
|
550
|
+
const gzip = await compress(prefix);
|
|
551
|
+
if (gzip.length <= targetBytes && gzip.length <= hardCap) {
|
|
552
|
+
best = { events: prefix, gzip };
|
|
553
|
+
lo = mid + 1;
|
|
554
|
+
} else {
|
|
555
|
+
hi = mid - 1;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
return {
|
|
559
|
+
events: best.events,
|
|
560
|
+
gzip: best.gzip,
|
|
561
|
+
remainder: events.slice(best.events.length)
|
|
562
|
+
};
|
|
563
|
+
}
|
|
344
564
|
let remaining = events;
|
|
345
565
|
while (remaining.length > 0) {
|
|
346
566
|
let lo = 1;
|
|
@@ -402,7 +622,7 @@
|
|
|
402
622
|
const full = events.find((e) => e.type === RRWEB_FULL_SNAPSHOT);
|
|
403
623
|
const details = {
|
|
404
624
|
segmentEventTypes: events.map((e) => e.type),
|
|
405
|
-
|
|
625
|
+
softTakeEstimatedBytes: SEGMENT_SIZE_BYTES,
|
|
406
626
|
targetCompressedSegmentBytes: TARGET_COMPRESSED_SEGMENT_BYTES,
|
|
407
627
|
maxCompressedSegmentBytes: MAX_COMPRESSED_SEGMENT_BYTES
|
|
408
628
|
};
|
|
@@ -13464,6 +13684,8 @@
|
|
|
13464
13684
|
this.linkableReplayId = null;
|
|
13465
13685
|
/** Last error-clip paint/upload failure (cleared on successful segment 0). */
|
|
13466
13686
|
this.lastReplayCaptureFailure = null;
|
|
13687
|
+
/** Cached once per init for event tags/extra. */
|
|
13688
|
+
this.browserContext = null;
|
|
13467
13689
|
}
|
|
13468
13690
|
init(options) {
|
|
13469
13691
|
if (this.options) {
|
|
@@ -13486,6 +13708,10 @@
|
|
|
13486
13708
|
this.errorClipDeadlineMs = null;
|
|
13487
13709
|
this.linkableReplayId = null;
|
|
13488
13710
|
this.lastReplayCaptureFailure = null;
|
|
13711
|
+
this.browserContext = parseBrowserContext();
|
|
13712
|
+
void collectBrowserContext().then((ctx) => {
|
|
13713
|
+
if (this.options) this.browserContext = ctx;
|
|
13714
|
+
});
|
|
13489
13715
|
this.buffer = new SegmentBuffer();
|
|
13490
13716
|
this.uploadChain = Promise.resolve();
|
|
13491
13717
|
this.sessionSampled = Math.random() < this.options.replaysSessionSampleRate;
|
|
@@ -13555,6 +13781,14 @@
|
|
|
13555
13781
|
}
|
|
13556
13782
|
async captureException(error, context) {
|
|
13557
13783
|
const err = error instanceof Error ? error : new Error(typeof error === "string" ? error : "Unknown error");
|
|
13784
|
+
const filename = typeof context?.extra?.filename === "string" ? context.extra.filename : void 0;
|
|
13785
|
+
if (isBrowserExtensionNoise({
|
|
13786
|
+
message: err.message,
|
|
13787
|
+
stack: err.stack,
|
|
13788
|
+
filename
|
|
13789
|
+
})) {
|
|
13790
|
+
return;
|
|
13791
|
+
}
|
|
13558
13792
|
await this.capture({
|
|
13559
13793
|
message: err.message || err.name || "Error",
|
|
13560
13794
|
level: "error",
|
|
@@ -13648,6 +13882,7 @@
|
|
|
13648
13882
|
this.closed = false;
|
|
13649
13883
|
this.linkableReplayId = null;
|
|
13650
13884
|
this.lastReplayCaptureFailure = null;
|
|
13885
|
+
this.browserContext = null;
|
|
13651
13886
|
}
|
|
13652
13887
|
onRrwebEvent(event) {
|
|
13653
13888
|
if (this.closed || !this.options) return;
|
|
@@ -13731,13 +13966,28 @@
|
|
|
13731
13966
|
}
|
|
13732
13967
|
const tags = applyReplayCaptureTags(
|
|
13733
13968
|
{
|
|
13969
|
+
...this.browserContext ? browserContextTags(this.browserContext) : {},
|
|
13734
13970
|
...this.options.tags ?? {},
|
|
13735
13971
|
...args.context?.tags ?? {}
|
|
13736
13972
|
},
|
|
13737
13973
|
errorClipOutcome
|
|
13738
13974
|
);
|
|
13739
13975
|
const extra = mergeReplayCaptureExtra(
|
|
13740
|
-
|
|
13976
|
+
{
|
|
13977
|
+
...this.browserContext ? {
|
|
13978
|
+
browser: {
|
|
13979
|
+
name: this.browserContext.name,
|
|
13980
|
+
version: this.browserContext.version,
|
|
13981
|
+
os: this.browserContext.os,
|
|
13982
|
+
osVersion: this.browserContext.osVersion,
|
|
13983
|
+
device: this.browserContext.device,
|
|
13984
|
+
language: this.browserContext.language,
|
|
13985
|
+
userAgent: this.browserContext.userAgent
|
|
13986
|
+
}
|
|
13987
|
+
} : {},
|
|
13988
|
+
sdk: { name: SDK_NAME, version: SDK_VERSION },
|
|
13989
|
+
...args.context?.extra
|
|
13990
|
+
},
|
|
13741
13991
|
errorClipOutcome
|
|
13742
13992
|
);
|
|
13743
13993
|
try {
|
|
@@ -13961,6 +14211,13 @@
|
|
|
13961
14211
|
const onError = (event) => {
|
|
13962
14212
|
if (isOpaqueCrossOriginScriptError(event)) return;
|
|
13963
14213
|
const error = event.error instanceof Error ? event.error : new Error(event.message || "window.onerror");
|
|
14214
|
+
if (isBrowserExtensionNoise({
|
|
14215
|
+
message: event.message || error.message,
|
|
14216
|
+
stack: error.stack,
|
|
14217
|
+
filename: event.filename
|
|
14218
|
+
})) {
|
|
14219
|
+
return;
|
|
14220
|
+
}
|
|
13964
14221
|
void this.captureException(error, {
|
|
13965
14222
|
extra: {
|
|
13966
14223
|
filename: event.filename,
|
|
@@ -14055,20 +14312,9 @@
|
|
|
14055
14312
|
}
|
|
14056
14313
|
const { events, gzip } = fitted;
|
|
14057
14314
|
if (this.segmentIndex === 0 && !events.some((e) => e.type === RRWEB_FULL_SNAPSHOT)) {
|
|
14058
|
-
const leftoverFs = this.buffer.peekFullSnapshot();
|
|
14059
|
-
let compressedBytes;
|
|
14060
|
-
if (leftoverFs) {
|
|
14061
|
-
try {
|
|
14062
|
-
compressedBytes = (await compressReplayEvents([leftoverFs])).length;
|
|
14063
|
-
} catch {
|
|
14064
|
-
}
|
|
14065
|
-
}
|
|
14066
14315
|
await this.abortUnusableClip(
|
|
14067
14316
|
"no_full_snapshot",
|
|
14068
|
-
paintBaseSizeDetails(
|
|
14069
|
-
leftoverFs ? [...events, leftoverFs] : events,
|
|
14070
|
-
{ fullSnapshotCompressedBytes: compressedBytes }
|
|
14071
|
-
)
|
|
14317
|
+
paintBaseSizeDetails(events)
|
|
14072
14318
|
);
|
|
14073
14319
|
return;
|
|
14074
14320
|
}
|
|
@@ -14113,6 +14359,14 @@
|
|
|
14113
14359
|
break;
|
|
14114
14360
|
}
|
|
14115
14361
|
if (isOversizedSegmentError(error)) {
|
|
14362
|
+
if (this.segmentIndex === 0 && paintBaseEventCount(events) > 0) {
|
|
14363
|
+
await this.abortUnusableClip("oversized_full_snapshot", {
|
|
14364
|
+
source: "server_reject_paint_base",
|
|
14365
|
+
...paintBaseSizeDetails(events),
|
|
14366
|
+
rejectedCompressedBytes: gzip.length
|
|
14367
|
+
});
|
|
14368
|
+
return;
|
|
14369
|
+
}
|
|
14116
14370
|
const plan = planOversizedRetry(events);
|
|
14117
14371
|
if (plan.action === "drop") {
|
|
14118
14372
|
const droppedType = events[0]?.type;
|
|
@@ -14120,7 +14374,7 @@
|
|
|
14120
14374
|
"@newtalaria/browser: dropping rrweb event rejected as oversized",
|
|
14121
14375
|
{ type: droppedType }
|
|
14122
14376
|
);
|
|
14123
|
-
if (
|
|
14377
|
+
if (droppedType === RRWEB_FULL_SNAPSHOT) {
|
|
14124
14378
|
await this.abortUnusableClip(
|
|
14125
14379
|
"oversized_full_snapshot",
|
|
14126
14380
|
{
|
|
@@ -14158,68 +14412,60 @@
|
|
|
14158
14412
|
}
|
|
14159
14413
|
/**
|
|
14160
14414
|
* Pull the largest event prefix that gzips under the target compressed size.
|
|
14161
|
-
*
|
|
14162
|
-
*
|
|
14415
|
+
*
|
|
14416
|
+
* Segment 0 keeps Meta+FullSnapshot (paint base) atomic through take + fit so
|
|
14417
|
+
* a soft estimated-byte window cannot orphan the FullSnapshot.
|
|
14163
14418
|
*/
|
|
14164
14419
|
async takeFittedSegment() {
|
|
14165
|
-
const
|
|
14420
|
+
const isPaintBaseSegment = this.segmentIndex === 0;
|
|
14421
|
+
const chunk = this.buffer.takeByEstimatedBytes(SEGMENT_SIZE_BYTES, {
|
|
14422
|
+
keepPaintBaseTogether: isPaintBaseSegment
|
|
14423
|
+
});
|
|
14166
14424
|
if (chunk.length === 0) return null;
|
|
14425
|
+
const atomicMin = isPaintBaseSegment ? paintBaseEventCount(chunk) : 0;
|
|
14167
14426
|
const fitted = await fitCompressedPrefix(
|
|
14168
14427
|
chunk,
|
|
14169
14428
|
compressReplayEvents,
|
|
14170
14429
|
TARGET_COMPRESSED_SEGMENT_BYTES,
|
|
14171
|
-
MAX_COMPRESSED_SEGMENT_BYTES
|
|
14430
|
+
MAX_COMPRESSED_SEGMENT_BYTES,
|
|
14431
|
+
atomicMin > 0 ? { atomicMinCount: atomicMin } : void 0
|
|
14172
14432
|
);
|
|
14173
14433
|
if (!fitted) {
|
|
14174
|
-
|
|
14175
|
-
|
|
14176
|
-
"@newtalaria/browser: dropping rrweb event that exceeds max segment size",
|
|
14177
|
-
{ type: dropped?.type }
|
|
14178
|
-
);
|
|
14179
|
-
if (this.segmentIndex === 0 && dropped?.type === RRWEB_FULL_SNAPSHOT) {
|
|
14434
|
+
if (isPaintBaseSegment && atomicMin > 0) {
|
|
14435
|
+
const paint = chunk.slice(0, atomicMin);
|
|
14180
14436
|
let compressedBytes;
|
|
14181
|
-
|
|
14182
|
-
|
|
14183
|
-
|
|
14437
|
+
const full = paint.find((e) => e.type === RRWEB_FULL_SNAPSHOT);
|
|
14438
|
+
if (full) {
|
|
14439
|
+
try {
|
|
14440
|
+
compressedBytes = (await compressReplayEvents([full])).length;
|
|
14441
|
+
} catch {
|
|
14442
|
+
}
|
|
14443
|
+
}
|
|
14444
|
+
if (chunk.length > atomicMin) {
|
|
14445
|
+
this.buffer.prepend(chunk.slice(atomicMin));
|
|
14184
14446
|
}
|
|
14185
14447
|
return {
|
|
14186
14448
|
events: [],
|
|
14187
14449
|
gzip: new Uint8Array(),
|
|
14188
14450
|
abortReason: "oversized_full_snapshot",
|
|
14189
14451
|
abortDetails: {
|
|
14190
|
-
source: "
|
|
14191
|
-
...paintBaseSizeDetails(
|
|
14452
|
+
source: "paint_base_exceeds_hard_cap",
|
|
14453
|
+
...paintBaseSizeDetails(paint, {
|
|
14192
14454
|
fullSnapshotCompressedBytes: compressedBytes
|
|
14193
14455
|
})
|
|
14194
14456
|
}
|
|
14195
14457
|
};
|
|
14196
14458
|
}
|
|
14459
|
+
const dropped = chunk[0];
|
|
14460
|
+
console.warn(
|
|
14461
|
+
"@newtalaria/browser: dropping rrweb event that exceeds max segment size",
|
|
14462
|
+
{ type: dropped?.type }
|
|
14463
|
+
);
|
|
14197
14464
|
if (chunk.length > 1) {
|
|
14198
14465
|
this.buffer.prepend(chunk.slice(1));
|
|
14199
14466
|
}
|
|
14200
14467
|
return this.takeFittedSegment();
|
|
14201
14468
|
}
|
|
14202
|
-
if (this.segmentIndex === 0 && chunk.some((e) => e.type === RRWEB_FULL_SNAPSHOT) && !fitted.events.some((e) => e.type === RRWEB_FULL_SNAPSHOT)) {
|
|
14203
|
-
const full = chunk.find((e) => e.type === RRWEB_FULL_SNAPSHOT);
|
|
14204
|
-
let compressedBytes;
|
|
14205
|
-
if (full) {
|
|
14206
|
-
try {
|
|
14207
|
-
compressedBytes = (await compressReplayEvents([full])).length;
|
|
14208
|
-
} catch {
|
|
14209
|
-
}
|
|
14210
|
-
}
|
|
14211
|
-
return {
|
|
14212
|
-
events: [],
|
|
14213
|
-
gzip: new Uint8Array(),
|
|
14214
|
-
abortReason: "oversized_full_snapshot",
|
|
14215
|
-
abortDetails: {
|
|
14216
|
-
source: "fit_dropped_full_snapshot",
|
|
14217
|
-
...paintBaseSizeDetails(chunk, {
|
|
14218
|
-
fullSnapshotCompressedBytes: compressedBytes
|
|
14219
|
-
})
|
|
14220
|
-
}
|
|
14221
|
-
};
|
|
14222
|
-
}
|
|
14223
14469
|
if (fitted.remainder.length > 0) {
|
|
14224
14470
|
this.buffer.prepend(fitted.remainder);
|
|
14225
14471
|
}
|