@merittdev/horus-lens 0.4.0 → 0.5.1

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.
@@ -1,4 +1,4 @@
1
- import { L as LensConfig, a as LensClient } from './index.d-BYG_Mjs-.cjs';
1
+ import { L as LensConfig, a as LensClient } from './index.d-DeJ1mfh0.cjs';
2
2
  import { z } from 'zod';
3
3
  import './index.d-DTx4C384.cjs';
4
4
 
package/dist/browser.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { L as LensConfig, a as LensClient } from './index.d-Dyzc1E9T.js';
1
+ import { L as LensConfig, a as LensClient } from './index.d-BYuZWEkh.js';
2
2
  import { z } from 'zod';
3
3
  import './index.d-DTx4C384.js';
4
4
 
package/dist/browser.js CHANGED
@@ -7,8 +7,8 @@ import {
7
7
  isGateActive,
8
8
  loaderSnippet,
9
9
  processQueue
10
- } from "./chunk-GQREYSHC.js";
11
- import "./chunk-B7QI3T3E.js";
10
+ } from "./chunk-MN44USB4.js";
11
+ import "./chunk-GVULQ2IF.js";
12
12
  export {
13
13
  DEFAULT_CDN_URL,
14
14
  LOADER_SNIPPET,
@@ -29063,7 +29063,7 @@ async function domToBlob(node2, options) {
29063
29063
 
29064
29064
  // ../core/dist/index.js
29065
29065
  var SDK_NAME = "@merittdev/horus-lens";
29066
- var SDK_VERSION = true ? "0.4.0" : "0.0.0-dev";
29066
+ var SDK_VERSION = true ? "0.5.1" : "0.0.0-dev";
29067
29067
  var DEFAULT_ENDPOINT = "https://api.horus.sh";
29068
29068
  var DEFAULT_REPLAY_WINDOW_MS = 15e3;
29069
29069
  var DEFAULT_MAX_CONSOLE_ENTRIES = 200;
@@ -29124,11 +29124,15 @@ function startAutoCapture(client, options = {}) {
29124
29124
  const last = lastSeen.get(signature);
29125
29125
  if (last !== void 0 && now - last < AUTO_CAPTURE_COOLDOWN_MS) return;
29126
29126
  if (sessionCount >= AUTO_CAPTURE_SESSION_CAP) return;
29127
+ const previousSeen = lastSeen.get(signature);
29127
29128
  lastSeen.set(signature, now);
29128
29129
  sessionCount += 1;
29129
29130
  submitting = true;
29130
29131
  options.onCapture?.(signature);
29131
29132
  void Promise.resolve().then(() => client.submitReport({ trigger: "error", include: { screenshot: false } })).catch(() => {
29133
+ if (previousSeen === void 0) lastSeen.delete(signature);
29134
+ else lastSeen.set(signature, previousSeen);
29135
+ sessionCount -= 1;
29132
29136
  }).finally(() => {
29133
29137
  submitting = false;
29134
29138
  });
@@ -29183,6 +29187,14 @@ var RingBuffer = class {
29183
29187
  return this.items.length;
29184
29188
  }
29185
29189
  };
29190
+ var HttpError = class extends Error {
29191
+ constructor(status, message) {
29192
+ super(message);
29193
+ this.status = status;
29194
+ this.name = "HttpError";
29195
+ }
29196
+ status;
29197
+ };
29186
29198
  var Transport = class {
29187
29199
  constructor(config2) {
29188
29200
  this.config = config2;
@@ -29190,18 +29202,23 @@ var Transport = class {
29190
29202
  config;
29191
29203
  async initiate(assets) {
29192
29204
  const body = { accessId: this.config.accessId, assets };
29193
- return this.postJson(`${this.config.endpoint}/v1/reports/initiate`, body);
29205
+ return this.postJson(
29206
+ `${this.config.endpoint}/v1/reports/initiate`,
29207
+ body,
29208
+ { retries: 0 }
29209
+ );
29194
29210
  }
29195
29211
  /** Record a late-uploaded asset (background screenshot) on a finalized report. */
29196
29212
  async attachAsset(reportId, asset) {
29197
29213
  const body = { accessId: this.config.accessId, ...asset };
29198
29214
  await this.postJson(`${this.config.endpoint}/v1/reports/${encodeURIComponent(reportId)}/assets`, body);
29199
29215
  }
29200
- async uploadAsset(upload, blob2) {
29216
+ async uploadAsset(upload, blob2, signal) {
29201
29217
  const response = await fetch(upload.url, {
29202
29218
  method: upload.method,
29203
29219
  headers: upload.headers,
29204
- body: blob2
29220
+ body: blob2,
29221
+ signal
29205
29222
  });
29206
29223
  if (!response.ok) {
29207
29224
  throw new Error(`Asset upload failed (${upload.kind}): ${response.status}`);
@@ -29209,12 +29226,20 @@ var Transport = class {
29209
29226
  }
29210
29227
  async finalize(reportId, metadata, uploadedAssets) {
29211
29228
  const body = { accessId: this.config.accessId, metadata, uploadedAssets };
29212
- return this.postJson(
29213
- `${this.config.endpoint}/v1/reports/${reportId}/finalize`,
29214
- body
29215
- );
29229
+ try {
29230
+ return await this.postJson(
29231
+ `${this.config.endpoint}/v1/reports/${reportId}/finalize`,
29232
+ body
29233
+ );
29234
+ } catch (err) {
29235
+ if (err instanceof HttpError && err.status === 409) {
29236
+ return { reportId, status: "submitted" };
29237
+ }
29238
+ throw err;
29239
+ }
29216
29240
  }
29217
- async postJson(url2, body, retries = 1) {
29241
+ async postJson(url2, body, opts = {}) {
29242
+ const retries = opts.retries ?? 1;
29218
29243
  let lastError;
29219
29244
  for (let attempt = 0; attempt <= retries; attempt++) {
29220
29245
  try {
@@ -29225,7 +29250,10 @@ var Transport = class {
29225
29250
  });
29226
29251
  if (!response.ok) {
29227
29252
  const text = await response.text().catch(() => "");
29228
- throw new Error(`Request to ${url2} failed: ${response.status} ${text}`.trim());
29253
+ throw new HttpError(
29254
+ response.status,
29255
+ `Request to ${url2} failed: ${response.status} ${text}`.trim()
29256
+ );
29229
29257
  }
29230
29258
  return await response.json();
29231
29259
  } catch (err) {
@@ -29616,8 +29644,11 @@ function computeCropRect(crop, scale, rasterWidth, rasterHeight) {
29616
29644
  async function captureScreenshot(excludeHost, crop, composite) {
29617
29645
  if (typeof document === "undefined" || typeof window === "undefined") return null;
29618
29646
  try {
29619
- const viewportPixels = window.innerWidth * window.innerHeight || 1;
29620
- const pixelCap = Math.sqrt(SCREENSHOT_MAX_PIXELS / viewportPixels);
29647
+ const viewportOnly = !composite && !crop;
29648
+ const captureW = composite ? composite.docWidth : window.innerWidth;
29649
+ const captureH = composite ? composite.docHeight : window.innerHeight;
29650
+ const capturePixels = captureW * captureH || 1;
29651
+ const pixelCap = Math.sqrt(SCREENSHOT_MAX_PIXELS / capturePixels);
29621
29652
  const scale = Math.max(1, Math.min(window.devicePixelRatio || 1, SCREENSHOT_MAX_SCALE, pixelCap));
29622
29653
  const capture = domToBlob(document.documentElement, {
29623
29654
  scale,
@@ -29626,12 +29657,19 @@ async function captureScreenshot(excludeHost, crop, composite) {
29626
29657
  // JPEG has no alpha — transparent page regions (and any capture box
29627
29658
  // extending past short content) would otherwise encode as BLACK.
29628
29659
  backgroundColor: "#ffffff",
29629
- // When compositing annotations, force the capture box to the editor's
29630
- // document measurement (scrollHeight-based). modern-screenshot otherwise
29631
- // sizes from documentElement's bounding box, which on pages SHORTER than
29632
- // the viewport is the content height only — marks drawn in the lower
29633
- // viewport band would fall off the raster and the crop would clamp short.
29660
+ // Composite (annotations): force the capture box to the editor's document
29661
+ // measurement so marks in the lower viewport band aren't clipped.
29634
29662
  ...composite ? { width: composite.docWidth, height: composite.docHeight } : {},
29663
+ // Plain report: clip the render to the viewport and shift the scrolled
29664
+ // content into it, so only the visible region is rasterized/fetched.
29665
+ ...viewportOnly ? {
29666
+ width: window.innerWidth,
29667
+ height: window.innerHeight,
29668
+ style: {
29669
+ transform: `translate(${-window.scrollX}px, ${-window.scrollY}px)`,
29670
+ transformOrigin: "top left"
29671
+ }
29672
+ } : {},
29635
29673
  // Exclude the overlay from the capture CLONE — never hide the live node.
29636
29674
  filter: (node2) => node2 !== excludeHost,
29637
29675
  // Per-resource fetch budget: a single slow cross-origin image must not
@@ -30101,12 +30139,22 @@ var LensClient = class {
30101
30139
  input2.screenshotCrop,
30102
30140
  input2.annotationComposite
30103
30141
  ) : Promise.resolve(null);
30142
+ const consumingManualRecording = wantReplay && (this.replay?.hasManualRecording() ?? false) && !(this.replay?.isManualRecording() ?? false);
30104
30143
  const replayEvents = wantReplay ? this.replay?.snapshot() ?? [] : [];
30105
30144
  const errorEntries = this.errorBuffer.drain();
30106
- const consoleTail = this.consoleBuffer.tail(CONSOLE_TAIL_SIZE);
30107
- const networkTail = this.networkBuffer.tail(NETWORK_TAIL_SIZE);
30108
30145
  const consoleEntries = this.consoleBuffer.drain();
30109
30146
  const networkEntries = this.networkBuffer.drain();
30147
+ this.errorBuffer.clear();
30148
+ this.consoleBuffer.clear();
30149
+ this.networkBuffer.clear();
30150
+ const consoleTail = consoleEntries.slice(-CONSOLE_TAIL_SIZE);
30151
+ const isNetFailure = (e) => e.ok !== true || Boolean(e.error);
30152
+ const recentFailures = networkEntries.filter(isNetFailure).slice(-NETWORK_TAIL_SIZE);
30153
+ const keep = /* @__PURE__ */ new Set([
30154
+ ...recentFailures,
30155
+ ...networkEntries.slice(-NETWORK_TAIL_SIZE)
30156
+ ]);
30157
+ const networkTail = networkEntries.filter((e) => keep.has(e)).slice(-NETWORK_TAIL_SIZE);
30110
30158
  let metadata = {
30111
30159
  annotations: input2.annotations ?? [],
30112
30160
  env: collectEnv(),
@@ -30160,13 +30208,14 @@ var LensClient = class {
30160
30208
  }
30161
30209
  if (!asset.blob) continue;
30162
30210
  try {
30163
- await this.transport.uploadAsset(upload, asset.blob);
30211
+ await this.uploadAssetWithTimeout(upload, asset.blob);
30164
30212
  uploadedAssets.push({ kind: upload.kind, key: upload.key, byteSize: asset.blob.size });
30165
30213
  } catch (err) {
30166
30214
  this.warn(`asset upload failed (${upload.kind})`, err);
30167
30215
  }
30168
30216
  }
30169
30217
  const finalized = await this.transport.finalize(initiated.reportId, metadata, uploadedAssets);
30218
+ if (consumingManualRecording) this.replay?.discardManualRecording();
30170
30219
  if (screenshotUpload) {
30171
30220
  const upload = screenshotUpload;
30172
30221
  void screenshotPromise.then(async (blob2) => {
@@ -30184,6 +30233,21 @@ var LensClient = class {
30184
30233
  }
30185
30234
  return { reportId: finalized.reportId };
30186
30235
  }
30236
+ /**
30237
+ * Upload a foreground asset with a bounded timeout. Without this a stuck S3
30238
+ * PUT (dropped connection, half-open socket) would hang `submitReport`
30239
+ * forever; the timeout aborts the individual PUT so it fails as one asset
30240
+ * (caught by the caller) instead of wedging the whole submit.
30241
+ */
30242
+ async uploadAssetWithTimeout(upload, blob2) {
30243
+ const controller = new AbortController();
30244
+ const timer = setTimeout(() => controller.abort(), SCREENSHOT_TIMEOUT_MS);
30245
+ try {
30246
+ await this.transport.uploadAsset(upload, blob2, controller.signal);
30247
+ } finally {
30248
+ clearTimeout(timer);
30249
+ }
30250
+ }
30187
30251
  warn(message, err) {
30188
30252
  if (!this.debug) return;
30189
30253
  if (err !== void 0) console.warn(`[lens] ${message}`, err);
@@ -30241,4 +30305,4 @@ rrweb/dist/rrweb.js:
30241
30305
  PERFORMANCE OF THIS SOFTWARE.
30242
30306
  ***************************************************************************** *)
30243
30307
  */
30244
- //# sourceMappingURL=chunk-B7QI3T3E.js.map
30308
+ //# sourceMappingURL=chunk-GVULQ2IF.js.map