@merittdev/horus-lens 0.5.1 → 0.6.0
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 +16 -0
- package/dist/browser.cjs +282 -213
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.js +3 -2
- package/dist/chunk-AS225HUA.js +15344 -0
- package/dist/chunk-AS225HUA.js.map +1 -0
- package/dist/{chunk-MN44USB4.js → chunk-RVIL2GL4.js} +5 -3
- package/dist/{chunk-MN44USB4.js.map → chunk-RVIL2GL4.js.map} +1 -1
- package/dist/chunk-XGZ7BZTS.js +15082 -0
- package/dist/chunk-XGZ7BZTS.js.map +1 -0
- package/dist/index.cjs +282 -213
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -4
- package/dist/index.d.ts +2 -4
- package/dist/index.js +4 -2
- package/dist/lens.min.js +31 -31
- package/dist/{chunk-GVULQ2IF.js → react-native.cjs} +2741 -15965
- package/dist/react-native.cjs.map +1 -0
- package/dist/react-native.d.cts +492 -0
- package/dist/react-native.d.ts +492 -0
- package/dist/react-native.js +2105 -0
- package/dist/react-native.js.map +1 -0
- package/dist/react.cjs +282 -213
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +3 -2
- package/dist/react.js.map +1 -1
- package/package.json +25 -3
- package/dist/chunk-GVULQ2IF.js.map +0 -1
package/README.md
CHANGED
|
@@ -44,6 +44,22 @@ the reporter picks. Use `enabled={isPreviewEnv}` to force the overlay on (e.g. e
|
|
|
44
44
|
preview deployment). Wrap children with `<Lens>` only when a descendant needs the
|
|
45
45
|
`useLens()` hook for programmatic control.
|
|
46
46
|
|
|
47
|
+
### React Native (initial implementation)
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
import { Lens } from '@merittdev/horus-lens/react-native';
|
|
51
|
+
|
|
52
|
+
<Lens enabled={isQA} accessId="lai_your_access_id"
|
|
53
|
+
origin="https://www.example.com" route={pathname} />
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`enabled` defaults to false. This entry mounts a native issue sheet and captures
|
|
57
|
+
fetch metadata, device/route context and explicitly supplied errors. Console
|
|
58
|
+
capture is opt-in. Supply an origin already allowed by your Lens project.
|
|
59
|
+
Screenshot preview, annotation tools and explicit visual recording are available with native capture support (react-native-view-shot and react-native-svg). Native recording uses image frames; DOM/component inspection and automatic input masking are browser-specific. See
|
|
60
|
+
[the native SDK guide](https://github.com/Meritt-dev/horus-lens/blob/master/packages/react-native/README.md)
|
|
61
|
+
for redaction, hooks and capture controls.
|
|
62
|
+
|
|
47
63
|
### Script tag
|
|
48
64
|
|
|
49
65
|
Context can be pushed before the bundle even loads:
|
package/dist/browser.cjs
CHANGED
|
@@ -31,6 +31,260 @@ __export(browser_exports, {
|
|
|
31
31
|
});
|
|
32
32
|
module.exports = __toCommonJS(browser_exports);
|
|
33
33
|
|
|
34
|
+
// ../core/dist/chunk-YBVE6CBL.js
|
|
35
|
+
var SDK_NAME = "@merittdev/horus-lens";
|
|
36
|
+
var SDK_VERSION = true ? "0.6.0" : "0.0.0-dev";
|
|
37
|
+
var DEFAULT_ENDPOINT = "https://api.horus.sh";
|
|
38
|
+
var DEFAULT_REPLAY_WINDOW_MS = 15e3;
|
|
39
|
+
var DEFAULT_MAX_CONSOLE_ENTRIES = 200;
|
|
40
|
+
var DEFAULT_MAX_NETWORK_ENTRIES = 100;
|
|
41
|
+
var MAX_ERROR_ENTRIES = 100;
|
|
42
|
+
var CONSOLE_TAIL_SIZE = 50;
|
|
43
|
+
var NETWORK_TAIL_SIZE = 30;
|
|
44
|
+
var MAX_SERIALIZED_ARG_CHARS = 2e3;
|
|
45
|
+
var MAX_DOM_BYTES = 2 * 1024 * 1024;
|
|
46
|
+
var MAX_TARGET_TEXT_CHARS = 200;
|
|
47
|
+
var SCREENSHOT_TIMEOUT_MS = 2e4;
|
|
48
|
+
var SCREENSHOT_RESOURCE_TIMEOUT_MS = 3500;
|
|
49
|
+
var SCREENSHOT_MAX_SCALE = 2;
|
|
50
|
+
var SCREENSHOT_MAX_PIXELS = 25e5;
|
|
51
|
+
var AUTO_CAPTURE_COOLDOWN_MS = 10 * 60 * 1e3;
|
|
52
|
+
var AUTO_CAPTURE_SESSION_CAP = 5;
|
|
53
|
+
var MAX_MANUAL_RECORDING_MS = 12e4;
|
|
54
|
+
var MAX_MANUAL_RECORDING_EVENTS = 1e4;
|
|
55
|
+
var LENS_OVERLAY_HOST_ID = "__lens_root__";
|
|
56
|
+
var RingBuffer = class {
|
|
57
|
+
constructor(capacity) {
|
|
58
|
+
this.capacity = capacity;
|
|
59
|
+
if (capacity <= 0) throw new Error("RingBuffer capacity must be > 0");
|
|
60
|
+
}
|
|
61
|
+
capacity;
|
|
62
|
+
items = [];
|
|
63
|
+
push(item) {
|
|
64
|
+
this.items.push(item);
|
|
65
|
+
if (this.items.length > this.capacity) {
|
|
66
|
+
this.items.splice(0, this.items.length - this.capacity);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/** Snapshot of current contents, oldest first. */
|
|
70
|
+
drain() {
|
|
71
|
+
return [...this.items];
|
|
72
|
+
}
|
|
73
|
+
tail(count) {
|
|
74
|
+
return this.items.slice(-count);
|
|
75
|
+
}
|
|
76
|
+
clear() {
|
|
77
|
+
this.items = [];
|
|
78
|
+
}
|
|
79
|
+
get size() {
|
|
80
|
+
return this.items.length;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
var HttpError = class extends Error {
|
|
84
|
+
constructor(status, message) {
|
|
85
|
+
super(message);
|
|
86
|
+
this.status = status;
|
|
87
|
+
this.name = "HttpError";
|
|
88
|
+
}
|
|
89
|
+
status;
|
|
90
|
+
};
|
|
91
|
+
var Transport = class {
|
|
92
|
+
constructor(config2) {
|
|
93
|
+
this.config = config2;
|
|
94
|
+
}
|
|
95
|
+
config;
|
|
96
|
+
async initiate(assets) {
|
|
97
|
+
const body = {
|
|
98
|
+
accessId: this.config.accessId,
|
|
99
|
+
assets
|
|
100
|
+
};
|
|
101
|
+
return this.postJson(
|
|
102
|
+
`${this.config.endpoint}/v1/reports/initiate`,
|
|
103
|
+
body,
|
|
104
|
+
{ retries: 0 }
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
/** Record a late-uploaded asset (background screenshot) on a finalized report. */
|
|
108
|
+
async attachAsset(reportId, asset) {
|
|
109
|
+
const body = {
|
|
110
|
+
accessId: this.config.accessId,
|
|
111
|
+
...asset
|
|
112
|
+
};
|
|
113
|
+
await this.postJson(
|
|
114
|
+
`${this.config.endpoint}/v1/reports/${encodeURIComponent(reportId)}/assets`,
|
|
115
|
+
body
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
async uploadAsset(upload, blob2, signal) {
|
|
119
|
+
const session = this.config.signal;
|
|
120
|
+
const controller = session ? new AbortController() : void 0;
|
|
121
|
+
const abort = () => controller?.abort();
|
|
122
|
+
if (session?.aborted || signal?.aborted) abort();
|
|
123
|
+
session?.addEventListener("abort", abort);
|
|
124
|
+
if (controller) signal?.addEventListener("abort", abort);
|
|
125
|
+
try {
|
|
126
|
+
const response = await (this.config.fetch ?? fetch)(upload.url, {
|
|
127
|
+
method: upload.method,
|
|
128
|
+
headers: upload.headers,
|
|
129
|
+
body: blob2,
|
|
130
|
+
signal: controller?.signal ?? signal
|
|
131
|
+
});
|
|
132
|
+
if (!response.ok) {
|
|
133
|
+
throw new Error(
|
|
134
|
+
`Asset upload failed (${upload.kind}): ${response.status}`
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
} finally {
|
|
138
|
+
session?.removeEventListener("abort", abort);
|
|
139
|
+
if (controller) signal?.removeEventListener("abort", abort);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
async finalize(reportId, metadata, uploadedAssets) {
|
|
143
|
+
const body = {
|
|
144
|
+
accessId: this.config.accessId,
|
|
145
|
+
metadata,
|
|
146
|
+
uploadedAssets
|
|
147
|
+
};
|
|
148
|
+
try {
|
|
149
|
+
return await this.postJson(
|
|
150
|
+
`${this.config.endpoint}/v1/reports/${reportId}/finalize`,
|
|
151
|
+
body
|
|
152
|
+
);
|
|
153
|
+
} catch (err) {
|
|
154
|
+
if (err instanceof HttpError && err.status === 409) {
|
|
155
|
+
return { reportId, status: "submitted" };
|
|
156
|
+
}
|
|
157
|
+
throw err;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
async postJson(url2, body, opts = {}) {
|
|
161
|
+
const retries = opts.retries ?? 1;
|
|
162
|
+
let lastError;
|
|
163
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
164
|
+
const controller = this.config.requestTimeoutMs || this.config.signal ? new AbortController() : void 0;
|
|
165
|
+
const abort = () => controller?.abort();
|
|
166
|
+
const session = this.config.signal;
|
|
167
|
+
if (session?.aborted) throw new Error("Capture session ended");
|
|
168
|
+
session?.addEventListener("abort", abort);
|
|
169
|
+
const timeout = controller && this.config.requestTimeoutMs ? setTimeout(() => controller.abort(), this.config.requestTimeoutMs) : void 0;
|
|
170
|
+
try {
|
|
171
|
+
const response = await (this.config.fetch ?? fetch)(url2, {
|
|
172
|
+
method: "POST",
|
|
173
|
+
headers: {
|
|
174
|
+
"content-type": "application/json",
|
|
175
|
+
...this.config.origin ? { Origin: this.config.origin } : {}
|
|
176
|
+
},
|
|
177
|
+
body: JSON.stringify(body),
|
|
178
|
+
...controller ? { signal: controller.signal } : {}
|
|
179
|
+
});
|
|
180
|
+
if (!response.ok) {
|
|
181
|
+
const text = await response.text().catch(() => "");
|
|
182
|
+
throw new HttpError(
|
|
183
|
+
response.status,
|
|
184
|
+
`Request to ${url2} failed: ${response.status} ${text}`.trim()
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
return await response.json();
|
|
188
|
+
} catch (err) {
|
|
189
|
+
lastError = err;
|
|
190
|
+
if (this.config.debug) {
|
|
191
|
+
console.warn(
|
|
192
|
+
`[lens] request failed (attempt ${attempt + 1}/${retries + 1})`,
|
|
193
|
+
err
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
} finally {
|
|
197
|
+
if (timeout !== void 0) clearTimeout(timeout);
|
|
198
|
+
session?.removeEventListener("abort", abort);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
throw lastError;
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
var LEVELS = ["log", "info", "warn", "error", "debug"];
|
|
205
|
+
function cap(value) {
|
|
206
|
+
return value.length > MAX_SERIALIZED_ARG_CHARS ? `${value.slice(0, MAX_SERIALIZED_ARG_CHARS)}\u2026[truncated]` : value;
|
|
207
|
+
}
|
|
208
|
+
function serializeArg(value) {
|
|
209
|
+
if (value === null) return "null";
|
|
210
|
+
if (value === void 0) return "undefined";
|
|
211
|
+
const type = typeof value;
|
|
212
|
+
if (type === "string") return cap(value);
|
|
213
|
+
if (type === "number" || type === "boolean" || type === "bigint" || type === "symbol") {
|
|
214
|
+
return String(value);
|
|
215
|
+
}
|
|
216
|
+
if (type === "function") {
|
|
217
|
+
return `[Function: ${value.name || "anonymous"}]`;
|
|
218
|
+
}
|
|
219
|
+
if (value instanceof Error) {
|
|
220
|
+
return cap(value.stack ? `${value.name}: ${value.message}
|
|
221
|
+
${value.stack}` : `${value.name}: ${value.message}`);
|
|
222
|
+
}
|
|
223
|
+
try {
|
|
224
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
225
|
+
const json2 = JSON.stringify(value, (_key, val) => {
|
|
226
|
+
if (typeof val === "bigint") return String(val);
|
|
227
|
+
if (typeof val === "function") return "[Function]";
|
|
228
|
+
if (typeof val === "object" && val !== null) {
|
|
229
|
+
if (seen.has(val)) return "[Circular]";
|
|
230
|
+
seen.add(val);
|
|
231
|
+
}
|
|
232
|
+
return val;
|
|
233
|
+
});
|
|
234
|
+
return cap(json2 ?? String(value));
|
|
235
|
+
} catch {
|
|
236
|
+
return cap(String(value));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
function firstErrorStack(args) {
|
|
240
|
+
for (const arg of args) {
|
|
241
|
+
if (arg instanceof Error && arg.stack) return arg.stack;
|
|
242
|
+
}
|
|
243
|
+
return void 0;
|
|
244
|
+
}
|
|
245
|
+
function installConsole(consoleBuffer, errorBuffer) {
|
|
246
|
+
if (typeof console === "undefined") return () => {
|
|
247
|
+
};
|
|
248
|
+
const target = console;
|
|
249
|
+
const restores = [];
|
|
250
|
+
let active = true;
|
|
251
|
+
for (const level of LEVELS) {
|
|
252
|
+
const original = target[level];
|
|
253
|
+
if (typeof original !== "function") continue;
|
|
254
|
+
const wrapped = (...args) => {
|
|
255
|
+
if (!active) {
|
|
256
|
+
original.apply(console, args);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
try {
|
|
260
|
+
const serialized = args.map(serializeArg);
|
|
261
|
+
const timestamp = Date.now();
|
|
262
|
+
consoleBuffer.push({ level, args: serialized, timestamp });
|
|
263
|
+
if (level === "error") {
|
|
264
|
+
const stack = firstErrorStack(args);
|
|
265
|
+
const entry = {
|
|
266
|
+
message: serialized.join(" ") || "console.error",
|
|
267
|
+
source: "console.error",
|
|
268
|
+
timestamp
|
|
269
|
+
};
|
|
270
|
+
if (stack) entry.stack = stack;
|
|
271
|
+
errorBuffer.push(entry);
|
|
272
|
+
}
|
|
273
|
+
} catch {
|
|
274
|
+
}
|
|
275
|
+
original.apply(console, args);
|
|
276
|
+
};
|
|
277
|
+
target[level] = wrapped;
|
|
278
|
+
restores.push(() => {
|
|
279
|
+
if (target[level] === wrapped) target[level] = original;
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
return () => {
|
|
283
|
+
active = false;
|
|
284
|
+
for (const restore of restores) restore();
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
34
288
|
// ../../node_modules/.pnpm/rrweb@2.1.0/node_modules/rrweb/dist/rrweb.js
|
|
35
289
|
var __defProp2 = Object.defineProperty;
|
|
36
290
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
@@ -11680,9 +11934,9 @@ var saveWebGLVar = (value, win, ctx) => {
|
|
|
11680
11934
|
}
|
|
11681
11935
|
return index2;
|
|
11682
11936
|
};
|
|
11683
|
-
function
|
|
11937
|
+
function serializeArg2(value, win, ctx) {
|
|
11684
11938
|
if (value instanceof Array) {
|
|
11685
|
-
return value.map((arg) =>
|
|
11939
|
+
return value.map((arg) => serializeArg2(arg, win, ctx));
|
|
11686
11940
|
} else if (value === null) {
|
|
11687
11941
|
return value;
|
|
11688
11942
|
} else if (value instanceof Float32Array || value instanceof Float64Array || value instanceof Int32Array || value instanceof Uint32Array || value instanceof Uint8Array || value instanceof Uint16Array || value instanceof Int16Array || value instanceof Int8Array || value instanceof Uint8ClampedArray) {
|
|
@@ -11708,7 +11962,7 @@ function serializeArg(value, win, ctx) {
|
|
|
11708
11962
|
return {
|
|
11709
11963
|
rr_type: name,
|
|
11710
11964
|
args: [
|
|
11711
|
-
|
|
11965
|
+
serializeArg2(value.buffer, win, ctx),
|
|
11712
11966
|
value.byteOffset,
|
|
11713
11967
|
value.byteLength
|
|
11714
11968
|
]
|
|
@@ -11731,7 +11985,7 @@ function serializeArg(value, win, ctx) {
|
|
|
11731
11985
|
const name = value.constructor.name;
|
|
11732
11986
|
return {
|
|
11733
11987
|
rr_type: name,
|
|
11734
|
-
args: [
|
|
11988
|
+
args: [serializeArg2(value.data, win, ctx), value.width, value.height]
|
|
11735
11989
|
};
|
|
11736
11990
|
} else if (isInstanceOfWebGLObject(value, win) || typeof value === "object") {
|
|
11737
11991
|
const name = value.constructor.name;
|
|
@@ -11744,7 +11998,7 @@ function serializeArg(value, win, ctx) {
|
|
|
11744
11998
|
return value;
|
|
11745
11999
|
}
|
|
11746
12000
|
var serializeArgs = (args, win, ctx) => {
|
|
11747
|
-
return args.map((arg) =>
|
|
12001
|
+
return args.map((arg) => serializeArg2(arg, win, ctx));
|
|
11748
12002
|
};
|
|
11749
12003
|
var isInstanceOfWebGLObject = (value, win) => {
|
|
11750
12004
|
const webGLConstructorNames = [
|
|
@@ -29081,26 +29335,6 @@ var ApiErrorSchema = external_exports.object({
|
|
|
29081
29335
|
});
|
|
29082
29336
|
|
|
29083
29337
|
// ../core/dist/index.js
|
|
29084
|
-
var SDK_NAME = "@merittdev/horus-lens";
|
|
29085
|
-
var SDK_VERSION = true ? "0.5.1" : "0.0.0-dev";
|
|
29086
|
-
var DEFAULT_ENDPOINT = "https://api.horus.sh";
|
|
29087
|
-
var DEFAULT_REPLAY_WINDOW_MS = 15e3;
|
|
29088
|
-
var DEFAULT_MAX_CONSOLE_ENTRIES = 200;
|
|
29089
|
-
var DEFAULT_MAX_NETWORK_ENTRIES = 100;
|
|
29090
|
-
var MAX_ERROR_ENTRIES = 100;
|
|
29091
|
-
var CONSOLE_TAIL_SIZE = 50;
|
|
29092
|
-
var NETWORK_TAIL_SIZE = 30;
|
|
29093
|
-
var MAX_SERIALIZED_ARG_CHARS = 2e3;
|
|
29094
|
-
var MAX_DOM_BYTES = 2 * 1024 * 1024;
|
|
29095
|
-
var MAX_TARGET_TEXT_CHARS = 200;
|
|
29096
|
-
var SCREENSHOT_TIMEOUT_MS = 2e4;
|
|
29097
|
-
var SCREENSHOT_MAX_SCALE = 2;
|
|
29098
|
-
var SCREENSHOT_MAX_PIXELS = 25e5;
|
|
29099
|
-
var AUTO_CAPTURE_COOLDOWN_MS = 10 * 60 * 1e3;
|
|
29100
|
-
var AUTO_CAPTURE_SESSION_CAP = 5;
|
|
29101
|
-
var MAX_MANUAL_RECORDING_MS = 12e4;
|
|
29102
|
-
var MAX_MANUAL_RECORDING_EVENTS = 1e4;
|
|
29103
|
-
var LENS_OVERLAY_HOST_ID = "__lens_root__";
|
|
29104
29338
|
function autoCaptureSignature(message) {
|
|
29105
29339
|
return message.toLowerCase().replace(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/g, "<uuid>").replace(/\d+/g, "<n>").replace(/\s+/g, " ").trim();
|
|
29106
29340
|
}
|
|
@@ -29179,189 +29413,6 @@ function startAutoCapture(client, options = {}) {
|
|
|
29179
29413
|
window.removeEventListener("unhandledrejection", onRejection);
|
|
29180
29414
|
};
|
|
29181
29415
|
}
|
|
29182
|
-
var RingBuffer = class {
|
|
29183
|
-
constructor(capacity) {
|
|
29184
|
-
this.capacity = capacity;
|
|
29185
|
-
if (capacity <= 0) throw new Error("RingBuffer capacity must be > 0");
|
|
29186
|
-
}
|
|
29187
|
-
capacity;
|
|
29188
|
-
items = [];
|
|
29189
|
-
push(item) {
|
|
29190
|
-
this.items.push(item);
|
|
29191
|
-
if (this.items.length > this.capacity) {
|
|
29192
|
-
this.items.splice(0, this.items.length - this.capacity);
|
|
29193
|
-
}
|
|
29194
|
-
}
|
|
29195
|
-
/** Snapshot of current contents, oldest first. */
|
|
29196
|
-
drain() {
|
|
29197
|
-
return [...this.items];
|
|
29198
|
-
}
|
|
29199
|
-
tail(count) {
|
|
29200
|
-
return this.items.slice(-count);
|
|
29201
|
-
}
|
|
29202
|
-
clear() {
|
|
29203
|
-
this.items = [];
|
|
29204
|
-
}
|
|
29205
|
-
get size() {
|
|
29206
|
-
return this.items.length;
|
|
29207
|
-
}
|
|
29208
|
-
};
|
|
29209
|
-
var HttpError = class extends Error {
|
|
29210
|
-
constructor(status, message) {
|
|
29211
|
-
super(message);
|
|
29212
|
-
this.status = status;
|
|
29213
|
-
this.name = "HttpError";
|
|
29214
|
-
}
|
|
29215
|
-
status;
|
|
29216
|
-
};
|
|
29217
|
-
var Transport = class {
|
|
29218
|
-
constructor(config2) {
|
|
29219
|
-
this.config = config2;
|
|
29220
|
-
}
|
|
29221
|
-
config;
|
|
29222
|
-
async initiate(assets) {
|
|
29223
|
-
const body = { accessId: this.config.accessId, assets };
|
|
29224
|
-
return this.postJson(
|
|
29225
|
-
`${this.config.endpoint}/v1/reports/initiate`,
|
|
29226
|
-
body,
|
|
29227
|
-
{ retries: 0 }
|
|
29228
|
-
);
|
|
29229
|
-
}
|
|
29230
|
-
/** Record a late-uploaded asset (background screenshot) on a finalized report. */
|
|
29231
|
-
async attachAsset(reportId, asset) {
|
|
29232
|
-
const body = { accessId: this.config.accessId, ...asset };
|
|
29233
|
-
await this.postJson(`${this.config.endpoint}/v1/reports/${encodeURIComponent(reportId)}/assets`, body);
|
|
29234
|
-
}
|
|
29235
|
-
async uploadAsset(upload, blob2, signal) {
|
|
29236
|
-
const response = await fetch(upload.url, {
|
|
29237
|
-
method: upload.method,
|
|
29238
|
-
headers: upload.headers,
|
|
29239
|
-
body: blob2,
|
|
29240
|
-
signal
|
|
29241
|
-
});
|
|
29242
|
-
if (!response.ok) {
|
|
29243
|
-
throw new Error(`Asset upload failed (${upload.kind}): ${response.status}`);
|
|
29244
|
-
}
|
|
29245
|
-
}
|
|
29246
|
-
async finalize(reportId, metadata, uploadedAssets) {
|
|
29247
|
-
const body = { accessId: this.config.accessId, metadata, uploadedAssets };
|
|
29248
|
-
try {
|
|
29249
|
-
return await this.postJson(
|
|
29250
|
-
`${this.config.endpoint}/v1/reports/${reportId}/finalize`,
|
|
29251
|
-
body
|
|
29252
|
-
);
|
|
29253
|
-
} catch (err) {
|
|
29254
|
-
if (err instanceof HttpError && err.status === 409) {
|
|
29255
|
-
return { reportId, status: "submitted" };
|
|
29256
|
-
}
|
|
29257
|
-
throw err;
|
|
29258
|
-
}
|
|
29259
|
-
}
|
|
29260
|
-
async postJson(url2, body, opts = {}) {
|
|
29261
|
-
const retries = opts.retries ?? 1;
|
|
29262
|
-
let lastError;
|
|
29263
|
-
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
29264
|
-
try {
|
|
29265
|
-
const response = await fetch(url2, {
|
|
29266
|
-
method: "POST",
|
|
29267
|
-
headers: { "content-type": "application/json" },
|
|
29268
|
-
body: JSON.stringify(body)
|
|
29269
|
-
});
|
|
29270
|
-
if (!response.ok) {
|
|
29271
|
-
const text = await response.text().catch(() => "");
|
|
29272
|
-
throw new HttpError(
|
|
29273
|
-
response.status,
|
|
29274
|
-
`Request to ${url2} failed: ${response.status} ${text}`.trim()
|
|
29275
|
-
);
|
|
29276
|
-
}
|
|
29277
|
-
return await response.json();
|
|
29278
|
-
} catch (err) {
|
|
29279
|
-
lastError = err;
|
|
29280
|
-
if (this.config.debug) {
|
|
29281
|
-
console.warn(`[lens] request failed (attempt ${attempt + 1}/${retries + 1})`, err);
|
|
29282
|
-
}
|
|
29283
|
-
}
|
|
29284
|
-
}
|
|
29285
|
-
throw lastError;
|
|
29286
|
-
}
|
|
29287
|
-
};
|
|
29288
|
-
var LEVELS = ["log", "info", "warn", "error", "debug"];
|
|
29289
|
-
function cap(value) {
|
|
29290
|
-
return value.length > MAX_SERIALIZED_ARG_CHARS ? `${value.slice(0, MAX_SERIALIZED_ARG_CHARS)}\u2026[truncated]` : value;
|
|
29291
|
-
}
|
|
29292
|
-
function serializeArg2(value) {
|
|
29293
|
-
if (value === null) return "null";
|
|
29294
|
-
if (value === void 0) return "undefined";
|
|
29295
|
-
const type = typeof value;
|
|
29296
|
-
if (type === "string") return cap(value);
|
|
29297
|
-
if (type === "number" || type === "boolean" || type === "bigint" || type === "symbol") {
|
|
29298
|
-
return String(value);
|
|
29299
|
-
}
|
|
29300
|
-
if (type === "function") {
|
|
29301
|
-
return `[Function: ${value.name || "anonymous"}]`;
|
|
29302
|
-
}
|
|
29303
|
-
if (value instanceof Error) {
|
|
29304
|
-
return cap(value.stack ? `${value.name}: ${value.message}
|
|
29305
|
-
${value.stack}` : `${value.name}: ${value.message}`);
|
|
29306
|
-
}
|
|
29307
|
-
try {
|
|
29308
|
-
const seen = /* @__PURE__ */ new WeakSet();
|
|
29309
|
-
const json2 = JSON.stringify(value, (_key, val) => {
|
|
29310
|
-
if (typeof val === "bigint") return String(val);
|
|
29311
|
-
if (typeof val === "function") return "[Function]";
|
|
29312
|
-
if (typeof val === "object" && val !== null) {
|
|
29313
|
-
if (seen.has(val)) return "[Circular]";
|
|
29314
|
-
seen.add(val);
|
|
29315
|
-
}
|
|
29316
|
-
return val;
|
|
29317
|
-
});
|
|
29318
|
-
return cap(json2 ?? String(value));
|
|
29319
|
-
} catch {
|
|
29320
|
-
return cap(String(value));
|
|
29321
|
-
}
|
|
29322
|
-
}
|
|
29323
|
-
function firstErrorStack(args) {
|
|
29324
|
-
for (const arg of args) {
|
|
29325
|
-
if (arg instanceof Error && arg.stack) return arg.stack;
|
|
29326
|
-
}
|
|
29327
|
-
return void 0;
|
|
29328
|
-
}
|
|
29329
|
-
function installConsole(consoleBuffer, errorBuffer) {
|
|
29330
|
-
if (typeof console === "undefined") return () => {
|
|
29331
|
-
};
|
|
29332
|
-
const target = console;
|
|
29333
|
-
const restores = [];
|
|
29334
|
-
for (const level of LEVELS) {
|
|
29335
|
-
const original = target[level];
|
|
29336
|
-
if (typeof original !== "function") continue;
|
|
29337
|
-
const wrapped = (...args) => {
|
|
29338
|
-
try {
|
|
29339
|
-
const serialized = args.map(serializeArg2);
|
|
29340
|
-
const timestamp = Date.now();
|
|
29341
|
-
consoleBuffer.push({ level, args: serialized, timestamp });
|
|
29342
|
-
if (level === "error") {
|
|
29343
|
-
const stack = firstErrorStack(args);
|
|
29344
|
-
const entry = {
|
|
29345
|
-
message: serialized.join(" ") || "console.error",
|
|
29346
|
-
source: "console.error",
|
|
29347
|
-
timestamp
|
|
29348
|
-
};
|
|
29349
|
-
if (stack) entry.stack = stack;
|
|
29350
|
-
errorBuffer.push(entry);
|
|
29351
|
-
}
|
|
29352
|
-
} catch {
|
|
29353
|
-
}
|
|
29354
|
-
original.apply(console, args);
|
|
29355
|
-
};
|
|
29356
|
-
target[level] = wrapped;
|
|
29357
|
-
restores.push(() => {
|
|
29358
|
-
target[level] = original;
|
|
29359
|
-
});
|
|
29360
|
-
}
|
|
29361
|
-
return () => {
|
|
29362
|
-
for (const restore of restores) restore();
|
|
29363
|
-
};
|
|
29364
|
-
}
|
|
29365
29416
|
function nowMs() {
|
|
29366
29417
|
return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
29367
29418
|
}
|
|
@@ -29648,6 +29699,14 @@ var ReplayRecorder = class {
|
|
|
29648
29699
|
}
|
|
29649
29700
|
};
|
|
29650
29701
|
var SVG_RASTERIZE_TIMEOUT_MS = 3e3;
|
|
29702
|
+
function isOffscreenMedia(node2) {
|
|
29703
|
+
if (!(node2 instanceof HTMLImageElement) && !(node2 instanceof HTMLIFrameElement) && !(typeof HTMLVideoElement !== "undefined" && node2 instanceof HTMLVideoElement)) {
|
|
29704
|
+
return false;
|
|
29705
|
+
}
|
|
29706
|
+
const r = node2.getBoundingClientRect();
|
|
29707
|
+
if (r.width === 0 && r.height === 0) return false;
|
|
29708
|
+
return r.bottom < 0 || r.top > window.innerHeight || r.right < 0 || r.left > window.innerWidth;
|
|
29709
|
+
}
|
|
29651
29710
|
function computeCropRect(crop, scale, rasterWidth, rasterHeight) {
|
|
29652
29711
|
let sx = Math.round(crop.x * scale);
|
|
29653
29712
|
let sy = Math.round(crop.y * scale);
|
|
@@ -29689,12 +29748,22 @@ async function captureScreenshot(excludeHost, crop, composite) {
|
|
|
29689
29748
|
transformOrigin: "top left"
|
|
29690
29749
|
}
|
|
29691
29750
|
} : {},
|
|
29692
|
-
// Exclude the overlay from the capture CLONE
|
|
29693
|
-
|
|
29694
|
-
//
|
|
29695
|
-
|
|
29751
|
+
// Exclude the overlay from the capture CLONE (never hide the live node),
|
|
29752
|
+
// and — for a plain viewport report — drop images/iframes entirely outside
|
|
29753
|
+
// the viewport so we don't fetch dozens of off-screen cross-origin assets.
|
|
29754
|
+
filter: (node2) => {
|
|
29755
|
+
if (node2 === excludeHost) return false;
|
|
29756
|
+
if (viewportOnly && isOffscreenMedia(node2)) return false;
|
|
29757
|
+
return true;
|
|
29758
|
+
},
|
|
29759
|
+
// Skip web-font download/embed. On font-heavy pages (multiple custom
|
|
29760
|
+
// families) this is a dominant, flaky cost; fallback fonts are fine for a
|
|
29761
|
+
// bug-report screenshot and capture becomes far more reliable.
|
|
29762
|
+
font: false,
|
|
29696
29763
|
fetch: { requestInit: { cache: "force-cache" }, bypassingCache: false },
|
|
29697
|
-
|
|
29764
|
+
// PER-RESOURCE cap (NOT the overall budget): a hanging asset fails fast to
|
|
29765
|
+
// a placeholder instead of stalling the whole capture.
|
|
29766
|
+
timeout: SCREENSHOT_RESOURCE_TIMEOUT_MS
|
|
29698
29767
|
});
|
|
29699
29768
|
const started = Date.now();
|
|
29700
29769
|
const timeout = new Promise((resolve) => {
|