@insitue/sdk 0.1.8 → 0.1.10

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,7 +1,7 @@
1
1
  import {
2
2
  mountCaptureOnly
3
- } from "./chunk-KAHON3H3.js";
4
- import "./chunk-AMC2RGMK.js";
3
+ } from "./chunk-2MZGFEX6.js";
4
+ import "./chunk-CPPXBTE5.js";
5
5
  export {
6
6
  mountCaptureOnly
7
7
  };
@@ -11,7 +11,7 @@ import {
11
11
  setCaptureSettings,
12
12
  stopDisplayMedia,
13
13
  y
14
- } from "./chunk-AMC2RGMK.js";
14
+ } from "./chunk-CPPXBTE5.js";
15
15
 
16
16
  // src/capture-only.ts
17
17
  var DEFAULT_INGEST = "https://www.insitue.com/api/v1/capture";
@@ -351,11 +351,19 @@ function CaptureOnlyApp(props) {
351
351
  style: `display:flex;justify-content:space-between;padding:9px 16px;border-top:1px solid ${C.line};color:${C.faint};font-size:11px`
352
352
  },
353
353
  [
354
- k("span", {}, "\u{1F512} Secrets scrubbed automatically"),
354
+ // No "Secrets scrubbed" badge — the redaction is narrow
355
+ // (regex on attr-names + console.log args; see
356
+ // packages/sdk/src/runtime.ts safeArg, capture-core/dom.ts
357
+ // serializeNode). Visible password VALUES, URL query
358
+ // params, and any text rendered in the screenshot pixels
359
+ // are NOT scrubbed — claiming otherwise misleads reporters
360
+ // about what's safe to capture. If we ever build real
361
+ // form-value redaction the badge can come back.
362
+ k("span", {}, ""),
355
363
  k(
356
364
  "span",
357
- { title: `@insitue/sdk@${"0.1.8"}` },
358
- `InSitue \xB7 v${"0.1.8"}`
365
+ { title: `@insitue/sdk@${"0.1.10"}` },
366
+ `InSitue \xB7 v${"0.1.10"}`
359
367
  )
360
368
  ]
361
369
  )
@@ -1641,6 +1641,16 @@ async function toCanvas(node, options = {}) {
1641
1641
  }
1642
1642
 
1643
1643
  // src/capture.ts
1644
+ function crossOrigin(url) {
1645
+ if (!url || url.startsWith("data:") || url.startsWith("blob:")) {
1646
+ return false;
1647
+ }
1648
+ try {
1649
+ return new URL(url, location.href).origin !== location.origin;
1650
+ } catch {
1651
+ return false;
1652
+ }
1653
+ }
1644
1654
  var IMAGE_PLACEHOLDER = "data:image/svg+xml;base64," + (typeof btoa !== "undefined" ? btoa(
1645
1655
  '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><rect width="32" height="32" fill="#e8e8e8"/><path d="M0 0 L32 32 M32 0 L0 32" stroke="#b0b0b0" stroke-width="1.5"/></svg>'
1646
1656
  ) : "");
