@sightspool/sdk 0.3.0 → 0.4.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/CHANGELOG.md +15 -0
- package/README.md +111 -333
- package/SECURITY.md +16 -0
- package/dist/index.cjs +159 -1226
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -41
- package/dist/index.d.ts +20 -41
- package/dist/index.js +160 -761
- package/dist/index.js.map +1 -1
- package/dist/release.json +21 -0
- package/dist/sdk.global.js +2 -59
- package/dist/sdk.global.js.map +1 -1
- package/dist/source.json +11 -0
- package/package.json +14 -12
- package/site/CNAME +1 -0
- package/site/demo.html +6 -0
- package/site/demo.js +27 -0
- package/site/index.html +74 -0
- package/site/release.js +20 -0
- package/site/releases/0.4.0/2acf3463db7571c459ca01a4516341891bb4c2ec6265962f494ca8b553cc0e9f/release.json +21 -0
- package/site/releases/0.4.0/2acf3463db7571c459ca01a4516341891bb4c2ec6265962f494ca8b553cc0e9f/sdk.global.js +3 -0
- package/site/releases/0.4.0/2acf3463db7571c459ca01a4516341891bb4c2ec6265962f494ca8b553cc0e9f/sdk.global.js.map +1 -0
- package/site/releases/0.4.0/2acf3463db7571c459ca01a4516341891bb4c2ec6265962f494ca8b553cc0e9f/source.json +11 -0
- package/site/releases/0.4.0/c21bee307a5c53e3ebb49d21f0f1ef2cce06a4483fa9824122bd6897ad6ca3ac/release.json +21 -0
- package/site/releases/0.4.0/c21bee307a5c53e3ebb49d21f0f1ef2cce06a4483fa9824122bd6897ad6ca3ac/sdk.global.js +3 -0
- package/site/releases/0.4.0/c21bee307a5c53e3ebb49d21f0f1ef2cce06a4483fa9824122bd6897ad6ca3ac/sdk.global.js.map +1 -0
- package/site/releases/0.4.0/c21bee307a5c53e3ebb49d21f0f1ef2cce06a4483fa9824122bd6897ad6ca3ac/source.json +11 -0
- package/site/styles.css +69 -0
- package/site/trust.html +36 -0
- package/dist/chunk-4PADVIIO.js +0 -41
- package/dist/chunk-4PADVIIO.js.map +0 -1
- package/dist/probe-BNGMQQ5C.js +0 -113
- package/dist/probe-BNGMQQ5C.js.map +0 -1
- package/dist/prompt-GDFYC75D.js +0 -139
- package/dist/prompt-GDFYC75D.js.map +0 -1
- package/dist/survey-CICJQ5VI.js +0 -145
- package/dist/survey-CICJQ5VI.js.map +0 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.4.0 — prepared, unpublished
|
|
4
|
+
|
|
5
|
+
- Replace the public capture entry with research invitation delivery.
|
|
6
|
+
- Require an explicit all_visitors or signed_in audience; no fabricated visitor identity.
|
|
7
|
+
- Retain only local presence of identity, with no user IDs in offer payloads.
|
|
8
|
+
- Omit cookies and Referer from offer requests; pause, logout and cleanup reject late responses.
|
|
9
|
+
- Add versioned content-addressed browser files, SHA-384 integrity, source snapshots,
|
|
10
|
+
measured bundle sizes and manifest checks before publication.
|
|
11
|
+
- Add data/security documentation and an actual-SDK demo with simulated transport.
|
|
12
|
+
|
|
13
|
+
The app/backend handles participant consent, recording, transcription and analysis
|
|
14
|
+
separately. No independent security audit or new hosted acceptance is claimed.
|
|
15
|
+
Earlier capture API documentation is archived in docs/legacy.
|
package/README.md
CHANGED
|
@@ -1,384 +1,162 @@
|
|
|
1
1
|
# @sightspool/sdk
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
into your Sightspool workspace.
|
|
3
|
+
Embed Sightspool research in your product. The SDK invites website visitors and signed-in users into
|
|
4
|
+
owner-approved research during User Hours, then opens Sightspool for consent and
|
|
5
|
+
the interview. It does not capture page content, behavior, form values or analytics.
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
them).
|
|
7
|
+
This is the **0.4.0 research API**, a deliberate change from the old capture SDK.
|
|
8
|
+
Install the exact version below and review upgrades before deploying them.
|
|
9
|
+
Old capture code and documentation are archived in `docs/legacy`.
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
Read the [data and security guide](https://www.sightspool.com/sdk/data-security),
|
|
12
|
+
inspect the [public source](https://github.com/sightspool/sdk), or try the
|
|
13
|
+
[isolated SDK demo](https://www.sightspool.com/sdk/demo).
|
|
16
14
|
|
|
17
|
-
|
|
18
|
-
[llms.txt](https://sdk.sightspool.com/llms.txt) (the machine-readable install/CSP/config
|
|
19
|
-
doc for agents) · [npm](https://www.npmjs.com/package/@sightspool/sdk)
|
|
15
|
+
## npm
|
|
20
16
|
|
|
21
|
-
|
|
17
|
+
```sh
|
|
18
|
+
npm install --save-exact @sightspool/sdk@0.4.0
|
|
19
|
+
```
|
|
22
20
|
|
|
23
|
-
|
|
21
|
+
```ts
|
|
22
|
+
import Sightspool from '@sightspool/sdk'
|
|
24
23
|
|
|
25
|
-
|
|
24
|
+
Sightspool.init({ key: 'YOUR_GO_LIVE_WIDGET_UUID', audience: 'all_visitors' })
|
|
25
|
+
// Optional when a visitor signs in:
|
|
26
|
+
Sightspool.identify(currentUser.id)
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
// On logout (anonymous visitors remain eligible in all_visitors mode):
|
|
29
|
+
Sightspool.identify(null)
|
|
30
|
+
|
|
31
|
+
// When leaving the pages included in your research:
|
|
32
|
+
Sightspool.destroy()
|
|
29
33
|
```
|
|
30
34
|
|
|
31
|
-
|
|
32
|
-
import Sightspool from '@sightspool/sdk'
|
|
35
|
+
Choose an explicit `audience` matching the approved interview plan:
|
|
33
36
|
|
|
34
|
-
|
|
37
|
+
- `all_visitors`: anonymous website visitors and signed-in users. Initialize once on the relevant website pages. No identity or login is required; logout continues recruitment as a visitor.
|
|
38
|
+
- `signed_in`: only users with a real signed-in session. Initialize in your authenticated app shell and call `identify(actualUserId)` after authentication resolves. Until then, no request or invitation is made. `identify(null)` immediately removes invitations on logout.
|
|
35
39
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
40
|
+
There is no default audience. Missing or invalid audience configuration stays idle.
|
|
41
|
+
`identify` retains only whether an ID is present; the ID itself is not stored or
|
|
42
|
+
transmitted. Do not invent IDs for visitors. This client setting controls display;
|
|
43
|
+
it does not authenticate a participant or change the server-approved research cohort.
|
|
39
44
|
|
|
40
|
-
|
|
41
|
-
and **plan** — the only required wiring, and it's what lets Sightspool rank by customer
|
|
42
|
-
(and, with a connected billing source, by MRR). Most apps already make an equivalent
|
|
43
|
-
call for their analytics/support tools.
|
|
45
|
+
## Script tag
|
|
44
46
|
|
|
45
|
-
|
|
47
|
+
For visitors, no authentication integration is needed:
|
|
46
48
|
|
|
47
49
|
```html
|
|
48
|
-
<script
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
data-sightspool-key="pk_live_…"
|
|
52
|
-
></script>
|
|
50
|
+
<script async src="https://www.sightspool.com/sdk.global.js"
|
|
51
|
+
data-sightspool-key="YOUR_GO_LIVE_WIDGET_UUID"
|
|
52
|
+
data-sightspool-audience="all_visitors"></script>
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
For signed-in-only research, listen for readiness before loading the SDK, then
|
|
56
|
+
synchronize your actual session:
|
|
57
57
|
|
|
58
58
|
```html
|
|
59
59
|
<script>
|
|
60
|
-
|
|
60
|
+
let sightspoolUserId = null;
|
|
61
|
+
function syncSightspoolUser(userId) {
|
|
62
|
+
sightspoolUserId = userId;
|
|
63
|
+
window.Sightspool?.identify(userId);
|
|
64
|
+
}
|
|
65
|
+
window.addEventListener('sightspool:ready', () => {
|
|
66
|
+
window.Sightspool.identify(sightspoolUserId);
|
|
67
|
+
});
|
|
68
|
+
// Call syncSightspoolUser(actualUser.id) when authentication resolves.
|
|
69
|
+
// Call syncSightspoolUser(null) on logout.
|
|
61
70
|
</script>
|
|
71
|
+
<script async src="https://www.sightspool.com/sdk.global.js"
|
|
72
|
+
data-sightspool-key="YOUR_GO_LIVE_WIDGET_UUID"
|
|
73
|
+
data-sightspool-audience="signed_in"></script>
|
|
62
74
|
```
|
|
63
75
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
`init` options — comma-separate selector lists):
|
|
69
|
-
|
|
70
|
-
```html
|
|
71
|
-
<script
|
|
72
|
-
async
|
|
73
|
-
src="https://www.sightspool.com/sdk.global.js"
|
|
74
|
-
data-sightspool-key="pk_live_…"
|
|
75
|
-
data-sightspool-block=".billing-panel, [data-private]"
|
|
76
|
-
data-sightspool-redact=".customer-name"
|
|
77
|
-
data-sightspool-debug
|
|
78
|
-
data-sightspool-capture-localhost
|
|
79
|
-
></script>
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
---
|
|
83
|
-
|
|
84
|
-
## Configuration
|
|
85
|
-
|
|
86
|
-
`init(config)` — all optional except `key`:
|
|
87
|
-
|
|
88
|
-
| option | type | default | purpose |
|
|
89
|
-
|---|---|---|---|
|
|
90
|
-
| `key` | `string` | — | **required.** Your publishable key (`pk_test_…` / `pk_live_…`), from the Connections → In-product SDK card. Publishable — safe to ship in client JS. See [Keys & environments](#keys--environments). |
|
|
91
|
-
| `endpoint` | `string` | the bundle's origin (script tag) / `https://www.sightspool.com` (npm) | Ingest base URL. The `<script>` install auto-resolves it to wherever `sdk.global.js` was served from (your app), so the key alone is enough; override for a CDN-hosted bundle or dev. |
|
|
92
|
-
| `boundaryAsk` | `boolean` | `true` | Show the one-tap "did you do what you came to do?" ask at session boundaries. |
|
|
93
|
-
| `interventions` | `boolean` | `true` | Show human-approved **surveys** served by your Sightspool workspace (see [Interventions](#interventions-surveys)). Set `false` to capture only. |
|
|
94
|
-
| `consent` | `boolean` | `true` | Start capturing immediately. Set `false` to stay paused until you call `Sightspool.consent(true)` (or `start()`) after obtaining consent. |
|
|
95
|
-
| `redact` | `string[]` | `[]` | CSS selectors whose captured text is **masked** (replaced with `‹redacted›`) before anything leaves the page. The event is still recorded — only its label is masked. |
|
|
96
|
-
| `block` | `string[]` | `[]` | CSS selectors whose events are **dropped entirely** (the hard opt-out). Equivalent to putting `data-sightspool-ignore` on the element. |
|
|
97
|
-
| `captureOnLocalhost` | `boolean` | `false` | By default the SDK **no-ops on localhost** (`localhost`, `127.0.0.1`, `*.local`, `*.localhost`) so your `npm run dev` traffic never pollutes analytics. Set `true` to capture locally (e.g. to test the install). |
|
|
98
|
-
| `debug` | `boolean` | `false` | Log every capture decision to the console (`[sightspool] …`) so you can watch it work. |
|
|
99
|
-
|
|
100
|
-
Server-side config (allowed CORS origins, additional redaction rules) lives on the
|
|
101
|
-
Connections card and is enforced at ingest — the key alone can't post from an
|
|
102
|
-
un-allowlisted origin.
|
|
103
|
-
|
|
104
|
-
### Privacy controls at a glance
|
|
105
|
-
|
|
106
|
-
| You want to… | Use |
|
|
107
|
-
|---|---|
|
|
108
|
-
| Never capture a subtree (e.g. a billing panel) | `data-sightspool-ignore` on the element, or a `block` selector |
|
|
109
|
-
| Mask a field's text but still log the interaction | a `redact` selector (text → `‹redacted›`) |
|
|
110
|
-
| Wait for cookie-banner consent | init `{ consent: false }` then `Sightspool.consent(true)` |
|
|
111
|
-
| Keep dev traffic out of analytics | nothing — localhost is suppressed by default |
|
|
112
|
-
|
|
113
|
-
---
|
|
114
|
-
|
|
115
|
-
## Keys & environments
|
|
116
|
-
|
|
117
|
-
Your key is **publishable** — safe to ship in client JS (the Stripe `pk_` model). Two
|
|
118
|
-
prefixes, one per environment:
|
|
119
|
-
|
|
120
|
-
| prefix | use it for |
|
|
121
|
-
|---|---|
|
|
122
|
-
| `pk_test_…` | development / staging / preview deploys |
|
|
123
|
-
| `pk_live_…` | production |
|
|
124
|
-
|
|
125
|
-
The SDK treats both prefixes **identically** — there's no client-side special-casing; the
|
|
126
|
-
prefix tells *Sightspool* (at ingest) which environment a Signal belongs to, so test traffic
|
|
127
|
-
never mixes into production analytics. Issue both from **Connections → In-product SDK**.
|
|
128
|
-
|
|
129
|
-
> The SDK also **no-ops on localhost** by default (see `captureOnLocalhost`), so even a
|
|
130
|
-
> `pk_live_` key won't capture from `npm run dev`. Test keys are for *deployed* non-prod
|
|
131
|
-
> environments (staging, previews).
|
|
132
|
-
|
|
133
|
-
Keep the key in an environment variable rather than hardcoding it, and pick test vs live by
|
|
134
|
-
environment. The key is exposed to the browser, so use your framework's **client** env-var
|
|
135
|
-
prefix (`NEXT_PUBLIC_`, `VITE_`, `PUBLIC_`, …) — it's publishable, so that's expected:
|
|
136
|
-
|
|
137
|
-
```js
|
|
138
|
-
Sightspool.init({ key: process.env.NEXT_PUBLIC_SIGHTSPOOL_KEY })
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
```bash
|
|
142
|
-
# .env.development / preview
|
|
143
|
-
NEXT_PUBLIC_SIGHTSPOOL_KEY=pk_test_…
|
|
144
|
-
# .env.production
|
|
145
|
-
NEXT_PUBLIC_SIGHTSPOOL_KEY=pk_live_…
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
---
|
|
76
|
+
Go live supplies the correct key and endpoint. Local keys belong to the local
|
|
77
|
+
Sightspool database and cannot be paired with production. Script installs default
|
|
78
|
+
to the origin serving the bundle; `data-sightspool-endpoint` overrides that for a
|
|
79
|
+
CDN or self-hosted install. npm defaults to `https://www.sightspool.com`.
|
|
149
80
|
|
|
150
|
-
##
|
|
81
|
+
## API
|
|
151
82
|
|
|
152
|
-
|
|
83
|
+
| Method | Behavior |
|
|
84
|
+
| --- | --- |
|
|
85
|
+
| `init({ key, audience, endpoint? })` | Start research for the explicit audience; UUID key required. Repeating the same configuration is idempotent. Changing it tears down the previous runtime. |
|
|
86
|
+
| `identify(userId)` | Enable signed-in eligibility; only its boolean presence stays locally. `null` clears it; signed-in-only invitations disappear, all-visitors recruitment continues. |
|
|
87
|
+
| `pause()` | Pause recruitment, abort the current request and remove the launcher. |
|
|
88
|
+
| `resume()` | Resume recruitment for the configured audience. Does not bypass approval or hours. |
|
|
89
|
+
| `destroy()` | Remove the launcher, listeners and polling. An already opened interview is not silently ended. |
|
|
90
|
+
| `getStatus()` | `not_initialized`, `signed_out`, `paused`, `checking`, `unavailable`, `available` or `error`. |
|
|
153
91
|
|
|
154
|
-
|
|
155
|
-
|
|
92
|
+
`pause` and `resume` are recruitment controls, not consent to record. Participants
|
|
93
|
+
still review the offer, eligibility and recording consent in Sightspool. Owners
|
|
94
|
+
approve the research setup separately. The backend remains authoritative for
|
|
95
|
+
availability, capacity, signed offers and interview admission.
|
|
156
96
|
|
|
157
|
-
|
|
158
|
-
// app/layout.tsx
|
|
159
|
-
import Script from 'next/script'
|
|
160
|
-
|
|
161
|
-
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
162
|
-
return (
|
|
163
|
-
<html lang="en">
|
|
164
|
-
<body>
|
|
165
|
-
{children}
|
|
166
|
-
<Script
|
|
167
|
-
src="https://www.sightspool.com/sdk.global.js"
|
|
168
|
-
data-sightspool-key={process.env.NEXT_PUBLIC_SIGHTSPOOL_KEY}
|
|
169
|
-
strategy="afterInteractive"
|
|
170
|
-
/>
|
|
171
|
-
</body>
|
|
172
|
-
</html>
|
|
173
|
-
)
|
|
174
|
-
}
|
|
175
|
-
```
|
|
97
|
+
## React / Next.js
|
|
176
98
|
|
|
177
|
-
|
|
99
|
+
Use a client component on the pages included in your research. Identity is optional for all visitors:
|
|
178
100
|
|
|
179
101
|
```tsx
|
|
180
102
|
'use client'
|
|
181
|
-
useEffect(() => {
|
|
182
|
-
window.Sightspool?.identify(user.id, { account: user.account, plan: user.plan })
|
|
183
|
-
}, [user])
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
> `data-sightspool-key` is inlined at build time, so `NEXT_PUBLIC_SIGHTSPOOL_KEY` must be set
|
|
187
|
-
> in the environment Next builds in. `strategy="afterInteractive"` keeps it off the critical
|
|
188
|
-
> path.
|
|
189
|
-
|
|
190
|
-
### React / Next.js — `@sightspool/react`
|
|
191
|
-
|
|
192
|
-
For React apps, use the official bindings — a declarative provider over the core SDK:
|
|
193
|
-
|
|
194
|
-
```bash
|
|
195
|
-
npm install @sightspool/react @sightspool/sdk react
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
```tsx
|
|
199
|
-
import { SightspoolProvider } from '@sightspool/react'
|
|
200
|
-
|
|
201
|
-
<SightspoolProvider
|
|
202
|
-
apiKey={process.env.NEXT_PUBLIC_SIGHTSPOOL_KEY!}
|
|
203
|
-
identity={user && { userId: user.id, account: user.account, plan: user.plan }}
|
|
204
|
-
>
|
|
205
|
-
<App />
|
|
206
|
-
</SightspoolProvider>
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
It ships a `"use client"` banner (drops straight into a Next.js App Router **server**
|
|
210
|
-
layout), re-fires `identify` whenever `identity` changes, and takes a reactive `consent`
|
|
211
|
-
prop for cookie banners. Full API:
|
|
212
|
-
[@sightspool/react](https://www.npmjs.com/package/@sightspool/react) ·
|
|
213
|
-
[packages/react](https://github.com/sightspool/sdk/tree/main/packages/react).
|
|
214
|
-
|
|
215
|
-
<details><summary>Or wire the core SDK by hand (no wrapper)</summary>
|
|
216
|
-
|
|
217
|
-
```tsx
|
|
218
103
|
import { useEffect } from 'react'
|
|
219
104
|
import Sightspool from '@sightspool/sdk'
|
|
220
105
|
|
|
221
|
-
export function
|
|
222
|
-
useEffect(() => {
|
|
223
|
-
|
|
106
|
+
export function Research({ userId = null }: { userId?: string | null }) {
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
Sightspool.init({ key: 'YOUR_GO_LIVE_WIDGET_UUID', audience: 'all_visitors' })
|
|
109
|
+
Sightspool.identify(userId)
|
|
110
|
+
return () => Sightspool.destroy()
|
|
111
|
+
}, [userId])
|
|
224
112
|
return null
|
|
225
113
|
}
|
|
226
114
|
```
|
|
227
|
-
</details>
|
|
228
|
-
|
|
229
|
-
---
|
|
230
|
-
|
|
231
|
-
## Content-Security-Policy
|
|
232
|
-
|
|
233
|
-
If your app sets a CSP, allow the SDK's two footprints — the **script load** and the
|
|
234
|
-
**ingest beacon**. With the standard install they're the *same host* (the bundle is served
|
|
235
|
-
from the app it ingests to), so it's one host in two directives:
|
|
236
|
-
|
|
237
|
-
**Script-tag install**
|
|
238
|
-
|
|
239
|
-
```
|
|
240
|
-
script-src https://www.sightspool.com;
|
|
241
|
-
connect-src https://www.sightspool.com;
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
**npm / bundler install** — the SDK is bundled into your own first-party JS, so no
|
|
245
|
-
`script-src` host is needed; only the ingest origin:
|
|
246
|
-
|
|
247
|
-
```
|
|
248
|
-
connect-src https://www.sightspool.com;
|
|
249
|
-
```
|
|
250
115
|
|
|
251
|
-
|
|
252
|
-
|
|
116
|
+
Optional `@sightspool/react` 0.2.0 provides `SightspoolProvider` and research hooks.
|
|
117
|
+
It requires SDK 0.4.x; see `packages/react/README.md`. Use one instance per document.
|
|
253
118
|
|
|
254
|
-
|
|
255
|
-
ignored for scripts — give the `<script>` tag your per-request nonce (`nonce={nonce}` in
|
|
256
|
-
Next) so it's trusted. `connect-src` still needs the ingest host.
|
|
257
|
-
- **Prompt styles.** The one-tap prompt renders into a **shadow root** and injects its own
|
|
258
|
-
`<style>`. Under a strict `style-src` without `'unsafe-inline'`, those styles may not apply
|
|
259
|
-
— the prompt stays **fully functional but unstyled** (the SDK never throws into your page).
|
|
260
|
-
Add `'unsafe-inline'` to `style-src` if you want it styled.
|
|
119
|
+
## Privacy and verification
|
|
261
120
|
|
|
262
|
-
|
|
121
|
+
The offer request contains only the public workspace key, a random per-workspace
|
|
122
|
+
browser-session device token, and the operation name. No user ID, traits, auth
|
|
123
|
+
secrets, DOM text or page URL is included. Requests use `credentials: 'omit'`.
|
|
124
|
+
Offer requests explicitly suppress the Referer header; Origin and ordinary network metadata still reach the service.
|
|
125
|
+
No microphone or recording starts on initialization or launcher display.
|
|
263
126
|
|
|
264
|
-
|
|
127
|
+
Polling occurs at most once per 15-second interval while visible and eligible for the configured audience,
|
|
128
|
+
plus explicit identity/resume/visibility changes. Only one request is in flight;
|
|
129
|
+
requests time out after 10 seconds. Identity changes, pause, hidden tabs and destruction invalidate stale responses.
|
|
130
|
+
Logout removes signed-in-only invitations; all-visitors mode refreshes as a visitor.
|
|
265
131
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
132
|
+
A connection receipt proves that the SDK contacted the configured server. It does
|
|
133
|
+
not certify your app's complete login/logout flow or a successful interview. Test
|
|
134
|
+
anonymous visits, login, navigation, logout, slow loading and unmounting in both modes. Use Go live
|
|
135
|
+
for server connection checks. A closed User Hours window can connect without an
|
|
136
|
+
invitation. Do not fabricate approval or weaken an origin/CSP policy to make it show.
|
|
271
137
|
|
|
272
|
-
|
|
273
|
-
SDK loads (and on each route change) it asks your workspace *"anything to show this user
|
|
274
|
-
here?"*; if there's a matching approved survey, it renders a small card in a shadow root
|
|
275
|
-
(your CSS can't reach it; it leaks no styles), and posts the answer back.
|
|
138
|
+
## Verified script releases
|
|
276
139
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
```html
|
|
283
|
-
<!-- script-tag install: opt out with one attribute -->
|
|
284
|
-
<script src="https://www.sightspool.com/sdk.global.js"
|
|
285
|
-
data-sightspool-key="pk_live_…"
|
|
286
|
-
data-sightspool-no-interventions></script>
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
**What shows, and how often** — all server-gated, so you stay in control:
|
|
290
|
-
|
|
291
|
-
- **Targeting** — only to the route / account / plan the approver chose.
|
|
292
|
-
- **De-dup** — a survey a user has answered never reappears (a per-user key is kept
|
|
293
|
-
locally; it's anonymous unless you've called `identify()`).
|
|
294
|
-
- **Won't pester** — at most one survey on screen, one per session, and it shares a
|
|
295
|
-
cooldown with the boundary ask so the two never stack back-to-back. The run also stops
|
|
296
|
-
itself once it hits the approver's response target.
|
|
297
|
-
|
|
298
|
-
The survey widget is **lazily loaded** — it splits into its own chunk and adds nothing to
|
|
299
|
-
your bundle until a survey is actually served. Today the SDK renders one-tap / short-text
|
|
300
|
-
surveys; richer types (incl. voice micro-research) render as they ship.
|
|
301
|
-
|
|
302
|
-
## What it captures
|
|
303
|
-
|
|
304
|
-
- **Passively, no wiring** — route/screen sequence, clicks, **dead-clicks** and
|
|
305
|
-
**rage-clicks**, client-side errors and failed requests, and a rolling trail of the
|
|
306
|
-
last meaningful events.
|
|
307
|
-
- **Stated intent** — typed queries in search / filter / command-palette / empty-state
|
|
308
|
-
inputs, especially **zero-result** searches (the highest-signal intent, no question).
|
|
309
|
-
- **The one-tap ask** (at a session boundary or after detected friction, rate-limited
|
|
310
|
-
and fatigue-aware): *"Were you able to do what you came here to do today?"* → on "Not
|
|
311
|
-
really," a short shortlist of likely goals + an always-present "Something else."
|
|
312
|
-
|
|
313
|
-
Each capture emits one **Signal** (`intent + path + account + effort`). Intent and
|
|
314
|
-
effort are *constructed* server-side with calibrated confidence — the SDK ships the
|
|
315
|
-
raw trace and the answer; it never guesses.
|
|
140
|
+
Go live generates a versioned, content-addressed script URL with SHA-384 integrity,
|
|
141
|
+
`crossorigin="anonymous"` and `referrerpolicy="no-referrer"`. Use those values together.
|
|
142
|
+
The unversioned script examples above are a convenience URL whose contents can change.
|
|
143
|
+
To pin a reviewed release, use Go live or the manifest linked from the data and security guide.
|
|
316
144
|
|
|
317
|
-
|
|
145
|
+
For self-hosting, copy the reviewed bundle and preserve its integrity value. Set
|
|
146
|
+
`data-sightspool-endpoint` to the workspace Sightspool origin explicitly, because
|
|
147
|
+
script auto-init otherwise defaults to the bundle host. Keep your own CSP and nonce
|
|
148
|
+
requirements; allow only the required script host and Sightspool connection origin.
|
|
318
149
|
|
|
319
|
-
##
|
|
150
|
+
## Maintainers
|
|
320
151
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
are **never** captured.
|
|
327
|
-
- **No cookies, no `localStorage`, no raw keystrokes.** The SDK reads none of them. It
|
|
328
|
-
captures *debounced* search-input values (stated intent) and interaction events — not
|
|
329
|
-
a keylog.
|
|
330
|
-
- **You control the rest.** Drop any subtree with `data-sightspool-ignore` or a `block`
|
|
331
|
-
selector; mask a field's text with a `redact` selector; gate everything behind
|
|
332
|
-
`Sightspool.consent(false)` until your cookie banner says otherwise.
|
|
333
|
-
- **Suppressed on localhost** so dev traffic never pollutes analytics (opt back in with
|
|
334
|
-
`captureOnLocalhost`).
|
|
335
|
-
|
|
336
|
-
And the engineering guarantees:
|
|
337
|
-
|
|
338
|
-
- **Never throws into your app.** Every path is wrapped; capture degrades silently
|
|
339
|
-
(no Signal is worth a broken host UI).
|
|
340
|
-
- **Tiny + non-blocking.** The prompt UI lazy-loads into a shadow root, so it can't be
|
|
341
|
-
styled-broken by your CSS and adds ~nothing to your bundle until it's needed.
|
|
342
|
-
- **Processor posture.** Sightspool processes on your behalf; the prompt discloses that
|
|
343
|
-
diagnostic context is attached.
|
|
344
|
-
|
|
345
|
-
---
|
|
346
|
-
|
|
347
|
-
## API
|
|
348
|
-
|
|
349
|
-
```ts
|
|
350
|
-
Sightspool.init(config: SightspoolConfig): void
|
|
351
|
-
Sightspool.identify(userId: string, traits?: { account?: string; plan?: string }): void
|
|
352
|
-
Sightspool.consent(granted: boolean): void // runtime consent toggle (wire to your cookie banner)
|
|
353
|
-
Sightspool.start(): void // begin capture if init'd with { consent: false } (alias of consent(true))
|
|
354
|
-
Sightspool.stop(): void // pause capture and flush (alias of consent(false))
|
|
152
|
+
```sh
|
|
153
|
+
pnpm install --frozen-lockfile
|
|
154
|
+
pnpm -r --include-workspace-root build
|
|
155
|
+
pnpm -r --include-workspace-root test
|
|
156
|
+
npm publish --dry-run
|
|
355
157
|
```
|
|
356
158
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
Deliberately deferred from the v1 collect side. Most are **data-gated** — they need real
|
|
362
|
-
traffic to calibrate, so they wait for the first production installs.
|
|
363
|
-
|
|
364
|
-
- **Server-LLM State-B candidates** (`/api/sdk/candidates`). Today the prompt's candidate goals
|
|
365
|
-
are derived **locally** (recent search query + page label) — instant and free, but shallow. A
|
|
366
|
-
server endpoint would generate sharper candidates in the app's own feature vocabulary, at the
|
|
367
|
-
cost of a per-prompt round-trip; it must fall back to the local/generic ask within a tight
|
|
368
|
-
latency budget.
|
|
369
|
-
- **Adaptive micro-interview** on high-value / high-MRR friction — a short, session-grounded,
|
|
370
|
-
agent-authored follow-up beyond the one-tap default, under the same fatigue caps.
|
|
371
|
-
- **Reliable zero-result detection.** v1 harvests the typed query; flagging it as *zero-result*
|
|
372
|
-
(the highest-signal intent) is best-effort and needs per-app empty-state hints.
|
|
373
|
-
- **Server-to-server signed (HMAC) ingest** — for non-browser / backend Signal sources (the
|
|
374
|
-
publishable-key + origin-allowlist posture is browser-only).
|
|
375
|
-
- **Mobile / native SDK** — web-first for now.
|
|
376
|
-
|
|
377
|
-
## Develop
|
|
378
|
-
|
|
379
|
-
```bash
|
|
380
|
-
pnpm install
|
|
381
|
-
pnpm build # tsup → dist/ (ESM + CJS + types + dist/sdk.global.js)
|
|
382
|
-
pnpm type-check
|
|
383
|
-
pnpm test # node --test over the pure cores
|
|
384
|
-
```
|
|
159
|
+
The npm and browser builds share `src/research.ts`. The Sightspool app copies
|
|
160
|
+
`sdk.global.js` from this package and serves `research-widget.js` as a byte-identical
|
|
161
|
+
alias for recent internal snippets. Neither build imports the former capture engine.
|
|
162
|
+
See `docs/research-sdk-transition.md` for release sequencing. Apache-2.0.
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Security policy
|
|
2
|
+
|
|
3
|
+
Report suspected vulnerabilities in the Sightspool SDK privately to
|
|
4
|
+
security@sightspool.com. Include the package version, affected feature, impact and
|
|
5
|
+
a minimal reproduction without customer data or secrets. Do not open a public
|
|
6
|
+
issue containing exploit details or private information.
|
|
7
|
+
|
|
8
|
+
The existing disclosure policy is https://www.sightspool.com/.well-known/security.txt.
|
|
9
|
+
It covers the public SDK packages and Sightspool service, not testing a customer's
|
|
10
|
+
website without their authorization. There is no paid bounty or new response-time
|
|
11
|
+
commitment implied here.
|
|
12
|
+
|
|
13
|
+
Research SDK 0.4.0 is prepared locally and has not had an independent security audit.
|
|
14
|
+
Automated tests and release integrity checks are not a security certification.
|
|
15
|
+
The legacy capture API is retired; it is not the supported research installation.
|
|
16
|
+
For data access/deletion requests, contact privacy@sightspool.com.
|