@sightspool/sdk 0.4.0 → 0.4.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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## 0.4.1 — unpublished local candidate
2
+
3
+ - Replace popup interview delivery with one persistent corner panel.
4
+ - Keep open sessions mounted across minimize and recruitment cleanup.
5
+ - Restrict iframe permissions and validate parent message origin/source.
6
+
1
7
  # Changelog
2
8
 
3
9
  ## 0.4.0 — prepared, unpublished
package/README.md CHANGED
@@ -15,7 +15,7 @@ inspect the [public source](https://github.com/sightspool/sdk), or try the
15
15
  ## npm
16
16
 
17
17
  ```sh
18
- npm install --save-exact @sightspool/sdk@0.4.0
18
+ npm install --save-exact @sightspool/sdk@0.4.1
19
19
  ```
20
20
 
21
21
  ```ts
@@ -160,3 +160,24 @@ The npm and browser builds share `src/research.ts`. The Sightspool app copies
160
160
  `sdk.global.js` from this package and serves `research-widget.js` as a byte-identical
161
161
  alias for recent internal snippets. Neither build imports the former capture engine.
162
162
  See `docs/research-sdk-transition.md` for release sequencing. Apache-2.0.
163
+
164
+ ## Embedded interview panel (0.4.1, local candidate)
165
+
166
+ An available invitation opens one bottom-right panel on the product page. Consent,
167
+ waiting, supported founder audio or Sightspool text conversation, and completion
168
+ remain inside its isolated Sightspool iframe. Minimize or Escape hides the panel;
169
+ Return to interview reopens the same session. Minimize does not mute, end or withdraw.
170
+ Use the explicit in-panel controls to stop audio or delete interview evidence.
171
+
172
+ Allow the configured Sightspool origin in your site's `frame-src` policy and
173
+ permit microphone delegation to that origin if founder audio is used. Preserve
174
+ other CSP and Permissions Policy restrictions; a host policy may intentionally
175
+ block audio. The iframe requests a microphone only after explicit participant action.
176
+ The frame is restricted to the workspace's saved product origin and exchanges only
177
+ a minimize message with its parent, never interview text or audio. There is no popup.
178
+
179
+ Once opened, the panel stays mounted across visibility changes, pause, identity
180
+ changes and SDK destroy/remount, so recruitment cleanup cannot silently end a call.
181
+ Destroy stops further recruitment. The active panel remains reachable until the
182
+ page is left; a full page navigation may interrupt audio. Minimize preserves the
183
+ session only within the current document.
package/dist/index.cjs CHANGED
@@ -12,6 +12,7 @@ var current = () => {
12
12
  var eligible = (r) => r.audience === "all_visitors" || r.identified;
13
13
  function remove(r) {
14
14
  var _a;
15
+ if (r.frame) return;
15
16
  (_a = r.button) == null ? void 0 : _a.remove();
16
17
  r.button = null;
17
18
  r.offer = null;
@@ -26,8 +27,80 @@ function invalidate(r) {
26
27
  function status(r, value) {
27
28
  r.status = value;
28
29
  }
30
+ function expand(r, expanded) {
31
+ var _a;
32
+ if (!r.panel || !r.frame || !r.button) return;
33
+ r.expanded = expanded;
34
+ r.panel.hidden = !expanded;
35
+ r.panel.style.display = expanded ? "flex" : "none";
36
+ r.button.hidden = expanded;
37
+ r.button.setAttribute("aria-expanded", String(expanded));
38
+ if (expanded) {
39
+ (_a = r.panel.querySelector("button")) == null ? void 0 : _a.focus();
40
+ } else r.button.focus();
41
+ }
42
+ function openPanel(r) {
43
+ if (r.frame) {
44
+ expand(r, true);
45
+ return;
46
+ }
47
+ if (!r.offer || !r.button) return;
48
+ const panel = document.createElement("section");
49
+ panel.id = "sightspool-interview-panel";
50
+ panel.setAttribute("role", "dialog");
51
+ panel.setAttribute("aria-label", "Sightspool interview");
52
+ panel.style.cssText = "position:fixed;bottom:16px;right:16px;z-index:2147483001;box-sizing:border-box;width:420px;max-width:calc(100% - 32px);height:680px;max-height:calc(100dvh - 32px);border:1px solid #e6dfe3;border-radius:20px;background:#fff;color:#262024;box-shadow:0 12px 50px #0003;overflow:hidden;font:14px system-ui;display:flex;flex-direction:column";
53
+ const header = document.createElement("div");
54
+ header.style.cssText = "display:flex;align-items:center;justify-content:space-between;gap:12px;padding:14px 16px;border-bottom:1px solid #eee;flex-shrink:0";
55
+ const title = document.createElement("strong");
56
+ title.textContent = "Sightspool";
57
+ const minimize = document.createElement("button");
58
+ minimize.type = "button";
59
+ minimize.textContent = "Minimize";
60
+ minimize.style.cssText = "font:inherit;color:inherit;background:#fff;border:1px solid #d6cbd1;border-radius:8px;padding:8px 12px;cursor:pointer";
61
+ const hide = () => {
62
+ expand(r, false);
63
+ };
64
+ minimize.onclick = hide;
65
+ header.appendChild(title);
66
+ header.appendChild(minimize);
67
+ panel.appendChild(header);
68
+ const frame = document.createElement("iframe");
69
+ frame.title = "Sightspool research conversation";
70
+ frame.allow = "microphone; autoplay";
71
+ frame.setAttribute("sandbox", "allow-scripts allow-same-origin allow-forms");
72
+ frame.referrerPolicy = "no-referrer";
73
+ frame.style.cssText = "display:block;width:100%;flex:1;min-height:0;border:0;background:#fff";
74
+ const url = new URL(r.endpoint + "/interview-widget");
75
+ url.searchParams.set("key", r.key);
76
+ url.hash = new URLSearchParams({ offer: r.offer, device: r.device }).toString();
77
+ frame.src = url.href;
78
+ panel.appendChild(frame);
79
+ panel.onkeydown = (event) => {
80
+ if (event.key === "Escape") {
81
+ event.preventDefault();
82
+ hide();
83
+ }
84
+ };
85
+ r.panel = panel;
86
+ r.frame = frame;
87
+ r.message = (event) => {
88
+ var _a;
89
+ if (event.source !== frame.contentWindow || event.origin !== r.endpoint || ((_a = event.data) == null ? void 0 : _a.type) !== "sightspool:panel:minimize") return;
90
+ hide();
91
+ };
92
+ window.addEventListener("message", r.message);
93
+ r.button.textContent = "Return to interview";
94
+ r.button.setAttribute("aria-label", "Sightspool: return to your interview");
95
+ r.button.setAttribute("aria-controls", panel.id);
96
+ r.button.onclick = () => {
97
+ expand(r, true);
98
+ };
99
+ document.body.appendChild(panel);
100
+ expand(r, true);
101
+ }
29
102
  async function check(r) {
30
- if (r.disposed || r.paused || !eligible(r) || r.pending || document.visibilityState !== "visible") return;
103
+ if (r.frame || r.disposed || r.paused || !eligible(r) || r.pending || document.visibilityState !== "visible") return;
31
104
  const generation = r.generation;
32
105
  const request = new AbortController();
33
106
  r.pending = request;
@@ -56,18 +129,13 @@ async function check(r) {
56
129
  if (r.button) return;
57
130
  const button = document.createElement("button");
58
131
  button.type = "button";
59
- button.textContent = "Talk to the founder \xB7 5 min";
132
+ button.textContent = "Share your experience \xB7 5 min";
60
133
  button.setAttribute("aria-label", "Sightspool: join a five-minute user interview");
61
- button.style.cssText = "position:fixed;bottom:20px;right:20px;z-index:2147483000;padding:14px 18px;border:0;border-radius:16px;background:#171717;color:white;font:500 14px system-ui;box-shadow:0 8px 30px #0003;cursor:pointer;max-width:calc(100vw - 40px)";
134
+ button.style.cssText = "position:fixed;bottom:20px;right:20px;z-index:2147483000;padding:14px 18px;border:0;border-radius:999px;background:#ad1668;color:white;font:500 14px system-ui;box-shadow:0 8px 30px #0003;cursor:pointer;max-width:calc(100vw - 40px)";
62
135
  button.onclick = () => {
63
136
  try {
64
137
  if (r.disposed || r.paused || !eligible(r) || !r.offer) return;
65
- if (r.popup && !r.popup.closed) {
66
- r.popup.focus();
67
- return;
68
- }
69
- const hash = new URLSearchParams({ offer: r.offer, device: r.device });
70
- r.popup = window.open(r.endpoint + "/interview-widget#" + hash, "sightspool-interview", "popup,width=520,height=760");
138
+ openPanel(r);
71
139
  } catch (e) {
72
140
  }
73
141
  };
@@ -93,6 +161,7 @@ function init(config) {
93
161
  if (url.username || url.password) return;
94
162
  const previous = current();
95
163
  if (previous && previous.key === config.key && previous.endpoint === url.origin && previous.audience === config.audience) return;
164
+ if (previous == null ? void 0 : previous.frame) return;
96
165
  if (previous) destroy();
97
166
  let device = "";
98
167
  try {
@@ -118,7 +187,10 @@ function init(config) {
118
187
  device,
119
188
  offer: null,
120
189
  button: null,
121
- popup: null,
190
+ panel: null,
191
+ frame: null,
192
+ expanded: false,
193
+ message: null,
122
194
  pending: null,
123
195
  timer: 0,
124
196
  visibility: () => {
@@ -190,7 +262,7 @@ function destroy() {
190
262
  invalidate(r);
191
263
  window.clearInterval(r.timer);
192
264
  document.removeEventListener("visibilitychange", r.visibility);
193
- delete host()[slot];
265
+ if (!r.frame) delete host()[slot];
194
266
  } catch (e) {
195
267
  }
196
268
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/research.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["export type SightspoolConfig = {\n /** Public workspace widget key from Go live (UUID, not an old pk_live key). */\n key: string;\n /** Match the approved research audience. No default that widens recruitment. */\n audience: \"all_visitors\" | \"signed_in\";\n /** Sightspool origin. Defaults to the hosted application. */\n endpoint?: string;\n};\nexport type ResearchStatus =\n | \"not_initialized\" | \"signed_out\" | \"paused\" | \"checking\"\n | \"unavailable\" | \"available\" | \"error\";\n\ntype Runtime = {\n key: string; endpoint: string; audience: SightspoolConfig[\"audience\"]; identified: boolean; paused: boolean;\n disposed: boolean; generation: number; device: string; offer: string | null;\n button: HTMLButtonElement | null; popup: Window | null;\n pending: AbortController | null; timer: number; visibility: () => void;\n status: ResearchStatus;\n};\nconst slot = Symbol.for(\"sightspool.research.runtime.v1\");\ntype Host = Window & { [slot]?: Runtime };\nconst host = (): Host | null => typeof window === \"undefined\" ? null : window as Host;\nconst current = () => host()?.[slot];\nconst eligible = (r: Runtime) => r.audience === \"all_visitors\" || r.identified;\n\nfunction remove(r: Runtime) {\n r.button?.remove(); r.button = null; r.offer = null;\n}\nfunction invalidate(r: Runtime) {\n r.generation += 1; r.pending?.abort(); r.pending = null; remove(r);\n}\nfunction status(r: Runtime, value: ResearchStatus) { r.status = value; }\n\nasync function check(r: Runtime) {\n if (r.disposed || r.paused || !eligible(r) || r.pending || document.visibilityState !== \"visible\") return;\n const generation = r.generation;\n const request = new AbortController();\n r.pending = request;\n const timeout = window.setTimeout(() => request.abort(), 10_000);\n if (!r.button) status(r, \"checking\");\n try {\n const response = await fetch(r.endpoint + \"/widget-offer\", {\n method: \"POST\", credentials: \"omit\", cache: \"no-store\", referrerPolicy: \"no-referrer\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ operation: \"offer\", key: r.key, device: r.device }),\n signal: request.signal,\n });\n if (!response.ok) throw Error(\"offer unavailable\");\n const result = await response.json();\n if (r.disposed || generation !== r.generation || r.paused || !eligible(r)) return;\n if (result.available !== true || typeof result.offer !== \"string\" || !result.offer) {\n remove(r); status(r, \"unavailable\"); return;\n }\n r.offer = result.offer;\n status(r, \"available\");\n if (r.button) return;\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.textContent = \"Talk to the founder · 5 min\";\n button.setAttribute(\"aria-label\", \"Sightspool: join a five-minute user interview\");\n button.style.cssText = \"position:fixed;bottom:20px;right:20px;z-index:2147483000;padding:14px 18px;border:0;border-radius:16px;background:#171717;color:white;font:500 14px system-ui;box-shadow:0 8px 30px #0003;cursor:pointer;max-width:calc(100vw - 40px)\";\n button.onclick = () => {\n try {\n if (r.disposed || r.paused || !eligible(r) || !r.offer) return;\n if (r.popup && !r.popup.closed) { r.popup.focus(); return; }\n const hash = new URLSearchParams({ offer: r.offer, device: r.device });\n r.popup = window.open(r.endpoint + \"/interview-widget#\" + hash, \"sightspool-interview\", \"popup,width=520,height=760\");\n } catch { /* Host pages remain usable if popups are blocked. */ }\n };\n r.button = button;\n document.body.appendChild(button);\n } catch {\n if (!r.disposed && generation === r.generation) { remove(r); status(r, \"error\"); }\n } finally {\n window.clearTimeout(timeout);\n if (r.pending === request) r.pending = null;\n }\n}\n\n/** Start research for the chosen audience. Signed-in-only waits for identify(). */\nexport function init(config: SightspoolConfig): void {\n try {\n const browser = host();\n if (!browser || !config || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(config.key)) return;\n if (config.audience !== \"all_visitors\" && config.audience !== \"signed_in\") return;\n const url = new URL(config.endpoint || \"https://www.sightspool.com\");\n if (url.protocol !== \"https:\" && !(url.protocol === \"http:\" && [\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(url.hostname))) return;\n if (url.username || url.password) return;\n const previous = current();\n if (previous && previous.key === config.key && previous.endpoint === url.origin && previous.audience === config.audience) return;\n if (previous) destroy();\n let device = \"\";\n try { device = sessionStorage.getItem(\"sightspool-widget-device:\" + config.key) || \"\"; } catch {}\n if (!/^ss_fcd_[A-Za-z0-9_-]{43}$/.test(device)) {\n const bytes = crypto.getRandomValues(new Uint8Array(32));\n device = \"ss_fcd_\" + btoa(String.fromCharCode(...bytes)).replaceAll(\"+\", \"-\").replaceAll(\"/\", \"_\").replace(/=+$/, \"\");\n try { sessionStorage.setItem(\"sightspool-widget-device:\" + config.key, device); } catch {}\n }\n const r: Runtime = {\n key: config.key, endpoint: url.origin, audience: config.audience, identified: false, paused: false,\n disposed: false, generation: 0, device, offer: null, button: null,\n popup: null, pending: null, timer: 0, visibility: () => {}, status: \"signed_out\",\n };\n browser[slot] = r;\n r.visibility = () => {\n try {\n if (document.visibilityState === \"visible\") void check(r);\n else { invalidate(r); status(r, r.paused ? \"paused\" : eligible(r) ? \"unavailable\" : \"signed_out\"); }\n } catch {}\n };\n document.addEventListener(\"visibilitychange\", r.visibility);\n r.timer = window.setInterval(() => { void check(r); }, 15_000);\n if (eligible(r)) void check(r);\n } catch { /* Never throw into the host application. */ }\n}\n\n/** Only the presence of an ID is retained. The ID itself is never stored or sent. */\nexport function identify(userId: string | null | undefined): void {\n try {\n const r = current();\n if (!r) return;\n const identified = typeof userId === \"string\" && userId.trim().length > 0;\n if (!identified && !r.identified) { if (!r.paused && eligible(r)) void check(r); return; }\n invalidate(r);\n r.identified = identified;\n status(r, r.paused ? \"paused\" : eligible(r) ? \"unavailable\" : \"signed_out\");\n if (eligible(r) && !r.paused) void check(r);\n } catch {}\n}\nexport function pause(): void {\n try { const r = current(); if (r) { r.paused = true; invalidate(r); status(r, \"paused\"); } } catch {}\n}\nexport function resume(): void {\n try { const r = current(); if (r) { r.paused = false; if (!eligible(r)) status(r, \"signed_out\"); void check(r); } } catch {}\n}\n/** Stop recruitment and remove listeners/UI. Does not end an already opened interview. */\nexport function destroy(): void {\n try {\n const r = current();\n if (!r) return;\n r.disposed = true; invalidate(r);\n window.clearInterval(r.timer);\n document.removeEventListener(\"visibilitychange\", r.visibility);\n delete host()![slot];\n } catch {}\n}\nexport function getStatus(): ResearchStatus {\n try { return current()?.status ?? \"not_initialized\"; } catch { return \"error\"; }\n}\n","// Research is the public SDK. The former capture engine is parked.\nexport { init, identify, pause, resume, destroy, getStatus } from \"./research\";\nexport type { SightspoolConfig, ResearchStatus } from \"./research\";\nimport { init, identify, pause, resume, destroy, getStatus } from \"./research\";\nexport default { init, identify, pause, resume, destroy, getStatus };\n"]}
1
+ {"version":3,"sources":["../src/research.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["export type SightspoolConfig = {\n /** Public workspace widget key from Go live (UUID, not an old pk_live key). */\n key: string;\n /** Match the approved research audience. No default that widens recruitment. */\n audience: \"all_visitors\" | \"signed_in\";\n /** Sightspool origin. Defaults to the hosted application. */\n endpoint?: string;\n};\nexport type ResearchStatus =\n | \"not_initialized\" | \"signed_out\" | \"paused\" | \"checking\"\n | \"unavailable\" | \"available\" | \"error\";\n\ntype Runtime = {\n key: string; endpoint: string; audience: SightspoolConfig[\"audience\"]; identified: boolean; paused: boolean;\n disposed: boolean; generation: number; device: string; offer: string | null;\n button: HTMLButtonElement | null; panel: HTMLElement | null; frame: HTMLIFrameElement | null;\n expanded: boolean; message: ((event: MessageEvent) => void) | null;\n pending: AbortController | null; timer: number; visibility: () => void;\n status: ResearchStatus;\n};\nconst slot = Symbol.for(\"sightspool.research.runtime.v1\");\ntype Host = Window & { [slot]?: Runtime };\nconst host = (): Host | null => typeof window === \"undefined\" ? null : window as Host;\nconst current = () => host()?.[slot];\nconst eligible = (r: Runtime) => r.audience === \"all_visitors\" || r.identified;\n\nfunction remove(r: Runtime) {\n // An opened interview owns its lifetime. Recruitment changes cannot end it.\n if (r.frame) return;\n r.button?.remove(); r.button = null; r.offer = null;\n}\nfunction invalidate(r: Runtime) {\n r.generation += 1; r.pending?.abort(); r.pending = null; remove(r);\n}\nfunction status(r: Runtime, value: ResearchStatus) { r.status = value; }\n\nfunction expand(r: Runtime, expanded: boolean) {\n if (!r.panel || !r.frame || !r.button) return;\n r.expanded = expanded;\n r.panel.hidden = !expanded;\n r.panel.style.display = expanded ? \"flex\" : \"none\";\n r.button.hidden = expanded;\n r.button.setAttribute(\"aria-expanded\", String(expanded));\n if (expanded) {\n r.panel.querySelector<HTMLButtonElement>(\"button\")?.focus();\n } else r.button.focus();\n}\nfunction openPanel(r: Runtime) {\n if (r.frame) { expand(r, true); return; }\n if (!r.offer || !r.button) return;\n const panel = document.createElement(\"section\");\n panel.id = \"sightspool-interview-panel\";\n panel.setAttribute(\"role\", \"dialog\");\n panel.setAttribute(\"aria-label\", \"Sightspool interview\");\n panel.style.cssText = \"position:fixed;bottom:16px;right:16px;z-index:2147483001;box-sizing:border-box;width:420px;max-width:calc(100% - 32px);height:680px;max-height:calc(100dvh - 32px);border:1px solid #e6dfe3;border-radius:20px;background:#fff;color:#262024;box-shadow:0 12px 50px #0003;overflow:hidden;font:14px system-ui;display:flex;flex-direction:column\";\n const header = document.createElement(\"div\");\n header.style.cssText = \"display:flex;align-items:center;justify-content:space-between;gap:12px;padding:14px 16px;border-bottom:1px solid #eee;flex-shrink:0\";\n const title = document.createElement(\"strong\"); title.textContent = \"Sightspool\";\n const minimize = document.createElement(\"button\");\n minimize.type = \"button\"; minimize.textContent = \"Minimize\";\n minimize.style.cssText = \"font:inherit;color:inherit;background:#fff;border:1px solid #d6cbd1;border-radius:8px;padding:8px 12px;cursor:pointer\";\n const hide = () => { expand(r, false); };\n minimize.onclick = hide;\n header.appendChild(title); header.appendChild(minimize); panel.appendChild(header);\n const frame = document.createElement(\"iframe\");\n frame.title = \"Sightspool research conversation\";\n frame.allow = \"microphone; autoplay\";\n frame.setAttribute(\"sandbox\", \"allow-scripts allow-same-origin allow-forms\");\n frame.referrerPolicy = \"no-referrer\";\n frame.style.cssText = \"display:block;width:100%;flex:1;min-height:0;border:0;background:#fff\";\n const url = new URL(r.endpoint + \"/interview-widget\");\n url.searchParams.set(\"key\", r.key);\n url.hash = new URLSearchParams({ offer: r.offer, device: r.device }).toString();\n frame.src = url.href;\n panel.appendChild(frame);\n panel.onkeydown = (event) => { if (event.key === \"Escape\") { event.preventDefault(); hide(); } };\n r.panel = panel; r.frame = frame;\n r.message = (event) => {\n // Only this exact embedded document may control its presentation. No interview\n // content, capabilities or account data cross the parent messaging boundary.\n if (event.source !== frame.contentWindow || event.origin !== r.endpoint || event.data?.type !== \"sightspool:panel:minimize\") return;\n hide();\n };\n window.addEventListener(\"message\", r.message);\n r.button.textContent = \"Return to interview\";\n r.button.setAttribute(\"aria-label\", \"Sightspool: return to your interview\");\n r.button.setAttribute(\"aria-controls\", panel.id);\n r.button.onclick = () => { expand(r, true); };\n document.body.appendChild(panel);\n expand(r, true);\n}\n\nasync function check(r: Runtime) {\n if (r.frame || r.disposed || r.paused || !eligible(r) || r.pending || document.visibilityState !== \"visible\") return;\n const generation = r.generation;\n const request = new AbortController();\n r.pending = request;\n const timeout = window.setTimeout(() => request.abort(), 10_000);\n if (!r.button) status(r, \"checking\");\n try {\n const response = await fetch(r.endpoint + \"/widget-offer\", {\n method: \"POST\", credentials: \"omit\", cache: \"no-store\", referrerPolicy: \"no-referrer\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ operation: \"offer\", key: r.key, device: r.device }),\n signal: request.signal,\n });\n if (!response.ok) throw Error(\"offer unavailable\");\n const result = await response.json();\n if (r.disposed || generation !== r.generation || r.paused || !eligible(r)) return;\n if (result.available !== true || typeof result.offer !== \"string\" || !result.offer) {\n remove(r); status(r, \"unavailable\"); return;\n }\n r.offer = result.offer;\n status(r, \"available\");\n if (r.button) return;\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.textContent = \"Share your experience · 5 min\";\n button.setAttribute(\"aria-label\", \"Sightspool: join a five-minute user interview\");\n button.style.cssText = \"position:fixed;bottom:20px;right:20px;z-index:2147483000;padding:14px 18px;border:0;border-radius:999px;background:#ad1668;color:white;font:500 14px system-ui;box-shadow:0 8px 30px #0003;cursor:pointer;max-width:calc(100vw - 40px)\";\n button.onclick = () => {\n try {\n if (r.disposed || r.paused || !eligible(r) || !r.offer) return;\n openPanel(r);\n } catch { /* Host pages remain usable if embedding is blocked. */ }\n };\n r.button = button;\n document.body.appendChild(button);\n } catch {\n if (!r.disposed && generation === r.generation) { remove(r); status(r, \"error\"); }\n } finally {\n window.clearTimeout(timeout);\n if (r.pending === request) r.pending = null;\n }\n}\n\n/** Start research for the chosen audience. Signed-in-only waits for identify(). */\nexport function init(config: SightspoolConfig): void {\n try {\n const browser = host();\n if (!browser || !config || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(config.key)) return;\n if (config.audience !== \"all_visitors\" && config.audience !== \"signed_in\") return;\n const url = new URL(config.endpoint || \"https://www.sightspool.com\");\n if (url.protocol !== \"https:\" && !(url.protocol === \"http:\" && [\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(url.hostname))) return;\n if (url.username || url.password) return;\n const previous = current();\n if (previous && previous.key === config.key && previous.endpoint === url.origin && previous.audience === config.audience) return;\n // A second loader/workspace must not replace a participant's open interview.\n if (previous?.frame) return;\n if (previous) destroy();\n let device = \"\";\n try { device = sessionStorage.getItem(\"sightspool-widget-device:\" + config.key) || \"\"; } catch {}\n if (!/^ss_fcd_[A-Za-z0-9_-]{43}$/.test(device)) {\n const bytes = crypto.getRandomValues(new Uint8Array(32));\n device = \"ss_fcd_\" + btoa(String.fromCharCode(...bytes)).replaceAll(\"+\", \"-\").replaceAll(\"/\", \"_\").replace(/=+$/, \"\");\n try { sessionStorage.setItem(\"sightspool-widget-device:\" + config.key, device); } catch {}\n }\n const r: Runtime = {\n key: config.key, endpoint: url.origin, audience: config.audience, identified: false, paused: false,\n disposed: false, generation: 0, device, offer: null, button: null,\n panel: null, frame: null, expanded: false, message: null, pending: null, timer: 0, visibility: () => {}, status: \"signed_out\",\n };\n browser[slot] = r;\n r.visibility = () => {\n try {\n if (document.visibilityState === \"visible\") void check(r);\n else { invalidate(r); status(r, r.paused ? \"paused\" : eligible(r) ? \"unavailable\" : \"signed_out\"); }\n } catch {}\n };\n document.addEventListener(\"visibilitychange\", r.visibility);\n r.timer = window.setInterval(() => { void check(r); }, 15_000);\n if (eligible(r)) void check(r);\n } catch { /* Never throw into the host application. */ }\n}\n\n/** Only the presence of an ID is retained. The ID itself is never stored or sent. */\nexport function identify(userId: string | null | undefined): void {\n try {\n const r = current();\n if (!r) return;\n const identified = typeof userId === \"string\" && userId.trim().length > 0;\n if (!identified && !r.identified) { if (!r.paused && eligible(r)) void check(r); return; }\n invalidate(r);\n r.identified = identified;\n status(r, r.paused ? \"paused\" : eligible(r) ? \"unavailable\" : \"signed_out\");\n if (eligible(r) && !r.paused) void check(r);\n } catch {}\n}\nexport function pause(): void {\n try { const r = current(); if (r) { r.paused = true; invalidate(r); status(r, \"paused\"); } } catch {}\n}\nexport function resume(): void {\n try { const r = current(); if (r) { r.paused = false; if (!eligible(r)) status(r, \"signed_out\"); void check(r); } } catch {}\n}\n/** Stop recruitment and remove listeners/UI. Does not end an already opened interview. */\nexport function destroy(): void {\n try {\n const r = current();\n if (!r) return;\n r.disposed = true; invalidate(r);\n window.clearInterval(r.timer);\n document.removeEventListener(\"visibilitychange\", r.visibility);\n // Keep the session panel and its launcher reachable until the page is left.\n // Minimize is presentation only; end/withdraw remain explicit inside the frame.\n if (!r.frame) delete host()![slot];\n } catch {}\n}\nexport function getStatus(): ResearchStatus {\n try { return current()?.status ?? \"not_initialized\"; } catch { return \"error\"; }\n}\n","// Research is the public SDK. The former capture engine is parked.\nexport { init, identify, pause, resume, destroy, getStatus } from \"./research\";\nexport type { SightspoolConfig, ResearchStatus } from \"./research\";\nimport { init, identify, pause, resume, destroy, getStatus } from \"./research\";\nexport default { init, identify, pause, resume, destroy, getStatus };\n"]}
package/dist/index.js CHANGED
@@ -8,6 +8,7 @@ var current = () => {
8
8
  var eligible = (r) => r.audience === "all_visitors" || r.identified;
9
9
  function remove(r) {
10
10
  var _a;
11
+ if (r.frame) return;
11
12
  (_a = r.button) == null ? void 0 : _a.remove();
12
13
  r.button = null;
13
14
  r.offer = null;
@@ -22,8 +23,80 @@ function invalidate(r) {
22
23
  function status(r, value) {
23
24
  r.status = value;
24
25
  }
26
+ function expand(r, expanded) {
27
+ var _a;
28
+ if (!r.panel || !r.frame || !r.button) return;
29
+ r.expanded = expanded;
30
+ r.panel.hidden = !expanded;
31
+ r.panel.style.display = expanded ? "flex" : "none";
32
+ r.button.hidden = expanded;
33
+ r.button.setAttribute("aria-expanded", String(expanded));
34
+ if (expanded) {
35
+ (_a = r.panel.querySelector("button")) == null ? void 0 : _a.focus();
36
+ } else r.button.focus();
37
+ }
38
+ function openPanel(r) {
39
+ if (r.frame) {
40
+ expand(r, true);
41
+ return;
42
+ }
43
+ if (!r.offer || !r.button) return;
44
+ const panel = document.createElement("section");
45
+ panel.id = "sightspool-interview-panel";
46
+ panel.setAttribute("role", "dialog");
47
+ panel.setAttribute("aria-label", "Sightspool interview");
48
+ panel.style.cssText = "position:fixed;bottom:16px;right:16px;z-index:2147483001;box-sizing:border-box;width:420px;max-width:calc(100% - 32px);height:680px;max-height:calc(100dvh - 32px);border:1px solid #e6dfe3;border-radius:20px;background:#fff;color:#262024;box-shadow:0 12px 50px #0003;overflow:hidden;font:14px system-ui;display:flex;flex-direction:column";
49
+ const header = document.createElement("div");
50
+ header.style.cssText = "display:flex;align-items:center;justify-content:space-between;gap:12px;padding:14px 16px;border-bottom:1px solid #eee;flex-shrink:0";
51
+ const title = document.createElement("strong");
52
+ title.textContent = "Sightspool";
53
+ const minimize = document.createElement("button");
54
+ minimize.type = "button";
55
+ minimize.textContent = "Minimize";
56
+ minimize.style.cssText = "font:inherit;color:inherit;background:#fff;border:1px solid #d6cbd1;border-radius:8px;padding:8px 12px;cursor:pointer";
57
+ const hide = () => {
58
+ expand(r, false);
59
+ };
60
+ minimize.onclick = hide;
61
+ header.appendChild(title);
62
+ header.appendChild(minimize);
63
+ panel.appendChild(header);
64
+ const frame = document.createElement("iframe");
65
+ frame.title = "Sightspool research conversation";
66
+ frame.allow = "microphone; autoplay";
67
+ frame.setAttribute("sandbox", "allow-scripts allow-same-origin allow-forms");
68
+ frame.referrerPolicy = "no-referrer";
69
+ frame.style.cssText = "display:block;width:100%;flex:1;min-height:0;border:0;background:#fff";
70
+ const url = new URL(r.endpoint + "/interview-widget");
71
+ url.searchParams.set("key", r.key);
72
+ url.hash = new URLSearchParams({ offer: r.offer, device: r.device }).toString();
73
+ frame.src = url.href;
74
+ panel.appendChild(frame);
75
+ panel.onkeydown = (event) => {
76
+ if (event.key === "Escape") {
77
+ event.preventDefault();
78
+ hide();
79
+ }
80
+ };
81
+ r.panel = panel;
82
+ r.frame = frame;
83
+ r.message = (event) => {
84
+ var _a;
85
+ if (event.source !== frame.contentWindow || event.origin !== r.endpoint || ((_a = event.data) == null ? void 0 : _a.type) !== "sightspool:panel:minimize") return;
86
+ hide();
87
+ };
88
+ window.addEventListener("message", r.message);
89
+ r.button.textContent = "Return to interview";
90
+ r.button.setAttribute("aria-label", "Sightspool: return to your interview");
91
+ r.button.setAttribute("aria-controls", panel.id);
92
+ r.button.onclick = () => {
93
+ expand(r, true);
94
+ };
95
+ document.body.appendChild(panel);
96
+ expand(r, true);
97
+ }
25
98
  async function check(r) {
26
- if (r.disposed || r.paused || !eligible(r) || r.pending || document.visibilityState !== "visible") return;
99
+ if (r.frame || r.disposed || r.paused || !eligible(r) || r.pending || document.visibilityState !== "visible") return;
27
100
  const generation = r.generation;
28
101
  const request = new AbortController();
29
102
  r.pending = request;
@@ -52,18 +125,13 @@ async function check(r) {
52
125
  if (r.button) return;
53
126
  const button = document.createElement("button");
54
127
  button.type = "button";
55
- button.textContent = "Talk to the founder \xB7 5 min";
128
+ button.textContent = "Share your experience \xB7 5 min";
56
129
  button.setAttribute("aria-label", "Sightspool: join a five-minute user interview");
57
- button.style.cssText = "position:fixed;bottom:20px;right:20px;z-index:2147483000;padding:14px 18px;border:0;border-radius:16px;background:#171717;color:white;font:500 14px system-ui;box-shadow:0 8px 30px #0003;cursor:pointer;max-width:calc(100vw - 40px)";
130
+ button.style.cssText = "position:fixed;bottom:20px;right:20px;z-index:2147483000;padding:14px 18px;border:0;border-radius:999px;background:#ad1668;color:white;font:500 14px system-ui;box-shadow:0 8px 30px #0003;cursor:pointer;max-width:calc(100vw - 40px)";
58
131
  button.onclick = () => {
59
132
  try {
60
133
  if (r.disposed || r.paused || !eligible(r) || !r.offer) return;
61
- if (r.popup && !r.popup.closed) {
62
- r.popup.focus();
63
- return;
64
- }
65
- const hash = new URLSearchParams({ offer: r.offer, device: r.device });
66
- r.popup = window.open(r.endpoint + "/interview-widget#" + hash, "sightspool-interview", "popup,width=520,height=760");
134
+ openPanel(r);
67
135
  } catch (e) {
68
136
  }
69
137
  };
@@ -89,6 +157,7 @@ function init(config) {
89
157
  if (url.username || url.password) return;
90
158
  const previous = current();
91
159
  if (previous && previous.key === config.key && previous.endpoint === url.origin && previous.audience === config.audience) return;
160
+ if (previous == null ? void 0 : previous.frame) return;
92
161
  if (previous) destroy();
93
162
  let device = "";
94
163
  try {
@@ -114,7 +183,10 @@ function init(config) {
114
183
  device,
115
184
  offer: null,
116
185
  button: null,
117
- popup: null,
186
+ panel: null,
187
+ frame: null,
188
+ expanded: false,
189
+ message: null,
118
190
  pending: null,
119
191
  timer: 0,
120
192
  visibility: () => {
@@ -186,7 +258,7 @@ function destroy() {
186
258
  invalidate(r);
187
259
  window.clearInterval(r.timer);
188
260
  document.removeEventListener("visibilitychange", r.visibility);
189
- delete host()[slot];
261
+ if (!r.frame) delete host()[slot];
190
262
  } catch (e) {
191
263
  }
192
264
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/research.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["export type SightspoolConfig = {\n /** Public workspace widget key from Go live (UUID, not an old pk_live key). */\n key: string;\n /** Match the approved research audience. No default that widens recruitment. */\n audience: \"all_visitors\" | \"signed_in\";\n /** Sightspool origin. Defaults to the hosted application. */\n endpoint?: string;\n};\nexport type ResearchStatus =\n | \"not_initialized\" | \"signed_out\" | \"paused\" | \"checking\"\n | \"unavailable\" | \"available\" | \"error\";\n\ntype Runtime = {\n key: string; endpoint: string; audience: SightspoolConfig[\"audience\"]; identified: boolean; paused: boolean;\n disposed: boolean; generation: number; device: string; offer: string | null;\n button: HTMLButtonElement | null; popup: Window | null;\n pending: AbortController | null; timer: number; visibility: () => void;\n status: ResearchStatus;\n};\nconst slot = Symbol.for(\"sightspool.research.runtime.v1\");\ntype Host = Window & { [slot]?: Runtime };\nconst host = (): Host | null => typeof window === \"undefined\" ? null : window as Host;\nconst current = () => host()?.[slot];\nconst eligible = (r: Runtime) => r.audience === \"all_visitors\" || r.identified;\n\nfunction remove(r: Runtime) {\n r.button?.remove(); r.button = null; r.offer = null;\n}\nfunction invalidate(r: Runtime) {\n r.generation += 1; r.pending?.abort(); r.pending = null; remove(r);\n}\nfunction status(r: Runtime, value: ResearchStatus) { r.status = value; }\n\nasync function check(r: Runtime) {\n if (r.disposed || r.paused || !eligible(r) || r.pending || document.visibilityState !== \"visible\") return;\n const generation = r.generation;\n const request = new AbortController();\n r.pending = request;\n const timeout = window.setTimeout(() => request.abort(), 10_000);\n if (!r.button) status(r, \"checking\");\n try {\n const response = await fetch(r.endpoint + \"/widget-offer\", {\n method: \"POST\", credentials: \"omit\", cache: \"no-store\", referrerPolicy: \"no-referrer\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ operation: \"offer\", key: r.key, device: r.device }),\n signal: request.signal,\n });\n if (!response.ok) throw Error(\"offer unavailable\");\n const result = await response.json();\n if (r.disposed || generation !== r.generation || r.paused || !eligible(r)) return;\n if (result.available !== true || typeof result.offer !== \"string\" || !result.offer) {\n remove(r); status(r, \"unavailable\"); return;\n }\n r.offer = result.offer;\n status(r, \"available\");\n if (r.button) return;\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.textContent = \"Talk to the founder · 5 min\";\n button.setAttribute(\"aria-label\", \"Sightspool: join a five-minute user interview\");\n button.style.cssText = \"position:fixed;bottom:20px;right:20px;z-index:2147483000;padding:14px 18px;border:0;border-radius:16px;background:#171717;color:white;font:500 14px system-ui;box-shadow:0 8px 30px #0003;cursor:pointer;max-width:calc(100vw - 40px)\";\n button.onclick = () => {\n try {\n if (r.disposed || r.paused || !eligible(r) || !r.offer) return;\n if (r.popup && !r.popup.closed) { r.popup.focus(); return; }\n const hash = new URLSearchParams({ offer: r.offer, device: r.device });\n r.popup = window.open(r.endpoint + \"/interview-widget#\" + hash, \"sightspool-interview\", \"popup,width=520,height=760\");\n } catch { /* Host pages remain usable if popups are blocked. */ }\n };\n r.button = button;\n document.body.appendChild(button);\n } catch {\n if (!r.disposed && generation === r.generation) { remove(r); status(r, \"error\"); }\n } finally {\n window.clearTimeout(timeout);\n if (r.pending === request) r.pending = null;\n }\n}\n\n/** Start research for the chosen audience. Signed-in-only waits for identify(). */\nexport function init(config: SightspoolConfig): void {\n try {\n const browser = host();\n if (!browser || !config || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(config.key)) return;\n if (config.audience !== \"all_visitors\" && config.audience !== \"signed_in\") return;\n const url = new URL(config.endpoint || \"https://www.sightspool.com\");\n if (url.protocol !== \"https:\" && !(url.protocol === \"http:\" && [\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(url.hostname))) return;\n if (url.username || url.password) return;\n const previous = current();\n if (previous && previous.key === config.key && previous.endpoint === url.origin && previous.audience === config.audience) return;\n if (previous) destroy();\n let device = \"\";\n try { device = sessionStorage.getItem(\"sightspool-widget-device:\" + config.key) || \"\"; } catch {}\n if (!/^ss_fcd_[A-Za-z0-9_-]{43}$/.test(device)) {\n const bytes = crypto.getRandomValues(new Uint8Array(32));\n device = \"ss_fcd_\" + btoa(String.fromCharCode(...bytes)).replaceAll(\"+\", \"-\").replaceAll(\"/\", \"_\").replace(/=+$/, \"\");\n try { sessionStorage.setItem(\"sightspool-widget-device:\" + config.key, device); } catch {}\n }\n const r: Runtime = {\n key: config.key, endpoint: url.origin, audience: config.audience, identified: false, paused: false,\n disposed: false, generation: 0, device, offer: null, button: null,\n popup: null, pending: null, timer: 0, visibility: () => {}, status: \"signed_out\",\n };\n browser[slot] = r;\n r.visibility = () => {\n try {\n if (document.visibilityState === \"visible\") void check(r);\n else { invalidate(r); status(r, r.paused ? \"paused\" : eligible(r) ? \"unavailable\" : \"signed_out\"); }\n } catch {}\n };\n document.addEventListener(\"visibilitychange\", r.visibility);\n r.timer = window.setInterval(() => { void check(r); }, 15_000);\n if (eligible(r)) void check(r);\n } catch { /* Never throw into the host application. */ }\n}\n\n/** Only the presence of an ID is retained. The ID itself is never stored or sent. */\nexport function identify(userId: string | null | undefined): void {\n try {\n const r = current();\n if (!r) return;\n const identified = typeof userId === \"string\" && userId.trim().length > 0;\n if (!identified && !r.identified) { if (!r.paused && eligible(r)) void check(r); return; }\n invalidate(r);\n r.identified = identified;\n status(r, r.paused ? \"paused\" : eligible(r) ? \"unavailable\" : \"signed_out\");\n if (eligible(r) && !r.paused) void check(r);\n } catch {}\n}\nexport function pause(): void {\n try { const r = current(); if (r) { r.paused = true; invalidate(r); status(r, \"paused\"); } } catch {}\n}\nexport function resume(): void {\n try { const r = current(); if (r) { r.paused = false; if (!eligible(r)) status(r, \"signed_out\"); void check(r); } } catch {}\n}\n/** Stop recruitment and remove listeners/UI. Does not end an already opened interview. */\nexport function destroy(): void {\n try {\n const r = current();\n if (!r) return;\n r.disposed = true; invalidate(r);\n window.clearInterval(r.timer);\n document.removeEventListener(\"visibilitychange\", r.visibility);\n delete host()![slot];\n } catch {}\n}\nexport function getStatus(): ResearchStatus {\n try { return current()?.status ?? \"not_initialized\"; } catch { return \"error\"; }\n}\n","// Research is the public SDK. The former capture engine is parked.\nexport { init, identify, pause, resume, destroy, getStatus } from \"./research\";\nexport type { SightspoolConfig, ResearchStatus } from \"./research\";\nimport { init, identify, pause, resume, destroy, getStatus } from \"./research\";\nexport default { init, identify, pause, resume, destroy, getStatus };\n"]}
1
+ {"version":3,"sources":["../src/research.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["export type SightspoolConfig = {\n /** Public workspace widget key from Go live (UUID, not an old pk_live key). */\n key: string;\n /** Match the approved research audience. No default that widens recruitment. */\n audience: \"all_visitors\" | \"signed_in\";\n /** Sightspool origin. Defaults to the hosted application. */\n endpoint?: string;\n};\nexport type ResearchStatus =\n | \"not_initialized\" | \"signed_out\" | \"paused\" | \"checking\"\n | \"unavailable\" | \"available\" | \"error\";\n\ntype Runtime = {\n key: string; endpoint: string; audience: SightspoolConfig[\"audience\"]; identified: boolean; paused: boolean;\n disposed: boolean; generation: number; device: string; offer: string | null;\n button: HTMLButtonElement | null; panel: HTMLElement | null; frame: HTMLIFrameElement | null;\n expanded: boolean; message: ((event: MessageEvent) => void) | null;\n pending: AbortController | null; timer: number; visibility: () => void;\n status: ResearchStatus;\n};\nconst slot = Symbol.for(\"sightspool.research.runtime.v1\");\ntype Host = Window & { [slot]?: Runtime };\nconst host = (): Host | null => typeof window === \"undefined\" ? null : window as Host;\nconst current = () => host()?.[slot];\nconst eligible = (r: Runtime) => r.audience === \"all_visitors\" || r.identified;\n\nfunction remove(r: Runtime) {\n // An opened interview owns its lifetime. Recruitment changes cannot end it.\n if (r.frame) return;\n r.button?.remove(); r.button = null; r.offer = null;\n}\nfunction invalidate(r: Runtime) {\n r.generation += 1; r.pending?.abort(); r.pending = null; remove(r);\n}\nfunction status(r: Runtime, value: ResearchStatus) { r.status = value; }\n\nfunction expand(r: Runtime, expanded: boolean) {\n if (!r.panel || !r.frame || !r.button) return;\n r.expanded = expanded;\n r.panel.hidden = !expanded;\n r.panel.style.display = expanded ? \"flex\" : \"none\";\n r.button.hidden = expanded;\n r.button.setAttribute(\"aria-expanded\", String(expanded));\n if (expanded) {\n r.panel.querySelector<HTMLButtonElement>(\"button\")?.focus();\n } else r.button.focus();\n}\nfunction openPanel(r: Runtime) {\n if (r.frame) { expand(r, true); return; }\n if (!r.offer || !r.button) return;\n const panel = document.createElement(\"section\");\n panel.id = \"sightspool-interview-panel\";\n panel.setAttribute(\"role\", \"dialog\");\n panel.setAttribute(\"aria-label\", \"Sightspool interview\");\n panel.style.cssText = \"position:fixed;bottom:16px;right:16px;z-index:2147483001;box-sizing:border-box;width:420px;max-width:calc(100% - 32px);height:680px;max-height:calc(100dvh - 32px);border:1px solid #e6dfe3;border-radius:20px;background:#fff;color:#262024;box-shadow:0 12px 50px #0003;overflow:hidden;font:14px system-ui;display:flex;flex-direction:column\";\n const header = document.createElement(\"div\");\n header.style.cssText = \"display:flex;align-items:center;justify-content:space-between;gap:12px;padding:14px 16px;border-bottom:1px solid #eee;flex-shrink:0\";\n const title = document.createElement(\"strong\"); title.textContent = \"Sightspool\";\n const minimize = document.createElement(\"button\");\n minimize.type = \"button\"; minimize.textContent = \"Minimize\";\n minimize.style.cssText = \"font:inherit;color:inherit;background:#fff;border:1px solid #d6cbd1;border-radius:8px;padding:8px 12px;cursor:pointer\";\n const hide = () => { expand(r, false); };\n minimize.onclick = hide;\n header.appendChild(title); header.appendChild(minimize); panel.appendChild(header);\n const frame = document.createElement(\"iframe\");\n frame.title = \"Sightspool research conversation\";\n frame.allow = \"microphone; autoplay\";\n frame.setAttribute(\"sandbox\", \"allow-scripts allow-same-origin allow-forms\");\n frame.referrerPolicy = \"no-referrer\";\n frame.style.cssText = \"display:block;width:100%;flex:1;min-height:0;border:0;background:#fff\";\n const url = new URL(r.endpoint + \"/interview-widget\");\n url.searchParams.set(\"key\", r.key);\n url.hash = new URLSearchParams({ offer: r.offer, device: r.device }).toString();\n frame.src = url.href;\n panel.appendChild(frame);\n panel.onkeydown = (event) => { if (event.key === \"Escape\") { event.preventDefault(); hide(); } };\n r.panel = panel; r.frame = frame;\n r.message = (event) => {\n // Only this exact embedded document may control its presentation. No interview\n // content, capabilities or account data cross the parent messaging boundary.\n if (event.source !== frame.contentWindow || event.origin !== r.endpoint || event.data?.type !== \"sightspool:panel:minimize\") return;\n hide();\n };\n window.addEventListener(\"message\", r.message);\n r.button.textContent = \"Return to interview\";\n r.button.setAttribute(\"aria-label\", \"Sightspool: return to your interview\");\n r.button.setAttribute(\"aria-controls\", panel.id);\n r.button.onclick = () => { expand(r, true); };\n document.body.appendChild(panel);\n expand(r, true);\n}\n\nasync function check(r: Runtime) {\n if (r.frame || r.disposed || r.paused || !eligible(r) || r.pending || document.visibilityState !== \"visible\") return;\n const generation = r.generation;\n const request = new AbortController();\n r.pending = request;\n const timeout = window.setTimeout(() => request.abort(), 10_000);\n if (!r.button) status(r, \"checking\");\n try {\n const response = await fetch(r.endpoint + \"/widget-offer\", {\n method: \"POST\", credentials: \"omit\", cache: \"no-store\", referrerPolicy: \"no-referrer\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ operation: \"offer\", key: r.key, device: r.device }),\n signal: request.signal,\n });\n if (!response.ok) throw Error(\"offer unavailable\");\n const result = await response.json();\n if (r.disposed || generation !== r.generation || r.paused || !eligible(r)) return;\n if (result.available !== true || typeof result.offer !== \"string\" || !result.offer) {\n remove(r); status(r, \"unavailable\"); return;\n }\n r.offer = result.offer;\n status(r, \"available\");\n if (r.button) return;\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.textContent = \"Share your experience · 5 min\";\n button.setAttribute(\"aria-label\", \"Sightspool: join a five-minute user interview\");\n button.style.cssText = \"position:fixed;bottom:20px;right:20px;z-index:2147483000;padding:14px 18px;border:0;border-radius:999px;background:#ad1668;color:white;font:500 14px system-ui;box-shadow:0 8px 30px #0003;cursor:pointer;max-width:calc(100vw - 40px)\";\n button.onclick = () => {\n try {\n if (r.disposed || r.paused || !eligible(r) || !r.offer) return;\n openPanel(r);\n } catch { /* Host pages remain usable if embedding is blocked. */ }\n };\n r.button = button;\n document.body.appendChild(button);\n } catch {\n if (!r.disposed && generation === r.generation) { remove(r); status(r, \"error\"); }\n } finally {\n window.clearTimeout(timeout);\n if (r.pending === request) r.pending = null;\n }\n}\n\n/** Start research for the chosen audience. Signed-in-only waits for identify(). */\nexport function init(config: SightspoolConfig): void {\n try {\n const browser = host();\n if (!browser || !config || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(config.key)) return;\n if (config.audience !== \"all_visitors\" && config.audience !== \"signed_in\") return;\n const url = new URL(config.endpoint || \"https://www.sightspool.com\");\n if (url.protocol !== \"https:\" && !(url.protocol === \"http:\" && [\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(url.hostname))) return;\n if (url.username || url.password) return;\n const previous = current();\n if (previous && previous.key === config.key && previous.endpoint === url.origin && previous.audience === config.audience) return;\n // A second loader/workspace must not replace a participant's open interview.\n if (previous?.frame) return;\n if (previous) destroy();\n let device = \"\";\n try { device = sessionStorage.getItem(\"sightspool-widget-device:\" + config.key) || \"\"; } catch {}\n if (!/^ss_fcd_[A-Za-z0-9_-]{43}$/.test(device)) {\n const bytes = crypto.getRandomValues(new Uint8Array(32));\n device = \"ss_fcd_\" + btoa(String.fromCharCode(...bytes)).replaceAll(\"+\", \"-\").replaceAll(\"/\", \"_\").replace(/=+$/, \"\");\n try { sessionStorage.setItem(\"sightspool-widget-device:\" + config.key, device); } catch {}\n }\n const r: Runtime = {\n key: config.key, endpoint: url.origin, audience: config.audience, identified: false, paused: false,\n disposed: false, generation: 0, device, offer: null, button: null,\n panel: null, frame: null, expanded: false, message: null, pending: null, timer: 0, visibility: () => {}, status: \"signed_out\",\n };\n browser[slot] = r;\n r.visibility = () => {\n try {\n if (document.visibilityState === \"visible\") void check(r);\n else { invalidate(r); status(r, r.paused ? \"paused\" : eligible(r) ? \"unavailable\" : \"signed_out\"); }\n } catch {}\n };\n document.addEventListener(\"visibilitychange\", r.visibility);\n r.timer = window.setInterval(() => { void check(r); }, 15_000);\n if (eligible(r)) void check(r);\n } catch { /* Never throw into the host application. */ }\n}\n\n/** Only the presence of an ID is retained. The ID itself is never stored or sent. */\nexport function identify(userId: string | null | undefined): void {\n try {\n const r = current();\n if (!r) return;\n const identified = typeof userId === \"string\" && userId.trim().length > 0;\n if (!identified && !r.identified) { if (!r.paused && eligible(r)) void check(r); return; }\n invalidate(r);\n r.identified = identified;\n status(r, r.paused ? \"paused\" : eligible(r) ? \"unavailable\" : \"signed_out\");\n if (eligible(r) && !r.paused) void check(r);\n } catch {}\n}\nexport function pause(): void {\n try { const r = current(); if (r) { r.paused = true; invalidate(r); status(r, \"paused\"); } } catch {}\n}\nexport function resume(): void {\n try { const r = current(); if (r) { r.paused = false; if (!eligible(r)) status(r, \"signed_out\"); void check(r); } } catch {}\n}\n/** Stop recruitment and remove listeners/UI. Does not end an already opened interview. */\nexport function destroy(): void {\n try {\n const r = current();\n if (!r) return;\n r.disposed = true; invalidate(r);\n window.clearInterval(r.timer);\n document.removeEventListener(\"visibilitychange\", r.visibility);\n // Keep the session panel and its launcher reachable until the page is left.\n // Minimize is presentation only; end/withdraw remain explicit inside the frame.\n if (!r.frame) delete host()![slot];\n } catch {}\n}\nexport function getStatus(): ResearchStatus {\n try { return current()?.status ?? \"not_initialized\"; } catch { return \"error\"; }\n}\n","// Research is the public SDK. The former capture engine is parked.\nexport { init, identify, pause, resume, destroy, getStatus } from \"./research\";\nexport type { SightspoolConfig, ResearchStatus } from \"./research\";\nimport { init, identify, pause, resume, destroy, getStatus } from \"./research\";\nexport default { init, identify, pause, resume, destroy, getStatus };\n"]}
package/dist/release.json CHANGED
@@ -1,21 +1,21 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "package": "@sightspool/sdk",
4
- "version": "0.4.0",
5
- "sourceCommit": "7b10230d4b59aedbdac0266aef4f4df88d574509",
4
+ "version": "0.4.1",
5
+ "sourceCommit": "56617f341eb259bf5b8e3a941d2d21926bac463b",
6
6
  "sourceDirty": false,
7
- "artifactId": "781ab339e13fe18b86b56cc9d84dd308bebf6482a885c8aae46a8b3b84801f6e",
7
+ "artifactId": "116419831a4c93513a867dcb4d3b808e8802ee27f5ad732460c997cefb8c804b",
8
8
  "bundle": {
9
9
  "file": "sdk.global.js",
10
- "path": "releases/0.4.0/781ab339e13fe18b86b56cc9d84dd308bebf6482a885c8aae46a8b3b84801f6e/sdk.global.js",
11
- "sha256": "ed80800d20ddd91aebde61c355bb15918c0c5cbd712b91e13604795f2d2a8aa9",
12
- "integrity": "sha384-kfq0QJNOieKKBsyznFRRW9kAaC5fcXvDzu56zgNsSzbXCVw2aReY66mlq8Fldy6K",
13
- "bytes": 4902,
14
- "gzipBytes": 2238
10
+ "path": "releases/0.4.1/116419831a4c93513a867dcb4d3b808e8802ee27f5ad732460c997cefb8c804b/sdk.global.js",
11
+ "sha256": "2fa0129e586e13dd79281a0d9be4ea918bf4860f9b17035841e22879de0b4d35",
12
+ "integrity": "sha384-DbCMC7ep3HQYO+22STNxgkZAkamnsyiGyP2xstE6PHvqnJnq6c4TCI20Kf6xuZOs",
13
+ "bytes": 7166,
14
+ "gzipBytes": 3024
15
15
  },
16
16
  "source": {
17
17
  "file": "source.json",
18
- "sha256": "563f39e79e11d55279cd9ddfb02e2d5c7c4cdc653b4d9f57ab6c36dc4874dfa4"
18
+ "sha256": "4deb7131013374d8facea8679e98e4617a3386449c85c1cdd907014590525da1"
19
19
  },
20
20
  "runtimeDependencies": []
21
21
  }
@@ -1,3 +1,3 @@
1
- var Sightspool=(function(exports){'use strict';var y=Symbol.for("sightspool.research.runtime.v1"),v=()=>typeof window=="undefined"?null:window,l=()=>{var e;return (e=v())==null?void 0:e[y]},a=e=>e.audience==="all_visitors"||e.identified;function h(e){var t;(t=e.button)==null||t.remove(),e.button=null,e.offer=null;}function c(e){var t;e.generation+=1,(t=e.pending)==null||t.abort(),e.pending=null,h(e);}function r(e,t){e.status=t;}async function u(e){if(e.disposed||e.paused||!a(e)||e.pending||document.visibilityState!=="visible")return;let t=e.generation,i=new AbortController;e.pending=i;let d=window.setTimeout(()=>i.abort(),1e4);e.button||r(e,"checking");try{let s=await fetch(e.endpoint+"/widget-offer",{method:"POST",credentials:"omit",cache:"no-store",referrerPolicy:"no-referrer",headers:{"Content-Type":"application/json"},body:JSON.stringify({operation:"offer",key:e.key,device:e.device}),signal:i.signal});if(!s.ok)throw Error("offer unavailable");let n=await s.json();if(e.disposed||t!==e.generation||e.paused||!a(e))return;if(n.available!==!0||typeof n.offer!="string"||!n.offer){h(e),r(e,"unavailable");return}if(e.offer=n.offer,r(e,"available"),e.button)return;let o=document.createElement("button");o.type="button",o.textContent="Talk to the founder \xB7 5 min",o.setAttribute("aria-label","Sightspool: join a five-minute user interview"),o.style.cssText="position:fixed;bottom:20px;right:20px;z-index:2147483000;padding:14px 18px;border:0;border-radius:16px;background:#171717;color:white;font:500 14px system-ui;box-shadow:0 8px 30px #0003;cursor:pointer;max-width:calc(100vw - 40px)",o.onclick=()=>{try{if(e.disposed||e.paused||!a(e)||!e.offer)return;if(e.popup&&!e.popup.closed){e.popup.focus();return}let g=new URLSearchParams({offer:e.offer,device:e.device});e.popup=window.open(e.endpoint+"/interview-widget#"+g,"sightspool-interview","popup,width=520,height=760");}catch(g){}},e.button=o,document.body.appendChild(o);}catch(s){!e.disposed&&t===e.generation&&(h(e),r(e,"error"));}finally{window.clearTimeout(d),e.pending===i&&(e.pending=null);}}function m(e){try{let t=v();if(!t||!e||!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(e.key)||e.audience!=="all_visitors"&&e.audience!=="signed_in")return;let i=new URL(e.endpoint||"https://www.sightspool.com");if(i.protocol!=="https:"&&!(i.protocol==="http:"&&["localhost","127.0.0.1","[::1]"].includes(i.hostname))||i.username||i.password)return;let d=l();if(d&&d.key===e.key&&d.endpoint===i.origin&&d.audience===e.audience)return;d&&f();let s="";try{s=sessionStorage.getItem("sightspool-widget-device:"+e.key)||"";}catch(o){}if(!/^ss_fcd_[A-Za-z0-9_-]{43}$/.test(s)){let o=crypto.getRandomValues(new Uint8Array(32));s="ss_fcd_"+btoa(String.fromCharCode(...o)).replaceAll("+","-").replaceAll("/","_").replace(/=+$/,"");try{sessionStorage.setItem("sightspool-widget-device:"+e.key,s);}catch(g){}}let n={key:e.key,endpoint:i.origin,audience:e.audience,identified:!1,paused:!1,disposed:!1,generation:0,device:s,offer:null,button:null,popup:null,pending:null,timer:0,visibility:()=>{},status:"signed_out"};t[y]=n,n.visibility=()=>{try{document.visibilityState==="visible"?u(n):(c(n),r(n,n.paused?"paused":a(n)?"unavailable":"signed_out"));}catch(o){}},document.addEventListener("visibilitychange",n.visibility),n.timer=window.setInterval(()=>{u(n);},15e3),a(n)&&u(n);}catch(t){}}function b(e){try{let t=l();if(!t)return;let i=typeof e=="string"&&e.trim().length>0;if(!i&&!t.identified){!t.paused&&a(t)&&u(t);return}c(t),t.identified=i,r(t,t.paused?"paused":a(t)?"unavailable":"signed_out"),a(t)&&!t.paused&&u(t);}catch(t){}}function w(){try{let e=l();e&&(e.paused=!0,c(e),r(e,"paused"));}catch(e){}}function x(){try{let e=l();e&&(e.paused=!1,a(e)||r(e,"signed_out"),u(e));}catch(e){}}function f(){try{let e=l();if(!e)return;e.disposed=!0,c(e),window.clearInterval(e.timer),document.removeEventListener("visibilitychange",e.visibility),delete v()[y];}catch(e){}}function S(){var e,t;try{return (t=(e=l())==null?void 0:e.status)!=null?t:"not_initialized"}catch(i){return "error"}}var p={init:m,identify:b,pause:w,resume:x,destroy:f,getStatus:S};var A=p;try{let e=document.currentScript,t=(e==null?void 0:e.dataset.sightspoolKey)||(e==null?void 0:e.dataset.key),i=e==null?void 0:e.dataset.sightspoolAudience;t&&(i==="all_visitors"||i==="signed_in")&&(p.init({key:t,audience:i,endpoint:(e==null?void 0:e.dataset.sightspoolEndpoint)||new URL(e.src).origin}),e!=null&&e.dataset.userId&&p.identify(e.dataset.userId)),window.SightspoolResearch=p,Promise.resolve().then(()=>window.dispatchEvent(new Event("sightspool:ready")));}catch(e){}
2
- exports.default=A;exports.destroy=f;exports.getStatus=S;exports.identify=b;exports.init=m;exports.pause=w;exports.resume=x;Object.defineProperty(exports,'__esModule',{value:true});return exports;})({});//# sourceMappingURL=sdk.global.js.map
1
+ var Sightspool=(function(exports){'use strict';var y=Symbol.for("sightspool.research.runtime.v1"),x=()=>typeof window=="undefined"?null:window,c=()=>{var e;return (e=x())==null?void 0:e[y]},r=e=>e.audience==="all_visitors"||e.identified;function b(e){var t;e.frame||((t=e.button)==null||t.remove(),e.button=null,e.offer=null);}function h(e){var t;e.generation+=1,(t=e.pending)==null||t.abort(),e.pending=null,b(e);}function d(e,t){e.status=t;}function m(e,t){var i;!e.panel||!e.frame||!e.button||(e.expanded=t,e.panel.hidden=!t,e.panel.style.display=t?"flex":"none",e.button.hidden=t,e.button.setAttribute("aria-expanded",String(t)),t?(i=e.panel.querySelector("button"))==null||i.focus():e.button.focus());}function E(e){if(e.frame){m(e,true);return}if(!e.offer||!e.button)return;let t=document.createElement("section");t.id="sightspool-interview-panel",t.setAttribute("role","dialog"),t.setAttribute("aria-label","Sightspool interview"),t.style.cssText="position:fixed;bottom:16px;right:16px;z-index:2147483001;box-sizing:border-box;width:420px;max-width:calc(100% - 32px);height:680px;max-height:calc(100dvh - 32px);border:1px solid #e6dfe3;border-radius:20px;background:#fff;color:#262024;box-shadow:0 12px 50px #0003;overflow:hidden;font:14px system-ui;display:flex;flex-direction:column";let i=document.createElement("div");i.style.cssText="display:flex;align-items:center;justify-content:space-between;gap:12px;padding:14px 16px;border-bottom:1px solid #eee;flex-shrink:0";let a=document.createElement("strong");a.textContent="Sightspool";let s=document.createElement("button");s.type="button",s.textContent="Minimize",s.style.cssText="font:inherit;color:inherit;background:#fff;border:1px solid #d6cbd1;border-radius:8px;padding:8px 12px;cursor:pointer";let n=()=>{m(e,false);};s.onclick=n,i.appendChild(a),i.appendChild(s),t.appendChild(i);let o=document.createElement("iframe");o.title="Sightspool research conversation",o.allow="microphone; autoplay",o.setAttribute("sandbox","allow-scripts allow-same-origin allow-forms"),o.referrerPolicy="no-referrer",o.style.cssText="display:block;width:100%;flex:1;min-height:0;border:0;background:#fff";let p=new URL(e.endpoint+"/interview-widget");p.searchParams.set("key",e.key),p.hash=new URLSearchParams({offer:e.offer,device:e.device}).toString(),o.src=p.href,t.appendChild(o),t.onkeydown=l=>{l.key==="Escape"&&(l.preventDefault(),n());},e.panel=t,e.frame=o,e.message=l=>{var _;l.source!==o.contentWindow||l.origin!==e.endpoint||((_=l.data)==null?void 0:_.type)!=="sightspool:panel:minimize"||n();},window.addEventListener("message",e.message),e.button.textContent="Return to interview",e.button.setAttribute("aria-label","Sightspool: return to your interview"),e.button.setAttribute("aria-controls",t.id),e.button.onclick=()=>{m(e,true);},document.body.appendChild(t),m(e,true);}async function u(e){if(e.frame||e.disposed||e.paused||!r(e)||e.pending||document.visibilityState!=="visible")return;let t=e.generation,i=new AbortController;e.pending=i;let a=window.setTimeout(()=>i.abort(),1e4);e.button||d(e,"checking");try{let s=await fetch(e.endpoint+"/widget-offer",{method:"POST",credentials:"omit",cache:"no-store",referrerPolicy:"no-referrer",headers:{"Content-Type":"application/json"},body:JSON.stringify({operation:"offer",key:e.key,device:e.device}),signal:i.signal});if(!s.ok)throw Error("offer unavailable");let n=await s.json();if(e.disposed||t!==e.generation||e.paused||!r(e))return;if(n.available!==!0||typeof n.offer!="string"||!n.offer){b(e),d(e,"unavailable");return}if(e.offer=n.offer,d(e,"available"),e.button)return;let o=document.createElement("button");o.type="button",o.textContent="Share your experience \xB7 5 min",o.setAttribute("aria-label","Sightspool: join a five-minute user interview"),o.style.cssText="position:fixed;bottom:20px;right:20px;z-index:2147483000;padding:14px 18px;border:0;border-radius:999px;background:#ad1668;color:white;font:500 14px system-ui;box-shadow:0 8px 30px #0003;cursor:pointer;max-width:calc(100vw - 40px)",o.onclick=()=>{try{if(e.disposed||e.paused||!r(e)||!e.offer)return;E(e);}catch(p){}},e.button=o,document.body.appendChild(o);}catch(s){!e.disposed&&t===e.generation&&(b(e),d(e,"error"));}finally{window.clearTimeout(a),e.pending===i&&(e.pending=null);}}function v(e){try{let t=x();if(!t||!e||!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(e.key)||e.audience!=="all_visitors"&&e.audience!=="signed_in")return;let i=new URL(e.endpoint||"https://www.sightspool.com");if(i.protocol!=="https:"&&!(i.protocol==="http:"&&["localhost","127.0.0.1","[::1]"].includes(i.hostname))||i.username||i.password)return;let a=c();if(a&&a.key===e.key&&a.endpoint===i.origin&&a.audience===e.audience||a!=null&&a.frame)return;a&&g();let s="";try{s=sessionStorage.getItem("sightspool-widget-device:"+e.key)||"";}catch(o){}if(!/^ss_fcd_[A-Za-z0-9_-]{43}$/.test(s)){let o=crypto.getRandomValues(new Uint8Array(32));s="ss_fcd_"+btoa(String.fromCharCode(...o)).replaceAll("+","-").replaceAll("/","_").replace(/=+$/,"");try{sessionStorage.setItem("sightspool-widget-device:"+e.key,s);}catch(p){}}let n={key:e.key,endpoint:i.origin,audience:e.audience,identified:!1,paused:!1,disposed:!1,generation:0,device:s,offer:null,button:null,panel:null,frame:null,expanded:!1,message:null,pending:null,timer:0,visibility:()=>{},status:"signed_out"};t[y]=n,n.visibility=()=>{try{document.visibilityState==="visible"?u(n):(h(n),d(n,n.paused?"paused":r(n)?"unavailable":"signed_out"));}catch(o){}},document.addEventListener("visibilitychange",n.visibility),n.timer=window.setInterval(()=>{u(n);},15e3),r(n)&&u(n);}catch(t){}}function w(e){try{let t=c();if(!t)return;let i=typeof e=="string"&&e.trim().length>0;if(!i&&!t.identified){!t.paused&&r(t)&&u(t);return}h(t),t.identified=i,d(t,t.paused?"paused":r(t)?"unavailable":"signed_out"),r(t)&&!t.paused&&u(t);}catch(t){}}function S(){try{let e=c();e&&(e.paused=!0,h(e),d(e,"paused"));}catch(e){}}function k(){try{let e=c();e&&(e.paused=!1,r(e)||d(e,"signed_out"),u(e));}catch(e){}}function g(){try{let e=c();if(!e)return;e.disposed=!0,h(e),window.clearInterval(e.timer),document.removeEventListener("visibilitychange",e.visibility),e.frame||delete x()[y];}catch(e){}}function R(){var e,t;try{return (t=(e=c())==null?void 0:e.status)!=null?t:"not_initialized"}catch(i){return "error"}}var f={init:v,identify:w,pause:S,resume:k,destroy:g,getStatus:R};var H=f;try{let e=document.currentScript,t=(e==null?void 0:e.dataset.sightspoolKey)||(e==null?void 0:e.dataset.key),i=e==null?void 0:e.dataset.sightspoolAudience;t&&(i==="all_visitors"||i==="signed_in")&&(f.init({key:t,audience:i,endpoint:(e==null?void 0:e.dataset.sightspoolEndpoint)||new URL(e.src).origin}),e!=null&&e.dataset.userId&&f.identify(e.dataset.userId)),window.SightspoolResearch=f,Promise.resolve().then(()=>window.dispatchEvent(new Event("sightspool:ready")));}catch(e){}
2
+ exports.default=H;exports.destroy=g;exports.getStatus=R;exports.identify=w;exports.init=v;exports.pause=S;exports.resume=k;Object.defineProperty(exports,'__esModule',{value:true});return exports;})({});//# sourceMappingURL=sdk.global.js.map
3
3
  //# sourceMappingURL=sdk.global.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/research.ts","../src/index.ts","../src/browser.ts"],"names":["slot","host","current","_a","eligible","r","remove","invalidate","status","value","check","generation","request","timeout","response","result","button","hash","e","init","config","browser","url","previous","destroy","device","bytes","identify","userId","identified","pause","resume","getStatus","_b","src_default","browser_default","script","key","audience"],"mappings":"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","file":"sdk.global.js","sourcesContent":["export type SightspoolConfig = {\n /** Public workspace widget key from Go live (UUID, not an old pk_live key). */\n key: string;\n /** Match the approved research audience. No default that widens recruitment. */\n audience: \"all_visitors\" | \"signed_in\";\n /** Sightspool origin. Defaults to the hosted application. */\n endpoint?: string;\n};\nexport type ResearchStatus =\n | \"not_initialized\" | \"signed_out\" | \"paused\" | \"checking\"\n | \"unavailable\" | \"available\" | \"error\";\n\ntype Runtime = {\n key: string; endpoint: string; audience: SightspoolConfig[\"audience\"]; identified: boolean; paused: boolean;\n disposed: boolean; generation: number; device: string; offer: string | null;\n button: HTMLButtonElement | null; popup: Window | null;\n pending: AbortController | null; timer: number; visibility: () => void;\n status: ResearchStatus;\n};\nconst slot = Symbol.for(\"sightspool.research.runtime.v1\");\ntype Host = Window & { [slot]?: Runtime };\nconst host = (): Host | null => typeof window === \"undefined\" ? null : window as Host;\nconst current = () => host()?.[slot];\nconst eligible = (r: Runtime) => r.audience === \"all_visitors\" || r.identified;\n\nfunction remove(r: Runtime) {\n r.button?.remove(); r.button = null; r.offer = null;\n}\nfunction invalidate(r: Runtime) {\n r.generation += 1; r.pending?.abort(); r.pending = null; remove(r);\n}\nfunction status(r: Runtime, value: ResearchStatus) { r.status = value; }\n\nasync function check(r: Runtime) {\n if (r.disposed || r.paused || !eligible(r) || r.pending || document.visibilityState !== \"visible\") return;\n const generation = r.generation;\n const request = new AbortController();\n r.pending = request;\n const timeout = window.setTimeout(() => request.abort(), 10_000);\n if (!r.button) status(r, \"checking\");\n try {\n const response = await fetch(r.endpoint + \"/widget-offer\", {\n method: \"POST\", credentials: \"omit\", cache: \"no-store\", referrerPolicy: \"no-referrer\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ operation: \"offer\", key: r.key, device: r.device }),\n signal: request.signal,\n });\n if (!response.ok) throw Error(\"offer unavailable\");\n const result = await response.json();\n if (r.disposed || generation !== r.generation || r.paused || !eligible(r)) return;\n if (result.available !== true || typeof result.offer !== \"string\" || !result.offer) {\n remove(r); status(r, \"unavailable\"); return;\n }\n r.offer = result.offer;\n status(r, \"available\");\n if (r.button) return;\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.textContent = \"Talk to the founder · 5 min\";\n button.setAttribute(\"aria-label\", \"Sightspool: join a five-minute user interview\");\n button.style.cssText = \"position:fixed;bottom:20px;right:20px;z-index:2147483000;padding:14px 18px;border:0;border-radius:16px;background:#171717;color:white;font:500 14px system-ui;box-shadow:0 8px 30px #0003;cursor:pointer;max-width:calc(100vw - 40px)\";\n button.onclick = () => {\n try {\n if (r.disposed || r.paused || !eligible(r) || !r.offer) return;\n if (r.popup && !r.popup.closed) { r.popup.focus(); return; }\n const hash = new URLSearchParams({ offer: r.offer, device: r.device });\n r.popup = window.open(r.endpoint + \"/interview-widget#\" + hash, \"sightspool-interview\", \"popup,width=520,height=760\");\n } catch { /* Host pages remain usable if popups are blocked. */ }\n };\n r.button = button;\n document.body.appendChild(button);\n } catch {\n if (!r.disposed && generation === r.generation) { remove(r); status(r, \"error\"); }\n } finally {\n window.clearTimeout(timeout);\n if (r.pending === request) r.pending = null;\n }\n}\n\n/** Start research for the chosen audience. Signed-in-only waits for identify(). */\nexport function init(config: SightspoolConfig): void {\n try {\n const browser = host();\n if (!browser || !config || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(config.key)) return;\n if (config.audience !== \"all_visitors\" && config.audience !== \"signed_in\") return;\n const url = new URL(config.endpoint || \"https://www.sightspool.com\");\n if (url.protocol !== \"https:\" && !(url.protocol === \"http:\" && [\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(url.hostname))) return;\n if (url.username || url.password) return;\n const previous = current();\n if (previous && previous.key === config.key && previous.endpoint === url.origin && previous.audience === config.audience) return;\n if (previous) destroy();\n let device = \"\";\n try { device = sessionStorage.getItem(\"sightspool-widget-device:\" + config.key) || \"\"; } catch {}\n if (!/^ss_fcd_[A-Za-z0-9_-]{43}$/.test(device)) {\n const bytes = crypto.getRandomValues(new Uint8Array(32));\n device = \"ss_fcd_\" + btoa(String.fromCharCode(...bytes)).replaceAll(\"+\", \"-\").replaceAll(\"/\", \"_\").replace(/=+$/, \"\");\n try { sessionStorage.setItem(\"sightspool-widget-device:\" + config.key, device); } catch {}\n }\n const r: Runtime = {\n key: config.key, endpoint: url.origin, audience: config.audience, identified: false, paused: false,\n disposed: false, generation: 0, device, offer: null, button: null,\n popup: null, pending: null, timer: 0, visibility: () => {}, status: \"signed_out\",\n };\n browser[slot] = r;\n r.visibility = () => {\n try {\n if (document.visibilityState === \"visible\") void check(r);\n else { invalidate(r); status(r, r.paused ? \"paused\" : eligible(r) ? \"unavailable\" : \"signed_out\"); }\n } catch {}\n };\n document.addEventListener(\"visibilitychange\", r.visibility);\n r.timer = window.setInterval(() => { void check(r); }, 15_000);\n if (eligible(r)) void check(r);\n } catch { /* Never throw into the host application. */ }\n}\n\n/** Only the presence of an ID is retained. The ID itself is never stored or sent. */\nexport function identify(userId: string | null | undefined): void {\n try {\n const r = current();\n if (!r) return;\n const identified = typeof userId === \"string\" && userId.trim().length > 0;\n if (!identified && !r.identified) { if (!r.paused && eligible(r)) void check(r); return; }\n invalidate(r);\n r.identified = identified;\n status(r, r.paused ? \"paused\" : eligible(r) ? \"unavailable\" : \"signed_out\");\n if (eligible(r) && !r.paused) void check(r);\n } catch {}\n}\nexport function pause(): void {\n try { const r = current(); if (r) { r.paused = true; invalidate(r); status(r, \"paused\"); } } catch {}\n}\nexport function resume(): void {\n try { const r = current(); if (r) { r.paused = false; if (!eligible(r)) status(r, \"signed_out\"); void check(r); } } catch {}\n}\n/** Stop recruitment and remove listeners/UI. Does not end an already opened interview. */\nexport function destroy(): void {\n try {\n const r = current();\n if (!r) return;\n r.disposed = true; invalidate(r);\n window.clearInterval(r.timer);\n document.removeEventListener(\"visibilitychange\", r.visibility);\n delete host()![slot];\n } catch {}\n}\nexport function getStatus(): ResearchStatus {\n try { return current()?.status ?? \"not_initialized\"; } catch { return \"error\"; }\n}\n","// Research is the public SDK. The former capture engine is parked.\nexport { init, identify, pause, resume, destroy, getStatus } from \"./research\";\nexport type { SightspoolConfig, ResearchStatus } from \"./research\";\nimport { init, identify, pause, resume, destroy, getStatus } from \"./research\";\nexport default { init, identify, pause, resume, destroy, getStatus };\n","import api from \"./index\";\nexport * from \"./index\";\nexport default api;\n\ntry {\n const script = document.currentScript as HTMLScriptElement | null;\n const key = script?.dataset.sightspoolKey || script?.dataset.key;\n const audience = script?.dataset.sightspoolAudience;\n if (key && (audience === \"all_visitors\" || audience === \"signed_in\")) {\n api.init({ key, audience, endpoint: script?.dataset.sightspoolEndpoint || new URL(script!.src).origin });\n if (script?.dataset.userId) api.identify(script.dataset.userId);\n }\n // The old research-widget URL is an alias of this bundle, not another runtime.\n (window as Window & { SightspoolResearch?: typeof api }).SightspoolResearch = api;\n Promise.resolve().then(() => window.dispatchEvent(new Event(\"sightspool:ready\")));\n} catch { /* Browser auto-init must not interrupt the client app. */ }\n"]}
1
+ {"version":3,"sources":["../src/research.ts","../src/index.ts","../src/browser.ts"],"names":["slot","host","current","_a","eligible","r","remove","invalidate","status","value","expand","expanded","openPanel","panel","header","title","minimize","hide","frame","url","event","check","generation","request","timeout","response","result","button","e","init","config","browser","previous","destroy","device","bytes","identify","userId","identified","pause","resume","getStatus","_b","src_default","browser_default","script","key","audience"],"mappings":"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","file":"sdk.global.js","sourcesContent":["export type SightspoolConfig = {\n /** Public workspace widget key from Go live (UUID, not an old pk_live key). */\n key: string;\n /** Match the approved research audience. No default that widens recruitment. */\n audience: \"all_visitors\" | \"signed_in\";\n /** Sightspool origin. Defaults to the hosted application. */\n endpoint?: string;\n};\nexport type ResearchStatus =\n | \"not_initialized\" | \"signed_out\" | \"paused\" | \"checking\"\n | \"unavailable\" | \"available\" | \"error\";\n\ntype Runtime = {\n key: string; endpoint: string; audience: SightspoolConfig[\"audience\"]; identified: boolean; paused: boolean;\n disposed: boolean; generation: number; device: string; offer: string | null;\n button: HTMLButtonElement | null; panel: HTMLElement | null; frame: HTMLIFrameElement | null;\n expanded: boolean; message: ((event: MessageEvent) => void) | null;\n pending: AbortController | null; timer: number; visibility: () => void;\n status: ResearchStatus;\n};\nconst slot = Symbol.for(\"sightspool.research.runtime.v1\");\ntype Host = Window & { [slot]?: Runtime };\nconst host = (): Host | null => typeof window === \"undefined\" ? null : window as Host;\nconst current = () => host()?.[slot];\nconst eligible = (r: Runtime) => r.audience === \"all_visitors\" || r.identified;\n\nfunction remove(r: Runtime) {\n // An opened interview owns its lifetime. Recruitment changes cannot end it.\n if (r.frame) return;\n r.button?.remove(); r.button = null; r.offer = null;\n}\nfunction invalidate(r: Runtime) {\n r.generation += 1; r.pending?.abort(); r.pending = null; remove(r);\n}\nfunction status(r: Runtime, value: ResearchStatus) { r.status = value; }\n\nfunction expand(r: Runtime, expanded: boolean) {\n if (!r.panel || !r.frame || !r.button) return;\n r.expanded = expanded;\n r.panel.hidden = !expanded;\n r.panel.style.display = expanded ? \"flex\" : \"none\";\n r.button.hidden = expanded;\n r.button.setAttribute(\"aria-expanded\", String(expanded));\n if (expanded) {\n r.panel.querySelector<HTMLButtonElement>(\"button\")?.focus();\n } else r.button.focus();\n}\nfunction openPanel(r: Runtime) {\n if (r.frame) { expand(r, true); return; }\n if (!r.offer || !r.button) return;\n const panel = document.createElement(\"section\");\n panel.id = \"sightspool-interview-panel\";\n panel.setAttribute(\"role\", \"dialog\");\n panel.setAttribute(\"aria-label\", \"Sightspool interview\");\n panel.style.cssText = \"position:fixed;bottom:16px;right:16px;z-index:2147483001;box-sizing:border-box;width:420px;max-width:calc(100% - 32px);height:680px;max-height:calc(100dvh - 32px);border:1px solid #e6dfe3;border-radius:20px;background:#fff;color:#262024;box-shadow:0 12px 50px #0003;overflow:hidden;font:14px system-ui;display:flex;flex-direction:column\";\n const header = document.createElement(\"div\");\n header.style.cssText = \"display:flex;align-items:center;justify-content:space-between;gap:12px;padding:14px 16px;border-bottom:1px solid #eee;flex-shrink:0\";\n const title = document.createElement(\"strong\"); title.textContent = \"Sightspool\";\n const minimize = document.createElement(\"button\");\n minimize.type = \"button\"; minimize.textContent = \"Minimize\";\n minimize.style.cssText = \"font:inherit;color:inherit;background:#fff;border:1px solid #d6cbd1;border-radius:8px;padding:8px 12px;cursor:pointer\";\n const hide = () => { expand(r, false); };\n minimize.onclick = hide;\n header.appendChild(title); header.appendChild(minimize); panel.appendChild(header);\n const frame = document.createElement(\"iframe\");\n frame.title = \"Sightspool research conversation\";\n frame.allow = \"microphone; autoplay\";\n frame.setAttribute(\"sandbox\", \"allow-scripts allow-same-origin allow-forms\");\n frame.referrerPolicy = \"no-referrer\";\n frame.style.cssText = \"display:block;width:100%;flex:1;min-height:0;border:0;background:#fff\";\n const url = new URL(r.endpoint + \"/interview-widget\");\n url.searchParams.set(\"key\", r.key);\n url.hash = new URLSearchParams({ offer: r.offer, device: r.device }).toString();\n frame.src = url.href;\n panel.appendChild(frame);\n panel.onkeydown = (event) => { if (event.key === \"Escape\") { event.preventDefault(); hide(); } };\n r.panel = panel; r.frame = frame;\n r.message = (event) => {\n // Only this exact embedded document may control its presentation. No interview\n // content, capabilities or account data cross the parent messaging boundary.\n if (event.source !== frame.contentWindow || event.origin !== r.endpoint || event.data?.type !== \"sightspool:panel:minimize\") return;\n hide();\n };\n window.addEventListener(\"message\", r.message);\n r.button.textContent = \"Return to interview\";\n r.button.setAttribute(\"aria-label\", \"Sightspool: return to your interview\");\n r.button.setAttribute(\"aria-controls\", panel.id);\n r.button.onclick = () => { expand(r, true); };\n document.body.appendChild(panel);\n expand(r, true);\n}\n\nasync function check(r: Runtime) {\n if (r.frame || r.disposed || r.paused || !eligible(r) || r.pending || document.visibilityState !== \"visible\") return;\n const generation = r.generation;\n const request = new AbortController();\n r.pending = request;\n const timeout = window.setTimeout(() => request.abort(), 10_000);\n if (!r.button) status(r, \"checking\");\n try {\n const response = await fetch(r.endpoint + \"/widget-offer\", {\n method: \"POST\", credentials: \"omit\", cache: \"no-store\", referrerPolicy: \"no-referrer\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ operation: \"offer\", key: r.key, device: r.device }),\n signal: request.signal,\n });\n if (!response.ok) throw Error(\"offer unavailable\");\n const result = await response.json();\n if (r.disposed || generation !== r.generation || r.paused || !eligible(r)) return;\n if (result.available !== true || typeof result.offer !== \"string\" || !result.offer) {\n remove(r); status(r, \"unavailable\"); return;\n }\n r.offer = result.offer;\n status(r, \"available\");\n if (r.button) return;\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.textContent = \"Share your experience · 5 min\";\n button.setAttribute(\"aria-label\", \"Sightspool: join a five-minute user interview\");\n button.style.cssText = \"position:fixed;bottom:20px;right:20px;z-index:2147483000;padding:14px 18px;border:0;border-radius:999px;background:#ad1668;color:white;font:500 14px system-ui;box-shadow:0 8px 30px #0003;cursor:pointer;max-width:calc(100vw - 40px)\";\n button.onclick = () => {\n try {\n if (r.disposed || r.paused || !eligible(r) || !r.offer) return;\n openPanel(r);\n } catch { /* Host pages remain usable if embedding is blocked. */ }\n };\n r.button = button;\n document.body.appendChild(button);\n } catch {\n if (!r.disposed && generation === r.generation) { remove(r); status(r, \"error\"); }\n } finally {\n window.clearTimeout(timeout);\n if (r.pending === request) r.pending = null;\n }\n}\n\n/** Start research for the chosen audience. Signed-in-only waits for identify(). */\nexport function init(config: SightspoolConfig): void {\n try {\n const browser = host();\n if (!browser || !config || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(config.key)) return;\n if (config.audience !== \"all_visitors\" && config.audience !== \"signed_in\") return;\n const url = new URL(config.endpoint || \"https://www.sightspool.com\");\n if (url.protocol !== \"https:\" && !(url.protocol === \"http:\" && [\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(url.hostname))) return;\n if (url.username || url.password) return;\n const previous = current();\n if (previous && previous.key === config.key && previous.endpoint === url.origin && previous.audience === config.audience) return;\n // A second loader/workspace must not replace a participant's open interview.\n if (previous?.frame) return;\n if (previous) destroy();\n let device = \"\";\n try { device = sessionStorage.getItem(\"sightspool-widget-device:\" + config.key) || \"\"; } catch {}\n if (!/^ss_fcd_[A-Za-z0-9_-]{43}$/.test(device)) {\n const bytes = crypto.getRandomValues(new Uint8Array(32));\n device = \"ss_fcd_\" + btoa(String.fromCharCode(...bytes)).replaceAll(\"+\", \"-\").replaceAll(\"/\", \"_\").replace(/=+$/, \"\");\n try { sessionStorage.setItem(\"sightspool-widget-device:\" + config.key, device); } catch {}\n }\n const r: Runtime = {\n key: config.key, endpoint: url.origin, audience: config.audience, identified: false, paused: false,\n disposed: false, generation: 0, device, offer: null, button: null,\n panel: null, frame: null, expanded: false, message: null, pending: null, timer: 0, visibility: () => {}, status: \"signed_out\",\n };\n browser[slot] = r;\n r.visibility = () => {\n try {\n if (document.visibilityState === \"visible\") void check(r);\n else { invalidate(r); status(r, r.paused ? \"paused\" : eligible(r) ? \"unavailable\" : \"signed_out\"); }\n } catch {}\n };\n document.addEventListener(\"visibilitychange\", r.visibility);\n r.timer = window.setInterval(() => { void check(r); }, 15_000);\n if (eligible(r)) void check(r);\n } catch { /* Never throw into the host application. */ }\n}\n\n/** Only the presence of an ID is retained. The ID itself is never stored or sent. */\nexport function identify(userId: string | null | undefined): void {\n try {\n const r = current();\n if (!r) return;\n const identified = typeof userId === \"string\" && userId.trim().length > 0;\n if (!identified && !r.identified) { if (!r.paused && eligible(r)) void check(r); return; }\n invalidate(r);\n r.identified = identified;\n status(r, r.paused ? \"paused\" : eligible(r) ? \"unavailable\" : \"signed_out\");\n if (eligible(r) && !r.paused) void check(r);\n } catch {}\n}\nexport function pause(): void {\n try { const r = current(); if (r) { r.paused = true; invalidate(r); status(r, \"paused\"); } } catch {}\n}\nexport function resume(): void {\n try { const r = current(); if (r) { r.paused = false; if (!eligible(r)) status(r, \"signed_out\"); void check(r); } } catch {}\n}\n/** Stop recruitment and remove listeners/UI. Does not end an already opened interview. */\nexport function destroy(): void {\n try {\n const r = current();\n if (!r) return;\n r.disposed = true; invalidate(r);\n window.clearInterval(r.timer);\n document.removeEventListener(\"visibilitychange\", r.visibility);\n // Keep the session panel and its launcher reachable until the page is left.\n // Minimize is presentation only; end/withdraw remain explicit inside the frame.\n if (!r.frame) delete host()![slot];\n } catch {}\n}\nexport function getStatus(): ResearchStatus {\n try { return current()?.status ?? \"not_initialized\"; } catch { return \"error\"; }\n}\n","// Research is the public SDK. The former capture engine is parked.\nexport { init, identify, pause, resume, destroy, getStatus } from \"./research\";\nexport type { SightspoolConfig, ResearchStatus } from \"./research\";\nimport { init, identify, pause, resume, destroy, getStatus } from \"./research\";\nexport default { init, identify, pause, resume, destroy, getStatus };\n","import api from \"./index\";\nexport * from \"./index\";\nexport default api;\n\ntry {\n const script = document.currentScript as HTMLScriptElement | null;\n const key = script?.dataset.sightspoolKey || script?.dataset.key;\n const audience = script?.dataset.sightspoolAudience;\n if (key && (audience === \"all_visitors\" || audience === \"signed_in\")) {\n api.init({ key, audience, endpoint: script?.dataset.sightspoolEndpoint || new URL(script!.src).origin });\n if (script?.dataset.userId) api.identify(script.dataset.userId);\n }\n // The old research-widget URL is an alias of this bundle, not another runtime.\n (window as Window & { SightspoolResearch?: typeof api }).SightspoolResearch = api;\n Promise.resolve().then(() => window.dispatchEvent(new Event(\"sightspool:ready\")));\n} catch { /* Browser auto-init must not interrupt the client app. */ }\n"]}
package/dist/source.json CHANGED
@@ -2,9 +2,9 @@
2
2
  "files": {
3
3
  "src/index.ts": "// Research is the public SDK. The former capture engine is parked.\nexport { init, identify, pause, resume, destroy, getStatus } from \"./research\";\nexport type { SightspoolConfig, ResearchStatus } from \"./research\";\nimport { init, identify, pause, resume, destroy, getStatus } from \"./research\";\nexport default { init, identify, pause, resume, destroy, getStatus };\n",
4
4
  "src/browser.ts": "import api from \"./index\";\nexport * from \"./index\";\nexport default api;\n\ntry {\n const script = document.currentScript as HTMLScriptElement | null;\n const key = script?.dataset.sightspoolKey || script?.dataset.key;\n const audience = script?.dataset.sightspoolAudience;\n if (key && (audience === \"all_visitors\" || audience === \"signed_in\")) {\n api.init({ key, audience, endpoint: script?.dataset.sightspoolEndpoint || new URL(script!.src).origin });\n if (script?.dataset.userId) api.identify(script.dataset.userId);\n }\n // The old research-widget URL is an alias of this bundle, not another runtime.\n (window as Window & { SightspoolResearch?: typeof api }).SightspoolResearch = api;\n Promise.resolve().then(() => window.dispatchEvent(new Event(\"sightspool:ready\")));\n} catch { /* Browser auto-init must not interrupt the client app. */ }\n",
5
- "src/research.ts": "export type SightspoolConfig = {\n /** Public workspace widget key from Go live (UUID, not an old pk_live key). */\n key: string;\n /** Match the approved research audience. No default that widens recruitment. */\n audience: \"all_visitors\" | \"signed_in\";\n /** Sightspool origin. Defaults to the hosted application. */\n endpoint?: string;\n};\nexport type ResearchStatus =\n | \"not_initialized\" | \"signed_out\" | \"paused\" | \"checking\"\n | \"unavailable\" | \"available\" | \"error\";\n\ntype Runtime = {\n key: string; endpoint: string; audience: SightspoolConfig[\"audience\"]; identified: boolean; paused: boolean;\n disposed: boolean; generation: number; device: string; offer: string | null;\n button: HTMLButtonElement | null; popup: Window | null;\n pending: AbortController | null; timer: number; visibility: () => void;\n status: ResearchStatus;\n};\nconst slot = Symbol.for(\"sightspool.research.runtime.v1\");\ntype Host = Window & { [slot]?: Runtime };\nconst host = (): Host | null => typeof window === \"undefined\" ? null : window as Host;\nconst current = () => host()?.[slot];\nconst eligible = (r: Runtime) => r.audience === \"all_visitors\" || r.identified;\n\nfunction remove(r: Runtime) {\n r.button?.remove(); r.button = null; r.offer = null;\n}\nfunction invalidate(r: Runtime) {\n r.generation += 1; r.pending?.abort(); r.pending = null; remove(r);\n}\nfunction status(r: Runtime, value: ResearchStatus) { r.status = value; }\n\nasync function check(r: Runtime) {\n if (r.disposed || r.paused || !eligible(r) || r.pending || document.visibilityState !== \"visible\") return;\n const generation = r.generation;\n const request = new AbortController();\n r.pending = request;\n const timeout = window.setTimeout(() => request.abort(), 10_000);\n if (!r.button) status(r, \"checking\");\n try {\n const response = await fetch(r.endpoint + \"/widget-offer\", {\n method: \"POST\", credentials: \"omit\", cache: \"no-store\", referrerPolicy: \"no-referrer\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ operation: \"offer\", key: r.key, device: r.device }),\n signal: request.signal,\n });\n if (!response.ok) throw Error(\"offer unavailable\");\n const result = await response.json();\n if (r.disposed || generation !== r.generation || r.paused || !eligible(r)) return;\n if (result.available !== true || typeof result.offer !== \"string\" || !result.offer) {\n remove(r); status(r, \"unavailable\"); return;\n }\n r.offer = result.offer;\n status(r, \"available\");\n if (r.button) return;\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.textContent = \"Talk to the founder · 5 min\";\n button.setAttribute(\"aria-label\", \"Sightspool: join a five-minute user interview\");\n button.style.cssText = \"position:fixed;bottom:20px;right:20px;z-index:2147483000;padding:14px 18px;border:0;border-radius:16px;background:#171717;color:white;font:500 14px system-ui;box-shadow:0 8px 30px #0003;cursor:pointer;max-width:calc(100vw - 40px)\";\n button.onclick = () => {\n try {\n if (r.disposed || r.paused || !eligible(r) || !r.offer) return;\n if (r.popup && !r.popup.closed) { r.popup.focus(); return; }\n const hash = new URLSearchParams({ offer: r.offer, device: r.device });\n r.popup = window.open(r.endpoint + \"/interview-widget#\" + hash, \"sightspool-interview\", \"popup,width=520,height=760\");\n } catch { /* Host pages remain usable if popups are blocked. */ }\n };\n r.button = button;\n document.body.appendChild(button);\n } catch {\n if (!r.disposed && generation === r.generation) { remove(r); status(r, \"error\"); }\n } finally {\n window.clearTimeout(timeout);\n if (r.pending === request) r.pending = null;\n }\n}\n\n/** Start research for the chosen audience. Signed-in-only waits for identify(). */\nexport function init(config: SightspoolConfig): void {\n try {\n const browser = host();\n if (!browser || !config || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(config.key)) return;\n if (config.audience !== \"all_visitors\" && config.audience !== \"signed_in\") return;\n const url = new URL(config.endpoint || \"https://www.sightspool.com\");\n if (url.protocol !== \"https:\" && !(url.protocol === \"http:\" && [\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(url.hostname))) return;\n if (url.username || url.password) return;\n const previous = current();\n if (previous && previous.key === config.key && previous.endpoint === url.origin && previous.audience === config.audience) return;\n if (previous) destroy();\n let device = \"\";\n try { device = sessionStorage.getItem(\"sightspool-widget-device:\" + config.key) || \"\"; } catch {}\n if (!/^ss_fcd_[A-Za-z0-9_-]{43}$/.test(device)) {\n const bytes = crypto.getRandomValues(new Uint8Array(32));\n device = \"ss_fcd_\" + btoa(String.fromCharCode(...bytes)).replaceAll(\"+\", \"-\").replaceAll(\"/\", \"_\").replace(/=+$/, \"\");\n try { sessionStorage.setItem(\"sightspool-widget-device:\" + config.key, device); } catch {}\n }\n const r: Runtime = {\n key: config.key, endpoint: url.origin, audience: config.audience, identified: false, paused: false,\n disposed: false, generation: 0, device, offer: null, button: null,\n popup: null, pending: null, timer: 0, visibility: () => {}, status: \"signed_out\",\n };\n browser[slot] = r;\n r.visibility = () => {\n try {\n if (document.visibilityState === \"visible\") void check(r);\n else { invalidate(r); status(r, r.paused ? \"paused\" : eligible(r) ? \"unavailable\" : \"signed_out\"); }\n } catch {}\n };\n document.addEventListener(\"visibilitychange\", r.visibility);\n r.timer = window.setInterval(() => { void check(r); }, 15_000);\n if (eligible(r)) void check(r);\n } catch { /* Never throw into the host application. */ }\n}\n\n/** Only the presence of an ID is retained. The ID itself is never stored or sent. */\nexport function identify(userId: string | null | undefined): void {\n try {\n const r = current();\n if (!r) return;\n const identified = typeof userId === \"string\" && userId.trim().length > 0;\n if (!identified && !r.identified) { if (!r.paused && eligible(r)) void check(r); return; }\n invalidate(r);\n r.identified = identified;\n status(r, r.paused ? \"paused\" : eligible(r) ? \"unavailable\" : \"signed_out\");\n if (eligible(r) && !r.paused) void check(r);\n } catch {}\n}\nexport function pause(): void {\n try { const r = current(); if (r) { r.paused = true; invalidate(r); status(r, \"paused\"); } } catch {}\n}\nexport function resume(): void {\n try { const r = current(); if (r) { r.paused = false; if (!eligible(r)) status(r, \"signed_out\"); void check(r); } } catch {}\n}\n/** Stop recruitment and remove listeners/UI. Does not end an already opened interview. */\nexport function destroy(): void {\n try {\n const r = current();\n if (!r) return;\n r.disposed = true; invalidate(r);\n window.clearInterval(r.timer);\n document.removeEventListener(\"visibilitychange\", r.visibility);\n delete host()![slot];\n } catch {}\n}\nexport function getStatus(): ResearchStatus {\n try { return current()?.status ?? \"not_initialized\"; } catch { return \"error\"; }\n}\n",
5
+ "src/research.ts": "export type SightspoolConfig = {\n /** Public workspace widget key from Go live (UUID, not an old pk_live key). */\n key: string;\n /** Match the approved research audience. No default that widens recruitment. */\n audience: \"all_visitors\" | \"signed_in\";\n /** Sightspool origin. Defaults to the hosted application. */\n endpoint?: string;\n};\nexport type ResearchStatus =\n | \"not_initialized\" | \"signed_out\" | \"paused\" | \"checking\"\n | \"unavailable\" | \"available\" | \"error\";\n\ntype Runtime = {\n key: string; endpoint: string; audience: SightspoolConfig[\"audience\"]; identified: boolean; paused: boolean;\n disposed: boolean; generation: number; device: string; offer: string | null;\n button: HTMLButtonElement | null; panel: HTMLElement | null; frame: HTMLIFrameElement | null;\n expanded: boolean; message: ((event: MessageEvent) => void) | null;\n pending: AbortController | null; timer: number; visibility: () => void;\n status: ResearchStatus;\n};\nconst slot = Symbol.for(\"sightspool.research.runtime.v1\");\ntype Host = Window & { [slot]?: Runtime };\nconst host = (): Host | null => typeof window === \"undefined\" ? null : window as Host;\nconst current = () => host()?.[slot];\nconst eligible = (r: Runtime) => r.audience === \"all_visitors\" || r.identified;\n\nfunction remove(r: Runtime) {\n // An opened interview owns its lifetime. Recruitment changes cannot end it.\n if (r.frame) return;\n r.button?.remove(); r.button = null; r.offer = null;\n}\nfunction invalidate(r: Runtime) {\n r.generation += 1; r.pending?.abort(); r.pending = null; remove(r);\n}\nfunction status(r: Runtime, value: ResearchStatus) { r.status = value; }\n\nfunction expand(r: Runtime, expanded: boolean) {\n if (!r.panel || !r.frame || !r.button) return;\n r.expanded = expanded;\n r.panel.hidden = !expanded;\n r.panel.style.display = expanded ? \"flex\" : \"none\";\n r.button.hidden = expanded;\n r.button.setAttribute(\"aria-expanded\", String(expanded));\n if (expanded) {\n r.panel.querySelector<HTMLButtonElement>(\"button\")?.focus();\n } else r.button.focus();\n}\nfunction openPanel(r: Runtime) {\n if (r.frame) { expand(r, true); return; }\n if (!r.offer || !r.button) return;\n const panel = document.createElement(\"section\");\n panel.id = \"sightspool-interview-panel\";\n panel.setAttribute(\"role\", \"dialog\");\n panel.setAttribute(\"aria-label\", \"Sightspool interview\");\n panel.style.cssText = \"position:fixed;bottom:16px;right:16px;z-index:2147483001;box-sizing:border-box;width:420px;max-width:calc(100% - 32px);height:680px;max-height:calc(100dvh - 32px);border:1px solid #e6dfe3;border-radius:20px;background:#fff;color:#262024;box-shadow:0 12px 50px #0003;overflow:hidden;font:14px system-ui;display:flex;flex-direction:column\";\n const header = document.createElement(\"div\");\n header.style.cssText = \"display:flex;align-items:center;justify-content:space-between;gap:12px;padding:14px 16px;border-bottom:1px solid #eee;flex-shrink:0\";\n const title = document.createElement(\"strong\"); title.textContent = \"Sightspool\";\n const minimize = document.createElement(\"button\");\n minimize.type = \"button\"; minimize.textContent = \"Minimize\";\n minimize.style.cssText = \"font:inherit;color:inherit;background:#fff;border:1px solid #d6cbd1;border-radius:8px;padding:8px 12px;cursor:pointer\";\n const hide = () => { expand(r, false); };\n minimize.onclick = hide;\n header.appendChild(title); header.appendChild(minimize); panel.appendChild(header);\n const frame = document.createElement(\"iframe\");\n frame.title = \"Sightspool research conversation\";\n frame.allow = \"microphone; autoplay\";\n frame.setAttribute(\"sandbox\", \"allow-scripts allow-same-origin allow-forms\");\n frame.referrerPolicy = \"no-referrer\";\n frame.style.cssText = \"display:block;width:100%;flex:1;min-height:0;border:0;background:#fff\";\n const url = new URL(r.endpoint + \"/interview-widget\");\n url.searchParams.set(\"key\", r.key);\n url.hash = new URLSearchParams({ offer: r.offer, device: r.device }).toString();\n frame.src = url.href;\n panel.appendChild(frame);\n panel.onkeydown = (event) => { if (event.key === \"Escape\") { event.preventDefault(); hide(); } };\n r.panel = panel; r.frame = frame;\n r.message = (event) => {\n // Only this exact embedded document may control its presentation. No interview\n // content, capabilities or account data cross the parent messaging boundary.\n if (event.source !== frame.contentWindow || event.origin !== r.endpoint || event.data?.type !== \"sightspool:panel:minimize\") return;\n hide();\n };\n window.addEventListener(\"message\", r.message);\n r.button.textContent = \"Return to interview\";\n r.button.setAttribute(\"aria-label\", \"Sightspool: return to your interview\");\n r.button.setAttribute(\"aria-controls\", panel.id);\n r.button.onclick = () => { expand(r, true); };\n document.body.appendChild(panel);\n expand(r, true);\n}\n\nasync function check(r: Runtime) {\n if (r.frame || r.disposed || r.paused || !eligible(r) || r.pending || document.visibilityState !== \"visible\") return;\n const generation = r.generation;\n const request = new AbortController();\n r.pending = request;\n const timeout = window.setTimeout(() => request.abort(), 10_000);\n if (!r.button) status(r, \"checking\");\n try {\n const response = await fetch(r.endpoint + \"/widget-offer\", {\n method: \"POST\", credentials: \"omit\", cache: \"no-store\", referrerPolicy: \"no-referrer\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ operation: \"offer\", key: r.key, device: r.device }),\n signal: request.signal,\n });\n if (!response.ok) throw Error(\"offer unavailable\");\n const result = await response.json();\n if (r.disposed || generation !== r.generation || r.paused || !eligible(r)) return;\n if (result.available !== true || typeof result.offer !== \"string\" || !result.offer) {\n remove(r); status(r, \"unavailable\"); return;\n }\n r.offer = result.offer;\n status(r, \"available\");\n if (r.button) return;\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.textContent = \"Share your experience · 5 min\";\n button.setAttribute(\"aria-label\", \"Sightspool: join a five-minute user interview\");\n button.style.cssText = \"position:fixed;bottom:20px;right:20px;z-index:2147483000;padding:14px 18px;border:0;border-radius:999px;background:#ad1668;color:white;font:500 14px system-ui;box-shadow:0 8px 30px #0003;cursor:pointer;max-width:calc(100vw - 40px)\";\n button.onclick = () => {\n try {\n if (r.disposed || r.paused || !eligible(r) || !r.offer) return;\n openPanel(r);\n } catch { /* Host pages remain usable if embedding is blocked. */ }\n };\n r.button = button;\n document.body.appendChild(button);\n } catch {\n if (!r.disposed && generation === r.generation) { remove(r); status(r, \"error\"); }\n } finally {\n window.clearTimeout(timeout);\n if (r.pending === request) r.pending = null;\n }\n}\n\n/** Start research for the chosen audience. Signed-in-only waits for identify(). */\nexport function init(config: SightspoolConfig): void {\n try {\n const browser = host();\n if (!browser || !config || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(config.key)) return;\n if (config.audience !== \"all_visitors\" && config.audience !== \"signed_in\") return;\n const url = new URL(config.endpoint || \"https://www.sightspool.com\");\n if (url.protocol !== \"https:\" && !(url.protocol === \"http:\" && [\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(url.hostname))) return;\n if (url.username || url.password) return;\n const previous = current();\n if (previous && previous.key === config.key && previous.endpoint === url.origin && previous.audience === config.audience) return;\n // A second loader/workspace must not replace a participant's open interview.\n if (previous?.frame) return;\n if (previous) destroy();\n let device = \"\";\n try { device = sessionStorage.getItem(\"sightspool-widget-device:\" + config.key) || \"\"; } catch {}\n if (!/^ss_fcd_[A-Za-z0-9_-]{43}$/.test(device)) {\n const bytes = crypto.getRandomValues(new Uint8Array(32));\n device = \"ss_fcd_\" + btoa(String.fromCharCode(...bytes)).replaceAll(\"+\", \"-\").replaceAll(\"/\", \"_\").replace(/=+$/, \"\");\n try { sessionStorage.setItem(\"sightspool-widget-device:\" + config.key, device); } catch {}\n }\n const r: Runtime = {\n key: config.key, endpoint: url.origin, audience: config.audience, identified: false, paused: false,\n disposed: false, generation: 0, device, offer: null, button: null,\n panel: null, frame: null, expanded: false, message: null, pending: null, timer: 0, visibility: () => {}, status: \"signed_out\",\n };\n browser[slot] = r;\n r.visibility = () => {\n try {\n if (document.visibilityState === \"visible\") void check(r);\n else { invalidate(r); status(r, r.paused ? \"paused\" : eligible(r) ? \"unavailable\" : \"signed_out\"); }\n } catch {}\n };\n document.addEventListener(\"visibilitychange\", r.visibility);\n r.timer = window.setInterval(() => { void check(r); }, 15_000);\n if (eligible(r)) void check(r);\n } catch { /* Never throw into the host application. */ }\n}\n\n/** Only the presence of an ID is retained. The ID itself is never stored or sent. */\nexport function identify(userId: string | null | undefined): void {\n try {\n const r = current();\n if (!r) return;\n const identified = typeof userId === \"string\" && userId.trim().length > 0;\n if (!identified && !r.identified) { if (!r.paused && eligible(r)) void check(r); return; }\n invalidate(r);\n r.identified = identified;\n status(r, r.paused ? \"paused\" : eligible(r) ? \"unavailable\" : \"signed_out\");\n if (eligible(r) && !r.paused) void check(r);\n } catch {}\n}\nexport function pause(): void {\n try { const r = current(); if (r) { r.paused = true; invalidate(r); status(r, \"paused\"); } } catch {}\n}\nexport function resume(): void {\n try { const r = current(); if (r) { r.paused = false; if (!eligible(r)) status(r, \"signed_out\"); void check(r); } } catch {}\n}\n/** Stop recruitment and remove listeners/UI. Does not end an already opened interview. */\nexport function destroy(): void {\n try {\n const r = current();\n if (!r) return;\n r.disposed = true; invalidate(r);\n window.clearInterval(r.timer);\n document.removeEventListener(\"visibilitychange\", r.visibility);\n // Keep the session panel and its launcher reachable until the page is left.\n // Minimize is presentation only; end/withdraw remain explicit inside the frame.\n if (!r.frame) delete host()![slot];\n } catch {}\n}\nexport function getStatus(): ResearchStatus {\n try { return current()?.status ?? \"not_initialized\"; } catch { return \"error\"; }\n}\n",
6
6
  "tsup.config.ts": "import { defineConfig } from \"tsup\";\n\n// npm (ESM/CJS/types) and the script-tag bundle share the research runtime.\n// Parked capture modules are intentionally absent from both dependency graphs.\nexport default defineConfig([\n {\n entry: { index: \"src/index.ts\" },\n format: [\"esm\", \"cjs\"],\n dts: true,\n sourcemap: true,\n clean: true,\n treeshake: true,\n minify: false,\n target: \"es2018\",\n },\n {\n entry: { sdk: \"src/browser.ts\" },\n format: [\"iife\"],\n globalName: \"Sightspool\",\n sourcemap: true,\n minify: true,\n treeshake: true,\n target: \"es2018\",\n },\n]);\n",
7
- "package.json": "{\n \"name\": \"@sightspool/sdk\",\n \"version\": \"0.4.0\",\n \"description\": \"Sightspool research SDK \\u2014 invite website visitors and signed-in users into approved user research.\",\n \"type\": \"module\",\n \"main\": \"./dist/index.cjs\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\",\n \"require\": \"./dist/index.cjs\"\n }\n },\n \"files\": [\n \"dist\",\n \"README.md\",\n \"LICENSE\",\n \"NOTICE\",\n \"site\",\n \"SECURITY.md\",\n \"CHANGELOG.md\"\n ],\n \"sideEffects\": false,\n \"license\": \"Apache-2.0\",\n \"author\": \"Sightspool\",\n \"homepage\": \"https://www.sightspool.com/sdk\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/sightspool/sdk.git\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/sightspool/sdk/issues\"\n },\n \"keywords\": [\n \"sightspool\",\n \"user-research\",\n \"research\",\n \"interviews\",\n \"sdk\"\n ],\n \"engines\": {\n \"node\": \">=18\"\n },\n \"packageManager\": \"pnpm@9.15.0\",\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"scripts\": {\n \"build\": \"tsup && node scripts/build-release.mjs\",\n \"type-check\": \"tsc --noEmit\",\n \"test\": \"tsc --noEmit && node --test __tests__/*.test.ts\",\n \"prepublishOnly\": \"pnpm run build\",\n \"test:release\": \"node scripts/verify-release.mjs && node --test scripts/release.test.mjs\",\n \"build:site\": \"node scripts/stage-site.mjs\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^24.10.4\",\n \"tsup\": \"^8.5.0\",\n \"typescript\": \"^5.9.3\"\n }\n}\n",
7
+ "package.json": "{\n \"name\": \"@sightspool/sdk\",\n \"version\": \"0.4.1\",\n \"description\": \"Sightspool research SDK \\u2014 invite website visitors and signed-in users into approved user research.\",\n \"type\": \"module\",\n \"main\": \"./dist/index.cjs\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\",\n \"require\": \"./dist/index.cjs\"\n }\n },\n \"files\": [\n \"dist\",\n \"README.md\",\n \"LICENSE\",\n \"NOTICE\",\n \"site\",\n \"SECURITY.md\",\n \"CHANGELOG.md\"\n ],\n \"sideEffects\": false,\n \"license\": \"Apache-2.0\",\n \"author\": \"Sightspool\",\n \"homepage\": \"https://www.sightspool.com/sdk\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/sightspool/sdk.git\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/sightspool/sdk/issues\"\n },\n \"keywords\": [\n \"sightspool\",\n \"user-research\",\n \"research\",\n \"interviews\",\n \"sdk\"\n ],\n \"engines\": {\n \"node\": \">=18\"\n },\n \"packageManager\": \"pnpm@9.15.0\",\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"scripts\": {\n \"build\": \"tsup && node scripts/build-release.mjs\",\n \"type-check\": \"tsc --noEmit\",\n \"test\": \"tsc --noEmit && node --test __tests__/*.test.ts\",\n \"prepublishOnly\": \"pnpm run build\",\n \"test:release\": \"node scripts/verify-release.mjs && node --test scripts/release.test.mjs\",\n \"build:site\": \"node scripts/stage-site.mjs\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^24.10.4\",\n \"tsup\": \"^8.5.0\",\n \"typescript\": \"^5.9.3\"\n }\n}\n",
8
8
  "pnpm-lock.yaml": "lockfileVersion: '9.0'\n\nsettings:\n autoInstallPeers: true\n excludeLinksFromLockfile: false\n\nimporters:\n\n .:\n devDependencies:\n '@types/node':\n specifier: ^24.10.4\n version: 24.13.1\n tsup:\n specifier: ^8.5.0\n version: 8.5.1(typescript@5.9.3)\n typescript:\n specifier: ^5.9.3\n version: 5.9.3\n\n packages/react:\n devDependencies:\n '@sightspool/sdk':\n specifier: workspace:^\n version: link:../..\n '@types/react':\n specifier: ^19.0.0\n version: 19.2.17\n react:\n specifier: ^19.0.0\n version: 19.2.7\n tsup:\n specifier: ^8.5.0\n version: 8.5.1(typescript@5.9.3)\n typescript:\n specifier: ^5.9.3\n version: 5.9.3\n\npackages:\n\n '@esbuild/aix-ppc64@0.27.7':\n resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==}\n engines: {node: '>=18'}\n cpu: [ppc64]\n os: [aix]\n\n '@esbuild/android-arm64@0.27.7':\n resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==}\n engines: {node: '>=18'}\n cpu: [arm64]\n os: [android]\n\n '@esbuild/android-arm@0.27.7':\n resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==}\n engines: {node: '>=18'}\n cpu: [arm]\n os: [android]\n\n '@esbuild/android-x64@0.27.7':\n resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==}\n engines: {node: '>=18'}\n cpu: [x64]\n os: [android]\n\n '@esbuild/darwin-arm64@0.27.7':\n resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==}\n engines: {node: '>=18'}\n cpu: [arm64]\n os: [darwin]\n\n '@esbuild/darwin-x64@0.27.7':\n resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==}\n engines: {node: '>=18'}\n cpu: [x64]\n os: [darwin]\n\n '@esbuild/freebsd-arm64@0.27.7':\n resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==}\n engines: {node: '>=18'}\n cpu: [arm64]\n os: [freebsd]\n\n '@esbuild/freebsd-x64@0.27.7':\n resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==}\n engines: {node: '>=18'}\n cpu: [x64]\n os: [freebsd]\n\n '@esbuild/linux-arm64@0.27.7':\n resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==}\n engines: {node: '>=18'}\n cpu: [arm64]\n os: [linux]\n\n '@esbuild/linux-arm@0.27.7':\n resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==}\n engines: {node: '>=18'}\n cpu: [arm]\n os: [linux]\n\n '@esbuild/linux-ia32@0.27.7':\n resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==}\n engines: {node: '>=18'}\n cpu: [ia32]\n os: [linux]\n\n '@esbuild/linux-loong64@0.27.7':\n resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==}\n engines: {node: '>=18'}\n cpu: [loong64]\n os: [linux]\n\n '@esbuild/linux-mips64el@0.27.7':\n resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==}\n engines: {node: '>=18'}\n cpu: [mips64el]\n os: [linux]\n\n '@esbuild/linux-ppc64@0.27.7':\n resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==}\n engines: {node: '>=18'}\n cpu: [ppc64]\n os: [linux]\n\n '@esbuild/linux-riscv64@0.27.7':\n resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==}\n engines: {node: '>=18'}\n cpu: [riscv64]\n os: [linux]\n\n '@esbuild/linux-s390x@0.27.7':\n resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==}\n engines: {node: '>=18'}\n cpu: [s390x]\n os: [linux]\n\n '@esbuild/linux-x64@0.27.7':\n resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==}\n engines: {node: '>=18'}\n cpu: [x64]\n os: [linux]\n\n '@esbuild/netbsd-arm64@0.27.7':\n resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==}\n engines: {node: '>=18'}\n cpu: [arm64]\n os: [netbsd]\n\n '@esbuild/netbsd-x64@0.27.7':\n resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==}\n engines: {node: '>=18'}\n cpu: [x64]\n os: [netbsd]\n\n '@esbuild/openbsd-arm64@0.27.7':\n resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==}\n engines: {node: '>=18'}\n cpu: [arm64]\n os: [openbsd]\n\n '@esbuild/openbsd-x64@0.27.7':\n resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==}\n engines: {node: '>=18'}\n cpu: [x64]\n os: [openbsd]\n\n '@esbuild/openharmony-arm64@0.27.7':\n resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==}\n engines: {node: '>=18'}\n cpu: [arm64]\n os: [openharmony]\n\n '@esbuild/sunos-x64@0.27.7':\n resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==}\n engines: {node: '>=18'}\n cpu: [x64]\n os: [sunos]\n\n '@esbuild/win32-arm64@0.27.7':\n resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==}\n engines: {node: '>=18'}\n cpu: [arm64]\n os: [win32]\n\n '@esbuild/win32-ia32@0.27.7':\n resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==}\n engines: {node: '>=18'}\n cpu: [ia32]\n os: [win32]\n\n '@esbuild/win32-x64@0.27.7':\n resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==}\n engines: {node: '>=18'}\n cpu: [x64]\n os: [win32]\n\n '@jridgewell/gen-mapping@0.3.13':\n resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}\n\n '@jridgewell/resolve-uri@3.1.2':\n resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}\n engines: {node: '>=6.0.0'}\n\n '@jridgewell/sourcemap-codec@1.5.5':\n resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}\n\n '@jridgewell/trace-mapping@0.3.31':\n resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}\n\n '@rollup/rollup-android-arm-eabi@4.61.1':\n resolution: {integrity: sha512-JnBB8MdXj45cajvTuO5FmPlvFVJRQgvrz1uSEl3NwqFnReAPGwb8EanbGi4z2nRaqLzjJSv5/JmycoTKlRZxHA==}\n cpu: [arm]\n os: [android]\n\n '@rollup/rollup-android-arm64@4.61.1':\n resolution: {integrity: sha512-Jx2g7iSjw4AOT0HDPHM9RV3GNjRXwybWtSFZiZAYUTjUwjVrYIwq3kBf+LnhqJlzXFAqTAh2F7IGI+O568exPw==}\n cpu: [arm64]\n os: [android]\n\n '@rollup/rollup-darwin-arm64@4.61.1':\n resolution: {integrity: sha512-0F1L/Z3Eqv8mT2n3dCpeO8GcTvHvVqkP5/t6DMsn0KzhYVcg+s7Ncl5DS8qjKYEeio6Az0Gt6nyBORay5qIlCA==}\n cpu: [arm64]\n os: [darwin]\n\n '@rollup/rollup-darwin-x64@4.61.1':\n resolution: {integrity: sha512-qLttcH871ujY4YcVfUSShhOw+CsoTatYz8gRbHO7Bb92QH059/P0y5do1KMs41fY0BpD2x4AJH/gID0zFiqVKQ==}\n cpu: [x64]\n os: [darwin]\n\n '@rollup/rollup-freebsd-arm64@4.61.1':\n resolution: {integrity: sha512-fUI4RapGE0Oh3mb8mgfvC1O2nU1RpDZUKnDQm3xB1Ipg7C2wTs5Kstz7G2uWK99a8S2yTMq8/P4uycwNa0nJyw==}\n cpu: [arm64]\n os: [freebsd]\n\n '@rollup/rollup-freebsd-x64@4.61.1':\n resolution: {integrity: sha512-H5YrdvJaDtI/U9/emrD4b++xkvp3y/JvOe4rizHbxvkyMfRS/CiRYdji+Pl8D0brEaNFWUh1drQxgAGIl6Xudw==}\n cpu: [x64]\n os: [freebsd]\n\n '@rollup/rollup-linux-arm-gnueabihf@4.61.1':\n resolution: {integrity: sha512-Q8CBCCQtDFrYtXoeUXSrnFXKOnyUhx6bz+SkL6A0E7V8kAiCJ5pamq1WtbfpVGhR5TSpXY6ak3avmDc5fHTyJA==}\n cpu: [arm]\n os: [linux]\n\n '@rollup/rollup-linux-arm-musleabihf@4.61.1':\n resolution: {integrity: sha512-nwnhk1581l0FBVellGcVCAT0Oi06onEA3WB53sf01VO3I0UPBkMH9sXONYME2K0ovXcNayJfNtHfm6mpJElatQ==}\n cpu: [arm]\n os: [linux]\n\n '@rollup/rollup-linux-arm64-gnu@4.61.1':\n resolution: {integrity: sha512-x5Xr49hwt3hdW75UOZm3395YwwzPyauktslv29KpWL/T+vVAzoT3azLcTWv0eMciBNrx+DYjH4paehHoLpPvpg==}\n cpu: [arm64]\n os: [linux]\n\n '@rollup/rollup-linux-arm64-musl@4.61.1':\n resolution: {integrity: sha512-unMS3H73DpaoPyyEVPjGKleM/s0mkmsauTENpw4INQY8y4+IuLNjkueQ5QCtC0D3N38Y38yhAU8OoZ20S2Tm6w==}\n cpu: [arm64]\n os: [linux]\n\n '@rollup/rollup-linux-loong64-gnu@4.61.1':\n resolution: {integrity: sha512-zNZzGRnAhwjFEYmvphJRV5XaQGjs62cCmeYYHUT//NbvEnHauw+I85nGG+SiVg5ld4GX8D1IbKIX+ozITQnhMQ==}\n cpu: [loong64]\n os: [linux]\n\n '@rollup/rollup-linux-loong64-musl@4.61.1':\n resolution: {integrity: sha512-LdpWGL8X209B2SIvWjqlc8VZgM6PKfontSerGepuldQmHYrAOtnMCXeJkxXGbC+PPZVOuu5czJo7fNV6aeW8rQ==}\n cpu: [loong64]\n os: [linux]\n\n '@rollup/rollup-linux-ppc64-gnu@4.61.1':\n resolution: {integrity: sha512-EC5kTtNaNGOmbMGqar8dvJy6y/hg99GAwjfBz++pxZhQATXGcRjd6c5en5wcbru0vkRmiMGsQKdMJOOf6sza4g==}\n cpu: [ppc64]\n os: [linux]\n\n '@rollup/rollup-linux-ppc64-musl@4.61.1':\n resolution: {integrity: sha512-8hiwp6D4acEcNK78I4rP0/XtS1sknWIAMJBPdR4l6zUtyTm5KiTDr5bXmWt4foY7nAN7AThDHgkLIEZOWKbzWw==}\n cpu: [ppc64]\n os: [linux]\n\n '@rollup/rollup-linux-riscv64-gnu@4.61.1':\n resolution: {integrity: sha512-10dh/h/BqA7DuMPWSxkR8uks18FRwnwOEqr5zOTEl+NOwP/OMzKX8OFR/Of9xxDA7D5qef1Nzar5WDD2kCCr1g==}\n cpu: [riscv64]\n os: [linux]\n\n '@rollup/rollup-linux-riscv64-musl@4.61.1':\n resolution: {integrity: sha512-YKJ5lg35DP17gcAOggnihe+APw9HLyj1Xn7gsmGumBJAUDa6NGXNixJzmkWLhcK9TOuuyQjdamzvJefkO7qHZQ==}\n cpu: [riscv64]\n os: [linux]\n\n '@rollup/rollup-linux-s390x-gnu@4.61.1':\n resolution: {integrity: sha512-Mlil5G2Jj6a7B3LWGctg+XPL9vdXYuzCtNXfxOQ0nPjc2m6ueUktocPGH9bnAM0bNRKb/bAWTujUU7IJQdQA+g==}\n cpu: [s390x]\n os: [linux]\n\n '@rollup/rollup-linux-x64-gnu@4.61.1':\n resolution: {integrity: sha512-bVWIOIk6pV01p4CdUbPP7CJ/434z+OooYjDuFcR+44N35YvKUC66G8MGnvcWx5mWKW3g61J+t74l3Kj15Kwn2Q==}\n cpu: [x64]\n os: [linux]\n\n '@rollup/rollup-linux-x64-musl@4.61.1':\n resolution: {integrity: sha512-qy5pBvZbqNFheBz61R1rzsezjm0J7O2oNGoWtGoY89SZYLUfxAJTBAqDChqAIdB4rCiIbi9nF7yZ83GnNiLwSw==}\n cpu: [x64]\n os: [linux]\n\n '@rollup/rollup-openbsd-x64@4.61.1':\n resolution: {integrity: sha512-E83TXjI4zm0+5f2qO+UOudaCYIhYwpJ5jq6YCZNIZ+6CbfhKrkAGezeiASBL9ElxAxFsRS9ZhESv8mfnj6TKeg==}\n cpu: [x64]\n os: [openbsd]\n\n '@rollup/rollup-openharmony-arm64@4.61.1':\n resolution: {integrity: sha512-fbWnKqVkjrJN38vNe3ahkbk6iejS/3b0Nt7EEtPpE6RBacZcGXNKbzfHN3GUUlXOPghUg0j6XUGrtjX9z1sIvA==}\n cpu: [arm64]\n os: [openharmony]\n\n '@rollup/rollup-win32-arm64-msvc@4.61.1':\n resolution: {integrity: sha512-ArMl38iVAbk0New1ogihQNY6iphLi4ZaRsa037gUzv5yeKPY8TD3Dmy4x2RNC1VztU/uqm+G+/RwFrSka3Oy2g==}\n cpu: [arm64]\n os: [win32]\n\n '@rollup/rollup-win32-ia32-msvc@4.61.1':\n resolution: {integrity: sha512-0mYtjHS9ucAbcATycCNK9IGBk/cCe/ma7EmSLGZdsxnOA8cjRIyU04wDpVAD9NiOfLUR9KTxdiO53uOkherqjQ==}\n cpu: [ia32]\n os: [win32]\n\n '@rollup/rollup-win32-x64-gnu@4.61.1':\n resolution: {integrity: sha512-gK1iCEPfpoSG9wfBihXxvBMi8ZfcWffYkEsC/Eih+iFENTaewvNcrEQ69lIOWYO5pePHKLHHO7nq5AILGO/HQQ==}\n cpu: [x64]\n os: [win32]\n\n '@rollup/rollup-win32-x64-msvc@4.61.1':\n resolution: {integrity: sha512-X+zaP2x+j4RXGfbp/seSoRHWnPxzApilDszisZxbYH5C/jTxFhCtDNdPGZb9lJyYPs24wGxruPF7Y+sIXt9Gzw==}\n cpu: [x64]\n os: [win32]\n\n '@types/estree@1.0.9':\n resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}\n\n '@types/node@24.13.1':\n resolution: {integrity: sha512-RSpUJGmvsJ1ZeBehQZFhIdpsz+bIpES0nIQXko4Ybq+N+kX6XvOq3Jo+iJ82FWLdblFq85AsMikd3m35jgezYg==}\n\n '@types/react@19.2.17':\n resolution: {integrity: sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==}\n\n acorn@8.16.0:\n resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==}\n engines: {node: '>=0.4.0'}\n hasBin: true\n\n any-promise@1.3.0:\n resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}\n\n bundle-require@5.1.0:\n resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==}\n engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}\n peerDependencies:\n esbuild: '>=0.18'\n\n cac@6.7.14:\n resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}\n engines: {node: '>=8'}\n\n chokidar@4.0.3:\n resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}\n engines: {node: '>= 14.16.0'}\n\n commander@4.1.1:\n resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}\n engines: {node: '>= 6'}\n\n confbox@0.1.8:\n resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==}\n\n consola@3.4.2:\n resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==}\n engines: {node: ^14.18.0 || >=16.10.0}\n\n csstype@3.2.3:\n resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}\n\n debug@4.4.3:\n resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}\n engines: {node: '>=6.0'}\n peerDependencies:\n supports-color: '*'\n peerDependenciesMeta:\n supports-color:\n optional: true\n\n esbuild@0.27.7:\n resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==}\n engines: {node: '>=18'}\n hasBin: true\n\n fdir@6.5.0:\n resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}\n engines: {node: '>=12.0.0'}\n peerDependencies:\n picomatch: ^3 || ^4\n peerDependenciesMeta:\n picomatch:\n optional: true\n\n fix-dts-default-cjs-exports@1.0.1:\n resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==}\n\n fsevents@2.3.3:\n resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}\n engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}\n os: [darwin]\n\n joycon@3.1.1:\n resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==}\n engines: {node: '>=10'}\n\n lilconfig@3.1.3:\n resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==}\n engines: {node: '>=14'}\n\n lines-and-columns@1.2.4:\n resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}\n\n load-tsconfig@0.2.5:\n resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==}\n engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}\n\n magic-string@0.30.21:\n resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}\n\n mlly@1.8.2:\n resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==}\n\n ms@2.1.3:\n resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}\n\n mz@2.7.0:\n resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}\n\n object-assign@4.1.1:\n resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}\n engines: {node: '>=0.10.0'}\n\n pathe@2.0.3:\n resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}\n\n picocolors@1.1.1:\n resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}\n\n picomatch@4.0.4:\n resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==}\n engines: {node: '>=12'}\n\n pirates@4.0.7:\n resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==}\n engines: {node: '>= 6'}\n\n pkg-types@1.3.1:\n resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}\n\n postcss-load-config@6.0.1:\n resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==}\n engines: {node: '>= 18'}\n peerDependencies:\n jiti: '>=1.21.0'\n postcss: '>=8.0.9'\n tsx: ^4.8.1\n yaml: ^2.4.2\n peerDependenciesMeta:\n jiti:\n optional: true\n postcss:\n optional: true\n tsx:\n optional: true\n yaml:\n optional: true\n\n react@19.2.7:\n resolution: {integrity: sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==}\n engines: {node: '>=0.10.0'}\n\n readdirp@4.1.2:\n resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}\n engines: {node: '>= 14.18.0'}\n\n resolve-from@5.0.0:\n resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}\n engines: {node: '>=8'}\n\n rollup@4.61.1:\n resolution: {integrity: sha512-I4KW6iuRpuu2uHBLraZ1wNZe0DP7lnRha+VJ9tNaYVaVgKhW0aI3h4RYnoRPeql0flHm/Co55b7snEDcOfOJrA==}\n engines: {node: '>=18.0.0', npm: '>=8.0.0'}\n hasBin: true\n\n source-map@0.7.6:\n resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==}\n engines: {node: '>= 12'}\n\n sucrase@3.35.1:\n resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==}\n engines: {node: '>=16 || 14 >=14.17'}\n hasBin: true\n\n thenify-all@1.6.0:\n resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}\n engines: {node: '>=0.8'}\n\n thenify@3.3.1:\n resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}\n\n tinyexec@0.3.2:\n resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==}\n\n tinyglobby@0.2.17:\n resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}\n engines: {node: '>=12.0.0'}\n\n tree-kill@1.2.2:\n resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}\n hasBin: true\n\n ts-interface-checker@0.1.13:\n resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}\n\n tsup@8.5.1:\n resolution: {integrity: sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==}\n engines: {node: '>=18'}\n hasBin: true\n peerDependencies:\n '@microsoft/api-extractor': ^7.36.0\n '@swc/core': ^1\n postcss: ^8.4.12\n typescript: '>=4.5.0'\n peerDependenciesMeta:\n '@microsoft/api-extractor':\n optional: true\n '@swc/core':\n optional: true\n postcss:\n optional: true\n typescript:\n optional: true\n\n typescript@5.9.3:\n resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}\n engines: {node: '>=14.17'}\n hasBin: true\n\n ufo@1.6.4:\n resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==}\n\n undici-types@7.18.2:\n resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==}\n\nsnapshots:\n\n '@esbuild/aix-ppc64@0.27.7':\n optional: true\n\n '@esbuild/android-arm64@0.27.7':\n optional: true\n\n '@esbuild/android-arm@0.27.7':\n optional: true\n\n '@esbuild/android-x64@0.27.7':\n optional: true\n\n '@esbuild/darwin-arm64@0.27.7':\n optional: true\n\n '@esbuild/darwin-x64@0.27.7':\n optional: true\n\n '@esbuild/freebsd-arm64@0.27.7':\n optional: true\n\n '@esbuild/freebsd-x64@0.27.7':\n optional: true\n\n '@esbuild/linux-arm64@0.27.7':\n optional: true\n\n '@esbuild/linux-arm@0.27.7':\n optional: true\n\n '@esbuild/linux-ia32@0.27.7':\n optional: true\n\n '@esbuild/linux-loong64@0.27.7':\n optional: true\n\n '@esbuild/linux-mips64el@0.27.7':\n optional: true\n\n '@esbuild/linux-ppc64@0.27.7':\n optional: true\n\n '@esbuild/linux-riscv64@0.27.7':\n optional: true\n\n '@esbuild/linux-s390x@0.27.7':\n optional: true\n\n '@esbuild/linux-x64@0.27.7':\n optional: true\n\n '@esbuild/netbsd-arm64@0.27.7':\n optional: true\n\n '@esbuild/netbsd-x64@0.27.7':\n optional: true\n\n '@esbuild/openbsd-arm64@0.27.7':\n optional: true\n\n '@esbuild/openbsd-x64@0.27.7':\n optional: true\n\n '@esbuild/openharmony-arm64@0.27.7':\n optional: true\n\n '@esbuild/sunos-x64@0.27.7':\n optional: true\n\n '@esbuild/win32-arm64@0.27.7':\n optional: true\n\n '@esbuild/win32-ia32@0.27.7':\n optional: true\n\n '@esbuild/win32-x64@0.27.7':\n optional: true\n\n '@jridgewell/gen-mapping@0.3.13':\n dependencies:\n '@jridgewell/sourcemap-codec': 1.5.5\n '@jridgewell/trace-mapping': 0.3.31\n\n '@jridgewell/resolve-uri@3.1.2': {}\n\n '@jridgewell/sourcemap-codec@1.5.5': {}\n\n '@jridgewell/trace-mapping@0.3.31':\n dependencies:\n '@jridgewell/resolve-uri': 3.1.2\n '@jridgewell/sourcemap-codec': 1.5.5\n\n '@rollup/rollup-android-arm-eabi@4.61.1':\n optional: true\n\n '@rollup/rollup-android-arm64@4.61.1':\n optional: true\n\n '@rollup/rollup-darwin-arm64@4.61.1':\n optional: true\n\n '@rollup/rollup-darwin-x64@4.61.1':\n optional: true\n\n '@rollup/rollup-freebsd-arm64@4.61.1':\n optional: true\n\n '@rollup/rollup-freebsd-x64@4.61.1':\n optional: true\n\n '@rollup/rollup-linux-arm-gnueabihf@4.61.1':\n optional: true\n\n '@rollup/rollup-linux-arm-musleabihf@4.61.1':\n optional: true\n\n '@rollup/rollup-linux-arm64-gnu@4.61.1':\n optional: true\n\n '@rollup/rollup-linux-arm64-musl@4.61.1':\n optional: true\n\n '@rollup/rollup-linux-loong64-gnu@4.61.1':\n optional: true\n\n '@rollup/rollup-linux-loong64-musl@4.61.1':\n optional: true\n\n '@rollup/rollup-linux-ppc64-gnu@4.61.1':\n optional: true\n\n '@rollup/rollup-linux-ppc64-musl@4.61.1':\n optional: true\n\n '@rollup/rollup-linux-riscv64-gnu@4.61.1':\n optional: true\n\n '@rollup/rollup-linux-riscv64-musl@4.61.1':\n optional: true\n\n '@rollup/rollup-linux-s390x-gnu@4.61.1':\n optional: true\n\n '@rollup/rollup-linux-x64-gnu@4.61.1':\n optional: true\n\n '@rollup/rollup-linux-x64-musl@4.61.1':\n optional: true\n\n '@rollup/rollup-openbsd-x64@4.61.1':\n optional: true\n\n '@rollup/rollup-openharmony-arm64@4.61.1':\n optional: true\n\n '@rollup/rollup-win32-arm64-msvc@4.61.1':\n optional: true\n\n '@rollup/rollup-win32-ia32-msvc@4.61.1':\n optional: true\n\n '@rollup/rollup-win32-x64-gnu@4.61.1':\n optional: true\n\n '@rollup/rollup-win32-x64-msvc@4.61.1':\n optional: true\n\n '@types/estree@1.0.9': {}\n\n '@types/node@24.13.1':\n dependencies:\n undici-types: 7.18.2\n\n '@types/react@19.2.17':\n dependencies:\n csstype: 3.2.3\n\n acorn@8.16.0: {}\n\n any-promise@1.3.0: {}\n\n bundle-require@5.1.0(esbuild@0.27.7):\n dependencies:\n esbuild: 0.27.7\n load-tsconfig: 0.2.5\n\n cac@6.7.14: {}\n\n chokidar@4.0.3:\n dependencies:\n readdirp: 4.1.2\n\n commander@4.1.1: {}\n\n confbox@0.1.8: {}\n\n consola@3.4.2: {}\n\n csstype@3.2.3: {}\n\n debug@4.4.3:\n dependencies:\n ms: 2.1.3\n\n esbuild@0.27.7:\n optionalDependencies:\n '@esbuild/aix-ppc64': 0.27.7\n '@esbuild/android-arm': 0.27.7\n '@esbuild/android-arm64': 0.27.7\n '@esbuild/android-x64': 0.27.7\n '@esbuild/darwin-arm64': 0.27.7\n '@esbuild/darwin-x64': 0.27.7\n '@esbuild/freebsd-arm64': 0.27.7\n '@esbuild/freebsd-x64': 0.27.7\n '@esbuild/linux-arm': 0.27.7\n '@esbuild/linux-arm64': 0.27.7\n '@esbuild/linux-ia32': 0.27.7\n '@esbuild/linux-loong64': 0.27.7\n '@esbuild/linux-mips64el': 0.27.7\n '@esbuild/linux-ppc64': 0.27.7\n '@esbuild/linux-riscv64': 0.27.7\n '@esbuild/linux-s390x': 0.27.7\n '@esbuild/linux-x64': 0.27.7\n '@esbuild/netbsd-arm64': 0.27.7\n '@esbuild/netbsd-x64': 0.27.7\n '@esbuild/openbsd-arm64': 0.27.7\n '@esbuild/openbsd-x64': 0.27.7\n '@esbuild/openharmony-arm64': 0.27.7\n '@esbuild/sunos-x64': 0.27.7\n '@esbuild/win32-arm64': 0.27.7\n '@esbuild/win32-ia32': 0.27.7\n '@esbuild/win32-x64': 0.27.7\n\n fdir@6.5.0(picomatch@4.0.4):\n optionalDependencies:\n picomatch: 4.0.4\n\n fix-dts-default-cjs-exports@1.0.1:\n dependencies:\n magic-string: 0.30.21\n mlly: 1.8.2\n rollup: 4.61.1\n\n fsevents@2.3.3:\n optional: true\n\n joycon@3.1.1: {}\n\n lilconfig@3.1.3: {}\n\n lines-and-columns@1.2.4: {}\n\n load-tsconfig@0.2.5: {}\n\n magic-string@0.30.21:\n dependencies:\n '@jridgewell/sourcemap-codec': 1.5.5\n\n mlly@1.8.2:\n dependencies:\n acorn: 8.16.0\n pathe: 2.0.3\n pkg-types: 1.3.1\n ufo: 1.6.4\n\n ms@2.1.3: {}\n\n mz@2.7.0:\n dependencies:\n any-promise: 1.3.0\n object-assign: 4.1.1\n thenify-all: 1.6.0\n\n object-assign@4.1.1: {}\n\n pathe@2.0.3: {}\n\n picocolors@1.1.1: {}\n\n picomatch@4.0.4: {}\n\n pirates@4.0.7: {}\n\n pkg-types@1.3.1:\n dependencies:\n confbox: 0.1.8\n mlly: 1.8.2\n pathe: 2.0.3\n\n postcss-load-config@6.0.1:\n dependencies:\n lilconfig: 3.1.3\n\n react@19.2.7: {}\n\n readdirp@4.1.2: {}\n\n resolve-from@5.0.0: {}\n\n rollup@4.61.1:\n dependencies:\n '@types/estree': 1.0.9\n optionalDependencies:\n '@rollup/rollup-android-arm-eabi': 4.61.1\n '@rollup/rollup-android-arm64': 4.61.1\n '@rollup/rollup-darwin-arm64': 4.61.1\n '@rollup/rollup-darwin-x64': 4.61.1\n '@rollup/rollup-freebsd-arm64': 4.61.1\n '@rollup/rollup-freebsd-x64': 4.61.1\n '@rollup/rollup-linux-arm-gnueabihf': 4.61.1\n '@rollup/rollup-linux-arm-musleabihf': 4.61.1\n '@rollup/rollup-linux-arm64-gnu': 4.61.1\n '@rollup/rollup-linux-arm64-musl': 4.61.1\n '@rollup/rollup-linux-loong64-gnu': 4.61.1\n '@rollup/rollup-linux-loong64-musl': 4.61.1\n '@rollup/rollup-linux-ppc64-gnu': 4.61.1\n '@rollup/rollup-linux-ppc64-musl': 4.61.1\n '@rollup/rollup-linux-riscv64-gnu': 4.61.1\n '@rollup/rollup-linux-riscv64-musl': 4.61.1\n '@rollup/rollup-linux-s390x-gnu': 4.61.1\n '@rollup/rollup-linux-x64-gnu': 4.61.1\n '@rollup/rollup-linux-x64-musl': 4.61.1\n '@rollup/rollup-openbsd-x64': 4.61.1\n '@rollup/rollup-openharmony-arm64': 4.61.1\n '@rollup/rollup-win32-arm64-msvc': 4.61.1\n '@rollup/rollup-win32-ia32-msvc': 4.61.1\n '@rollup/rollup-win32-x64-gnu': 4.61.1\n '@rollup/rollup-win32-x64-msvc': 4.61.1\n fsevents: 2.3.3\n\n source-map@0.7.6: {}\n\n sucrase@3.35.1:\n dependencies:\n '@jridgewell/gen-mapping': 0.3.13\n commander: 4.1.1\n lines-and-columns: 1.2.4\n mz: 2.7.0\n pirates: 4.0.7\n tinyglobby: 0.2.17\n ts-interface-checker: 0.1.13\n\n thenify-all@1.6.0:\n dependencies:\n thenify: 3.3.1\n\n thenify@3.3.1:\n dependencies:\n any-promise: 1.3.0\n\n tinyexec@0.3.2: {}\n\n tinyglobby@0.2.17:\n dependencies:\n fdir: 6.5.0(picomatch@4.0.4)\n picomatch: 4.0.4\n\n tree-kill@1.2.2: {}\n\n ts-interface-checker@0.1.13: {}\n\n tsup@8.5.1(typescript@5.9.3):\n dependencies:\n bundle-require: 5.1.0(esbuild@0.27.7)\n cac: 6.7.14\n chokidar: 4.0.3\n consola: 3.4.2\n debug: 4.4.3\n esbuild: 0.27.7\n fix-dts-default-cjs-exports: 1.0.1\n joycon: 3.1.1\n picocolors: 1.1.1\n postcss-load-config: 6.0.1\n resolve-from: 5.0.0\n rollup: 4.61.1\n source-map: 0.7.6\n sucrase: 3.35.1\n tinyexec: 0.3.2\n tinyglobby: 0.2.17\n tree-kill: 1.2.2\n optionalDependencies:\n typescript: 5.9.3\n transitivePeerDependencies:\n - jiti\n - supports-color\n - tsx\n - yaml\n\n typescript@5.9.3: {}\n\n ufo@1.6.4: {}\n\n undici-types@7.18.2: {}\n",
9
9
  "scripts/build-release.mjs": "import { readFileSync, writeFileSync } from 'node:fs';\nimport { createHash } from 'node:crypto';\nimport { gzipSync } from 'node:zlib';\nimport { execFileSync } from 'node:child_process';\nconst digest = (bytes, algorithm = 'sha256', encoding = 'hex') => createHash(algorithm).update(bytes).digest(encoding);\nconst pkg = JSON.parse(readFileSync('package.json', 'utf8'));\nconst bundle = readFileSync('dist/sdk.global.js');\nconst sourceFiles = ['src/index.ts', 'src/browser.ts', 'src/research.ts', 'tsup.config.ts', 'package.json', 'pnpm-lock.yaml', 'scripts/build-release.mjs'];\nconst files = Object.fromEntries(sourceFiles.map(path => [path, readFileSync(path, 'utf8')]));\nconst source = JSON.stringify({ files }, null, 2) + '\\n';\nconst sha256 = digest(bundle);\nconst sourceCommit = execFileSync('git', ['rev-parse', 'HEAD'], {encoding:'utf8'}).trim();\nconst sourceDirty = Boolean(execFileSync('git', ['status', '--porcelain', '--untracked-files=normal'], {encoding:'utf8'}).trim());\nconst artifactId = digest(JSON.stringify([pkg.version, sha256, digest(source), sourceCommit, sourceDirty]));\nconst manifest = {\n schemaVersion: 1, package: pkg.name, version: pkg.version,\n sourceCommit, sourceDirty, artifactId,\n bundle: { file: 'sdk.global.js', path: `releases/${pkg.version}/${artifactId}/sdk.global.js`, sha256,\n integrity: 'sha384-' + digest(bundle, 'sha384', 'base64'), bytes: bundle.length, gzipBytes: gzipSync(bundle).length },\n source: { file: 'source.json', sha256: digest(source) },\n runtimeDependencies: Object.keys(pkg.dependencies || {}),\n};\nwriteFileSync('dist/source.json', source);\nwriteFileSync('dist/release.json', JSON.stringify(manifest, null, 2) + '\\n');\nconsole.log(`Prepared ${pkg.name} ${pkg.version}: ${bundle.length} bytes, ${manifest.bundle.gzipBytes} gzip bytes. No publication performed.`);\n"
10
10
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sightspool/sdk",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Sightspool research SDK \u2014 invite website visitors and signed-in users into approved user research.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
package/site/demo.js CHANGED
@@ -14,7 +14,16 @@
14
14
  document.getElementById('summary').textContent=`${count} simulated offer requests · test user ID in payload: ${identityLeaked?'YES':'no'}`;
15
15
  return new Response(JSON.stringify({available:true,offer:'synthetic-demo-not-a-valid-offer'}),{headers:{'Content-Type':'application/json'}});
16
16
  };
17
- window.open=()=>{show('SIMULATED HANDOFF: the real flow opens consent and eligibility review. No interview opened.');return null;};
17
+ // A synthetic offer must never load the actual interview service.
18
+ const createElement=document.createElement.bind(document);
19
+ document.createElement=(tag,...args)=>{
20
+ const el=createElement(tag,...args);
21
+ if(tag.toLowerCase()==='iframe') Object.defineProperty(el,'src',{set(){
22
+ el.srcdoc='<main style="font:16px system-ui;padding:24px"><h1>Simulated interview panel</h1><p>The real panel contains eligibility, consent and your interview. No interview, microphone or research request is started in this demo.</p></main>';
23
+ show('SIMULATED PANEL: minimize and reopen keep the same frame. No interview started.');
24
+ }});
25
+ return el;
26
+ };
18
27
  const script=document.createElement('script');script.src=manifest.bundle.path;script.integrity=manifest.bundle.integrity;script.crossOrigin='anonymous';script.referrerPolicy='no-referrer';
19
28
  await new Promise((resolve,reject)=>{script.onload=resolve;script.onerror=()=>reject(Error('Bundle failed to load or integrity did not match'));document.body.appendChild(script);});
20
29
  const sdk=window.Sightspool;
@@ -0,0 +1,21 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "package": "@sightspool/sdk",
4
+ "version": "0.4.0",
5
+ "sourceCommit": "7b10230d4b59aedbdac0266aef4f4df88d574509",
6
+ "sourceDirty": false,
7
+ "artifactId": "781ab339e13fe18b86b56cc9d84dd308bebf6482a885c8aae46a8b3b84801f6e",
8
+ "bundle": {
9
+ "file": "sdk.global.js",
10
+ "path": "releases/0.4.0/781ab339e13fe18b86b56cc9d84dd308bebf6482a885c8aae46a8b3b84801f6e/sdk.global.js",
11
+ "sha256": "ed80800d20ddd91aebde61c355bb15918c0c5cbd712b91e13604795f2d2a8aa9",
12
+ "integrity": "sha384-kfq0QJNOieKKBsyznFRRW9kAaC5fcXvDzu56zgNsSzbXCVw2aReY66mlq8Fldy6K",
13
+ "bytes": 4902,
14
+ "gzipBytes": 2238
15
+ },
16
+ "source": {
17
+ "file": "source.json",
18
+ "sha256": "563f39e79e11d55279cd9ddfb02e2d5c7c4cdc653b4d9f57ab6c36dc4874dfa4"
19
+ },
20
+ "runtimeDependencies": []
21
+ }
@@ -0,0 +1,3 @@
1
+ var Sightspool=(function(exports){'use strict';var y=Symbol.for("sightspool.research.runtime.v1"),v=()=>typeof window=="undefined"?null:window,l=()=>{var e;return (e=v())==null?void 0:e[y]},a=e=>e.audience==="all_visitors"||e.identified;function h(e){var t;(t=e.button)==null||t.remove(),e.button=null,e.offer=null;}function c(e){var t;e.generation+=1,(t=e.pending)==null||t.abort(),e.pending=null,h(e);}function r(e,t){e.status=t;}async function u(e){if(e.disposed||e.paused||!a(e)||e.pending||document.visibilityState!=="visible")return;let t=e.generation,i=new AbortController;e.pending=i;let d=window.setTimeout(()=>i.abort(),1e4);e.button||r(e,"checking");try{let s=await fetch(e.endpoint+"/widget-offer",{method:"POST",credentials:"omit",cache:"no-store",referrerPolicy:"no-referrer",headers:{"Content-Type":"application/json"},body:JSON.stringify({operation:"offer",key:e.key,device:e.device}),signal:i.signal});if(!s.ok)throw Error("offer unavailable");let n=await s.json();if(e.disposed||t!==e.generation||e.paused||!a(e))return;if(n.available!==!0||typeof n.offer!="string"||!n.offer){h(e),r(e,"unavailable");return}if(e.offer=n.offer,r(e,"available"),e.button)return;let o=document.createElement("button");o.type="button",o.textContent="Talk to the founder \xB7 5 min",o.setAttribute("aria-label","Sightspool: join a five-minute user interview"),o.style.cssText="position:fixed;bottom:20px;right:20px;z-index:2147483000;padding:14px 18px;border:0;border-radius:16px;background:#171717;color:white;font:500 14px system-ui;box-shadow:0 8px 30px #0003;cursor:pointer;max-width:calc(100vw - 40px)",o.onclick=()=>{try{if(e.disposed||e.paused||!a(e)||!e.offer)return;if(e.popup&&!e.popup.closed){e.popup.focus();return}let g=new URLSearchParams({offer:e.offer,device:e.device});e.popup=window.open(e.endpoint+"/interview-widget#"+g,"sightspool-interview","popup,width=520,height=760");}catch(g){}},e.button=o,document.body.appendChild(o);}catch(s){!e.disposed&&t===e.generation&&(h(e),r(e,"error"));}finally{window.clearTimeout(d),e.pending===i&&(e.pending=null);}}function m(e){try{let t=v();if(!t||!e||!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(e.key)||e.audience!=="all_visitors"&&e.audience!=="signed_in")return;let i=new URL(e.endpoint||"https://www.sightspool.com");if(i.protocol!=="https:"&&!(i.protocol==="http:"&&["localhost","127.0.0.1","[::1]"].includes(i.hostname))||i.username||i.password)return;let d=l();if(d&&d.key===e.key&&d.endpoint===i.origin&&d.audience===e.audience)return;d&&f();let s="";try{s=sessionStorage.getItem("sightspool-widget-device:"+e.key)||"";}catch(o){}if(!/^ss_fcd_[A-Za-z0-9_-]{43}$/.test(s)){let o=crypto.getRandomValues(new Uint8Array(32));s="ss_fcd_"+btoa(String.fromCharCode(...o)).replaceAll("+","-").replaceAll("/","_").replace(/=+$/,"");try{sessionStorage.setItem("sightspool-widget-device:"+e.key,s);}catch(g){}}let n={key:e.key,endpoint:i.origin,audience:e.audience,identified:!1,paused:!1,disposed:!1,generation:0,device:s,offer:null,button:null,popup:null,pending:null,timer:0,visibility:()=>{},status:"signed_out"};t[y]=n,n.visibility=()=>{try{document.visibilityState==="visible"?u(n):(c(n),r(n,n.paused?"paused":a(n)?"unavailable":"signed_out"));}catch(o){}},document.addEventListener("visibilitychange",n.visibility),n.timer=window.setInterval(()=>{u(n);},15e3),a(n)&&u(n);}catch(t){}}function b(e){try{let t=l();if(!t)return;let i=typeof e=="string"&&e.trim().length>0;if(!i&&!t.identified){!t.paused&&a(t)&&u(t);return}c(t),t.identified=i,r(t,t.paused?"paused":a(t)?"unavailable":"signed_out"),a(t)&&!t.paused&&u(t);}catch(t){}}function w(){try{let e=l();e&&(e.paused=!0,c(e),r(e,"paused"));}catch(e){}}function x(){try{let e=l();e&&(e.paused=!1,a(e)||r(e,"signed_out"),u(e));}catch(e){}}function f(){try{let e=l();if(!e)return;e.disposed=!0,c(e),window.clearInterval(e.timer),document.removeEventListener("visibilitychange",e.visibility),delete v()[y];}catch(e){}}function S(){var e,t;try{return (t=(e=l())==null?void 0:e.status)!=null?t:"not_initialized"}catch(i){return "error"}}var p={init:m,identify:b,pause:w,resume:x,destroy:f,getStatus:S};var A=p;try{let e=document.currentScript,t=(e==null?void 0:e.dataset.sightspoolKey)||(e==null?void 0:e.dataset.key),i=e==null?void 0:e.dataset.sightspoolAudience;t&&(i==="all_visitors"||i==="signed_in")&&(p.init({key:t,audience:i,endpoint:(e==null?void 0:e.dataset.sightspoolEndpoint)||new URL(e.src).origin}),e!=null&&e.dataset.userId&&p.identify(e.dataset.userId)),window.SightspoolResearch=p,Promise.resolve().then(()=>window.dispatchEvent(new Event("sightspool:ready")));}catch(e){}
2
+ exports.default=A;exports.destroy=f;exports.getStatus=S;exports.identify=b;exports.init=m;exports.pause=w;exports.resume=x;Object.defineProperty(exports,'__esModule',{value:true});return exports;})({});//# sourceMappingURL=sdk.global.js.map
3
+ //# sourceMappingURL=sdk.global.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/research.ts","../src/index.ts","../src/browser.ts"],"names":["slot","host","current","_a","eligible","r","remove","invalidate","status","value","check","generation","request","timeout","response","result","button","hash","e","init","config","browser","url","previous","destroy","device","bytes","identify","userId","identified","pause","resume","getStatus","_b","src_default","browser_default","script","key","audience"],"mappings":"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","file":"sdk.global.js","sourcesContent":["export type SightspoolConfig = {\n /** Public workspace widget key from Go live (UUID, not an old pk_live key). */\n key: string;\n /** Match the approved research audience. No default that widens recruitment. */\n audience: \"all_visitors\" | \"signed_in\";\n /** Sightspool origin. Defaults to the hosted application. */\n endpoint?: string;\n};\nexport type ResearchStatus =\n | \"not_initialized\" | \"signed_out\" | \"paused\" | \"checking\"\n | \"unavailable\" | \"available\" | \"error\";\n\ntype Runtime = {\n key: string; endpoint: string; audience: SightspoolConfig[\"audience\"]; identified: boolean; paused: boolean;\n disposed: boolean; generation: number; device: string; offer: string | null;\n button: HTMLButtonElement | null; popup: Window | null;\n pending: AbortController | null; timer: number; visibility: () => void;\n status: ResearchStatus;\n};\nconst slot = Symbol.for(\"sightspool.research.runtime.v1\");\ntype Host = Window & { [slot]?: Runtime };\nconst host = (): Host | null => typeof window === \"undefined\" ? null : window as Host;\nconst current = () => host()?.[slot];\nconst eligible = (r: Runtime) => r.audience === \"all_visitors\" || r.identified;\n\nfunction remove(r: Runtime) {\n r.button?.remove(); r.button = null; r.offer = null;\n}\nfunction invalidate(r: Runtime) {\n r.generation += 1; r.pending?.abort(); r.pending = null; remove(r);\n}\nfunction status(r: Runtime, value: ResearchStatus) { r.status = value; }\n\nasync function check(r: Runtime) {\n if (r.disposed || r.paused || !eligible(r) || r.pending || document.visibilityState !== \"visible\") return;\n const generation = r.generation;\n const request = new AbortController();\n r.pending = request;\n const timeout = window.setTimeout(() => request.abort(), 10_000);\n if (!r.button) status(r, \"checking\");\n try {\n const response = await fetch(r.endpoint + \"/widget-offer\", {\n method: \"POST\", credentials: \"omit\", cache: \"no-store\", referrerPolicy: \"no-referrer\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ operation: \"offer\", key: r.key, device: r.device }),\n signal: request.signal,\n });\n if (!response.ok) throw Error(\"offer unavailable\");\n const result = await response.json();\n if (r.disposed || generation !== r.generation || r.paused || !eligible(r)) return;\n if (result.available !== true || typeof result.offer !== \"string\" || !result.offer) {\n remove(r); status(r, \"unavailable\"); return;\n }\n r.offer = result.offer;\n status(r, \"available\");\n if (r.button) return;\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.textContent = \"Talk to the founder · 5 min\";\n button.setAttribute(\"aria-label\", \"Sightspool: join a five-minute user interview\");\n button.style.cssText = \"position:fixed;bottom:20px;right:20px;z-index:2147483000;padding:14px 18px;border:0;border-radius:16px;background:#171717;color:white;font:500 14px system-ui;box-shadow:0 8px 30px #0003;cursor:pointer;max-width:calc(100vw - 40px)\";\n button.onclick = () => {\n try {\n if (r.disposed || r.paused || !eligible(r) || !r.offer) return;\n if (r.popup && !r.popup.closed) { r.popup.focus(); return; }\n const hash = new URLSearchParams({ offer: r.offer, device: r.device });\n r.popup = window.open(r.endpoint + \"/interview-widget#\" + hash, \"sightspool-interview\", \"popup,width=520,height=760\");\n } catch { /* Host pages remain usable if popups are blocked. */ }\n };\n r.button = button;\n document.body.appendChild(button);\n } catch {\n if (!r.disposed && generation === r.generation) { remove(r); status(r, \"error\"); }\n } finally {\n window.clearTimeout(timeout);\n if (r.pending === request) r.pending = null;\n }\n}\n\n/** Start research for the chosen audience. Signed-in-only waits for identify(). */\nexport function init(config: SightspoolConfig): void {\n try {\n const browser = host();\n if (!browser || !config || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(config.key)) return;\n if (config.audience !== \"all_visitors\" && config.audience !== \"signed_in\") return;\n const url = new URL(config.endpoint || \"https://www.sightspool.com\");\n if (url.protocol !== \"https:\" && !(url.protocol === \"http:\" && [\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(url.hostname))) return;\n if (url.username || url.password) return;\n const previous = current();\n if (previous && previous.key === config.key && previous.endpoint === url.origin && previous.audience === config.audience) return;\n if (previous) destroy();\n let device = \"\";\n try { device = sessionStorage.getItem(\"sightspool-widget-device:\" + config.key) || \"\"; } catch {}\n if (!/^ss_fcd_[A-Za-z0-9_-]{43}$/.test(device)) {\n const bytes = crypto.getRandomValues(new Uint8Array(32));\n device = \"ss_fcd_\" + btoa(String.fromCharCode(...bytes)).replaceAll(\"+\", \"-\").replaceAll(\"/\", \"_\").replace(/=+$/, \"\");\n try { sessionStorage.setItem(\"sightspool-widget-device:\" + config.key, device); } catch {}\n }\n const r: Runtime = {\n key: config.key, endpoint: url.origin, audience: config.audience, identified: false, paused: false,\n disposed: false, generation: 0, device, offer: null, button: null,\n popup: null, pending: null, timer: 0, visibility: () => {}, status: \"signed_out\",\n };\n browser[slot] = r;\n r.visibility = () => {\n try {\n if (document.visibilityState === \"visible\") void check(r);\n else { invalidate(r); status(r, r.paused ? \"paused\" : eligible(r) ? \"unavailable\" : \"signed_out\"); }\n } catch {}\n };\n document.addEventListener(\"visibilitychange\", r.visibility);\n r.timer = window.setInterval(() => { void check(r); }, 15_000);\n if (eligible(r)) void check(r);\n } catch { /* Never throw into the host application. */ }\n}\n\n/** Only the presence of an ID is retained. The ID itself is never stored or sent. */\nexport function identify(userId: string | null | undefined): void {\n try {\n const r = current();\n if (!r) return;\n const identified = typeof userId === \"string\" && userId.trim().length > 0;\n if (!identified && !r.identified) { if (!r.paused && eligible(r)) void check(r); return; }\n invalidate(r);\n r.identified = identified;\n status(r, r.paused ? \"paused\" : eligible(r) ? \"unavailable\" : \"signed_out\");\n if (eligible(r) && !r.paused) void check(r);\n } catch {}\n}\nexport function pause(): void {\n try { const r = current(); if (r) { r.paused = true; invalidate(r); status(r, \"paused\"); } } catch {}\n}\nexport function resume(): void {\n try { const r = current(); if (r) { r.paused = false; if (!eligible(r)) status(r, \"signed_out\"); void check(r); } } catch {}\n}\n/** Stop recruitment and remove listeners/UI. Does not end an already opened interview. */\nexport function destroy(): void {\n try {\n const r = current();\n if (!r) return;\n r.disposed = true; invalidate(r);\n window.clearInterval(r.timer);\n document.removeEventListener(\"visibilitychange\", r.visibility);\n delete host()![slot];\n } catch {}\n}\nexport function getStatus(): ResearchStatus {\n try { return current()?.status ?? \"not_initialized\"; } catch { return \"error\"; }\n}\n","// Research is the public SDK. The former capture engine is parked.\nexport { init, identify, pause, resume, destroy, getStatus } from \"./research\";\nexport type { SightspoolConfig, ResearchStatus } from \"./research\";\nimport { init, identify, pause, resume, destroy, getStatus } from \"./research\";\nexport default { init, identify, pause, resume, destroy, getStatus };\n","import api from \"./index\";\nexport * from \"./index\";\nexport default api;\n\ntry {\n const script = document.currentScript as HTMLScriptElement | null;\n const key = script?.dataset.sightspoolKey || script?.dataset.key;\n const audience = script?.dataset.sightspoolAudience;\n if (key && (audience === \"all_visitors\" || audience === \"signed_in\")) {\n api.init({ key, audience, endpoint: script?.dataset.sightspoolEndpoint || new URL(script!.src).origin });\n if (script?.dataset.userId) api.identify(script.dataset.userId);\n }\n // The old research-widget URL is an alias of this bundle, not another runtime.\n (window as Window & { SightspoolResearch?: typeof api }).SightspoolResearch = api;\n Promise.resolve().then(() => window.dispatchEvent(new Event(\"sightspool:ready\")));\n} catch { /* Browser auto-init must not interrupt the client app. */ }\n"]}
@@ -0,0 +1,11 @@
1
+ {
2
+ "files": {
3
+ "src/index.ts": "// Research is the public SDK. The former capture engine is parked.\nexport { init, identify, pause, resume, destroy, getStatus } from \"./research\";\nexport type { SightspoolConfig, ResearchStatus } from \"./research\";\nimport { init, identify, pause, resume, destroy, getStatus } from \"./research\";\nexport default { init, identify, pause, resume, destroy, getStatus };\n",
4
+ "src/browser.ts": "import api from \"./index\";\nexport * from \"./index\";\nexport default api;\n\ntry {\n const script = document.currentScript as HTMLScriptElement | null;\n const key = script?.dataset.sightspoolKey || script?.dataset.key;\n const audience = script?.dataset.sightspoolAudience;\n if (key && (audience === \"all_visitors\" || audience === \"signed_in\")) {\n api.init({ key, audience, endpoint: script?.dataset.sightspoolEndpoint || new URL(script!.src).origin });\n if (script?.dataset.userId) api.identify(script.dataset.userId);\n }\n // The old research-widget URL is an alias of this bundle, not another runtime.\n (window as Window & { SightspoolResearch?: typeof api }).SightspoolResearch = api;\n Promise.resolve().then(() => window.dispatchEvent(new Event(\"sightspool:ready\")));\n} catch { /* Browser auto-init must not interrupt the client app. */ }\n",
5
+ "src/research.ts": "export type SightspoolConfig = {\n /** Public workspace widget key from Go live (UUID, not an old pk_live key). */\n key: string;\n /** Match the approved research audience. No default that widens recruitment. */\n audience: \"all_visitors\" | \"signed_in\";\n /** Sightspool origin. Defaults to the hosted application. */\n endpoint?: string;\n};\nexport type ResearchStatus =\n | \"not_initialized\" | \"signed_out\" | \"paused\" | \"checking\"\n | \"unavailable\" | \"available\" | \"error\";\n\ntype Runtime = {\n key: string; endpoint: string; audience: SightspoolConfig[\"audience\"]; identified: boolean; paused: boolean;\n disposed: boolean; generation: number; device: string; offer: string | null;\n button: HTMLButtonElement | null; popup: Window | null;\n pending: AbortController | null; timer: number; visibility: () => void;\n status: ResearchStatus;\n};\nconst slot = Symbol.for(\"sightspool.research.runtime.v1\");\ntype Host = Window & { [slot]?: Runtime };\nconst host = (): Host | null => typeof window === \"undefined\" ? null : window as Host;\nconst current = () => host()?.[slot];\nconst eligible = (r: Runtime) => r.audience === \"all_visitors\" || r.identified;\n\nfunction remove(r: Runtime) {\n r.button?.remove(); r.button = null; r.offer = null;\n}\nfunction invalidate(r: Runtime) {\n r.generation += 1; r.pending?.abort(); r.pending = null; remove(r);\n}\nfunction status(r: Runtime, value: ResearchStatus) { r.status = value; }\n\nasync function check(r: Runtime) {\n if (r.disposed || r.paused || !eligible(r) || r.pending || document.visibilityState !== \"visible\") return;\n const generation = r.generation;\n const request = new AbortController();\n r.pending = request;\n const timeout = window.setTimeout(() => request.abort(), 10_000);\n if (!r.button) status(r, \"checking\");\n try {\n const response = await fetch(r.endpoint + \"/widget-offer\", {\n method: \"POST\", credentials: \"omit\", cache: \"no-store\", referrerPolicy: \"no-referrer\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ operation: \"offer\", key: r.key, device: r.device }),\n signal: request.signal,\n });\n if (!response.ok) throw Error(\"offer unavailable\");\n const result = await response.json();\n if (r.disposed || generation !== r.generation || r.paused || !eligible(r)) return;\n if (result.available !== true || typeof result.offer !== \"string\" || !result.offer) {\n remove(r); status(r, \"unavailable\"); return;\n }\n r.offer = result.offer;\n status(r, \"available\");\n if (r.button) return;\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.textContent = \"Talk to the founder · 5 min\";\n button.setAttribute(\"aria-label\", \"Sightspool: join a five-minute user interview\");\n button.style.cssText = \"position:fixed;bottom:20px;right:20px;z-index:2147483000;padding:14px 18px;border:0;border-radius:16px;background:#171717;color:white;font:500 14px system-ui;box-shadow:0 8px 30px #0003;cursor:pointer;max-width:calc(100vw - 40px)\";\n button.onclick = () => {\n try {\n if (r.disposed || r.paused || !eligible(r) || !r.offer) return;\n if (r.popup && !r.popup.closed) { r.popup.focus(); return; }\n const hash = new URLSearchParams({ offer: r.offer, device: r.device });\n r.popup = window.open(r.endpoint + \"/interview-widget#\" + hash, \"sightspool-interview\", \"popup,width=520,height=760\");\n } catch { /* Host pages remain usable if popups are blocked. */ }\n };\n r.button = button;\n document.body.appendChild(button);\n } catch {\n if (!r.disposed && generation === r.generation) { remove(r); status(r, \"error\"); }\n } finally {\n window.clearTimeout(timeout);\n if (r.pending === request) r.pending = null;\n }\n}\n\n/** Start research for the chosen audience. Signed-in-only waits for identify(). */\nexport function init(config: SightspoolConfig): void {\n try {\n const browser = host();\n if (!browser || !config || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(config.key)) return;\n if (config.audience !== \"all_visitors\" && config.audience !== \"signed_in\") return;\n const url = new URL(config.endpoint || \"https://www.sightspool.com\");\n if (url.protocol !== \"https:\" && !(url.protocol === \"http:\" && [\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(url.hostname))) return;\n if (url.username || url.password) return;\n const previous = current();\n if (previous && previous.key === config.key && previous.endpoint === url.origin && previous.audience === config.audience) return;\n if (previous) destroy();\n let device = \"\";\n try { device = sessionStorage.getItem(\"sightspool-widget-device:\" + config.key) || \"\"; } catch {}\n if (!/^ss_fcd_[A-Za-z0-9_-]{43}$/.test(device)) {\n const bytes = crypto.getRandomValues(new Uint8Array(32));\n device = \"ss_fcd_\" + btoa(String.fromCharCode(...bytes)).replaceAll(\"+\", \"-\").replaceAll(\"/\", \"_\").replace(/=+$/, \"\");\n try { sessionStorage.setItem(\"sightspool-widget-device:\" + config.key, device); } catch {}\n }\n const r: Runtime = {\n key: config.key, endpoint: url.origin, audience: config.audience, identified: false, paused: false,\n disposed: false, generation: 0, device, offer: null, button: null,\n popup: null, pending: null, timer: 0, visibility: () => {}, status: \"signed_out\",\n };\n browser[slot] = r;\n r.visibility = () => {\n try {\n if (document.visibilityState === \"visible\") void check(r);\n else { invalidate(r); status(r, r.paused ? \"paused\" : eligible(r) ? \"unavailable\" : \"signed_out\"); }\n } catch {}\n };\n document.addEventListener(\"visibilitychange\", r.visibility);\n r.timer = window.setInterval(() => { void check(r); }, 15_000);\n if (eligible(r)) void check(r);\n } catch { /* Never throw into the host application. */ }\n}\n\n/** Only the presence of an ID is retained. The ID itself is never stored or sent. */\nexport function identify(userId: string | null | undefined): void {\n try {\n const r = current();\n if (!r) return;\n const identified = typeof userId === \"string\" && userId.trim().length > 0;\n if (!identified && !r.identified) { if (!r.paused && eligible(r)) void check(r); return; }\n invalidate(r);\n r.identified = identified;\n status(r, r.paused ? \"paused\" : eligible(r) ? \"unavailable\" : \"signed_out\");\n if (eligible(r) && !r.paused) void check(r);\n } catch {}\n}\nexport function pause(): void {\n try { const r = current(); if (r) { r.paused = true; invalidate(r); status(r, \"paused\"); } } catch {}\n}\nexport function resume(): void {\n try { const r = current(); if (r) { r.paused = false; if (!eligible(r)) status(r, \"signed_out\"); void check(r); } } catch {}\n}\n/** Stop recruitment and remove listeners/UI. Does not end an already opened interview. */\nexport function destroy(): void {\n try {\n const r = current();\n if (!r) return;\n r.disposed = true; invalidate(r);\n window.clearInterval(r.timer);\n document.removeEventListener(\"visibilitychange\", r.visibility);\n delete host()![slot];\n } catch {}\n}\nexport function getStatus(): ResearchStatus {\n try { return current()?.status ?? \"not_initialized\"; } catch { return \"error\"; }\n}\n",
6
+ "tsup.config.ts": "import { defineConfig } from \"tsup\";\n\n// npm (ESM/CJS/types) and the script-tag bundle share the research runtime.\n// Parked capture modules are intentionally absent from both dependency graphs.\nexport default defineConfig([\n {\n entry: { index: \"src/index.ts\" },\n format: [\"esm\", \"cjs\"],\n dts: true,\n sourcemap: true,\n clean: true,\n treeshake: true,\n minify: false,\n target: \"es2018\",\n },\n {\n entry: { sdk: \"src/browser.ts\" },\n format: [\"iife\"],\n globalName: \"Sightspool\",\n sourcemap: true,\n minify: true,\n treeshake: true,\n target: \"es2018\",\n },\n]);\n",
7
+ "package.json": "{\n \"name\": \"@sightspool/sdk\",\n \"version\": \"0.4.0\",\n \"description\": \"Sightspool research SDK \\u2014 invite website visitors and signed-in users into approved user research.\",\n \"type\": \"module\",\n \"main\": \"./dist/index.cjs\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\",\n \"require\": \"./dist/index.cjs\"\n }\n },\n \"files\": [\n \"dist\",\n \"README.md\",\n \"LICENSE\",\n \"NOTICE\",\n \"site\",\n \"SECURITY.md\",\n \"CHANGELOG.md\"\n ],\n \"sideEffects\": false,\n \"license\": \"Apache-2.0\",\n \"author\": \"Sightspool\",\n \"homepage\": \"https://www.sightspool.com/sdk\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/sightspool/sdk.git\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/sightspool/sdk/issues\"\n },\n \"keywords\": [\n \"sightspool\",\n \"user-research\",\n \"research\",\n \"interviews\",\n \"sdk\"\n ],\n \"engines\": {\n \"node\": \">=18\"\n },\n \"packageManager\": \"pnpm@9.15.0\",\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"scripts\": {\n \"build\": \"tsup && node scripts/build-release.mjs\",\n \"type-check\": \"tsc --noEmit\",\n \"test\": \"tsc --noEmit && node --test __tests__/*.test.ts\",\n \"prepublishOnly\": \"pnpm run build\",\n \"test:release\": \"node scripts/verify-release.mjs && node --test scripts/release.test.mjs\",\n \"build:site\": \"node scripts/stage-site.mjs\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^24.10.4\",\n \"tsup\": \"^8.5.0\",\n \"typescript\": \"^5.9.3\"\n }\n}\n",
8
+ "pnpm-lock.yaml": "lockfileVersion: '9.0'\n\nsettings:\n autoInstallPeers: true\n excludeLinksFromLockfile: false\n\nimporters:\n\n .:\n devDependencies:\n '@types/node':\n specifier: ^24.10.4\n version: 24.13.1\n tsup:\n specifier: ^8.5.0\n version: 8.5.1(typescript@5.9.3)\n typescript:\n specifier: ^5.9.3\n version: 5.9.3\n\n packages/react:\n devDependencies:\n '@sightspool/sdk':\n specifier: workspace:^\n version: link:../..\n '@types/react':\n specifier: ^19.0.0\n version: 19.2.17\n react:\n specifier: ^19.0.0\n version: 19.2.7\n tsup:\n specifier: ^8.5.0\n version: 8.5.1(typescript@5.9.3)\n typescript:\n specifier: ^5.9.3\n version: 5.9.3\n\npackages:\n\n '@esbuild/aix-ppc64@0.27.7':\n resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==}\n engines: {node: '>=18'}\n cpu: [ppc64]\n os: [aix]\n\n '@esbuild/android-arm64@0.27.7':\n resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==}\n engines: {node: '>=18'}\n cpu: [arm64]\n os: [android]\n\n '@esbuild/android-arm@0.27.7':\n resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==}\n engines: {node: '>=18'}\n cpu: [arm]\n os: [android]\n\n '@esbuild/android-x64@0.27.7':\n resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==}\n engines: {node: '>=18'}\n cpu: [x64]\n os: [android]\n\n '@esbuild/darwin-arm64@0.27.7':\n resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==}\n engines: {node: '>=18'}\n cpu: [arm64]\n os: [darwin]\n\n '@esbuild/darwin-x64@0.27.7':\n resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==}\n engines: {node: '>=18'}\n cpu: [x64]\n os: [darwin]\n\n '@esbuild/freebsd-arm64@0.27.7':\n resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==}\n engines: {node: '>=18'}\n cpu: [arm64]\n os: [freebsd]\n\n '@esbuild/freebsd-x64@0.27.7':\n resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==}\n engines: {node: '>=18'}\n cpu: [x64]\n os: [freebsd]\n\n '@esbuild/linux-arm64@0.27.7':\n resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==}\n engines: {node: '>=18'}\n cpu: [arm64]\n os: [linux]\n\n '@esbuild/linux-arm@0.27.7':\n resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==}\n engines: {node: '>=18'}\n cpu: [arm]\n os: [linux]\n\n '@esbuild/linux-ia32@0.27.7':\n resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==}\n engines: {node: '>=18'}\n cpu: [ia32]\n os: [linux]\n\n '@esbuild/linux-loong64@0.27.7':\n resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==}\n engines: {node: '>=18'}\n cpu: [loong64]\n os: [linux]\n\n '@esbuild/linux-mips64el@0.27.7':\n resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==}\n engines: {node: '>=18'}\n cpu: [mips64el]\n os: [linux]\n\n '@esbuild/linux-ppc64@0.27.7':\n resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==}\n engines: {node: '>=18'}\n cpu: [ppc64]\n os: [linux]\n\n '@esbuild/linux-riscv64@0.27.7':\n resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==}\n engines: {node: '>=18'}\n cpu: [riscv64]\n os: [linux]\n\n '@esbuild/linux-s390x@0.27.7':\n resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==}\n engines: {node: '>=18'}\n cpu: [s390x]\n os: [linux]\n\n '@esbuild/linux-x64@0.27.7':\n resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==}\n engines: {node: '>=18'}\n cpu: [x64]\n os: [linux]\n\n '@esbuild/netbsd-arm64@0.27.7':\n resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==}\n engines: {node: '>=18'}\n cpu: [arm64]\n os: [netbsd]\n\n '@esbuild/netbsd-x64@0.27.7':\n resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==}\n engines: {node: '>=18'}\n cpu: [x64]\n os: [netbsd]\n\n '@esbuild/openbsd-arm64@0.27.7':\n resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==}\n engines: {node: '>=18'}\n cpu: [arm64]\n os: [openbsd]\n\n '@esbuild/openbsd-x64@0.27.7':\n resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==}\n engines: {node: '>=18'}\n cpu: [x64]\n os: [openbsd]\n\n '@esbuild/openharmony-arm64@0.27.7':\n resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==}\n engines: {node: '>=18'}\n cpu: [arm64]\n os: [openharmony]\n\n '@esbuild/sunos-x64@0.27.7':\n resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==}\n engines: {node: '>=18'}\n cpu: [x64]\n os: [sunos]\n\n '@esbuild/win32-arm64@0.27.7':\n resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==}\n engines: {node: '>=18'}\n cpu: [arm64]\n os: [win32]\n\n '@esbuild/win32-ia32@0.27.7':\n resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==}\n engines: {node: '>=18'}\n cpu: [ia32]\n os: [win32]\n\n '@esbuild/win32-x64@0.27.7':\n resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==}\n engines: {node: '>=18'}\n cpu: [x64]\n os: [win32]\n\n '@jridgewell/gen-mapping@0.3.13':\n resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}\n\n '@jridgewell/resolve-uri@3.1.2':\n resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}\n engines: {node: '>=6.0.0'}\n\n '@jridgewell/sourcemap-codec@1.5.5':\n resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}\n\n '@jridgewell/trace-mapping@0.3.31':\n resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}\n\n '@rollup/rollup-android-arm-eabi@4.61.1':\n resolution: {integrity: sha512-JnBB8MdXj45cajvTuO5FmPlvFVJRQgvrz1uSEl3NwqFnReAPGwb8EanbGi4z2nRaqLzjJSv5/JmycoTKlRZxHA==}\n cpu: [arm]\n os: [android]\n\n '@rollup/rollup-android-arm64@4.61.1':\n resolution: {integrity: sha512-Jx2g7iSjw4AOT0HDPHM9RV3GNjRXwybWtSFZiZAYUTjUwjVrYIwq3kBf+LnhqJlzXFAqTAh2F7IGI+O568exPw==}\n cpu: [arm64]\n os: [android]\n\n '@rollup/rollup-darwin-arm64@4.61.1':\n resolution: {integrity: sha512-0F1L/Z3Eqv8mT2n3dCpeO8GcTvHvVqkP5/t6DMsn0KzhYVcg+s7Ncl5DS8qjKYEeio6Az0Gt6nyBORay5qIlCA==}\n cpu: [arm64]\n os: [darwin]\n\n '@rollup/rollup-darwin-x64@4.61.1':\n resolution: {integrity: sha512-qLttcH871ujY4YcVfUSShhOw+CsoTatYz8gRbHO7Bb92QH059/P0y5do1KMs41fY0BpD2x4AJH/gID0zFiqVKQ==}\n cpu: [x64]\n os: [darwin]\n\n '@rollup/rollup-freebsd-arm64@4.61.1':\n resolution: {integrity: sha512-fUI4RapGE0Oh3mb8mgfvC1O2nU1RpDZUKnDQm3xB1Ipg7C2wTs5Kstz7G2uWK99a8S2yTMq8/P4uycwNa0nJyw==}\n cpu: [arm64]\n os: [freebsd]\n\n '@rollup/rollup-freebsd-x64@4.61.1':\n resolution: {integrity: sha512-H5YrdvJaDtI/U9/emrD4b++xkvp3y/JvOe4rizHbxvkyMfRS/CiRYdji+Pl8D0brEaNFWUh1drQxgAGIl6Xudw==}\n cpu: [x64]\n os: [freebsd]\n\n '@rollup/rollup-linux-arm-gnueabihf@4.61.1':\n resolution: {integrity: sha512-Q8CBCCQtDFrYtXoeUXSrnFXKOnyUhx6bz+SkL6A0E7V8kAiCJ5pamq1WtbfpVGhR5TSpXY6ak3avmDc5fHTyJA==}\n cpu: [arm]\n os: [linux]\n\n '@rollup/rollup-linux-arm-musleabihf@4.61.1':\n resolution: {integrity: sha512-nwnhk1581l0FBVellGcVCAT0Oi06onEA3WB53sf01VO3I0UPBkMH9sXONYME2K0ovXcNayJfNtHfm6mpJElatQ==}\n cpu: [arm]\n os: [linux]\n\n '@rollup/rollup-linux-arm64-gnu@4.61.1':\n resolution: {integrity: sha512-x5Xr49hwt3hdW75UOZm3395YwwzPyauktslv29KpWL/T+vVAzoT3azLcTWv0eMciBNrx+DYjH4paehHoLpPvpg==}\n cpu: [arm64]\n os: [linux]\n\n '@rollup/rollup-linux-arm64-musl@4.61.1':\n resolution: {integrity: sha512-unMS3H73DpaoPyyEVPjGKleM/s0mkmsauTENpw4INQY8y4+IuLNjkueQ5QCtC0D3N38Y38yhAU8OoZ20S2Tm6w==}\n cpu: [arm64]\n os: [linux]\n\n '@rollup/rollup-linux-loong64-gnu@4.61.1':\n resolution: {integrity: sha512-zNZzGRnAhwjFEYmvphJRV5XaQGjs62cCmeYYHUT//NbvEnHauw+I85nGG+SiVg5ld4GX8D1IbKIX+ozITQnhMQ==}\n cpu: [loong64]\n os: [linux]\n\n '@rollup/rollup-linux-loong64-musl@4.61.1':\n resolution: {integrity: sha512-LdpWGL8X209B2SIvWjqlc8VZgM6PKfontSerGepuldQmHYrAOtnMCXeJkxXGbC+PPZVOuu5czJo7fNV6aeW8rQ==}\n cpu: [loong64]\n os: [linux]\n\n '@rollup/rollup-linux-ppc64-gnu@4.61.1':\n resolution: {integrity: sha512-EC5kTtNaNGOmbMGqar8dvJy6y/hg99GAwjfBz++pxZhQATXGcRjd6c5en5wcbru0vkRmiMGsQKdMJOOf6sza4g==}\n cpu: [ppc64]\n os: [linux]\n\n '@rollup/rollup-linux-ppc64-musl@4.61.1':\n resolution: {integrity: sha512-8hiwp6D4acEcNK78I4rP0/XtS1sknWIAMJBPdR4l6zUtyTm5KiTDr5bXmWt4foY7nAN7AThDHgkLIEZOWKbzWw==}\n cpu: [ppc64]\n os: [linux]\n\n '@rollup/rollup-linux-riscv64-gnu@4.61.1':\n resolution: {integrity: sha512-10dh/h/BqA7DuMPWSxkR8uks18FRwnwOEqr5zOTEl+NOwP/OMzKX8OFR/Of9xxDA7D5qef1Nzar5WDD2kCCr1g==}\n cpu: [riscv64]\n os: [linux]\n\n '@rollup/rollup-linux-riscv64-musl@4.61.1':\n resolution: {integrity: sha512-YKJ5lg35DP17gcAOggnihe+APw9HLyj1Xn7gsmGumBJAUDa6NGXNixJzmkWLhcK9TOuuyQjdamzvJefkO7qHZQ==}\n cpu: [riscv64]\n os: [linux]\n\n '@rollup/rollup-linux-s390x-gnu@4.61.1':\n resolution: {integrity: sha512-Mlil5G2Jj6a7B3LWGctg+XPL9vdXYuzCtNXfxOQ0nPjc2m6ueUktocPGH9bnAM0bNRKb/bAWTujUU7IJQdQA+g==}\n cpu: [s390x]\n os: [linux]\n\n '@rollup/rollup-linux-x64-gnu@4.61.1':\n resolution: {integrity: sha512-bVWIOIk6pV01p4CdUbPP7CJ/434z+OooYjDuFcR+44N35YvKUC66G8MGnvcWx5mWKW3g61J+t74l3Kj15Kwn2Q==}\n cpu: [x64]\n os: [linux]\n\n '@rollup/rollup-linux-x64-musl@4.61.1':\n resolution: {integrity: sha512-qy5pBvZbqNFheBz61R1rzsezjm0J7O2oNGoWtGoY89SZYLUfxAJTBAqDChqAIdB4rCiIbi9nF7yZ83GnNiLwSw==}\n cpu: [x64]\n os: [linux]\n\n '@rollup/rollup-openbsd-x64@4.61.1':\n resolution: {integrity: sha512-E83TXjI4zm0+5f2qO+UOudaCYIhYwpJ5jq6YCZNIZ+6CbfhKrkAGezeiASBL9ElxAxFsRS9ZhESv8mfnj6TKeg==}\n cpu: [x64]\n os: [openbsd]\n\n '@rollup/rollup-openharmony-arm64@4.61.1':\n resolution: {integrity: sha512-fbWnKqVkjrJN38vNe3ahkbk6iejS/3b0Nt7EEtPpE6RBacZcGXNKbzfHN3GUUlXOPghUg0j6XUGrtjX9z1sIvA==}\n cpu: [arm64]\n os: [openharmony]\n\n '@rollup/rollup-win32-arm64-msvc@4.61.1':\n resolution: {integrity: sha512-ArMl38iVAbk0New1ogihQNY6iphLi4ZaRsa037gUzv5yeKPY8TD3Dmy4x2RNC1VztU/uqm+G+/RwFrSka3Oy2g==}\n cpu: [arm64]\n os: [win32]\n\n '@rollup/rollup-win32-ia32-msvc@4.61.1':\n resolution: {integrity: sha512-0mYtjHS9ucAbcATycCNK9IGBk/cCe/ma7EmSLGZdsxnOA8cjRIyU04wDpVAD9NiOfLUR9KTxdiO53uOkherqjQ==}\n cpu: [ia32]\n os: [win32]\n\n '@rollup/rollup-win32-x64-gnu@4.61.1':\n resolution: {integrity: sha512-gK1iCEPfpoSG9wfBihXxvBMi8ZfcWffYkEsC/Eih+iFENTaewvNcrEQ69lIOWYO5pePHKLHHO7nq5AILGO/HQQ==}\n cpu: [x64]\n os: [win32]\n\n '@rollup/rollup-win32-x64-msvc@4.61.1':\n resolution: {integrity: sha512-X+zaP2x+j4RXGfbp/seSoRHWnPxzApilDszisZxbYH5C/jTxFhCtDNdPGZb9lJyYPs24wGxruPF7Y+sIXt9Gzw==}\n cpu: [x64]\n os: [win32]\n\n '@types/estree@1.0.9':\n resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}\n\n '@types/node@24.13.1':\n resolution: {integrity: sha512-RSpUJGmvsJ1ZeBehQZFhIdpsz+bIpES0nIQXko4Ybq+N+kX6XvOq3Jo+iJ82FWLdblFq85AsMikd3m35jgezYg==}\n\n '@types/react@19.2.17':\n resolution: {integrity: sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==}\n\n acorn@8.16.0:\n resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==}\n engines: {node: '>=0.4.0'}\n hasBin: true\n\n any-promise@1.3.0:\n resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}\n\n bundle-require@5.1.0:\n resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==}\n engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}\n peerDependencies:\n esbuild: '>=0.18'\n\n cac@6.7.14:\n resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}\n engines: {node: '>=8'}\n\n chokidar@4.0.3:\n resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}\n engines: {node: '>= 14.16.0'}\n\n commander@4.1.1:\n resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}\n engines: {node: '>= 6'}\n\n confbox@0.1.8:\n resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==}\n\n consola@3.4.2:\n resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==}\n engines: {node: ^14.18.0 || >=16.10.0}\n\n csstype@3.2.3:\n resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}\n\n debug@4.4.3:\n resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}\n engines: {node: '>=6.0'}\n peerDependencies:\n supports-color: '*'\n peerDependenciesMeta:\n supports-color:\n optional: true\n\n esbuild@0.27.7:\n resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==}\n engines: {node: '>=18'}\n hasBin: true\n\n fdir@6.5.0:\n resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}\n engines: {node: '>=12.0.0'}\n peerDependencies:\n picomatch: ^3 || ^4\n peerDependenciesMeta:\n picomatch:\n optional: true\n\n fix-dts-default-cjs-exports@1.0.1:\n resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==}\n\n fsevents@2.3.3:\n resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}\n engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}\n os: [darwin]\n\n joycon@3.1.1:\n resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==}\n engines: {node: '>=10'}\n\n lilconfig@3.1.3:\n resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==}\n engines: {node: '>=14'}\n\n lines-and-columns@1.2.4:\n resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}\n\n load-tsconfig@0.2.5:\n resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==}\n engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}\n\n magic-string@0.30.21:\n resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}\n\n mlly@1.8.2:\n resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==}\n\n ms@2.1.3:\n resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}\n\n mz@2.7.0:\n resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}\n\n object-assign@4.1.1:\n resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}\n engines: {node: '>=0.10.0'}\n\n pathe@2.0.3:\n resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}\n\n picocolors@1.1.1:\n resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}\n\n picomatch@4.0.4:\n resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==}\n engines: {node: '>=12'}\n\n pirates@4.0.7:\n resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==}\n engines: {node: '>= 6'}\n\n pkg-types@1.3.1:\n resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}\n\n postcss-load-config@6.0.1:\n resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==}\n engines: {node: '>= 18'}\n peerDependencies:\n jiti: '>=1.21.0'\n postcss: '>=8.0.9'\n tsx: ^4.8.1\n yaml: ^2.4.2\n peerDependenciesMeta:\n jiti:\n optional: true\n postcss:\n optional: true\n tsx:\n optional: true\n yaml:\n optional: true\n\n react@19.2.7:\n resolution: {integrity: sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==}\n engines: {node: '>=0.10.0'}\n\n readdirp@4.1.2:\n resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}\n engines: {node: '>= 14.18.0'}\n\n resolve-from@5.0.0:\n resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}\n engines: {node: '>=8'}\n\n rollup@4.61.1:\n resolution: {integrity: sha512-I4KW6iuRpuu2uHBLraZ1wNZe0DP7lnRha+VJ9tNaYVaVgKhW0aI3h4RYnoRPeql0flHm/Co55b7snEDcOfOJrA==}\n engines: {node: '>=18.0.0', npm: '>=8.0.0'}\n hasBin: true\n\n source-map@0.7.6:\n resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==}\n engines: {node: '>= 12'}\n\n sucrase@3.35.1:\n resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==}\n engines: {node: '>=16 || 14 >=14.17'}\n hasBin: true\n\n thenify-all@1.6.0:\n resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}\n engines: {node: '>=0.8'}\n\n thenify@3.3.1:\n resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}\n\n tinyexec@0.3.2:\n resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==}\n\n tinyglobby@0.2.17:\n resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}\n engines: {node: '>=12.0.0'}\n\n tree-kill@1.2.2:\n resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}\n hasBin: true\n\n ts-interface-checker@0.1.13:\n resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}\n\n tsup@8.5.1:\n resolution: {integrity: sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==}\n engines: {node: '>=18'}\n hasBin: true\n peerDependencies:\n '@microsoft/api-extractor': ^7.36.0\n '@swc/core': ^1\n postcss: ^8.4.12\n typescript: '>=4.5.0'\n peerDependenciesMeta:\n '@microsoft/api-extractor':\n optional: true\n '@swc/core':\n optional: true\n postcss:\n optional: true\n typescript:\n optional: true\n\n typescript@5.9.3:\n resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}\n engines: {node: '>=14.17'}\n hasBin: true\n\n ufo@1.6.4:\n resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==}\n\n undici-types@7.18.2:\n resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==}\n\nsnapshots:\n\n '@esbuild/aix-ppc64@0.27.7':\n optional: true\n\n '@esbuild/android-arm64@0.27.7':\n optional: true\n\n '@esbuild/android-arm@0.27.7':\n optional: true\n\n '@esbuild/android-x64@0.27.7':\n optional: true\n\n '@esbuild/darwin-arm64@0.27.7':\n optional: true\n\n '@esbuild/darwin-x64@0.27.7':\n optional: true\n\n '@esbuild/freebsd-arm64@0.27.7':\n optional: true\n\n '@esbuild/freebsd-x64@0.27.7':\n optional: true\n\n '@esbuild/linux-arm64@0.27.7':\n optional: true\n\n '@esbuild/linux-arm@0.27.7':\n optional: true\n\n '@esbuild/linux-ia32@0.27.7':\n optional: true\n\n '@esbuild/linux-loong64@0.27.7':\n optional: true\n\n '@esbuild/linux-mips64el@0.27.7':\n optional: true\n\n '@esbuild/linux-ppc64@0.27.7':\n optional: true\n\n '@esbuild/linux-riscv64@0.27.7':\n optional: true\n\n '@esbuild/linux-s390x@0.27.7':\n optional: true\n\n '@esbuild/linux-x64@0.27.7':\n optional: true\n\n '@esbuild/netbsd-arm64@0.27.7':\n optional: true\n\n '@esbuild/netbsd-x64@0.27.7':\n optional: true\n\n '@esbuild/openbsd-arm64@0.27.7':\n optional: true\n\n '@esbuild/openbsd-x64@0.27.7':\n optional: true\n\n '@esbuild/openharmony-arm64@0.27.7':\n optional: true\n\n '@esbuild/sunos-x64@0.27.7':\n optional: true\n\n '@esbuild/win32-arm64@0.27.7':\n optional: true\n\n '@esbuild/win32-ia32@0.27.7':\n optional: true\n\n '@esbuild/win32-x64@0.27.7':\n optional: true\n\n '@jridgewell/gen-mapping@0.3.13':\n dependencies:\n '@jridgewell/sourcemap-codec': 1.5.5\n '@jridgewell/trace-mapping': 0.3.31\n\n '@jridgewell/resolve-uri@3.1.2': {}\n\n '@jridgewell/sourcemap-codec@1.5.5': {}\n\n '@jridgewell/trace-mapping@0.3.31':\n dependencies:\n '@jridgewell/resolve-uri': 3.1.2\n '@jridgewell/sourcemap-codec': 1.5.5\n\n '@rollup/rollup-android-arm-eabi@4.61.1':\n optional: true\n\n '@rollup/rollup-android-arm64@4.61.1':\n optional: true\n\n '@rollup/rollup-darwin-arm64@4.61.1':\n optional: true\n\n '@rollup/rollup-darwin-x64@4.61.1':\n optional: true\n\n '@rollup/rollup-freebsd-arm64@4.61.1':\n optional: true\n\n '@rollup/rollup-freebsd-x64@4.61.1':\n optional: true\n\n '@rollup/rollup-linux-arm-gnueabihf@4.61.1':\n optional: true\n\n '@rollup/rollup-linux-arm-musleabihf@4.61.1':\n optional: true\n\n '@rollup/rollup-linux-arm64-gnu@4.61.1':\n optional: true\n\n '@rollup/rollup-linux-arm64-musl@4.61.1':\n optional: true\n\n '@rollup/rollup-linux-loong64-gnu@4.61.1':\n optional: true\n\n '@rollup/rollup-linux-loong64-musl@4.61.1':\n optional: true\n\n '@rollup/rollup-linux-ppc64-gnu@4.61.1':\n optional: true\n\n '@rollup/rollup-linux-ppc64-musl@4.61.1':\n optional: true\n\n '@rollup/rollup-linux-riscv64-gnu@4.61.1':\n optional: true\n\n '@rollup/rollup-linux-riscv64-musl@4.61.1':\n optional: true\n\n '@rollup/rollup-linux-s390x-gnu@4.61.1':\n optional: true\n\n '@rollup/rollup-linux-x64-gnu@4.61.1':\n optional: true\n\n '@rollup/rollup-linux-x64-musl@4.61.1':\n optional: true\n\n '@rollup/rollup-openbsd-x64@4.61.1':\n optional: true\n\n '@rollup/rollup-openharmony-arm64@4.61.1':\n optional: true\n\n '@rollup/rollup-win32-arm64-msvc@4.61.1':\n optional: true\n\n '@rollup/rollup-win32-ia32-msvc@4.61.1':\n optional: true\n\n '@rollup/rollup-win32-x64-gnu@4.61.1':\n optional: true\n\n '@rollup/rollup-win32-x64-msvc@4.61.1':\n optional: true\n\n '@types/estree@1.0.9': {}\n\n '@types/node@24.13.1':\n dependencies:\n undici-types: 7.18.2\n\n '@types/react@19.2.17':\n dependencies:\n csstype: 3.2.3\n\n acorn@8.16.0: {}\n\n any-promise@1.3.0: {}\n\n bundle-require@5.1.0(esbuild@0.27.7):\n dependencies:\n esbuild: 0.27.7\n load-tsconfig: 0.2.5\n\n cac@6.7.14: {}\n\n chokidar@4.0.3:\n dependencies:\n readdirp: 4.1.2\n\n commander@4.1.1: {}\n\n confbox@0.1.8: {}\n\n consola@3.4.2: {}\n\n csstype@3.2.3: {}\n\n debug@4.4.3:\n dependencies:\n ms: 2.1.3\n\n esbuild@0.27.7:\n optionalDependencies:\n '@esbuild/aix-ppc64': 0.27.7\n '@esbuild/android-arm': 0.27.7\n '@esbuild/android-arm64': 0.27.7\n '@esbuild/android-x64': 0.27.7\n '@esbuild/darwin-arm64': 0.27.7\n '@esbuild/darwin-x64': 0.27.7\n '@esbuild/freebsd-arm64': 0.27.7\n '@esbuild/freebsd-x64': 0.27.7\n '@esbuild/linux-arm': 0.27.7\n '@esbuild/linux-arm64': 0.27.7\n '@esbuild/linux-ia32': 0.27.7\n '@esbuild/linux-loong64': 0.27.7\n '@esbuild/linux-mips64el': 0.27.7\n '@esbuild/linux-ppc64': 0.27.7\n '@esbuild/linux-riscv64': 0.27.7\n '@esbuild/linux-s390x': 0.27.7\n '@esbuild/linux-x64': 0.27.7\n '@esbuild/netbsd-arm64': 0.27.7\n '@esbuild/netbsd-x64': 0.27.7\n '@esbuild/openbsd-arm64': 0.27.7\n '@esbuild/openbsd-x64': 0.27.7\n '@esbuild/openharmony-arm64': 0.27.7\n '@esbuild/sunos-x64': 0.27.7\n '@esbuild/win32-arm64': 0.27.7\n '@esbuild/win32-ia32': 0.27.7\n '@esbuild/win32-x64': 0.27.7\n\n fdir@6.5.0(picomatch@4.0.4):\n optionalDependencies:\n picomatch: 4.0.4\n\n fix-dts-default-cjs-exports@1.0.1:\n dependencies:\n magic-string: 0.30.21\n mlly: 1.8.2\n rollup: 4.61.1\n\n fsevents@2.3.3:\n optional: true\n\n joycon@3.1.1: {}\n\n lilconfig@3.1.3: {}\n\n lines-and-columns@1.2.4: {}\n\n load-tsconfig@0.2.5: {}\n\n magic-string@0.30.21:\n dependencies:\n '@jridgewell/sourcemap-codec': 1.5.5\n\n mlly@1.8.2:\n dependencies:\n acorn: 8.16.0\n pathe: 2.0.3\n pkg-types: 1.3.1\n ufo: 1.6.4\n\n ms@2.1.3: {}\n\n mz@2.7.0:\n dependencies:\n any-promise: 1.3.0\n object-assign: 4.1.1\n thenify-all: 1.6.0\n\n object-assign@4.1.1: {}\n\n pathe@2.0.3: {}\n\n picocolors@1.1.1: {}\n\n picomatch@4.0.4: {}\n\n pirates@4.0.7: {}\n\n pkg-types@1.3.1:\n dependencies:\n confbox: 0.1.8\n mlly: 1.8.2\n pathe: 2.0.3\n\n postcss-load-config@6.0.1:\n dependencies:\n lilconfig: 3.1.3\n\n react@19.2.7: {}\n\n readdirp@4.1.2: {}\n\n resolve-from@5.0.0: {}\n\n rollup@4.61.1:\n dependencies:\n '@types/estree': 1.0.9\n optionalDependencies:\n '@rollup/rollup-android-arm-eabi': 4.61.1\n '@rollup/rollup-android-arm64': 4.61.1\n '@rollup/rollup-darwin-arm64': 4.61.1\n '@rollup/rollup-darwin-x64': 4.61.1\n '@rollup/rollup-freebsd-arm64': 4.61.1\n '@rollup/rollup-freebsd-x64': 4.61.1\n '@rollup/rollup-linux-arm-gnueabihf': 4.61.1\n '@rollup/rollup-linux-arm-musleabihf': 4.61.1\n '@rollup/rollup-linux-arm64-gnu': 4.61.1\n '@rollup/rollup-linux-arm64-musl': 4.61.1\n '@rollup/rollup-linux-loong64-gnu': 4.61.1\n '@rollup/rollup-linux-loong64-musl': 4.61.1\n '@rollup/rollup-linux-ppc64-gnu': 4.61.1\n '@rollup/rollup-linux-ppc64-musl': 4.61.1\n '@rollup/rollup-linux-riscv64-gnu': 4.61.1\n '@rollup/rollup-linux-riscv64-musl': 4.61.1\n '@rollup/rollup-linux-s390x-gnu': 4.61.1\n '@rollup/rollup-linux-x64-gnu': 4.61.1\n '@rollup/rollup-linux-x64-musl': 4.61.1\n '@rollup/rollup-openbsd-x64': 4.61.1\n '@rollup/rollup-openharmony-arm64': 4.61.1\n '@rollup/rollup-win32-arm64-msvc': 4.61.1\n '@rollup/rollup-win32-ia32-msvc': 4.61.1\n '@rollup/rollup-win32-x64-gnu': 4.61.1\n '@rollup/rollup-win32-x64-msvc': 4.61.1\n fsevents: 2.3.3\n\n source-map@0.7.6: {}\n\n sucrase@3.35.1:\n dependencies:\n '@jridgewell/gen-mapping': 0.3.13\n commander: 4.1.1\n lines-and-columns: 1.2.4\n mz: 2.7.0\n pirates: 4.0.7\n tinyglobby: 0.2.17\n ts-interface-checker: 0.1.13\n\n thenify-all@1.6.0:\n dependencies:\n thenify: 3.3.1\n\n thenify@3.3.1:\n dependencies:\n any-promise: 1.3.0\n\n tinyexec@0.3.2: {}\n\n tinyglobby@0.2.17:\n dependencies:\n fdir: 6.5.0(picomatch@4.0.4)\n picomatch: 4.0.4\n\n tree-kill@1.2.2: {}\n\n ts-interface-checker@0.1.13: {}\n\n tsup@8.5.1(typescript@5.9.3):\n dependencies:\n bundle-require: 5.1.0(esbuild@0.27.7)\n cac: 6.7.14\n chokidar: 4.0.3\n consola: 3.4.2\n debug: 4.4.3\n esbuild: 0.27.7\n fix-dts-default-cjs-exports: 1.0.1\n joycon: 3.1.1\n picocolors: 1.1.1\n postcss-load-config: 6.0.1\n resolve-from: 5.0.0\n rollup: 4.61.1\n source-map: 0.7.6\n sucrase: 3.35.1\n tinyexec: 0.3.2\n tinyglobby: 0.2.17\n tree-kill: 1.2.2\n optionalDependencies:\n typescript: 5.9.3\n transitivePeerDependencies:\n - jiti\n - supports-color\n - tsx\n - yaml\n\n typescript@5.9.3: {}\n\n ufo@1.6.4: {}\n\n undici-types@7.18.2: {}\n",
9
+ "scripts/build-release.mjs": "import { readFileSync, writeFileSync } from 'node:fs';\nimport { createHash } from 'node:crypto';\nimport { gzipSync } from 'node:zlib';\nimport { execFileSync } from 'node:child_process';\nconst digest = (bytes, algorithm = 'sha256', encoding = 'hex') => createHash(algorithm).update(bytes).digest(encoding);\nconst pkg = JSON.parse(readFileSync('package.json', 'utf8'));\nconst bundle = readFileSync('dist/sdk.global.js');\nconst sourceFiles = ['src/index.ts', 'src/browser.ts', 'src/research.ts', 'tsup.config.ts', 'package.json', 'pnpm-lock.yaml', 'scripts/build-release.mjs'];\nconst files = Object.fromEntries(sourceFiles.map(path => [path, readFileSync(path, 'utf8')]));\nconst source = JSON.stringify({ files }, null, 2) + '\\n';\nconst sha256 = digest(bundle);\nconst sourceCommit = execFileSync('git', ['rev-parse', 'HEAD'], {encoding:'utf8'}).trim();\nconst sourceDirty = Boolean(execFileSync('git', ['status', '--porcelain', '--untracked-files=normal'], {encoding:'utf8'}).trim());\nconst artifactId = digest(JSON.stringify([pkg.version, sha256, digest(source), sourceCommit, sourceDirty]));\nconst manifest = {\n schemaVersion: 1, package: pkg.name, version: pkg.version,\n sourceCommit, sourceDirty, artifactId,\n bundle: { file: 'sdk.global.js', path: `releases/${pkg.version}/${artifactId}/sdk.global.js`, sha256,\n integrity: 'sha384-' + digest(bundle, 'sha384', 'base64'), bytes: bundle.length, gzipBytes: gzipSync(bundle).length },\n source: { file: 'source.json', sha256: digest(source) },\n runtimeDependencies: Object.keys(pkg.dependencies || {}),\n};\nwriteFileSync('dist/source.json', source);\nwriteFileSync('dist/release.json', JSON.stringify(manifest, null, 2) + '\\n');\nconsole.log(`Prepared ${pkg.name} ${pkg.version}: ${bundle.length} bytes, ${manifest.bundle.gzipBytes} gzip bytes. No publication performed.`);\n"
10
+ }
11
+ }
package/site/trust.html CHANGED
@@ -19,7 +19,7 @@
19
19
  <tr><th scope="row">Server contact receipt</th><td>A valid request updates the workspace’s last installation-contact time. An available offer binds a hash of the session identifier to a signed, expiring invitation. Connection alone does not prove an interview occurred.</td></tr>
20
20
  </tbody></table></div>
21
21
  <p>The SDK writes no cookies or localStorage. Sightspool’s own website and signed-in app have separate authentication and analytics behavior described in the <a href="https://www.sightspool.com/privacy">privacy notice</a>.</p></section>
22
- <section id="interviews"><h2>After someone chooses to participate</h2><p>Clicking the launcher opens Sightspool’s interview experience. The participant reviews eligibility, the thank-you offer and research/recording consent before starting. Recording, transcription and analysis are separate server features; they are not hidden inside the invitation SDK.</p>
22
+ <section id="interviews"><h2>After someone chooses to participate</h2><p>Clicking the launcher opens Sightspool’s interview experience in an isolated panel on the product page. Minimize preserves the same session; it does not mute, stop or withdraw an interview. The participant reviews eligibility, the thank-you offer and research/recording consent before starting. Recording, transcription and analysis are separate server features; they are not hidden inside the invitation SDK.</p>
23
23
  <ul><li><strong>Collected during an interview:</strong> consent and eligibility receipts, interview audio when agreed, transcript, operational call records and derived findings.</li><li><strong>Access:</strong> research belongs to the client workspace. Its authorized members and the service processes needed to deliver the interview have access under the product’s access controls.</li><li><strong>Retention and withdrawal:</strong> the approved interview specifies its retention period, up to 30 days in the current User Hours flow. Withdrawal or expiry triggers removal of audio, transcripts and derived findings. Removing the SDK only stops future recruitment; it does not delete interview records, account records or provider logs.</li><li><strong>Providers:</strong> hosting/storage use Vercel and Supabase; the voice path uses LiveKit, with Twilio for configured phone handoff; transcription/analysis use OpenAI. The exact path depends on the enabled features. See the privacy notice for wider product providers and contact us for a deployment-specific processing inventory.</li><li><strong>AI use:</strong> interview material is processed to transcribe and analyse the research. The privacy notice states that workspace content and research responses are not used to train models.</li></ul>
24
24
  <p class="note">The browser SDK source cannot establish hosting regions, provider log retention or backup deletion times. A verified deployment-specific statement covering those details and the full provider inventory is still being prepared. If your organization requires it for installation approval, contact <a href="mailto:privacy@sightspool.com">privacy@sightspool.com</a> before proceeding.</p></section>
25
25
  <section id="verify"><h2>Inspect the exact release</h2><p>Version numbers identify a release; hashes identify its bytes. The script URL includes both, and an integrity attribute lets the browser reject a modified bundle. The manifest and source snapshot are available beside the bundle.</p>
@@ -29,7 +29,7 @@
29
29
  <h3>npm installation</h3><pre><code>npm install --save-exact @sightspool/sdk@0.4.0</code></pre><p>Commit your lockfile and review upgrades. Our release workflow uses npm trusted publishing with provenance and verifies the tag against the package version. Check the package’s npm provenance record to verify the publishing workflow. Provenance links source and build history; it does not certify that code is secure. <a href="https://docs.npmjs.com/viewing-package-provenance/">How to check npm provenance</a>.</p>
30
30
  <h3>Script installation and self-hosting</h3><p>Go live supplies your workspace key, exact script URL, SHA-384 integrity, anonymous cross-origin loading and no-referrer policy. Keep them together. Avoid the mutable <code>/sdk.global.js</code> alias when you need controlled upgrades.</p><p>You can download and serve the reviewed bundle yourself. Keep its hash unchanged and explicitly set <code>data-sightspool-endpoint</code> to your Sightspool workspace host; otherwise script auto-init uses the bundle’s host. Self-hosting the bundle does not self-host the interview service.</p>
31
31
  <details><summary>Verify the downloaded bundle yourself</summary><pre><code>openssl dgst -sha384 -binary sdk.global.js | openssl base64 -A</code></pre><p>Compare the result with the manifest’s integrity value after its <code>sha384-</code> prefix. Review the source snapshot as well: a hash supplied beside a file establishes consistency, not independent trust in the publisher.</p></details></section>
32
- <section id="controls"><h2>You control where it runs</h2><ul><li><strong>Audience:</strong> choose <code>all_visitors</code> or <code>signed_in</code>. There is no implicit default. Match this to your approved research plan.</li><li><strong>Pause:</strong> <code>Sightspool.pause()</code> removes the invitation and stops offer requests. <code>resume()</code> resumes for the selected audience.</li><li><strong>Remove:</strong> call <code>destroy()</code> to remove the launcher, timers and listeners, then remove the loader/component and package. An already opened interview is not silently ended. You may remove the specific sessionStorage key above if you also want to clear the SDK’s local session identifier.</li><li><strong>Rollback:</strong> retain the previously reviewed bundle and matching hash or restore the previous lockfile. Test compatibility with the current interview service; do not roll back to the retired 0.3.x capture API.</li></ul>
32
+ <section id="controls"><h2>You control where it runs</h2><ul><li><strong>Audience:</strong> choose <code>all_visitors</code> or <code>signed_in</code>. There is no implicit default. Match this to your approved research plan.</li><li><strong>Pause:</strong> <code>Sightspool.pause()</code> removes the invitation and stops offer requests. <code>resume()</code> resumes for the selected audience.</li><li><strong>Remove:</strong> call <code>destroy()</code> to stop recruitment timers and listeners, then remove the loader/component and package. An already opened interview panel remains reachable until you leave the page. You may remove the specific sessionStorage key above if you also want to clear the SDK’s local session identifier.</li><li><strong>Rollback:</strong> retain the previously reviewed bundle and matching hash or restore the previous lockfile. Test compatibility with the current interview service; do not roll back to the retired 0.3.x capture API.</li></ul>
33
33
  <p>Offer polling runs every 15 seconds while the page is visible and the selected audience is eligible, plus initialization and explicit lifecycle changes. One current request is in flight, with a ten-second timeout. Pausing, identity changes, hiding the tab and destruction invalidate late responses. Failures remove the invitation and are handled without throwing into normal host-page interaction.</p><p>Bundle and gzip sizes above are measured build artifacts, not a promise about page speed. Test your app’s responsiveness, layout, network use, slow loads, blocked storage and logout on your supported browsers. The SDK uses inline launcher styling; validate it against your CSP. Preserve your CSP and nonce rules—do not disable them to make installation pass.</p></section>
34
34
  <section id="security"><h2>Security questions and review status</h2><p><strong>No independent security audit is claimed for research SDK 0.4.0.</strong> Automated checks cover request fields, identity exclusion, stale responses, cleanup and artifact integrity. Their scope is narrower than a security assessment of the SDK and interview backend together.</p><p>Report a suspected vulnerability privately to <a href="mailto:security@sightspool.com">security@sightspool.com</a>. Include the version, affected feature and a minimal reproduction without customer data. See <a href="https://www.sightspool.com/.well-known/security.txt">security.txt</a> for the existing disclosure contact and scope. Data-access and deletion questions go to <a href="mailto:privacy@sightspool.com">privacy@sightspool.com</a>.</p></section>
35
35
  <footer>Apache-2.0 · <a href="https://github.com/sightspool/sdk">SDK source</a> · <a href="https://www.sightspool.com/privacy">Privacy notice</a> · <a href="index.html">Installation guide</a></footer>