@@ -1666,20 +1676,34 @@ async function renderViewportCrop(cropRect, pixelRatio) {
1666
1676
  const htmlBg = getComputedStyle(document.documentElement).backgroundColor;
1667
1677
  const backgroundColor = bodyBg && bodyBg !== "rgba(0, 0, 0, 0)" && bodyBg !== "transparent" ? bodyBg : htmlBg && htmlBg !== "rgba(0, 0, 0, 0)" && htmlBg !== "transparent" ? htmlBg : "#ffffff";
1668
1678
  const failedImages = /* @__PURE__ */ new Set();
1679
+ const out = document.createElement("canvas");
1680
+ out.width = Math.max(1, Math.round(cropRect.width * pixelRatio));
1681
+ out.height = Math.max(1, Math.round(cropRect.height * pixelRatio));
1682
+ const ctx = out.getContext("2d");
1683
+ if (!ctx) return { dataUrl: null, failedImages };
1684
+ const drawnImgs = drawAbsoluteImagesOnto(
1685
+ ctx,
1686
+ cropRect,
1687
+ pixelRatio,
1688
+ failedImages
1689
+ );
1669
1690
  const fullCanvas = await toCanvas(document.documentElement, {
1670
1691
  pixelRatio,
1671
1692
  cacheBust: true,
1672
1693
  backgroundColor,
1673
1694
  imagePlaceholder: IMAGE_PLACEHOLDER,
1674
- filter: (n2) => !(n2 instanceof Element && n2.closest?.("#insitu-root, [data-insitu-layer]"))
1695
+ filter: (n2) => {
1696
+ if (n2 instanceof Element && n2.closest?.("#insitu-root, [data-insitu-layer]")) {
1697
+ return false;
1698
+ }
1699
+ if (n2 instanceof HTMLImageElement && drawnImgs.has(n2)) {
1700
+ return false;
1701
+ }
1702
+ return true;
1703
+ }
1675
1704
  });
1676
1705
  const sx = window.scrollX;
1677
1706
  const sy = window.scrollY;
1678
- const out = document.createElement("canvas");
1679
- out.width = Math.max(1, Math.round(cropRect.width * pixelRatio));
1680
- out.height = Math.max(1, Math.round(cropRect.height * pixelRatio));
1681
- const ctx = out.getContext("2d");
1682
- if (!ctx) return { dataUrl: null, failedImages };
1683
1707
  ctx.drawImage(
1684
1708
  fullCanvas,
1685
1709
  Math.round((cropRect.x + sx) * pixelRatio),
@@ -1697,6 +1721,95 @@ async function renderViewportCrop(cropRect, pixelRatio) {
1697
1721
  detectUnrenderedImages(ctx, cropRect, out, pixelRatio, failedImages);
1698
1722
  return { dataUrl: out.toDataURL("image/png"), failedImages };
1699
1723
  }
1724
+ function drawAbsoluteImagesOnto(ctx, cropRect, pixelRatio, failedImages) {
1725
+ const drawn = /* @__PURE__ */ new Set();
1726
+ const imgs = Array.from(
1727
+ document.querySelectorAll("img")
1728
+ ).filter(
1729
+ (img) => !img.closest?.("#insitu-root, [data-insitu-layer]")
1730
+ );
1731
+ for (const img of imgs) {
1732
+ const r3 = img.getBoundingClientRect();
1733
+ if (r3.width <= 0 || r3.height <= 0) continue;
1734
+ const cs = getComputedStyle(img);
1735
+ if (cs.position !== "absolute" && cs.position !== "fixed") continue;
1736
+ if (r3.right < cropRect.x || r3.left > cropRect.x + cropRect.width || r3.bottom < cropRect.y || r3.top > cropRect.y + cropRect.height) {
1737
+ continue;
1738
+ }
1739
+ const src = img.currentSrc || img.src;
1740
+ if (!src) continue;
1741
+ if (crossOrigin(src) && img.crossOrigin !== "anonymous" && img.crossOrigin !== "use-credentials") {
1742
+ failedImages.add(img);
1743
+ continue;
1744
+ }
1745
+ if (!img.complete || img.naturalWidth === 0 || img.naturalHeight === 0) {
1746
+ failedImages.add(img);
1747
+ continue;
1748
+ }
1749
+ const dest = {
1750
+ x: (r3.left - cropRect.x) * pixelRatio,
1751
+ y: (r3.top - cropRect.y) * pixelRatio,
1752
+ w: r3.width * pixelRatio,
1753
+ h: r3.height * pixelRatio
1754
+ };
1755
+ const source = computeObjectFitSource(img, cs);
1756
+ try {
1757
+ ctx.drawImage(
1758
+ img,
1759
+ source.sx,
1760
+ source.sy,
1761
+ source.sw,
1762
+ source.sh,
1763
+ dest.x,
1764
+ dest.y,
1765
+ dest.w,
1766
+ dest.h
1767
+ );
1768
+ drawn.add(img);
1769
+ } catch {
1770
+ failedImages.add(img);
1771
+ }
1772
+ }
1773
+ return drawn;
1774
+ }
1775
+ function computeObjectFitSource(img, cs) {
1776
+ const nw = img.naturalWidth;
1777
+ const nh = img.naturalHeight;
1778
+ const r3 = img.getBoundingClientRect();
1779
+ const dw = r3.width;
1780
+ const dh = r3.height;
1781
+ if (!nw || !nh || !dw || !dh) {
1782
+ return { sx: 0, sy: 0, sw: nw || 1, sh: nh || 1 };
1783
+ }
1784
+ const fit = cs.objectFit || "fill";
1785
+ if (fit === "fill") {
1786
+ return { sx: 0, sy: 0, sw: nw, sh: nh };
1787
+ }
1788
+ const destAR = dw / dh;
1789
+ const srcAR = nw / nh;
1790
+ if (fit === "cover") {
1791
+ if (srcAR > destAR) {
1792
+ const sw = nh * destAR;
1793
+ return { sx: (nw - sw) / 2, sy: 0, sw, sh: nh };
1794
+ }
1795
+ const sh = nw / destAR;
1796
+ return { sx: 0, sy: (nh - sh) / 2, sw: nw, sh };
1797
+ }
1798
+ if (fit === "contain" || fit === "scale-down") {
1799
+ return { sx: 0, sy: 0, sw: nw, sh: nh };
1800
+ }
1801
+ if (fit === "none") {
1802
+ const sw = Math.min(nw, dw);
1803
+ const sh = Math.min(nh, dh);
1804
+ return {
1805
+ sx: (nw - sw) / 2,
1806
+ sy: (nh - sh) / 2,
1807
+ sw,
1808
+ sh
1809
+ };
1810
+ }
1811
+ return { sx: 0, sy: 0, sw: nw, sh: nh };
1812
+ }
1700
1813
  function detectUnrenderedImages(cropCtx, cropRect, cropCanvas, pixelRatio, failedImages) {
1701
1814
  const imgs = Array.from(
1702
1815
  document.querySelectorAll("img")
@@ -15,7 +15,7 @@ import {
15
15
  setCaptureSettings,
16
16
  stopDisplayMedia,
17
17
  y
18
- } from "./chunk-AMC2RGMK.js";
18
+ } from "./chunk-CPPXBTE5.js";
19
19
 
20
20
  // src/client.ts
21
21
  var CompanionClient = class {
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  mountCaptureOnly
3
- } from "./chunk-KAHON3H3.js";
3
+ } from "./chunk-2MZGFEX6.js";
4
4
  import {
5
5
  mountInSitue
6
- } from "./chunk-DBHURN5L.js";
7
- import "./chunk-AMC2RGMK.js";
6
+ } from "./chunk-DSHJX2LF.js";
7
+ import "./chunk-CPPXBTE5.js";
8
8
 
9
9
  // src/InSitue.tsx
10
10
  import { useEffect } from "react";
@@ -52,7 +52,7 @@ function InSitueCapture({
52
52
  }
53
53
 
54
54
  // src/index.ts
55
- var SDK_VERSION = "0.1.8";
55
+ var SDK_VERSION = "0.1.10";
56
56
  export {
57
57
  InSitue,
58
58
  InSitueCapture,
package/dist/overlay.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  mountInSitue
3
- } from "./chunk-DBHURN5L.js";
4
- import "./chunk-AMC2RGMK.js";
3
+ } from "./chunk-DSHJX2LF.js";
4
+ import "./chunk-CPPXBTE5.js";
5
5
  export {
6
6
  mountInSitue
7
7
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@insitue/sdk",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "InSitue capture SDK — drop one snippet into your deployed app; your users point at a bug, InSitue opens a verified pull request.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -50,9 +50,12 @@
50
50
  "devDependencies": {
51
51
  "@types/node": "^22.9.0",
52
52
  "@types/react": "^19.0.0",
53
+ "@vitest/browser": "^3.2.4",
54
+ "playwright": "^1.60.0",
53
55
  "react": "^19.0.0",
54
56
  "tsup": "^8.3.5",
55
57
  "typescript": "^5.6.3",
58
+ "vitest": "^3.2.4",
56
59
  "@insitue/capture-core": "0.0.0"
57
60
  },
58
61
  "repository": {
@@ -84,6 +87,8 @@
84
87
  "build": "tsup",
85
88
  "dev": "tsup --watch",
86
89
  "typecheck": "tsc --noEmit",
87
- "lint": "tsc --noEmit"
90
+ "lint": "tsc --noEmit",
91
+ "test": "vitest run",
92
+ "test:watch": "vitest"
88
93
  }
89
94
  }