@goliapkg/sentori-react-native 0.7.0 → 0.7.2

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 +1 @@
1
- {"version":3,"file":"promise.d.ts","sourceRoot":"","sources":["../../src/handlers/promise.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,qBAAqB,QAAO,IAwBxC,CAAC"}
1
+ {"version":3,"file":"promise.d.ts","sourceRoot":"","sources":["../../src/handlers/promise.ts"],"names":[],"mappings":"AAeA,eAAO,MAAM,qBAAqB,QAAO,IA0BxC,CAAC"}
@@ -1,3 +1,4 @@
1
+ import { coerceError } from '@goliapkg/sentori-core';
1
2
  import { captureError } from '../capture';
2
3
  let _installed = false;
3
4
  export const installPromiseHandler = () => {
@@ -11,8 +12,11 @@ export const installPromiseHandler = () => {
11
12
  allRejections: true,
12
13
  onUnhandled: (_id, rejection) => {
13
14
  try {
14
- const err = rejection instanceof Error ? rejection : new Error(String(rejection));
15
- captureError(err);
15
+ // `coerceError` keeps the actual rejection visible. JS code
16
+ // routinely rejects with plain objects (`Promise.reject({code})`),
17
+ // which would otherwise collapse to the literal text
18
+ // `[object Object]` in the dashboard.
19
+ captureError(coerceError(rejection));
16
20
  }
17
21
  catch {
18
22
  // never throw
@@ -1 +1 @@
1
- {"version":3,"file":"promise.js","sourceRoot":"","sources":["../../src/handlers/promise.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAW1C,IAAI,UAAU,GAAG,KAAK,CAAC;AAEvB,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAS,EAAE;IAC9C,IAAI,UAAU;QAAE,OAAO;IAEvB,MAAM,MAAM,GAAI,UAAsD;SACnE,cAAc,CAAC;IAClB,IAAI,MAAM,EAAE,6BAA6B,EAAE,CAAC;QAC1C,UAAU,GAAG,IAAI,CAAC;QAClB,MAAM,CAAC,6BAA6B,CAAC;YACnC,aAAa,EAAE,IAAI;YACnB,WAAW,EAAE,CAAC,GAAG,EAAE,SAAS,EAAE,EAAE;gBAC9B,IAAI,CAAC;oBACH,MAAM,GAAG,GACP,SAAS,YAAY,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;oBACxE,YAAY,CAAC,GAAG,CAAC,CAAC;gBACpB,CAAC;gBAAC,MAAM,CAAC;oBACP,cAAc;gBAChB,CAAC;YACH,CAAC;SACF,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IAED,wEAAwE;IACxE,mFAAmF;AACrF,CAAC,CAAC"}
1
+ {"version":3,"file":"promise.js","sourceRoot":"","sources":["../../src/handlers/promise.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAErD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAW1C,IAAI,UAAU,GAAG,KAAK,CAAC;AAEvB,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAS,EAAE;IAC9C,IAAI,UAAU;QAAE,OAAO;IAEvB,MAAM,MAAM,GAAI,UAAsD;SACnE,cAAc,CAAC;IAClB,IAAI,MAAM,EAAE,6BAA6B,EAAE,CAAC;QAC1C,UAAU,GAAG,IAAI,CAAC;QAClB,MAAM,CAAC,6BAA6B,CAAC;YACnC,aAAa,EAAE,IAAI;YACnB,WAAW,EAAE,CAAC,GAAG,EAAE,SAAS,EAAE,EAAE;gBAC9B,IAAI,CAAC;oBACH,4DAA4D;oBAC5D,mEAAmE;oBACnE,qDAAqD;oBACrD,sCAAsC;oBACtC,YAAY,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC;gBACvC,CAAC;gBAAC,MAAM,CAAC;oBACP,cAAc;gBAChB,CAAC;YACH,CAAC;SACF,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IAED,wEAAwE;IACxE,mFAAmF;AACrF,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"screenshot.d.ts","sourceRoot":"","sources":["../../src/handlers/screenshot.ts"],"names":[],"mappings":"AAoDA,8DAA8D;AAC9D,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;;;GAIG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAmCxE"}
1
+ {"version":3,"file":"screenshot.d.ts","sourceRoot":"","sources":["../../src/handlers/screenshot.ts"],"names":[],"mappings":"AA6DA,8DAA8D;AAC9D,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;;;GAIG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAmDxE"}
@@ -2,13 +2,10 @@
2
2
  // on `captureException`. Off-main-thread, best-effort, opt-in.
3
3
  //
4
4
  // Performance contract (sub-D.04):
5
- // - Wait for the in-flight RN interaction batch to drain before
6
- // touching the view shot (`InteractionManager.runAfterInteractions`)
7
- // so we never extend the active gesture / animation by a frame.
8
- // - Yield one paint by chaining a `requestAnimationFrame` so the
9
- // screenshot reflects post-error UI state, not the frame that
10
- // was already half-laid-out.
11
- // - Capped output: 480 px on the longest edge, WebP q=70. Typical
5
+ // - Yield one paint via `requestAnimationFrame` before snapshotting
6
+ // so the screenshot reflects post-error UI state, not the frame
7
+ // that was already half-laid-out.
8
+ // - Capped output: 480 px on the longest edge, JPEG q=70. Typical
12
9
  // payload 30-80 KB; multipart hard cap is 500 KB.
13
10
  // - On any failure we silently return null. The error event still
14
11
  // goes to the server; the user just doesn't see a thumbnail.
@@ -17,7 +14,19 @@
17
14
  // lazily so apps that don't install it never pay the bundle cost
18
15
  // or fail at import time. Without it, `captureScreenshot()` returns
19
16
  // `null` immediately.
20
- import { InteractionManager } from 'react-native';
17
+ //
18
+ // 2026-05-15 — dropped `InteractionManager.runAfterInteractions`.
19
+ // RN's docs mark `InteractionManager` as deprecated and recommend
20
+ // `requestIdleCallback`, but `requestIdleCallback` doesn't actually
21
+ // exist in RN (it's a web API), so the deprecation print is
22
+ // unactionable for SDK consumers. The defensive "wait for the active
23
+ // gesture batch to drain" semantics it provided is not reachable
24
+ // from a screenshot triggered on captureException — by the time an
25
+ // error fires, the user is between actions, not mid-gesture — so
26
+ // removing it has no observable effect except silencing the warning.
27
+ // The `requestAnimationFrame` calls below still guarantee one paint
28
+ // commit before captureRef snapshots.
29
+ import { engageMasks } from '../mask';
21
30
  function loadCaptureRef() {
22
31
  try {
23
32
  // eslint-disable-next-line @typescript-eslint/no-require-imports
@@ -40,12 +49,25 @@ export async function captureScreenshot() {
40
49
  const captureRef = loadCaptureRef();
41
50
  if (!captureRef)
42
51
  return null;
43
- // Wait for the in-flight RN interaction batch to drain. This is
44
- // why screenshot capture doesn't visibly stall the user's last
45
- // action we let React commit before we ask the OS to render.
52
+ // Yield one paint frame so the post-error UI has committed before
53
+ // we ask the OS to snapshot it. The previous
54
+ // `InteractionManager.runAfterInteractions` step was removed: see
55
+ // file header — its replacement (`requestIdleCallback`) doesn't
56
+ // exist in RN, and on captureException the user is between actions
57
+ // anyway, so the gesture-batch-drain semantics never came into
58
+ // play in practice.
46
59
  await new Promise((resolve) => {
47
- InteractionManager.runAfterInteractions(() => resolve());
60
+ requestAnimationFrame(() => resolve());
48
61
  });
62
+ // Phase 48 sub-B — flip every registered MaskRegion overlay to
63
+ // opacity 1 (black covers the children) and every imperative
64
+ // setMaskedNode ref to opacity 0 (subtree disappears). Held for
65
+ // exactly one frame's worth of capture, then restored.
66
+ const restoreMasks = engageMasks();
67
+ // Yield one more frame so the overlay paint reaches the screen
68
+ // before captureRef snapshots. Without this the overlay opacity
69
+ // change is queued but the screenshotter may see the previous
70
+ // frame.
49
71
  await new Promise((resolve) => {
50
72
  requestAnimationFrame(() => resolve());
51
73
  });
@@ -58,6 +80,7 @@ export async function captureScreenshot() {
58
80
  // when only one dimension is set.
59
81
  width: MAX_LONG_EDGE_PX,
60
82
  }), CAPTURE_TIMEOUT_MS);
83
+ restoreMasks();
61
84
  if (!base64)
62
85
  return null;
63
86
  // view-shot doesn't ship a WebP encoder on every RN version.
@@ -67,6 +90,7 @@ export async function captureScreenshot() {
67
90
  return { base64, mediaType: 'image/jpeg' };
68
91
  }
69
92
  catch {
93
+ restoreMasks();
70
94
  return null;
71
95
  }
72
96
  }
@@ -1 +1 @@
1
- {"version":3,"file":"screenshot.js","sourceRoot":"","sources":["../../src/handlers/screenshot.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,+DAA+D;AAC/D,EAAE;AACF,mCAAmC;AACnC,kEAAkE;AAClE,yEAAyE;AACzE,oEAAoE;AACpE,mEAAmE;AACnE,kEAAkE;AAClE,iCAAiC;AACjC,oEAAoE;AACpE,sDAAsD;AACtD,oEAAoE;AACpE,iEAAiE;AACjE,EAAE;AACF,kEAAkE;AAClE,iEAAiE;AACjE,oEAAoE;AACpE,sBAAsB;AAEtB,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAkBlD,SAAS,cAAc;IACrB,IAAI,CAAC;QACH,iEAAiE;QACjE,MAAM,GAAG,GAAG,OAAO,CAAC,wBAAwB,CAAmB,CAAC;QAChE,OAAO,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,OAAO,EAAE,UAAU,IAAI,IAAI,CAAC;IAC3D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAC7B,MAAM,YAAY,GAAG,GAAG,CAAC;AACzB,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAQhC;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,MAAM,UAAU,GAAG,cAAc,EAAE,CAAC;IACpC,IAAI,CAAC,UAAU;QAAE,OAAO,IAAI,CAAC;IAE7B,gEAAgE;IAChE,+DAA+D;IAC/D,+DAA+D;IAC/D,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAClC,kBAAkB,CAAC,oBAAoB,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IACH,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAClC,qBAAqB,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,WAAW,CAC9B,UAAU,CAAC,SAAS,EAAE;YACpB,MAAM,EAAE,KAAK;YACb,OAAO,EAAE,YAAY;YACrB,MAAM,EAAE,QAAQ;YAChB,6DAA6D;YAC7D,kCAAkC;YAClC,KAAK,EAAE,gBAAgB;SACxB,CAAC,EACF,kBAAkB,CACnB,CAAC;QACF,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QACzB,6DAA6D;QAC7D,8DAA8D;QAC9D,+DAA+D;QAC/D,2CAA2C;QAC3C,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAAI,CAAa,EAAE,EAAU;IAC/C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,CAAC,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAoB,CAAC,EAAE,EAAE,CAAC,CAAC;QAC9D,CAAC,CAAC,IAAI,CACJ,CAAC,CAAC,EAAE,EAAE;YACJ,YAAY,CAAC,CAAC,CAAC,CAAC;YAChB,OAAO,CAAC,CAAC,CAAC,CAAC;QACb,CAAC,EACD,GAAG,EAAE;YACH,YAAY,CAAC,CAAC,CAAC,CAAC;YAChB,OAAO,CAAC,IAAoB,CAAC,CAAC;QAChC,CAAC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"screenshot.js","sourceRoot":"","sources":["../../src/handlers/screenshot.ts"],"names":[],"mappings":"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"}
package/lib/mask.d.ts CHANGED
@@ -1,25 +1,46 @@
1
1
  import React, { type ReactNode } from 'react';
2
- import { View, type ViewProps } from 'react-native';
2
+ import { type ViewProps } from 'react-native';
3
+ /** What we drive in the capture window: any handle with
4
+ * `setNativeProps({ opacity })`. RN's View instance satisfies this. */
5
+ type Maskable = {
6
+ setNativeProps?: (props: {
7
+ style?: {
8
+ opacity?: number;
9
+ };
10
+ }) => void;
11
+ };
3
12
  /**
4
- * Imperative registration: when you can't wrap the sensitive view
5
- * in `<MaskRegion>`, drop a ref on it and call `setMaskedNode(ref)`.
6
- * Future captures will mask the subtree.
13
+ * Imperative registration: pass a ref obtained via `useRef()` /
14
+ * `createRef()` to a `<View>` you want hidden from screenshots.
7
15
  */
8
- export declare function setMaskedNode(node: React.Component | View | null | unknown): void;
9
- /** Removes a previously registered ref. Pair this with mount/unmount
10
- * lifecycle hooks if the node is short-lived. */
11
- export declare function unsetMaskedNode(node: React.Component | View | null | unknown): void;
16
+ export declare function setMaskedNode(node: null | Maskable | unknown): void;
17
+ export declare function unsetMaskedNode(node: null | Maskable | unknown): void;
12
18
  /** Returns the current set of registered masked nodes + nativeIDs.
13
- * Read by the native screenshotter layer in sub-E / sub-F. */
19
+ * Read by the native screenshotter layer (iOS / Android sub-E / F). */
14
20
  export declare function getMaskedRegions(): {
15
- refs: Set<unknown>;
16
21
  nativeIds: Set<string>;
22
+ overlays: Set<Maskable>;
23
+ refs: Set<Maskable>;
17
24
  };
25
+ /**
26
+ * Phase 48 sub-B — engage masking right before screenshot capture.
27
+ * Returns a function the caller must invoke once capture is done so
28
+ * the user never sees the black overlays.
29
+ *
30
+ * Two paths:
31
+ * - Overlays from `<MaskRegion>`: flip opacity 0 → 1 (cover children).
32
+ * - Imperative refs from `setMaskedNode`: flip opacity 1 → 0 on the
33
+ * view itself (whole subtree disappears for one frame).
34
+ *
35
+ * All `setNativeProps` calls are best-effort — a failure on one
36
+ * doesn't block the others or the capture.
37
+ */
38
+ export declare function engageMasks(): () => void;
18
39
  /**
19
40
  * Declarative redaction. `<MaskRegion>{children}</MaskRegion>` keeps
20
- * the children visible in normal flight; under capture, the wrapping
21
- * view is repainted black so the rendered screenshot doesn't leak
22
- * the underlying pixels.
41
+ * the children visible in normal flight; under capture the overlay's
42
+ * opacity is flipped to 1 and the children are hidden behind a black
43
+ * square in the rendered screenshot.
23
44
  */
24
45
  export declare function MaskRegion({ children, nativeID, ...rest }: {
25
46
  children: ReactNode;
@@ -27,4 +48,5 @@ export declare function MaskRegion({ children, nativeID, ...rest }: {
27
48
  } & ViewProps): React.JSX.Element;
28
49
  /** Test-only — flush registration tables. */
29
50
  export declare function __resetMaskedRegionsForTests(): void;
51
+ export {};
30
52
  //# sourceMappingURL=mask.d.ts.map
package/lib/mask.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"mask.d.ts","sourceRoot":"","sources":["../src/mask.tsx"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,EAAE,KAAK,SAAS,EAAqB,MAAM,OAAO,CAAC;AACjE,OAAO,EAAE,IAAI,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAMpD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,KAAK,CAAC,SAAS,GAAG,IAAI,GAAG,IAAI,GAAG,OAAO,GAAG,IAAI,CAGjF;AAED;kDACkD;AAClD,wBAAgB,eAAe,CAAC,IAAI,EAAE,KAAK,CAAC,SAAS,GAAG,IAAI,GAAG,IAAI,GAAG,OAAO,GAAG,IAAI,CAGnF;AAED;+DAC+D;AAC/D,wBAAgB,gBAAgB,IAAI;IAClC,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IACnB,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACxB,CAEA;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,QAAQ,EACR,GAAG,IAAI,EACR,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAoB5E;AAED,6CAA6C;AAC7C,wBAAgB,4BAA4B,IAAI,IAAI,CAGnD"}
1
+ {"version":3,"file":"mask.d.ts","sourceRoot":"","sources":["../src/mask.tsx"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,EAAE,KAAK,SAAS,EAAqB,MAAM,OAAO,CAAC;AACjE,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAEhE;wEACwE;AACxE,KAAK,QAAQ,GAAG;IACd,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,KAAK,CAAC,EAAE;YAAE,OAAO,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,KAAK,IAAI,CAAC;CACpE,CAAC;AAMF;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,GAAG,QAAQ,GAAG,OAAO,GAAG,IAAI,CAGnE;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,GAAG,QAAQ,GAAG,OAAO,GAAG,IAAI,CAGrE;AAED;wEACwE;AACxE,wBAAgB,gBAAgB,IAAI;IAClC,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACvB,QAAQ,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC;IACxB,IAAI,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC;CACrB,CAEA;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,IAAI,MAAM,IAAI,CAmCxC;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,QAAQ,EACR,GAAG,IAAI,EACR,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CA2B5E;AAED,6CAA6C;AAC7C,wBAAgB,4BAA4B,IAAI,IAAI,CAInD"}
package/lib/mask.js CHANGED
@@ -1,77 +1,125 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- // Phase 42 sub-D.09/10 — mark UI regions as "do not screenshot".
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // Phase 42 sub-D.09/10 + Phase 48 sub-B — mark UI regions as "do not
3
+ // screenshot" AND actually redact them at capture time.
3
4
  //
4
- // `<MaskRegion>` wraps any subtree the SDK should redact before
5
- // shipping a crash screenshot. It's purely declarative the
6
- // component renders its children as-is in normal flight, but its
7
- // `View` is tagged with `collapsable={false}` + a sentinel
8
- // `nativeID` so the platform-level screenshotters
9
- // (`react-native-view-shot`, the iOS / Android native crash
10
- // capturers we add in sub-E / sub-F) can find it and paint over.
5
+ // `<MaskRegion>` renders its children normally, plus a black overlay
6
+ // `<View>` that sits on top of them with `opacity: 0`. Right before
7
+ // `captureScreenshot()` calls into react-native-view-shot, the SDK
8
+ // flips every registered overlay to `opacity: 1` (black square covers
9
+ // the children), captures, then flips back to `opacity: 0` so the
10
+ // user never sees the overlay. The overlay uses `pointerEvents="none"`
11
+ // so it never intercepts touches.
11
12
  //
12
- // `setMaskedNode(ref)` is the imperative escape hatch: useful
13
- // when the sensitive view isn't yours to wrap (a third-party
14
- // modal, a video player, etc.). Pass a React ref obtained via
15
- // `createRef()` / `useRef()` and the SDK will redact that
16
- // subtree the next time it captures.
17
- //
18
- // `getMaskedRegions()` returns the current set of native tags;
19
- // `captureScreenshot()` would consult this list, but
20
- // `react-native-view-shot` doesn't expose a "redact these rects"
21
- // hook — so this iteration ships the registration API only and
22
- // the rendered overlay lives behind a default-on
23
- // `<View style={{ backgroundColor: '#000' }}>` you can wrap
24
- // yourself. The iOS / Android crash-time screenshotters in
25
- // sub-E / sub-F will read this list before drawing.
13
+ // `setMaskedNode(ref)` is the imperative escape hatch for views you
14
+ // can't wrap. We can't inject an overlay into a foreign view, so the
15
+ // imperative path falls back to setting the registered view's own
16
+ // `opacity: 0` for the capture window content underneath may show
17
+ // through, but that beats sending the sensitive content to the server.
18
+ // Prefer `<MaskRegion>` when you control the subtree.
26
19
  import { useEffect, useRef } from 'react';
27
- import { View } from 'react-native';
28
- /** Component-level node identifiers we've been asked to redact. */
20
+ import { StyleSheet, View } from 'react-native';
29
21
  const _maskedRefs = new Set();
22
+ const _maskedOverlays = new Set();
30
23
  const _maskedNativeIds = new Set();
31
24
  /**
32
- * Imperative registration: when you can't wrap the sensitive view
33
- * in `<MaskRegion>`, drop a ref on it and call `setMaskedNode(ref)`.
34
- * Future captures will mask the subtree.
25
+ * Imperative registration: pass a ref obtained via `useRef()` /
26
+ * `createRef()` to a `<View>` you want hidden from screenshots.
35
27
  */
36
28
  export function setMaskedNode(node) {
37
- if (!node)
29
+ if (!node || typeof node.setNativeProps !== 'function')
38
30
  return;
39
31
  _maskedRefs.add(node);
40
32
  }
41
- /** Removes a previously registered ref. Pair this with mount/unmount
42
- * lifecycle hooks if the node is short-lived. */
43
33
  export function unsetMaskedNode(node) {
44
34
  if (!node)
45
35
  return;
46
36
  _maskedRefs.delete(node);
47
37
  }
48
38
  /** Returns the current set of registered masked nodes + nativeIDs.
49
- * Read by the native screenshotter layer in sub-E / sub-F. */
39
+ * Read by the native screenshotter layer (iOS / Android sub-E / F). */
50
40
  export function getMaskedRegions() {
51
- return { nativeIds: _maskedNativeIds, refs: _maskedRefs };
41
+ return { nativeIds: _maskedNativeIds, overlays: _maskedOverlays, refs: _maskedRefs };
42
+ }
43
+ /**
44
+ * Phase 48 sub-B — engage masking right before screenshot capture.
45
+ * Returns a function the caller must invoke once capture is done so
46
+ * the user never sees the black overlays.
47
+ *
48
+ * Two paths:
49
+ * - Overlays from `<MaskRegion>`: flip opacity 0 → 1 (cover children).
50
+ * - Imperative refs from `setMaskedNode`: flip opacity 1 → 0 on the
51
+ * view itself (whole subtree disappears for one frame).
52
+ *
53
+ * All `setNativeProps` calls are best-effort — a failure on one
54
+ * doesn't block the others or the capture.
55
+ */
56
+ export function engageMasks() {
57
+ const overlaysEngaged = [];
58
+ for (const o of _maskedOverlays) {
59
+ try {
60
+ o.setNativeProps?.({ style: { opacity: 1 } });
61
+ overlaysEngaged.push(o);
62
+ }
63
+ catch {
64
+ // skip
65
+ }
66
+ }
67
+ const refsEngaged = [];
68
+ for (const r of _maskedRefs) {
69
+ try {
70
+ r.setNativeProps?.({ style: { opacity: 0 } });
71
+ refsEngaged.push(r);
72
+ }
73
+ catch {
74
+ // skip
75
+ }
76
+ }
77
+ return () => {
78
+ for (const o of overlaysEngaged) {
79
+ try {
80
+ o.setNativeProps?.({ style: { opacity: 0 } });
81
+ }
82
+ catch {
83
+ // skip
84
+ }
85
+ }
86
+ for (const r of refsEngaged) {
87
+ try {
88
+ r.setNativeProps?.({ style: { opacity: 1 } });
89
+ }
90
+ catch {
91
+ // skip
92
+ }
93
+ }
94
+ };
52
95
  }
53
96
  /**
54
97
  * Declarative redaction. `<MaskRegion>{children}</MaskRegion>` keeps
55
- * the children visible in normal flight; under capture, the wrapping
56
- * view is repainted black so the rendered screenshot doesn't leak
57
- * the underlying pixels.
98
+ * the children visible in normal flight; under capture the overlay's
99
+ * opacity is flipped to 1 and the children are hidden behind a black
100
+ * square in the rendered screenshot.
58
101
  */
59
102
  export function MaskRegion({ children, nativeID, ...rest }) {
60
- // Auto-generate a stable nativeID per mount so the native
61
- // screenshotter can find this view by ID at capture time.
62
103
  const idRef = useRef(nativeID ?? `sentori-mask-${Math.random().toString(36).slice(2, 10)}`);
104
+ const overlayRef = useRef(null);
63
105
  useEffect(() => {
64
106
  const id = idRef.current;
65
107
  _maskedNativeIds.add(id);
108
+ const overlay = overlayRef.current;
109
+ if (overlay)
110
+ _maskedOverlays.add(overlay);
66
111
  return () => {
67
112
  _maskedNativeIds.delete(id);
113
+ if (overlay)
114
+ _maskedOverlays.delete(overlay);
68
115
  };
69
116
  }, []);
70
- return (_jsx(View, { collapsable: false, nativeID: idRef.current, ...rest, children: children }));
117
+ return (_jsxs(View, { collapsable: false, nativeID: idRef.current, ...rest, children: [children, _jsx(View, { pointerEvents: "none", ref: overlayRef, style: [StyleSheet.absoluteFill, { backgroundColor: '#000', opacity: 0 }] })] }));
71
118
  }
72
119
  /** Test-only — flush registration tables. */
73
120
  export function __resetMaskedRegionsForTests() {
74
121
  _maskedRefs.clear();
122
+ _maskedOverlays.clear();
75
123
  _maskedNativeIds.clear();
76
124
  }
77
125
  //# sourceMappingURL=mask.js.map
package/lib/mask.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"mask.js","sourceRoot":"","sources":["../src/mask.tsx"],"names":[],"mappings":";AAAA,iEAAiE;AACjE,EAAE;AACF,gEAAgE;AAChE,6DAA6D;AAC7D,iEAAiE;AACjE,2DAA2D;AAC3D,kDAAkD;AAClD,4DAA4D;AAC5D,iEAAiE;AACjE,EAAE;AACF,8DAA8D;AAC9D,6DAA6D;AAC7D,8DAA8D;AAC9D,0DAA0D;AAC1D,qCAAqC;AACrC,EAAE;AACF,+DAA+D;AAC/D,qDAAqD;AACrD,iEAAiE;AACjE,+DAA+D;AAC/D,iDAAiD;AACjD,4DAA4D;AAC5D,2DAA2D;AAC3D,oDAAoD;AAEpD,OAAc,EAAkB,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AACjE,OAAO,EAAE,IAAI,EAAkB,MAAM,cAAc,CAAC;AAEpD,mEAAmE;AACnE,MAAM,WAAW,GAAG,IAAI,GAAG,EAAoC,CAAC;AAChE,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAAU,CAAC;AAE3C;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,IAA6C;IACzE,IAAI,CAAC,IAAI;QAAE,OAAO;IAClB,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACxB,CAAC;AAED;kDACkD;AAClD,MAAM,UAAU,eAAe,CAAC,IAA6C;IAC3E,IAAI,CAAC,IAAI;QAAE,OAAO;IAClB,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AAC3B,CAAC;AAED;+DAC+D;AAC/D,MAAM,UAAU,gBAAgB;IAI9B,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;AAC5D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,EACzB,QAAQ,EACR,QAAQ,EACR,GAAG,IAAI,EACgD;IACvD,0DAA0D;IAC1D,0DAA0D;IAC1D,MAAM,KAAK,GAAG,MAAM,CAClB,QAAQ,IAAI,gBAAgB,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CACtE,CAAC;IAEF,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC;QACzB,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACzB,OAAO,GAAG,EAAE;YACV,gBAAgB,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC9B,CAAC,CAAC;IACJ,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,CACL,KAAC,IAAI,IAAC,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,CAAC,OAAO,KAAM,IAAI,YACxD,QAAQ,GACJ,CACR,CAAC;AACJ,CAAC;AAED,6CAA6C;AAC7C,MAAM,UAAU,4BAA4B;IAC1C,WAAW,CAAC,KAAK,EAAE,CAAC;IACpB,gBAAgB,CAAC,KAAK,EAAE,CAAC;AAC3B,CAAC"}
1
+ {"version":3,"file":"mask.js","sourceRoot":"","sources":["../src/mask.tsx"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAqBrC,eAAO,MAAM,OAAO,GAAI,OAAO,KAAK,KAAG,IAUtC,CAAC;AAEF,eAAO,MAAM,cAAc,QAAO,IAOjC,CAAC;AAEF,eAAO,MAAM,UAAU,QAAa,OAAO,CAAC,IAAI,CAe/C,CAAC;AAqBF,eAAO,MAAM,KAAK,QAAa,OAAO,CAAC,IAAI,CAkB1C,CAAC;AA4FF,eAAO,MAAM,iBAAiB,QAAa,OAAO,CAAC,IAAI,CAatD,CAAC;AAEF,eAAO,MAAM,eAAe,QAAO,IAOlC,CAAC;AAEF,eAAO,MAAM,WAAW,QAAO,SAAS,KAAK,EAAY,CAAC;AAE1D;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GAC1B,WAAW,MAAM,EACjB,OAAO,MAAM,EACb,MAAM,OAAO,KACZ,OAAO,CAAC,IAAI,CAcd,CAAC;AAMF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,gBAAgB,GAC3B,SAAS,MAAM,EACf,MAAM,OAAO,SAAS,EAAE,cAAc,CAAC,MAAM,CAAC,EAC9C,MAAM;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,EAC3C,OAAM;IAAE,MAAM,CAAC,EAAE,SAAS,GAAG,KAAK,GAAG,IAAI,CAAA;CAAO,KAC/C,OAAO,CAAC,OAAO,SAAS,EAAE,cAAc,GAAG,IAAI,CA6CjD,CAAC"}
1
+ {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAqBrC,eAAO,MAAM,OAAO,GAAI,OAAO,KAAK,KAAG,IAUtC,CAAC;AAEF,eAAO,MAAM,cAAc,QAAO,IAOjC,CAAC;AAEF,eAAO,MAAM,UAAU,QAAa,OAAO,CAAC,IAAI,CAe/C,CAAC;AAqBF,eAAO,MAAM,KAAK,QAAa,OAAO,CAAC,IAAI,CAkB1C,CAAC;AA4FF,eAAO,MAAM,iBAAiB,QAAa,OAAO,CAAC,IAAI,CAatD,CAAC;AAEF,eAAO,MAAM,eAAe,QAAO,IAOlC,CAAC;AAEF,eAAO,MAAM,WAAW,QAAO,SAAS,KAAK,EAAY,CAAC;AAE1D;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GAC1B,WAAW,MAAM,EACjB,OAAO,MAAM,EACb,MAAM,OAAO,KACZ,OAAO,CAAC,IAAI,CAcd,CAAC;AAMF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,gBAAgB,GAC3B,SAAS,MAAM,EACf,MAAM,OAAO,SAAS,EAAE,cAAc,CAAC,MAAM,CAAC,EAC9C,MAAM;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,EAC3C,OAAM;IAAE,MAAM,CAAC,EAAE,SAAS,GAAG,KAAK,GAAG,IAAI,CAAA;CAAO,KAC/C,OAAO,CAAC,OAAO,SAAS,EAAE,cAAc,GAAG,IAAI,CAmDjD,CAAC"}
package/lib/transport.js CHANGED
@@ -256,9 +256,16 @@ export const uploadAttachment = async (eventId, kind, blob, opts = {}) => {
256
256
  },
257
257
  method: 'POST',
258
258
  });
259
- if (resp.status !== 201)
259
+ // Phase 48 sub-A: accept any 2xx instead of strict 201. Reverse
260
+ // proxies in front of ingest occasionally rewrite 201 → 202 (the
261
+ // exact symptom Insight observed), and a 200 is also a valid
262
+ // "stored" response. We still require a JSON body shaped like
263
+ // UploadResponse; non-JSON bodies fall through to null.
264
+ if (resp.status < 200 || resp.status >= 300)
265
+ return null;
266
+ const j = (await resp.json().catch(() => null));
267
+ if (!j || !j.refId)
260
268
  return null;
261
- const j = (await resp.json());
262
269
  return {
263
270
  kind,
264
271
  mediaType: j.mediaType,
@@ -1 +1 @@
1
- {"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goliapkg/sentori-react-native",
3
- "version": "0.7.0",
3
+ "version": "0.7.2",
4
4
  "description": "Sentori SDK for React Native \u2014 JS-layer error capture, native crash handlers (iOS / Android), batched transport, fetch + react-navigation tracing.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://sentori.golia.jp",
@@ -69,6 +69,6 @@
69
69
  "access": "public"
70
70
  },
71
71
  "dependencies": {
72
- "@goliapkg/sentori-core": "0.6.0"
72
+ "@goliapkg/sentori-core": "0.7.0"
73
73
  }
74
74
  }
@@ -1,3 +1,5 @@
1
+ import { coerceError } from '@goliapkg/sentori-core';
2
+
1
3
  import { captureError } from '../capture';
2
4
 
3
5
  type RejectionTracker = (opts: {
@@ -22,9 +24,11 @@ export const installPromiseHandler = (): void => {
22
24
  allRejections: true,
23
25
  onUnhandled: (_id, rejection) => {
24
26
  try {
25
- const err =
26
- rejection instanceof Error ? rejection : new Error(String(rejection));
27
- captureError(err);
27
+ // `coerceError` keeps the actual rejection visible. JS code
28
+ // routinely rejects with plain objects (`Promise.reject({code})`),
29
+ // which would otherwise collapse to the literal text
30
+ // `[object Object]` in the dashboard.
31
+ captureError(coerceError(rejection));
28
32
  } catch {
29
33
  // never throw
30
34
  }
@@ -2,13 +2,10 @@
2
2
  // on `captureException`. Off-main-thread, best-effort, opt-in.
3
3
  //
4
4
  // Performance contract (sub-D.04):
5
- // - Wait for the in-flight RN interaction batch to drain before
6
- // touching the view shot (`InteractionManager.runAfterInteractions`)
7
- // so we never extend the active gesture / animation by a frame.
8
- // - Yield one paint by chaining a `requestAnimationFrame` so the
9
- // screenshot reflects post-error UI state, not the frame that
10
- // was already half-laid-out.
11
- // - Capped output: 480 px on the longest edge, WebP q=70. Typical
5
+ // - Yield one paint via `requestAnimationFrame` before snapshotting
6
+ // so the screenshot reflects post-error UI state, not the frame
7
+ // that was already half-laid-out.
8
+ // - Capped output: 480 px on the longest edge, JPEG q=70. Typical
12
9
  // payload 30-80 KB; multipart hard cap is 500 KB.
13
10
  // - On any failure we silently return null. The error event still
14
11
  // goes to the server; the user just doesn't see a thumbnail.
@@ -17,8 +14,20 @@
17
14
  // lazily so apps that don't install it never pay the bundle cost
18
15
  // or fail at import time. Without it, `captureScreenshot()` returns
19
16
  // `null` immediately.
17
+ //
18
+ // 2026-05-15 — dropped `InteractionManager.runAfterInteractions`.
19
+ // RN's docs mark `InteractionManager` as deprecated and recommend
20
+ // `requestIdleCallback`, but `requestIdleCallback` doesn't actually
21
+ // exist in RN (it's a web API), so the deprecation print is
22
+ // unactionable for SDK consumers. The defensive "wait for the active
23
+ // gesture batch to drain" semantics it provided is not reachable
24
+ // from a screenshot triggered on captureException — by the time an
25
+ // error fires, the user is between actions, not mid-gesture — so
26
+ // removing it has no observable effect except silencing the warning.
27
+ // The `requestAnimationFrame` calls below still guarantee one paint
28
+ // commit before captureRef snapshots.
20
29
 
21
- import { InteractionManager } from 'react-native';
30
+ import { engageMasks } from '../mask';
22
31
 
23
32
  type CaptureRef = (
24
33
  // Phase 42: the lib accepts a React ref or — when we pass `undefined` —
@@ -65,12 +74,26 @@ export async function captureScreenshot(): Promise<ScreenshotBlob | null> {
65
74
  const captureRef = loadCaptureRef();
66
75
  if (!captureRef) return null;
67
76
 
68
- // Wait for the in-flight RN interaction batch to drain. This is
69
- // why screenshot capture doesn't visibly stall the user's last
70
- // action we let React commit before we ask the OS to render.
77
+ // Yield one paint frame so the post-error UI has committed before
78
+ // we ask the OS to snapshot it. The previous
79
+ // `InteractionManager.runAfterInteractions` step was removed: see
80
+ // file header — its replacement (`requestIdleCallback`) doesn't
81
+ // exist in RN, and on captureException the user is between actions
82
+ // anyway, so the gesture-batch-drain semantics never came into
83
+ // play in practice.
71
84
  await new Promise<void>((resolve) => {
72
- InteractionManager.runAfterInteractions(() => resolve());
85
+ requestAnimationFrame(() => resolve());
73
86
  });
87
+
88
+ // Phase 48 sub-B — flip every registered MaskRegion overlay to
89
+ // opacity 1 (black covers the children) and every imperative
90
+ // setMaskedNode ref to opacity 0 (subtree disappears). Held for
91
+ // exactly one frame's worth of capture, then restored.
92
+ const restoreMasks = engageMasks();
93
+ // Yield one more frame so the overlay paint reaches the screen
94
+ // before captureRef snapshots. Without this the overlay opacity
95
+ // change is queued but the screenshotter may see the previous
96
+ // frame.
74
97
  await new Promise<void>((resolve) => {
75
98
  requestAnimationFrame(() => resolve());
76
99
  });
@@ -87,6 +110,7 @@ export async function captureScreenshot(): Promise<ScreenshotBlob | null> {
87
110
  }),
88
111
  CAPTURE_TIMEOUT_MS,
89
112
  );
113
+ restoreMasks();
90
114
  if (!base64) return null;
91
115
  // view-shot doesn't ship a WebP encoder on every RN version.
92
116
  // JPEG q=70 fits the budget too (typical 40-100 KB) and every
@@ -94,6 +118,7 @@ export async function captureScreenshot(): Promise<ScreenshotBlob | null> {
94
118
  // RN minimum we support has it everywhere.
95
119
  return { base64, mediaType: 'image/jpeg' };
96
120
  } catch {
121
+ restoreMasks();
97
122
  return null;
98
123
  }
99
124
  }
package/src/mask.tsx CHANGED
@@ -1,89 +1,143 @@
1
- // Phase 42 sub-D.09/10 — mark UI regions as "do not screenshot".
1
+ // Phase 42 sub-D.09/10 + Phase 48 sub-B — mark UI regions as "do not
2
+ // screenshot" AND actually redact them at capture time.
2
3
  //
3
- // `<MaskRegion>` wraps any subtree the SDK should redact before
4
- // shipping a crash screenshot. It's purely declarative the
5
- // component renders its children as-is in normal flight, but its
6
- // `View` is tagged with `collapsable={false}` + a sentinel
7
- // `nativeID` so the platform-level screenshotters
8
- // (`react-native-view-shot`, the iOS / Android native crash
9
- // capturers we add in sub-E / sub-F) can find it and paint over.
4
+ // `<MaskRegion>` renders its children normally, plus a black overlay
5
+ // `<View>` that sits on top of them with `opacity: 0`. Right before
6
+ // `captureScreenshot()` calls into react-native-view-shot, the SDK
7
+ // flips every registered overlay to `opacity: 1` (black square covers
8
+ // the children), captures, then flips back to `opacity: 0` so the
9
+ // user never sees the overlay. The overlay uses `pointerEvents="none"`
10
+ // so it never intercepts touches.
10
11
  //
11
- // `setMaskedNode(ref)` is the imperative escape hatch: useful
12
- // when the sensitive view isn't yours to wrap (a third-party
13
- // modal, a video player, etc.). Pass a React ref obtained via
14
- // `createRef()` / `useRef()` and the SDK will redact that
15
- // subtree the next time it captures.
16
- //
17
- // `getMaskedRegions()` returns the current set of native tags;
18
- // `captureScreenshot()` would consult this list, but
19
- // `react-native-view-shot` doesn't expose a "redact these rects"
20
- // hook — so this iteration ships the registration API only and
21
- // the rendered overlay lives behind a default-on
22
- // `<View style={{ backgroundColor: '#000' }}>` you can wrap
23
- // yourself. The iOS / Android crash-time screenshotters in
24
- // sub-E / sub-F will read this list before drawing.
12
+ // `setMaskedNode(ref)` is the imperative escape hatch for views you
13
+ // can't wrap. We can't inject an overlay into a foreign view, so the
14
+ // imperative path falls back to setting the registered view's own
15
+ // `opacity: 0` for the capture window content underneath may show
16
+ // through, but that beats sending the sensitive content to the server.
17
+ // Prefer `<MaskRegion>` when you control the subtree.
25
18
 
26
19
  import React, { type ReactNode, useEffect, useRef } from 'react';
27
- import { View, type ViewProps } from 'react-native';
20
+ import { StyleSheet, View, type ViewProps } from 'react-native';
21
+
22
+ /** What we drive in the capture window: any handle with
23
+ * `setNativeProps({ opacity })`. RN's View instance satisfies this. */
24
+ type Maskable = {
25
+ setNativeProps?: (props: { style?: { opacity?: number } }) => void;
26
+ };
28
27
 
29
- /** Component-level node identifiers we've been asked to redact. */
30
- const _maskedRefs = new Set<React.Component | View | unknown>();
28
+ const _maskedRefs = new Set<Maskable>();
29
+ const _maskedOverlays = new Set<Maskable>();
31
30
  const _maskedNativeIds = new Set<string>();
32
31
 
33
32
  /**
34
- * Imperative registration: when you can't wrap the sensitive view
35
- * in `<MaskRegion>`, drop a ref on it and call `setMaskedNode(ref)`.
36
- * Future captures will mask the subtree.
33
+ * Imperative registration: pass a ref obtained via `useRef()` /
34
+ * `createRef()` to a `<View>` you want hidden from screenshots.
37
35
  */
38
- export function setMaskedNode(node: React.Component | View | null | unknown): void {
39
- if (!node) return;
40
- _maskedRefs.add(node);
36
+ export function setMaskedNode(node: null | Maskable | unknown): void {
37
+ if (!node || typeof (node as Maskable).setNativeProps !== 'function') return;
38
+ _maskedRefs.add(node as Maskable);
41
39
  }
42
40
 
43
- /** Removes a previously registered ref. Pair this with mount/unmount
44
- * lifecycle hooks if the node is short-lived. */
45
- export function unsetMaskedNode(node: React.Component | View | null | unknown): void {
41
+ export function unsetMaskedNode(node: null | Maskable | unknown): void {
46
42
  if (!node) return;
47
- _maskedRefs.delete(node);
43
+ _maskedRefs.delete(node as Maskable);
48
44
  }
49
45
 
50
46
  /** Returns the current set of registered masked nodes + nativeIDs.
51
- * Read by the native screenshotter layer in sub-E / sub-F. */
47
+ * Read by the native screenshotter layer (iOS / Android sub-E / F). */
52
48
  export function getMaskedRegions(): {
53
- refs: Set<unknown>;
54
49
  nativeIds: Set<string>;
50
+ overlays: Set<Maskable>;
51
+ refs: Set<Maskable>;
55
52
  } {
56
- return { nativeIds: _maskedNativeIds, refs: _maskedRefs };
53
+ return { nativeIds: _maskedNativeIds, overlays: _maskedOverlays, refs: _maskedRefs };
54
+ }
55
+
56
+ /**
57
+ * Phase 48 sub-B — engage masking right before screenshot capture.
58
+ * Returns a function the caller must invoke once capture is done so
59
+ * the user never sees the black overlays.
60
+ *
61
+ * Two paths:
62
+ * - Overlays from `<MaskRegion>`: flip opacity 0 → 1 (cover children).
63
+ * - Imperative refs from `setMaskedNode`: flip opacity 1 → 0 on the
64
+ * view itself (whole subtree disappears for one frame).
65
+ *
66
+ * All `setNativeProps` calls are best-effort — a failure on one
67
+ * doesn't block the others or the capture.
68
+ */
69
+ export function engageMasks(): () => void {
70
+ const overlaysEngaged: Maskable[] = [];
71
+ for (const o of _maskedOverlays) {
72
+ try {
73
+ o.setNativeProps?.({ style: { opacity: 1 } });
74
+ overlaysEngaged.push(o);
75
+ } catch {
76
+ // skip
77
+ }
78
+ }
79
+ const refsEngaged: Maskable[] = [];
80
+ for (const r of _maskedRefs) {
81
+ try {
82
+ r.setNativeProps?.({ style: { opacity: 0 } });
83
+ refsEngaged.push(r);
84
+ } catch {
85
+ // skip
86
+ }
87
+ }
88
+ return () => {
89
+ for (const o of overlaysEngaged) {
90
+ try {
91
+ o.setNativeProps?.({ style: { opacity: 0 } });
92
+ } catch {
93
+ // skip
94
+ }
95
+ }
96
+ for (const r of refsEngaged) {
97
+ try {
98
+ r.setNativeProps?.({ style: { opacity: 1 } });
99
+ } catch {
100
+ // skip
101
+ }
102
+ }
103
+ };
57
104
  }
58
105
 
59
106
  /**
60
107
  * Declarative redaction. `<MaskRegion>{children}</MaskRegion>` keeps
61
- * the children visible in normal flight; under capture, the wrapping
62
- * view is repainted black so the rendered screenshot doesn't leak
63
- * the underlying pixels.
108
+ * the children visible in normal flight; under capture the overlay's
109
+ * opacity is flipped to 1 and the children are hidden behind a black
110
+ * square in the rendered screenshot.
64
111
  */
65
112
  export function MaskRegion({
66
113
  children,
67
114
  nativeID,
68
115
  ...rest
69
116
  }: { children: ReactNode; nativeID?: string } & ViewProps): React.JSX.Element {
70
- // Auto-generate a stable nativeID per mount so the native
71
- // screenshotter can find this view by ID at capture time.
72
117
  const idRef = useRef<string>(
73
118
  nativeID ?? `sentori-mask-${Math.random().toString(36).slice(2, 10)}`,
74
119
  );
120
+ const overlayRef = useRef<null | View>(null);
75
121
 
76
122
  useEffect(() => {
77
123
  const id = idRef.current;
78
124
  _maskedNativeIds.add(id);
125
+ const overlay = overlayRef.current as null | Maskable;
126
+ if (overlay) _maskedOverlays.add(overlay);
79
127
  return () => {
80
128
  _maskedNativeIds.delete(id);
129
+ if (overlay) _maskedOverlays.delete(overlay);
81
130
  };
82
131
  }, []);
83
132
 
84
133
  return (
85
134
  <View collapsable={false} nativeID={idRef.current} {...rest}>
86
135
  {children}
136
+ <View
137
+ pointerEvents="none"
138
+ ref={overlayRef}
139
+ style={[StyleSheet.absoluteFill, { backgroundColor: '#000', opacity: 0 }]}
140
+ />
87
141
  </View>
88
142
  );
89
143
  }
@@ -91,5 +145,6 @@ export function MaskRegion({
91
145
  /** Test-only — flush registration tables. */
92
146
  export function __resetMaskedRegionsForTests(): void {
93
147
  _maskedRefs.clear();
148
+ _maskedOverlays.clear();
94
149
  _maskedNativeIds.clear();
95
150
  }
package/src/transport.ts CHANGED
@@ -295,13 +295,19 @@ export const uploadAttachment = async (
295
295
  },
296
296
  method: 'POST',
297
297
  });
298
- if (resp.status !== 201) return null;
299
- const j = (await resp.json()) as {
298
+ // Phase 48 sub-A: accept any 2xx instead of strict 201. Reverse
299
+ // proxies in front of ingest occasionally rewrite 201 → 202 (the
300
+ // exact symptom Insight observed), and a 200 is also a valid
301
+ // "stored" response. We still require a JSON body shaped like
302
+ // UploadResponse; non-JSON bodies fall through to null.
303
+ if (resp.status < 200 || resp.status >= 300) return null;
304
+ const j = (await resp.json().catch(() => null)) as null | {
300
305
  refId: string;
301
306
  sizeBytes: number;
302
307
  mediaType: string;
303
308
  kind: string;
304
309
  };
310
+ if (!j || !j.refId) return null;
305
311
  return {
306
312
  kind,
307
313
  mediaType: j.mediaType,