@intray/live 0.5.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lowside Labs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,97 @@
1
+ # @intray/live
2
+
3
+ Live session watching for [Intray](https://intray.ai). While someone on your team has the Live view open, the SDK streams the signed-in customer's page to them. Nobody watching means nothing is captured. Intray stores no replay.
4
+
5
+ ```sh
6
+ npm install @intray/live
7
+ ```
8
+
9
+ ## How the parts connect
10
+
11
+ ```
12
+ createLiveWatch (browser) -> your backend route -> Intray POST /v1/support/sessions
13
+ ```
14
+
15
+ The browser never holds the Intray key. Your backend reads the signed-in user from its own session, asks Intray for a short-lived recorder token, and returns it.
16
+
17
+ ## Server
18
+
19
+ Create the application in Intray under Live, Manage applications, with your product's exact HTTPS origin. Store its key as `INTRAY_LIVE_KEY`.
20
+
21
+ ```ts
22
+ // Server only. Take the user and account from your own session.
23
+ export async function bootstrapLiveSession(req: Request, sessionId: string) {
24
+ const user = await requireUser(req);
25
+
26
+ const res = await fetch(process.env.INTRAY_LIVE_BOOTSTRAP_URL!, {
27
+ method: "POST",
28
+ headers: {
29
+ Authorization: `Bearer ${process.env.INTRAY_LIVE_KEY}`,
30
+ "Content-Type": "application/json",
31
+ },
32
+ body: JSON.stringify({
33
+ sessionId,
34
+ userId: user.id,
35
+ accountId: user.accountId,
36
+ displayName: user.name,
37
+ origin: process.env.SITE_URL,
38
+ }),
39
+ });
40
+ if (!res.ok) throw new Error("Intray refused the live session");
41
+ return res.json(); // { token, expiresAt, serverUrl, sessionId }
42
+ }
43
+ ```
44
+
45
+ Do not let browser arguments choose `userId`, `accountId`, or `origin`. Tokens last 120 seconds. The SDK calls `getSession` again, with the same id, on every reconnect and renewal.
46
+
47
+ ## Browser
48
+
49
+ ```ts
50
+ import { createLiveWatch } from "@intray/live";
51
+
52
+ const live = createLiveWatch({
53
+ getSession: (sessionId) => api.bootstrapLiveSession({ sessionId }),
54
+ isPageAllowed: () => location.pathname.startsWith("/app"),
55
+ blockSelector: "[data-support-private]",
56
+ });
57
+
58
+ await live.start();
59
+
60
+ // On logout or account switch
61
+ live.destroy();
62
+ ```
63
+
64
+ ### Options
65
+
66
+ | Option | Type | Notes |
67
+ | -------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
68
+ | `getSession` | `(sessionId) => Promise<LiveBootstrap>` | Required. Calls your backend route. |
69
+ | `isPageAllowed` | `() => boolean` | Checked as the page changes. Capture pauses while it returns false. |
70
+ | `blockSelector` | `string` | Added to the default block list, which already has `[data-private]`. A blocked element renders as an empty box. |
71
+ | `maskTextSelector` | `string` | Defaults to `*`. All text is masked. |
72
+ | `publicTextSelector` | `string` | Opt-in for reviewed static labels, for example `[data-support-public]`. |
73
+ | `onState` | `(state) => void` | `connecting`, `idle`, `live`, `paused`, `hidden`, `reconnecting`, `unavailable`, `stopped`. |
74
+ | `assistance` | `boolean` | Off by default. Lets an operator request customer-approved clicks on `data-support-control` buttons. |
75
+ | `controlsContainer` | `HTMLElement` | Where the consent prompt mounts. Defaults to a floating prompt. |
76
+
77
+ ### Methods
78
+
79
+ `start()`, `stopSharing()`, `resumeSharing()`, `destroy()`. Use `stopSharing` and `resumeSharing` to give customers their own switch. Destroy the instance on logout or account change. Do not reuse it across identities.
80
+
81
+ ## Privacy defaults
82
+
83
+ - All text and every input value are masked before they leave the browser.
84
+ - Password and payment fields, canvas, and iframes are never sent.
85
+ - URL query strings and hashes are stripped.
86
+ - `.rr-mask`, `[data-support-mask]`, `[data-live-mask]`, inputs, and editable content stay masked even inside a public marker.
87
+ - Never put `publicTextSelector` markers on a container that can hold customer content.
88
+
89
+ Viewing shows no indicator in your product. Disclose live viewing in your privacy policy and terms.
90
+
91
+ ## Bundle
92
+
93
+ ESM only, browser target ES2022. The recorder loads on demand, so `rrweb` stays out of your first chunk. `rrweb` is pinned as a dependency.
94
+
95
+ ## License
96
+
97
+ MIT
@@ -0,0 +1,59 @@
1
+ /** Always blocked. A configured `blockSelector` adds to this list and cannot shorten it. */
2
+ declare const DEFAULT_BLOCK_SELECTOR: string;
3
+ /** Pilot default: every text node is masked. */
4
+ declare const DEFAULT_MASK_TEXT_SELECTOR = "*";
5
+
6
+ /**
7
+ * What the host backend returns from `POST /v1/support/sessions`.
8
+ *
9
+ * Declared here, not re-exported, because the protocol package is private and
10
+ * a published type file cannot import it. The check below fails the build when
11
+ * the two drift.
12
+ */
13
+ type LiveBootstrap = {
14
+ token: string;
15
+ expiresAt: number;
16
+ serverUrl: string;
17
+ sessionId: string;
18
+ };
19
+
20
+ type LiveWatchState = "connecting"
21
+ /** Connected and sharing. Nobody is watching, so nothing is captured. */
22
+ | "idle"
23
+ /** A viewer is present and the page is being captured. */
24
+ | "live"
25
+ /** The user stopped sharing. */
26
+ | "paused"
27
+ /** The tab is hidden or the page is not allowed. */
28
+ | "hidden" | "reconnecting"
29
+ /** The live service refused this session. No further attempts are made. */
30
+ | "unavailable" | "stopped";
31
+ type LiveWatchOptions = {
32
+ getSession: (sessionId: string) => Promise<LiveBootstrap>;
33
+ onState?: (state: string) => void;
34
+ /** Place the control consent prompt in the product layout instead of an overlay. */
35
+ controlsContainer?: HTMLElement;
36
+ /** Explicitly enable customer-approved clicks on data-support-control buttons. Defaults off. */
37
+ assistance?: boolean;
38
+ blockSelector?: string;
39
+ maskTextSelector?: string;
40
+ publicTextSelector?: string;
41
+ isPageAllowed?: () => boolean;
42
+ };
43
+ type LiveWatch = {
44
+ start(): Promise<void>;
45
+ stopSharing(): void;
46
+ resumeSharing(): void;
47
+ destroy(): void;
48
+ };
49
+
50
+ /**
51
+ * Shares the current page with Intray staff while one of them is watching.
52
+ *
53
+ * `getSession` must call the host app's authenticated backend, which returns a
54
+ * short-lived recorder token for the given session id. It is called again, with
55
+ * the same id, on every reconnect and renewal.
56
+ */
57
+ declare function createLiveWatch(options: LiveWatchOptions): LiveWatch;
58
+
59
+ export { DEFAULT_BLOCK_SELECTOR, DEFAULT_MASK_TEXT_SELECTOR, type LiveBootstrap, type LiveWatch, type LiveWatchOptions, type LiveWatchState, createLiveWatch };