@sightspool/sdk 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +45 -1
- package/README.md +64 -7
- package/dist/index.cjs +290 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -1
- package/dist/index.d.ts +17 -1
- package/dist/index.js +290 -30
- package/dist/index.js.map +1 -1
- package/dist/release.json +9 -9
- package/dist/sdk.global.js +52 -2
- package/dist/sdk.global.js.map +1 -1
- package/dist/source.json +3 -3
- package/package.json +1 -1
- package/site/demo.js +10 -1
- package/site/index.html +15 -5
- package/site/releases/0.4.0/781ab339e13fe18b86b56cc9d84dd308bebf6482a885c8aae46a8b3b84801f6e/release.json +21 -0
- package/site/releases/0.4.0/781ab339e13fe18b86b56cc9d84dd308bebf6482a885c8aae46a8b3b84801f6e/sdk.global.js +3 -0
- package/site/releases/0.4.0/781ab339e13fe18b86b56cc9d84dd308bebf6482a885c8aae46a8b3b84801f6e/sdk.global.js.map +1 -0
- package/site/releases/0.4.0/781ab339e13fe18b86b56cc9d84dd308bebf6482a885c8aae46a8b3b84801f6e/source.json +11 -0
- package/site/releases/0.4.1/116419831a4c93513a867dcb4d3b808e8802ee27f5ad732460c997cefb8c804b/release.json +21 -0
- package/site/releases/0.4.1/116419831a4c93513a867dcb4d3b808e8802ee27f5ad732460c997cefb8c804b/sdk.global.js +3 -0
- package/site/releases/0.4.1/116419831a4c93513a867dcb4d3b808e8802ee27f5ad732460c997cefb8c804b/sdk.global.js.map +1 -0
- package/site/releases/0.4.1/116419831a4c93513a867dcb4d3b808e8802ee27f5ad732460c997cefb8c804b/source.json +11 -0
- package/site/trust.html +3 -3
package/dist/source.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
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
|
-
"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",
|
|
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 const theme = script?.dataset.sightspoolTheme;\n api.init({ key, audience, theme: theme === \"light\" || theme === \"auto\" ? theme : \"dark\", 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 /** Match the client site, or follow the visitor’s system preference. */\n theme?: \"light\" | \"dark\" | \"auto\";\n};\nexport type ResearchStatus =\n | \"not_initialized\" | \"signed_out\" | \"paused\" | \"checking\"\n | \"unavailable\" | \"available\" | \"error\";\n\ntype Runtime = {\n theme: \"light\" | \"dark\" | \"auto\";\n key: string; endpoint: string; audience: SightspoolConfig[\"audience\"]; identity: string | null; paused: boolean;\n disposed: boolean; generation: number; device: string; offer: string | null;\n button: HTMLButtonElement | null; panel: HTMLElement | null; frame: HTMLIFrameElement | null;\n attention: boolean; dismissed: boolean; completed: boolean; restoring: boolean; copy: {title:string;subtitle:string}; shell: HTMLElement | null; dismiss: HTMLButtonElement | null;\n expanded: boolean; message: ((event: MessageEvent) => void) | null;\n pending: AbortController | null; timer: number; visibility: () => void;\n status: ResearchStatus;\n};\n// The server accepts a trimmed, non-empty id of at most this length\n// (apps/web/lib/cohortIdentity.ts). Trimming before the bound is applied keeps\n// both sides agreeing on exactly which ids are valid.\nconst MAX_IDENTITY_LENGTH = 200;\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 markerKey = (r: Runtime) => \"sightspool-widget-session:\" + r.key;\nfunction saveMarker(r: Runtime, value: string) { try { sessionStorage.setItem(markerKey(r), value); } catch {} }\nconst eligible = (r: Runtime) => r.audience === \"all_visitors\" || r.identity !== null;\n\nfunction remove(r: Runtime) {\n // An opened interview owns its lifetime. Recruitment changes cannot end it.\n if (r.frame || r.restoring) return;\n r.shell?.remove(); r.shell = null; r.dismiss = null; 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 renderLauncher(r: Runtime) {\n if (!r.button) return;\n const iconOnly = r.completed || (r.dismissed && !r.expanded);\n r.button.className = \"ss-launcher\" + (iconOnly ? \" ss-iconOnly\" : !r.frame && r.attention ? \" ss-attention\" : \"\");\n const fresh = r.button.children.length === 0;\n const mark = (r.button.children[0] as HTMLElement) ?? document.createElement(\"span\"); mark.className = \"ss-launcherMark\";\n if (fresh) {const logo = document.createElement(\"img\"); logo.src = r.endpoint + \"/sightspool-monomark-dark-bg.svg\"; logo.width = 29; logo.height = 29; logo.alt = \"\"; mark.appendChild(logo);}\n const copy = (r.button.children[1] as HTMLElement) ?? document.createElement(\"span\"); copy.className = \"ss-launcherCopy\"; copy.setAttribute(\"aria-hidden\", String(iconOnly));\n const title = (copy.children[0] as HTMLElement) ?? document.createElement(\"strong\"); title.textContent = r.completed ? \"Your thank-you\" : r.expanded ? \"Minimise conversation\" : r.frame ? \"Back to your conversation\" : r.copy.title;\n const subtitle = (copy.children[1] as HTMLElement) ?? document.createElement(\"small\"); subtitle.textContent = r.completed ? \"Your accepted offer is saved here\" : r.frame ? \"Return to your interview\" : r.copy.subtitle;\n if(fresh){copy.appendChild(title); copy.appendChild(subtitle);}\n const arrow = (r.button.children[2] as HTMLElement) ?? document.createElement(\"span\"); arrow.className = \"ss-launcherAction\"; arrow.textContent = r.expanded ? \"−\" : \"→\"; arrow.setAttribute(\"aria-hidden\", \"true\");\n if(fresh){r.button.appendChild(mark); r.button.appendChild(copy); r.button.appendChild(arrow);}\n r.button.setAttribute(\"aria-label\", r.completed ? \"View your accepted thank-you\" : iconOnly ? \"Show invitation message\" : title.textContent + \". \" + subtitle.textContent);\n if (r.completed && !r.button.children[3]) { const check = document.createElement(\"span\"); check.className=\"ss-receiptCheck\"; check.textContent=\"✓\"; check.setAttribute(\"aria-hidden\",\"true\"); r.button.appendChild(check); }\n if (r.dismiss) { r.dismiss.disabled = Boolean(r.frame || r.dismissed); r.dismiss.setAttribute(\"data-visible\", String(!r.frame && !r.dismissed)); }\n}\nfunction mountLauncher(r: Runtime) {\n if (r.button) { renderLauncher(r); return; }\n const shell = document.createElement(\"aside\"); shell.className=\"ss-widgetPosition\"; shell.setAttribute(\"data-theme\",r.theme); shell.setAttribute(\"aria-label\",\"Sightspool research\");\n const style = document.createElement(\"style\"); style.textContent = `.ss-widgetPosition { position: fixed; bottom: max(22px,env(safe-area-inset-bottom)); right: max(24px,env(safe-area-inset-right)); z-index: 60; width: min(366px,calc(100vw - 32px)); pointer-events: none; }\n.ss-widgetPosition > section[data-open=\"true\"], .ss-launcher, .ss-dismissInvitation[data-visible=\"true\"] { pointer-events: auto; }\n.ss-launcherRow { display: flex; align-items: center; justify-content: flex-end; margin-top: 12px; }\n.ss-launcher { position: relative; width: max-content; max-width: 100%; interpolate-size: allow-keywords; height: 64px; min-height: 54px; display: grid; grid-template-columns: 44px minmax(0,1fr) 18px; align-items: center; gap: 12px; padding: 8px 19px 8px 9px; color: #fff; background: #18181b; border: 1px solid #ffffff20; border-radius: 999px; box-shadow: 0 8px 32px #18101d30,0 2px 6px #18101d18; text-align: left; transition: width 460ms cubic-bezier(.22,1,.36,1), height 360ms cubic-bezier(.22,1,.36,1), padding 460ms cubic-bezier(.22,1,.36,1), gap 460ms cubic-bezier(.22,1,.36,1), grid-template-columns 460ms cubic-bezier(.22,1,.36,1), transform 180ms, box-shadow 240ms; }\n.ss-launcherGroup { position: relative; max-width: 100%; }\n.ss-dismissInvitation { position: absolute; top: -12px; right: 0; width: 28px; height: 28px; display: grid; place-items: center; border: 1px solid #dfdbe0; border-radius: 50%; background: #fcfaf9; color: #6e6470; box-shadow: 0 2px 8px #18101d15; opacity: 0; transform: scale(.7); visibility: hidden; transition: opacity 140ms, transform 240ms, visibility 0s 240ms; }\n.ss-dismissInvitation[data-visible=\"true\"] { opacity: 1; transform: scale(1); visibility: visible; transition-delay: 220ms,220ms,0s; }\n.ss-dismissInvitation:hover { color: #241e25; background: #eee8ed; }\n.ss-launcher.ss-attention { animation: invitation-enter 450ms cubic-bezier(.2,.8,.2,1) both, invitation-glow 2400ms 450ms ease-in-out; }\n@keyframes invitation-enter { from { opacity: 0; transform: translateY(12px) scale(.96); } to { opacity: 1; transform: translateY(0) scale(1); } }\n@keyframes invitation-glow { 0%,100% { box-shadow: 0 8px 32px #18101d30,0 0 0 0 #c6fa6400; } 25%,70% { box-shadow: 0 8px 32px #18101d30,0 0 0 5px #c6fa6440,0 0 28px #a8176830; } 48% { box-shadow: 0 8px 32px #18101d30,0 0 0 1px #c6fa6410; } }\n.ss-launcher.ss-iconOnly { position: relative; min-height: 54px; width: 54px; height: 54px; padding: 4px; gap: 0; grid-template-columns: 44px minmax(0,0fr) 0px; box-shadow: 0 3px 14px #18101d20; }\n.ss-compactActiveBadge { position: absolute; right: 1px; top: 1px; width: 10px; height: 10px; border: 2px solid #18181b; border-radius: 50%; background: #c6fa64; }\n.ss-launcher::after { content: \"\"; position: absolute; inset: 0; border-radius: inherit; pointer-events: none; opacity: 0; box-shadow: -5px 0 20px #a8176830,5px 0 18px #c6fa641c,inset 0 0 0 1px #ffffff12; transition: opacity 280ms ease; }\n@media (hover: hover) {\n .ss-launcher:not(.ss-iconOnly):hover::after { opacity: 1; animation: hover-halo 2200ms ease-in-out infinite; }\n .ss-launcher:not(.ss-iconOnly):hover .ss-launcherMark::before { animation: hover-logo-glow 2200ms ease-in-out infinite; }\n}\n.ss-launcher:focus-visible::after { opacity: 1; }\n@keyframes hover-halo { 0%,100% { opacity: .4; } 50% { opacity: 1; } }\n@keyframes hover-logo-glow { 0%,100% { opacity: .925; transform: scale(1); } 50% { opacity: 1; transform: scale(1.08); filter: brightness(1.18); } }\n.ss-launcherMark { position: relative; isolation: isolate; display: grid; place-items: center; width: 44px; height: 44px; flex-shrink: 0; border-radius: 50%; }\n.ss-launcherMark::before { content: \"\"; position: absolute; inset: -8px; border-radius: 50%; pointer-events: none; background: radial-gradient(ellipse at 30% 30%,rgb(168 23 104 / 87%),transparent 72%),radial-gradient(ellipse at 74% 68%,rgb(198 250 100 / 50%),transparent 70%); mask-image: radial-gradient(closest-side,#000 59%,transparent 100%); opacity: .925; }\n.ss-launcherMark img { position: relative; }\n.ss-launcherCopy { min-width: 0; overflow: hidden; white-space: nowrap; opacity: 1; transform: translateX(0); transition: opacity 200ms 100ms, transform 360ms cubic-bezier(.22,1,.36,1); }\n.ss-iconOnly .ss-launcherCopy { opacity: 0; transform: translateX(12px); transition-delay: 0s; }\n.ss-launcherAction { display: grid; place-items: center; overflow: hidden; color: #c6fa64; opacity: 1; transition: opacity 180ms 140ms, transform 360ms; }\n.ss-iconOnly .ss-launcherAction { opacity: 0; transform: translateX(8px); transition-delay: 0s; }\n.ss-launcherCopy strong { display: block; font-size: 13px; font-weight: 600; line-height: 1.4; }\n.ss-launcherCopy small { display: block; margin-top: 4px; font-size: 12px; color: #d5d0d8; line-height: 1.4; }\n.ss-launcher > svg { flex-shrink: 0; color: #c6fa64; }\n.ss-activeBadge { flex-shrink: 0; width: 8px; height: 8px; background: #c6fa64; border-radius: 50%; box-shadow: 0 0 0 4px #c6fa6418; }\n\n.ss-widgetPosition{z-index:2147483000;font:14px Arial,Helvetica,sans-serif;line-height:1.5}\n.ss-widgetPosition *{box-sizing:border-box}\n.ss-widgetPosition button{font:inherit;cursor:pointer;outline-offset:4px}\n.ss-panel{position:absolute;bottom:calc(100% + 12px);right:0;width:100%;height:610px;max-height:calc(100dvh - max(22px,env(safe-area-inset-bottom)) - 104px);border:1px solid #e7e0e6;border-radius:20px;background:#fcfaf9;color:#241e25;box-shadow:0 16px 64px #18101d26;overflow:hidden;display:flex;flex-direction:column;transform-origin:bottom right;opacity:0;visibility:hidden;transform:translateY(18px) scale(.82,.2);transition:transform 340ms cubic-bezier(.4,0,.6,1),opacity 180ms,visibility 0s 340ms;pointer-events:none}\n.ss-panel[data-open=\"true\"]{opacity:1;visibility:visible;transform:translateY(0) scale(1);transition:transform 480ms cubic-bezier(.16,1,.3,1),opacity 200ms,visibility 0s;pointer-events:auto}\n.ss-panel>div,.ss-panel>iframe{opacity:0;transition:opacity 120ms}\n.ss-panel[data-open=\"true\"]>div,.ss-panel[data-open=\"true\"]>iframe{opacity:1;transition:opacity 240ms 170ms}\n.ss-panel>div{display:flex;align-items:center;justify-content:space-between;padding:12px 18px;border-bottom:1px solid #e7e0e6}\n.ss-panel>div>button{border:0;background:transparent;color:#554b58;padding:8px;border-radius:8px}\n.ss-panel iframe{display:block;width:100%;flex:1;min-height:0;border:0;background:#fcfaf9}\n[data-theme=\"dark\"] .ss-panel{background:#18181b;color:#fff;border-color:#ffffff1c}\n[data-theme=\"dark\"] .ss-panel>div{border-color:#ffffff1c}\n[data-theme=\"dark\"] .ss-panel>div>button{color:#cbc5cd}\n@media(prefers-color-scheme:dark){[data-theme=\"auto\"] .ss-panel{background:#18181b;color:#fff;border-color:#ffffff1c}[data-theme=\"auto\"] .ss-panel>div{border-color:#ffffff1c}[data-theme=\"auto\"] .ss-panel>div>button{color:#cbc5cd}}\n.ss-receiptCheck{position:absolute;right:0;top:0;display:grid;place-items:center;width:16px;height:16px;border:2px solid #18181b;border-radius:50%;background:#c6fa64;color:#18181b;font-size:10px}\n@media(max-width:560px){.ss-widgetPosition{bottom:max(12px,env(safe-area-inset-bottom));right:auto;left:50%;transform:translateX(-50%);width:min(366px,calc(100vw - 24px))}.ss-panel{max-height:calc(100dvh - max(12px,env(safe-area-inset-bottom)) - 100px);transform-origin:bottom center}.ss-launcher{gap:10px;padding-right:15px;height:80px}.ss-launcherCopy{white-space:normal}.ss-launcherRow{justify-content:center}}\n@media(prefers-reduced-motion:reduce){.ss-widgetPosition *,.ss-widgetPosition *::before,.ss-widgetPosition *::after{animation:none!important;transition:none!important}}\n`;\n shell.appendChild(style);\n const row = document.createElement(\"div\"); row.className=\"ss-launcherRow\";\n const group = document.createElement(\"div\"); group.className=\"ss-launcherGroup\";\n const button = document.createElement(\"button\"); button.type=\"button\";\n const dismiss = document.createElement(\"button\"); dismiss.type=\"button\"; dismiss.className=\"ss-dismissInvitation\"; dismiss.textContent=\"×\";\n dismiss.setAttribute(\"aria-label\",\"Dismiss invitation and keep a small button\");\n dismiss.onclick=()=>{r.attention=false;r.dismissed=true;try{sessionStorage.setItem(\"sightspool-widget-dismissed:\"+r.key,\"1\");}catch{}renderLauncher(r);button.focus();};\n button.onanimationend=(event)=>{if(event.animationName.includes(\"invitation-glow\")){r.attention=false;renderLauncher(r);}};\n button.onclick=()=>{\n if(r.completed || r.frame || r.restoring){if(r.frame)expand(r,!r.expanded);else openPanel(r);return;}\n if(r.dismissed){r.attention=false;r.dismissed=false;try{sessionStorage.setItem(\"sightspool-widget-dismissed:\"+r.key,\"0\");}catch{}renderLauncher(r);return;}\n if(!r.disposed&&!r.paused&&eligible(r))openPanel(r);\n };\n group.appendChild(button);group.appendChild(dismiss);row.appendChild(group);shell.appendChild(row);\n r.shell=shell;r.button=button;r.dismiss=dismiss;document.body.appendChild(shell);renderLauncher(r);\n}\nfunction expand(r: Runtime, expanded: boolean) {\n if (!r.panel || !r.frame || !r.button) return;\n if (r.expanded === expanded) return;\n const ownedFocus = r.panel.contains(document.activeElement);\n r.expanded = expanded;\n r.panel.setAttribute(\"data-open\", String(expanded));\n r.panel.setAttribute(\"aria-hidden\", String(!expanded));\n r.panel.inert = !expanded;\n r.button.setAttribute(\"aria-expanded\", String(expanded));\n renderLauncher(r);\n if (expanded) r.panel.querySelector<HTMLButtonElement>(\"button\")?.focus();\n else if (ownedFocus) r.button.focus();\n}\nfunction openPanel(r: Runtime) {\n if (r.frame) { expand(r, true); return; }\n if ((!r.offer && !r.restoring) || !r.button || !r.shell) return;\n const panel = document.createElement(\"section\"); panel.className=\"ss-panel\";\n panel.id=\"sightspool-interview-panel\";panel.setAttribute(\"role\",\"dialog\");panel.setAttribute(\"aria-label\",\"Sightspool interview\");\n const header=document.createElement(\"div\");const title=document.createElement(\"strong\");title.textContent=\"Sightspool\";\n const minimize=document.createElement(\"button\");minimize.type=\"button\";minimize.textContent=\"Minimise\";\n const hide=()=>expand(r,false);minimize.onclick=hide;header.appendChild(title);header.appendChild(minimize);panel.appendChild(header);\n const frame=document.createElement(\"iframe\");frame.title=\"Sightspool research conversation\";frame.allow=\"microphone; autoplay; clipboard-write\";\n frame.setAttribute(\"sandbox\",\"allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox\");frame.referrerPolicy=\"no-referrer\";\n const url=new URL(r.endpoint+\"/interview-widget\");url.searchParams.set(\"key\",r.key);url.searchParams.set(\"theme\",r.theme);\n url.hash=new URLSearchParams(r.restoring ? {device:r.device,restore:\"1\"} : {offer:r.offer!,device:r.device}).toString();frame.src=url.href;\n panel.appendChild(frame);panel.onkeydown=(event)=>{if(event.key===\"Escape\"){event.preventDefault();hide();}};\n r.panel=panel;r.frame=frame;\n r.message=(event)=>{\n if(event.source!==frame.contentWindow||event.origin!==r.endpoint)return;\n if(event.data?.type===\"sightspool:panel:minimize\")hide();\n if(event.data?.type===\"sightspool:interview:accepted\"){saveMarker(r,\"active\");}\n if(event.data?.type===\"sightspool:interview:ended\"){r.completed=true;saveMarker(r,\"ended\");renderLauncher(r);}\n };\n window.addEventListener(\"message\",r.message);r.button.setAttribute(\"aria-controls\",panel.id);r.shell.appendChild(panel);expand(r,true);\n}\n\nasync function check(r: Runtime) {\n if (r.frame || r.restoring || 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 // Only the offer call carries the identity. The interview page is served by\n // Sightspool itself and is handed nothing but the offer and device, so the\n // cohort decision is frozen into the signed offer instead (SIG-122 §10).\n const payload: { operation: string; key: string; device: string; identity?: string } =\n { operation: \"offer\", key: r.key, device: r.device };\n if (r.identity) payload.identity = r.identity;\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(payload),\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 (result.invitation && typeof result.invitation.title === \"string\" && typeof result.invitation.subtitle === \"string\") {\n r.copy={title:result.invitation.title.slice(0,200),subtitle:result.invitation.subtitle.slice(0,240)};\n }\n mountLauncher(r);\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 || previous?.restoring) 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 theme: config.theme === \"light\" || config.theme === \"auto\" ? config.theme : \"dark\",\n key: config.key, endpoint: url.origin, audience: config.audience, identity: null, paused: false,\n attention: true, dismissed: false, completed: false, restoring: false, copy:{title:\"Share your experience\",subtitle:\"A research conversation\"}, shell:null,dismiss:null,\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 try {\n r.dismissed=sessionStorage.getItem(\"sightspool-widget-dismissed:\"+r.key)===\"1\";\n const marker=sessionStorage.getItem(markerKey(r));\n r.restoring=marker===\"active\"||marker===\"ended\";r.completed=marker===\"ended\";\n } catch {}\n browser[slot] = r;\n if(r.restoring){mountLauncher(r);return;}\n\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/**\n * Identify the signed-in user, and carry that id to the offer request so a\n * cohort-gated research card can tell whether this person is in its cohort.\n *\n * The id is held in memory for the page's lifetime and sent, over TLS, only to\n * this workspace's own Sightspool endpoint, only on `operation: \"offer\"`. It is\n * hashed workspace-scoped on arrival and never stored raw in any table, log or\n * payload. The SDK never writes it to sessionStorage, localStorage, a cookie, a\n * URL, a fragment or a log.\n *\n * `null` clears it, as does any value that is not a non-empty string of at most\n * 200 characters after trimming. An over-length id is never truncated: a\n * truncated id is a different person, and on a gated card that invites the\n * wrong one. Failing as unidentified is the safe direction.\n */\nexport function identify(userId: string | null | undefined): void {\n try {\n const r = current();\n if (!r) return;\n const trimmed = typeof userId === \"string\" ? userId.trim() : \"\";\n const identity = trimmed.length > 0 && trimmed.length <= MAX_IDENTITY_LENGTH ? trimmed : null;\n // Re-stating the same person leaves their live invitation alone. Any change,\n // including to unidentified, takes the previous person's pending check and\n // minted offer with it, so an account switch never inherits an invitation.\n if (identity === r.identity) { if (!r.paused && eligible(r)) void check(r); return; }\n invalidate(r);\n r.identity = identity;\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 && !r.restoring) 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.
|
|
7
|
+
"package.json": "{\n \"name\": \"@sightspool/sdk\",\n \"version\": \"0.5.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
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
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
|
-
|
|
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;
|
package/site/index.html
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<!doctype html>
|
|
2
2
|
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Sightspool research SDK</title><link rel="stylesheet" href="styles.css"></head><body><main class="wrap"><nav aria-label="SDK documentation"><a href="index.html">SDK guide</a><a href="trust.html">Data & security</a><a href="demo.html">Try the SDK</a><a href="https://github.com/sightspool/sdk">GitHub</a></nav><header><h1>@sightspool/sdk</h1></header>
|
|
3
3
|
<p>Embed Sightspool research in your product. The SDK invites website visitors and signed-in users into owner-approved research during User Hours, then opens Sightspool for consent and the interview. It does not capture page content, behavior, form values or analytics.</p>
|
|
4
|
-
<p>This is the <strong>0.4.0
|
|
4
|
+
<p>This is the <strong>research API introduced in 0.4.0</strong>, a deliberate change from the old capture SDK. Install the exact version below and review upgrades before deploying them — 0.5.0 narrows a privacy promise, so read the CHANGELOG before taking it. Old capture code and documentation are archived in <code>docs/legacy</code>.</p>
|
|
5
5
|
<p>Read the <a href="https://www.sightspool.com/sdk/data-security">data and security guide</a>, inspect the <a href="https://github.com/sightspool/sdk">public source</a>, or try the <a href="https://www.sightspool.com/sdk/demo">isolated SDK demo</a>.</p>
|
|
6
6
|
<h2>npm</h2>
|
|
7
|
-
<pre><code>npm install --save-exact @sightspool/sdk@0.
|
|
7
|
+
<pre><code>npm install --save-exact @sightspool/sdk@0.5.0</code></pre>
|
|
8
8
|
<pre><code>import Sightspool from '@sightspool/sdk'
|
|
9
9
|
|
|
10
10
|
Sightspool.init({ key: 'YOUR_GO_LIVE_WIDGET_UUID', audience: 'all_visitors' })
|
|
@@ -18,7 +18,11 @@ Sightspool.identify(null)
|
|
|
18
18
|
Sightspool.destroy()</code></pre>
|
|
19
19
|
<p>Choose an explicit <code>audience</code> matching the approved interview plan:</p>
|
|
20
20
|
<p>- <code>all_visitors</code>: anonymous website visitors and signed-in users. Initialize once on the relevant website pages. No identity or login is required; logout continues recruitment as a visitor. - <code>signed_in</code>: only users with a real signed-in session. Initialize in your authenticated app shell and call <code>identify(actualUserId)</code> after authentication resolves. Until then, no request or invitation is made. <code>identify(null)</code> immediately removes invitations on logout.</p>
|
|
21
|
-
<p>There is no default audience. Missing or invalid audience configuration stays idle
|
|
21
|
+
<p>There is no default audience. Missing or invalid audience configuration stays idle.</p>
|
|
22
|
+
<p><code>identify</code> sends the id you pass, over TLS, to your own workspace's Sightspool endpoint, on the offer request only. Sightspool hashes it workspace-scoped on arrival and never stores it raw in any table, log or payload. The SDK holds it in memory for the page's lifetime and never writes it to storage, a cookie, a URL, a fragment or a log. <strong>Through 0.4.2 the id was never transmitted; from 0.5.0 it is</strong> — it is what lets a cohort-targeted card tell whether you are one of the people its research question is about. Do not invent IDs for visitors. This client setting controls display; it does not authenticate a participant or change the server-approved research cohort.</p>
|
|
23
|
+
<p>An id that is not a non-empty string of at most 200 characters after trimming is treated as unidentified, exactly like <code>identify(null)</code>. Over-length ids are never truncated: a truncated id is a different person.</p>
|
|
24
|
+
<h2>Matching your analytics identity</h2>
|
|
25
|
+
<p>Cohort-targeted cards match against journey cohorts derived from PostHog. Your product <strong>must call <code>posthog.identify()</code> with the same id it passes Sightspool.</strong> If you identify PostHog with an email and Sightspool with an internal UUID, those are different identities that never meet — the match rate is a flat zero that looks exactly like an empty cohort rather than like a misconfiguration.</p>
|
|
22
26
|
<h2>Script tag</h2>
|
|
23
27
|
<p>For visitors, no authentication integration is needed:</p>
|
|
24
28
|
<pre><code><script async src="https://www.sightspool.com/sdk.global.js"
|
|
@@ -42,7 +46,7 @@ Sightspool.destroy()</code></pre>
|
|
|
42
46
|
data-sightspool-audience="signed_in"></script></code></pre>
|
|
43
47
|
<p>Go live supplies the correct key and endpoint. Local keys belong to the local Sightspool database and cannot be paired with production. Script installs default to the origin serving the bundle; <code>data-sightspool-endpoint</code> overrides that for a CDN or self-hosted install. npm defaults to <code>https://www.sightspool.com</code>.</p>
|
|
44
48
|
<h2>API</h2>
|
|
45
|
-
<table><tr><th>Method</th><th>Behavior</th></tr><tr><td><code>init({ key, audience, endpoint? })</code></td><td>Start research for the explicit audience; UUID key required. Repeating the same configuration is idempotent. Changing it tears down the previous runtime.</td></tr><tr><td><code>identify(userId)</code></td><td>Enable signed-in eligibility
|
|
49
|
+
<table><tr><th>Method</th><th>Behavior</th></tr><tr><td><code>init({ key, audience, endpoint? })</code></td><td>Start research for the explicit audience; UUID key required. Repeating the same configuration is idempotent. Changing it tears down the previous runtime.</td></tr><tr><td><code>identify(userId)</code></td><td>Enable signed-in eligibility, and send that id on the offer request so a cohort-targeted card can match it. Held in memory only, never stored. <code>null</code> clears it — as does any value that is not a non-empty string of at most 200 characters after trimming. Signed-in-only invitations disappear; all-visitors recruitment continues.</td></tr><tr><td><code>pause()</code></td><td>Pause recruitment, abort the current request and remove the launcher.</td></tr><tr><td><code>resume()</code></td><td>Resume recruitment for the configured audience. Does not bypass approval or hours.</td></tr><tr><td><code>destroy()</code></td><td>Remove the launcher, listeners and polling. An already opened interview is not silently ended.</td></tr><tr><td><code>getStatus()</code></td><td><code>not_initialized</code>, <code>signed_out</code>, <code>paused</code>, <code>checking</code>, <code>unavailable</code>, <code>available</code> or <code>error</code>.</td></tr></table>
|
|
46
50
|
<p><code>pause</code> and <code>resume</code> are recruitment controls, not consent to record. Participants still review the offer, eligibility and recording consent in Sightspool. Owners approve the research setup separately. The backend remains authoritative for availability, capacity, signed offers and interview admission.</p>
|
|
47
51
|
<h2>React / Next.js</h2>
|
|
48
52
|
<p>Use a client component on the pages included in your research. Identity is optional for all visitors:</p>
|
|
@@ -71,4 +75,10 @@ export function Research({ userId = null }: { userId?: string | null }) {
|
|
|
71
75
|
pnpm -r --include-workspace-root build
|
|
72
76
|
pnpm -r --include-workspace-root test
|
|
73
77
|
npm publish --dry-run</code></pre>
|
|
74
|
-
<p>The npm and browser builds share <code>src/research.ts</code>. The Sightspool app copies <code>sdk.global.js</code> from this package and serves <code>research-widget.js</code> as a byte-identical alias for recent internal snippets. Neither build imports the former capture engine. See <code>docs/research-sdk-transition.md</code> for release sequencing. Apache-2.0.</p
|
|
78
|
+
<p>The npm and browser builds share <code>src/research.ts</code>. The Sightspool app copies <code>sdk.global.js</code> from this package and serves <code>research-widget.js</code> as a byte-identical alias for recent internal snippets. Neither build imports the former capture engine. See <code>docs/research-sdk-transition.md</code> for release sequencing. Apache-2.0.</p>
|
|
79
|
+
<h2>Embedded interview panel</h2>
|
|
80
|
+
<p>An available invitation opens one bottom-right panel on the product page. Consent, waiting, supported founder audio or Sightspool text conversation, and completion remain inside its isolated Sightspool iframe. Minimize or Escape hides the panel; Return to interview reopens the same session. Minimize does not mute, end or withdraw. Use the explicit in-panel controls to stop audio or delete interview evidence.</p>
|
|
81
|
+
<p>Allow the configured Sightspool origin in your site's <code>frame-src</code> policy and permit microphone delegation to that origin if founder audio is used. Preserve other CSP and Permissions Policy restrictions; a host policy may intentionally block audio. The iframe requests a microphone only after explicit participant action. The frame is restricted to the workspace's saved product origin and exchanges only presentation and accepted/ended lifecycle messages with its parent, never interview text, capabilities or audio. Interviews stay in the panel. Privacy and Sightspool attribution links open a separate tab.</p>
|
|
82
|
+
<p>Once opened, the panel stays mounted across visibility changes, pause, identity changes and SDK destroy/remount, so recruitment cleanup cannot silently end a call. Destroy stops further recruitment. The active panel remains reachable until the page is left. A full document navigation interrupts media. A tab-scoped marker restores the saved interview after reload; the app validates the saved capability and a participant must explicitly resume before enabling a microphone. This does not promise uninterrupted audio or a founder-phone reconnect grace period.</p>
|
|
83
|
+
<p>The launcher uses the approved invitation duration and thank-you label supplied by Sightspool. Dismissal leaves a small logo button: the first click restores the message and the second opens the interview. After completion or early stopping, the small checked button opens the accepted thank-you receipt directly. The app minimises after eight seconds once it is safe to close the recording UI. Claim instructions come from the frozen interview terms; the SDK issues no rewards. Tab restoration supplements the server's contact and capacity rules.</p>
|
|
84
|
+
<p>Placement is bottom-right on desktop and bottom-centre up to 560px, with safe-area spacing and reduced-motion support. Set <code>theme: "light" | "dark" | "auto"</code> in <code>init()</code> or <code>data-sightspool-theme</code> on a script installation; the default is dark. Another bottom-right customer widget may require coordinated placement; configurable offsets are not supplied.</p></main></body></html>
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"package": "@sightspool/sdk",
|
|
4
|
+
"version": "0.4.1",
|
|
5
|
+
"sourceCommit": "56617f341eb259bf5b8e3a941d2d21926bac463b",
|
|
6
|
+
"sourceDirty": false,
|
|
7
|
+
"artifactId": "116419831a4c93513a867dcb4d3b808e8802ee27f5ad732460c997cefb8c804b",
|
|
8
|
+
"bundle": {
|
|
9
|
+
"file": "sdk.global.js",
|
|
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
|
+
},
|
|
16
|
+
"source": {
|
|
17
|
+
"file": "source.json",
|
|
18
|
+
"sha256": "4deb7131013374d8facea8679e98e4617a3386449c85c1cdd907014590525da1"
|
|
19
|
+
},
|
|
20
|
+
"runtimeDependencies": []
|
|
21
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
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
|
+
//# sourceMappingURL=sdk.global.js.map
|