@guidelytech/sdk-core 0.5.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/capture-C3faAYb7.d.cts +99 -0
- package/dist/capture-Dqd7up2n.d.ts +99 -0
- package/dist/capture.cjs +313 -0
- package/dist/capture.cjs.map +1 -0
- package/dist/capture.d.cts +3 -0
- package/dist/capture.d.ts +3 -0
- package/dist/capture.js +235 -0
- package/dist/capture.js.map +1 -0
- package/dist/{chunk-GJT7CFAC.js → chunk-AKNE7D5M.js} +14 -5
- package/dist/chunk-AKNE7D5M.js.map +1 -0
- package/dist/chunk-CYAG6ZYX.js +85 -0
- package/dist/chunk-CYAG6ZYX.js.map +1 -0
- package/dist/{chunk-HLR3W63Z.js → chunk-GDF6RCHQ.js} +6 -79
- package/dist/chunk-GDF6RCHQ.js.map +1 -0
- package/dist/headless.cjs +18 -5
- package/dist/headless.cjs.map +1 -1
- package/dist/headless.d.cts +3 -49
- package/dist/headless.d.ts +3 -49
- package/dist/headless.js +6 -4
- package/dist/http-47tiAlOM.d.cts +86 -0
- package/dist/http-47tiAlOM.d.ts +86 -0
- package/dist/index.cjs +309 -63
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -4
- package/dist/index.d.ts +23 -4
- package/dist/index.js +273 -39
- package/dist/index.js.map +1 -1
- package/dist/recorder.cjs +1 -1
- package/dist/recorder.cjs.map +1 -1
- package/dist/recorder.js +2 -2
- package/dist/recorder.js.map +1 -1
- package/dist/{types-BZ9r3Fs9.d.cts → types-RyeRQd9f.d.ts} +9 -30
- package/dist/{types-BZ9r3Fs9.d.ts → types-d4eOerxi.d.cts} +9 -30
- package/package.json +8 -3
- package/dist/chunk-GJT7CFAC.js.map +0 -1
- package/dist/chunk-HLR3W63Z.js.map +0 -1
package/dist/recorder.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/recorder.ts","../src/network.ts","../src/replay.ts"],"sourcesContent":["/**\n * Session-replay chunk: the only module that imports rrweb. Built as its own entry and loaded on\n * demand (`import(\"./recorder\")`) together with the replay controller, so the base SDK pays for\n * neither. The script-tag build ships it as `guidely-recorder.js` (assigns `window.__guidelyRecorder`).\n */\nimport { record } from \"@rrweb/record\";\nimport { getRecordConsolePlugin } from \"@rrweb/rrweb-plugin-console-record\";\nimport type { RecordPlugin } from \"@rrweb/types\";\nimport type { RecorderApi, RecorderHandle, RecorderStartOptions } from \"./recorder-types\";\nimport { networkRecordPlugin } from \"./network\";\nimport { ReplayController } from \"./replay\";\n\nexport type { RecorderApi, RecorderHandle, RecorderStartOptions } from \"./recorder-types\";\nexport { ReplayController } from \"./replay\";\nexport type { ReplayControllerOptions, ReplayRecordingConfig, ReplayChunkMeta } from \"./replay\";\nexport type { NetworkCaptureOptions } from \"./network\";\n\n/** Starts an rrweb recording with Guidely's privacy defaults (inputs always masked, no canvas, no fonts). */\nexport function startRecorder(options: RecorderStartOptions): RecorderHandle | null {\n const onError = (err: unknown) => {\n try {\n options.onError?.(err);\n } catch {\n /* ignore */\n }\n return true; // handled: never rethrow into the host page\n };\n try {\n const network = options.network ? networkRecordPlugin(options.network) : null;\n const plugins: RecordPlugin[] = [];\n if (options.recordConsole) plugins.push(getRecordConsolePlugin({ level: [\"error\", \"warn\", \"info\", \"log\"], lengthThreshold: 200 }));\n if (network) plugins.push(network.plugin as unknown as RecordPlugin);\n const stop = record({\n emit: (event) => options.emit(event as { type: number; timestamp: number }),\n maskAllInputs: true,\n maskTextSelector: options.maskTextSelector || undefined,\n blockSelector: options.blockSelector || undefined,\n slimDOMOptions: { script: true, comment: true, headFavicon: true, headWhitespace: true, headMetaSocial: true, headMetaRobots: true, headMetaHttpEquiv: true, headMetaVerification: true, headMetaAuthorship: true, headMetaDescKeywords: true },\n sampling: { mousemove: 50, scroll: 150, input: \"last\" },\n inlineStylesheet: true,\n recordCanvas: false,\n collectFonts: false,\n inlineImages: false,\n plugins,\n errorHandler: onError,\n });\n if (!stop) return null;\n return {\n stop: () => {\n try {\n stop();\n } catch (err) {\n onError(err);\n }\n },\n addCustomEvent: (tag, payload) => {\n try {\n record.addCustomEvent(tag, payload);\n } catch {\n /* not recording */\n }\n },\n flush: () => network?.flush(),\n };\n } catch (err) {\n onError(err);\n return null;\n }\n}\n\n/** The recorder chunk's API (the script-tag build substitutes a loader that resolves to it). */\nexport const recorder: RecorderApi = { startRecorder, createReplay: (options) => new ReplayController({ ...options, startRecorder }) };\n","/**\n * Network recording for session replays (part of the lazily loaded recorder chunk, never the base\n * SDK): patches `fetch`, `XMLHttpRequest` and `WebSocket` while recording, observes resource timing,\n * and emits batched `NetworkPluginPayload`s as rrweb plugin events (`NETWORK_PLUGIN`).\n * The page's own requests behave exactly as before: the original promise / XHR / socket is returned\n * untouched, bodies are read from clones, and every recording step is wrapped in try/catch.\n */\nimport { NETWORK_PLUGIN, SENSITIVE_HEADER_PATTERN, SENSITIVE_PARAM_PATTERN, WS_MAX_MESSAGE_BYTES, WS_MAX_MESSAGES_PER_SOCKET, randomId, type NetworkPluginPayload, type NetworkRequest, type NetworkRequestType, type WebSocketMessage } from \"@guidelytech/shared/runtime\";\n\n/** Network recording settings (merged from the project setting and SDK options by the client). */\nexport interface NetworkCaptureOptions {\n /** Record headers and text bodies (`recordNetworkPayloads`). */\n payloads: boolean;\n /** Guidely API base URL: requests to `<origin>/api/v1/sdk/` are never recorded. */\n apiUrl?: string;\n /** Last say over each request: return a modified copy, or null to drop it (exceptions drop it too). */\n mask?: (request: NetworkRequest) => NetworkRequest | null;\n}\n\n/** Bodies above this many characters are cut and marked `[truncated]`. */\nexport const MAX_BODY_CHARS = 10 * 1024;\nexport const MAX_REQUESTS_PER_PAGE = 500;\nconst BATCH_SIZE = 20;\nconst BATCH_MS = 1000;\nconst BODY_TIMEOUT_MS = 2000;\nconst REDACTED = \"[redacted]\";\n\n// Per page load (the chunk is loaded once per page).\nlet requestCount = 0;\n/** Resource-timing entries that started before this time were already reported by an earlier recording. */\nlet perfCutoff = 0;\n\nconst now = () => Date.now();\n\n/** Absolute URL with sensitive query parameter values replaced by `[redacted]`. */\nexport function redactUrl(raw: string, base = typeof location !== \"undefined\" ? location.href : \"http://localhost/\"): string {\n try {\n const u = new URL(raw, base);\n u.searchParams.forEach((_v, k) => {\n if (SENSITIVE_PARAM_PATTERN.test(k)) u.searchParams.set(k, REDACTED);\n });\n return u.toString().replace(/%5Bredacted%5D/g, REDACTED);\n } catch {\n return String(raw);\n }\n}\n\n/** Recursively masks values of sensitive-looking keys. */\nexport function maskJson(value: unknown, depth = 0): unknown {\n if (depth > 20 || !value || typeof value !== \"object\") return value;\n if (Array.isArray(value)) return value.map((v) => maskJson(v, depth + 1));\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value)) out[k] = SENSITIVE_PARAM_PATTERN.test(k) ? REDACTED : maskJson(v, depth + 1);\n return out;\n}\n\nfunction cap(text: string, max: number): string {\n return text.length > max ? `${text.slice(0, max)}[truncated]` : text;\n}\n\n/** Body as recorded: text/JSON/form only (JSON keys masked), ≤10KB; `[binary]` otherwise. */\nexport function formatBody(body: unknown, contentType: string | null | undefined): string | undefined {\n if (body == null || body === \"\") return undefined;\n const ct = (contentType || \"\").toLowerCase();\n if (typeof URLSearchParams !== \"undefined\" && body instanceof URLSearchParams) return formatBody(body.toString(), \"application/x-www-form-urlencoded\");\n if (typeof body !== \"string\") return \"[binary]\";\n if (ct && !/^text\\/|json|x-www-form-urlencoded/.test(ct)) return \"[binary]\";\n if (ct.includes(\"x-www-form-urlencoded\")) {\n const params = new URLSearchParams(body);\n params.forEach((_v, k) => {\n if (SENSITIVE_PARAM_PATTERN.test(k)) params.set(k, REDACTED);\n });\n return cap(params.toString().replace(/%5Bredacted%5D/g, REDACTED), MAX_BODY_CHARS);\n }\n if (ct.includes(\"json\") || (!ct && /^\\s*[[{]/.test(body))) {\n try {\n return cap(JSON.stringify(maskJson(JSON.parse(body))), MAX_BODY_CHARS);\n } catch {\n /* not JSON after all */\n }\n }\n return cap(body, MAX_BODY_CHARS);\n}\n\n/** Lower-cased header names; sensitive values `[redacted]`. */\nexport function formatHeaders(entries: Iterable<[string, string]>): Record<string, string> {\n const out: Record<string, string> = {};\n for (const [k, v] of entries) {\n const name = k.toLowerCase();\n out[name] = SENSITIVE_HEADER_PATTERN.test(name) ? REDACTED : String(v);\n }\n return out;\n}\n\nfunction headerEntries(h: unknown): Array<[string, string]> {\n if (!h) return [];\n if (Array.isArray(h)) return h.map(([k, v]) => [String(k), String(v)]);\n if (typeof (h as Headers).forEach === \"function\") {\n const out: Array<[string, string]> = [];\n (h as Headers).forEach((v, k) => out.push([k, v]));\n return out;\n }\n return Object.entries(h as Record<string, string>).map(([k, v]) => [k, String(v)]);\n}\n\n/** UTF-8 byte length of a string, or the size of binary data. */\nexport function byteSize(data: unknown): number {\n if (typeof data === \"string\") return typeof TextEncoder !== \"undefined\" ? new TextEncoder().encode(data).length : data.length;\n if (data instanceof ArrayBuffer) return data.byteLength;\n if (ArrayBuffer.isView(data)) return data.byteLength;\n if (typeof Blob !== \"undefined\" && data instanceof Blob) return data.size;\n return 0;\n}\n\nfunction frameData(data: unknown, size: number): string {\n if (typeof data !== \"string\") return `[binary ${size} B]`;\n let text = data;\n try {\n if (/^\\s*[[{]/.test(text)) text = JSON.stringify(maskJson(JSON.parse(text)));\n } catch {\n /* plain text */\n }\n return cap(text, WS_MAX_MESSAGE_BYTES);\n}\n\nconst RESOURCE_TYPES: Record<string, NetworkRequestType> = { script: \"script\", link: \"stylesheet\", css: \"stylesheet\", img: \"image\", image: \"image\", audio: \"media\", video: \"media\", track: \"media\", beacon: \"beacon\", navigation: \"document\", iframe: \"document\", frame: \"document\" };\n\n/** A running capture: `flush()` emits pending items now; `stop()` flushes and restores everything. */\nexport interface NetworkCapture {\n flush(): void;\n stop(): void;\n}\n\ntype Win = Window & typeof globalThis;\n\n/** Starts recording the window's network activity; `emit` receives batched payloads. */\nexport function startNetworkCapture(win: Win, opts: NetworkCaptureOptions, emit: (payload: NetworkPluginPayload) => void): NetworkCapture {\n let active = true;\n const startedAt = now();\n let requests: NetworkRequest[] = [];\n let messages: WebSocketMessage[] = [];\n let droppedRequests = 0;\n let droppedMessages = 0;\n let timer: ReturnType<typeof setTimeout> | null = null;\n /** Recent fetch/XHR requests, for enriching with resource timing. */\n const recent: NetworkRequest[] = [];\n const cleanups: Array<() => void> = [];\n let apiPrefix = \"\";\n try {\n if (opts.apiUrl) apiPrefix = `${new URL(opts.apiUrl).origin}/api/v1/sdk/`;\n } catch {\n /* invalid apiUrl */\n }\n\n const excluded = (url: string) => /^(data|blob|about|chrome-extension):/i.test(url) || (!!apiPrefix && url.replace(/^ws(s?):/i, \"http$1:\").startsWith(apiPrefix)) || /\\/guidely-recorder(\\.min)?\\.js(\\?|$)/.test(url);\n\n const flush = () => {\n if (timer) clearTimeout(timer);\n timer = null;\n if (!requests.length && !messages.length && !droppedRequests && !droppedMessages) return;\n const payload: NetworkPluginPayload = {};\n if (requests.length) payload.requests = requests;\n if (messages.length) payload.messages = messages;\n if (droppedRequests || droppedMessages) payload.dropped = { ...(droppedRequests ? { requests: droppedRequests } : {}), ...(droppedMessages ? { messages: droppedMessages } : {}) };\n requests = [];\n messages = [];\n droppedRequests = 0;\n droppedMessages = 0;\n try {\n emit(payload);\n } catch {\n /* recorder stopped */\n }\n };\n const schedule = () => {\n if (requests.length + messages.length >= BATCH_SIZE) flush();\n else timer ??= setTimeout(flush, BATCH_MS);\n };\n\n /** Applies the mask hook; null drops. `counted` requests (re-emissions) skip the per-page cap. */\n const push = (req: NetworkRequest, counted = false): boolean => {\n if (!active) return false;\n if (!counted) {\n if (requestCount >= MAX_REQUESTS_PER_PAGE) {\n droppedRequests += 1;\n schedule();\n return false;\n }\n requestCount += 1;\n }\n // Pushed as-is (resource timing may still enrich it before the batch is sent); the hook gets a copy.\n let out: NetworkRequest | null = req;\n if (opts.mask) {\n try {\n out = opts.mask({ ...req });\n } catch {\n out = null;\n }\n }\n if (!out) return false;\n requests.push(out);\n schedule();\n return true;\n };\n const finish = (req: NetworkRequest, fields: Partial<NetworkRequest>) => {\n Object.assign(req, fields);\n req.endTime = now();\n req.durationMs = req.endTime - req.startTime;\n };\n const remember = (req: NetworkRequest) => {\n recent.push(req);\n if (recent.length > 100) recent.shift();\n };\n\n // --- fetch -------------------------------------------------------------------------------------\n const origFetch = win.fetch;\n if (typeof origFetch === \"function\") {\n const patched = function (this: unknown, input: RequestInfo | URL, init?: RequestInit): Promise<Response> {\n const p = origFetch.call(this === undefined || this === patched ? win : (this as Win), input, init);\n if (!active) return p;\n try {\n const isReq = typeof Request !== \"undefined\" && input instanceof Request;\n const rawUrl = isReq ? (input as Request).url : String(input instanceof URL ? input.href : input);\n const url = redactUrl(rawUrl);\n if (excluded(url) || excluded(rawUrl)) return p;\n const req: NetworkRequest = { id: randomId(12), type: \"fetch\", method: String(init?.method || (isReq ? (input as Request).method : \"GET\")).toUpperCase(), url, startTime: now() };\n if (opts.payloads) {\n const headers = headerEntries(init?.headers ?? (isReq ? (input as Request).headers : undefined));\n req.requestHeaders = formatHeaders(headers);\n const body = formatBody(init?.body, req.requestHeaders[\"content-type\"] ?? (typeof init?.body === \"string\" ? \"text/plain\" : undefined));\n if (body !== undefined) req.requestBody = body;\n }\n remember(req);\n p.then(\n (res) => {\n try {\n finish(req, { status: res.status });\n if (!opts.payloads) return void push(req);\n req.responseHeaders = formatHeaders(headerEntries(res.headers));\n const ct = res.headers.get(\"content-type\");\n if (!ct || !/^text\\/(?!event-stream)|json|x-www-form-urlencoded/i.test(ct) || !res.body) {\n if (ct && res.body) req.responseBody = \"[binary]\";\n return void push(req);\n }\n // Read a clone (never the app's stream), at most ~10KB and 2s, then send.\n const reader = res.clone().body!.getReader();\n const decoder = new TextDecoder();\n let text = \"\";\n const done = () => {\n req.responseBody = formatBody(text, ct);\n push(req);\n };\n const t = setTimeout(() => void reader.cancel().catch(() => undefined), BODY_TIMEOUT_MS);\n const read = (): Promise<void> =>\n reader.read().then(({ done: end, value }) => {\n if (value) text += decoder.decode(value, { stream: true });\n if (end) return;\n if (text.length > MAX_BODY_CHARS * 4) return void reader.cancel().catch(() => undefined);\n return read();\n });\n read()\n .catch(() => undefined)\n .then(() => {\n clearTimeout(t);\n done();\n });\n } catch {\n /* never affect the app */\n }\n },\n (err: unknown) => {\n try {\n finish(req, { status: 0, error: err instanceof Error && err.name === \"AbortError\" ? \"aborted\" : String((err as Error)?.message || err) });\n push(req);\n } catch {\n /* ignore */\n }\n },\n );\n } catch {\n /* never affect the app */\n }\n return p;\n };\n win.fetch = patched as typeof fetch;\n cleanups.push(() => {\n if (win.fetch === (patched as typeof fetch)) win.fetch = origFetch;\n });\n }\n\n // --- XMLHttpRequest ----------------------------------------------------------------------------\n const XHR = win.XMLHttpRequest;\n if (XHR?.prototype) {\n const proto = XHR.prototype;\n const { open, send, setRequestHeader } = proto;\n const state = new WeakMap<XMLHttpRequest, { method: string; url: string; headers: Array<[string, string]> }>();\n const pOpen = function (this: XMLHttpRequest, method: string, url: string | URL, ...rest: unknown[]) {\n try {\n if (active) state.set(this, { method: String(method).toUpperCase(), url: String(url), headers: [] });\n } catch {\n /* ignore */\n }\n return (open as (...a: unknown[]) => void).call(this, method, url, ...rest);\n };\n const pSetHeader = function (this: XMLHttpRequest, name: string, value: string) {\n try {\n state.get(this)?.headers.push([name, value]);\n } catch {\n /* ignore */\n }\n return setRequestHeader.call(this, name, value);\n };\n const pSend = function (this: XMLHttpRequest, body?: Document | XMLHttpRequestBodyInit | null) {\n try {\n const s = state.get(this);\n const url = s ? redactUrl(s.url) : \"\";\n if (active && s && !excluded(url) && !excluded(s.url)) {\n const req: NetworkRequest = { id: randomId(12), type: \"xhr\", method: s.method, url, startTime: now() };\n if (opts.payloads) {\n req.requestHeaders = formatHeaders(s.headers);\n const b = formatBody(body, req.requestHeaders[\"content-type\"] ?? (typeof body === \"string\" ? \"text/plain\" : undefined));\n if (b !== undefined) req.requestBody = b;\n }\n remember(req);\n let ended = false;\n const end = (error?: string) => () => {\n if (ended) return;\n ended = true;\n try {\n finish(req, { status: error ? 0 : this.status, ...(error ? { error } : {}) });\n if (opts.payloads && !error) {\n const raw = this.getAllResponseHeaders() || \"\";\n req.responseHeaders = formatHeaders(raw.trim().split(/[\\r\\n]+/).filter(Boolean).map((line) => [line.slice(0, line.indexOf(\":\")).trim(), line.slice(line.indexOf(\":\") + 1).trim()] as [string, string]));\n const ct = this.getResponseHeader(\"content-type\");\n const type = this.responseType;\n const text = type === \"\" || type === \"text\" ? this.responseText : type === \"json\" ? JSON.stringify(this.response) : null;\n const formatted = text == null ? (this.response ? \"[binary]\" : undefined) : formatBody(text, ct ?? (type === \"json\" ? \"application/json\" : \"text/plain\"));\n if (formatted !== undefined) req.responseBody = formatted;\n }\n push(req);\n } catch {\n /* ignore */\n }\n };\n this.addEventListener(\"load\", end());\n this.addEventListener(\"error\", end(\"network error\"));\n this.addEventListener(\"abort\", end(\"aborted\"));\n this.addEventListener(\"timeout\", end(\"timeout\"));\n }\n } catch {\n /* never affect the app */\n }\n return send.call(this, body);\n };\n proto.open = pOpen as typeof open;\n proto.setRequestHeader = pSetHeader;\n proto.send = pSend;\n cleanups.push(() => {\n if (proto.open === (pOpen as typeof open)) proto.open = open;\n if (proto.setRequestHeader === pSetHeader) proto.setRequestHeader = setRequestHeader;\n if (proto.send === pSend) proto.send = send;\n });\n }\n\n // --- WebSocket ---------------------------------------------------------------------------------\n const WS = win.WebSocket;\n if (typeof WS === \"function\") {\n const track = (ws: WebSocket, rawUrl: string) => {\n const url = redactUrl(rawUrl);\n if (excluded(url)) return;\n const req: NetworkRequest = { id: randomId(12), type: \"websocket\", method: \"GET\", url, startTime: now() };\n const totals = { sent: 0, received: 0, sentBytes: 0, receivedBytes: 0, recorded: 0 };\n let dropped = false;\n let counted = false;\n const emitConn = (fields: Partial<NetworkRequest>) => {\n if (dropped || !active) return;\n Object.assign(req, fields, { messages: { ...totals } });\n dropped = !push({ ...req }, counted);\n counted = true;\n };\n const frame = (dir: \"send\" | \"recv\", data: unknown) => {\n try {\n const size = byteSize(data);\n if (dir === \"send\") {\n totals.sent += 1;\n totals.sentBytes += size;\n } else {\n totals.received += 1;\n totals.receivedBytes += size;\n }\n if (dropped || !active) return;\n if (totals.recorded >= WS_MAX_MESSAGES_PER_SOCKET) {\n droppedMessages += 1;\n return schedule();\n }\n totals.recorded += 1;\n const m: WebSocketMessage = { socketId: req.id, dir, time: now(), size };\n if (typeof data !== \"string\") m.binary = true;\n if (opts.payloads) m.data = frameData(data, size);\n messages.push(m);\n schedule();\n } catch {\n /* ignore */\n }\n };\n const nativeSend = ws.send;\n // Instance-level wrapper: the socket keeps working after recording stops (it just stops being recorded).\n ws.send = function (this: WebSocket, data: string | ArrayBufferLike | Blob | ArrayBufferView) {\n const r = nativeSend.call(this, data); // throws (unrecorded) when the socket is not open\n frame(\"send\", data);\n return r;\n };\n ws.addEventListener(\"open\", () => emitConn({ protocol: ws.protocol || undefined }));\n ws.addEventListener(\"message\", (e: MessageEvent) => frame(\"recv\", e.data));\n let errored = false;\n ws.addEventListener(\"error\", () => {\n errored = true;\n });\n ws.addEventListener(\"close\", (e: CloseEvent) => {\n const endTime = now();\n emitConn({ endTime, durationMs: endTime - req.startTime, closeCode: e.code, closeReason: e.reason || undefined, ...(errored || e.code === 1006 ? { error: \"connection error\" } : {}) });\n });\n };\n const Patched = class extends WS {\n constructor(url: string | URL, protocols?: string | string[]) {\n super(url, protocols);\n try {\n if (active) track(this, String(url instanceof URL ? url.href : url));\n } catch {\n /* never affect the app */\n }\n }\n static override [Symbol.hasInstance](x: unknown): boolean {\n return x instanceof WS;\n }\n };\n Object.defineProperty(Patched, \"name\", { value: WS.name });\n win.WebSocket = Patched as typeof WebSocket;\n cleanups.push(() => {\n if (win.WebSocket === (Patched as typeof WebSocket)) win.WebSocket = WS;\n });\n }\n\n // --- Resource timing -------------------------------------------------------------------------\n const PO = win.PerformanceObserver;\n if (typeof PO === \"function\") {\n const origin = win.performance?.timeOrigin ?? startedAt - (win.performance?.now?.() ?? 0);\n const cutoff = perfCutoff;\n const seen = new Set<string>(); // browsers re-deliver the navigation entry once the load completes\n const onEntries = (list: PerformanceObserverEntryList) => {\n for (const e of list.getEntries() as PerformanceResourceTiming[]) {\n try {\n const start = Math.round(origin + e.startTime);\n const key = `${e.entryType}|${e.name}|${e.startTime}`;\n if (start < cutoff || seen.has(key)) continue;\n seen.add(key);\n const url = redactUrl(e.name);\n if (excluded(url) || excluded(e.name)) continue;\n const status = (e as { responseStatus?: number }).responseStatus;\n const timing = { transferSize: e.transferSize, responseSize: e.decodedBodySize, ...(status ? { status } : {}) };\n if (e.initiatorType === \"fetch\" || e.initiatorType === \"xmlhttprequest\") {\n const match = recent.find((r) => r.url === url && Math.abs(r.startTime - start) <= 50);\n if (match) Object.assign(match, { transferSize: timing.transferSize, responseSize: timing.responseSize }, match.status === undefined && status ? { status } : {});\n continue;\n }\n const type = e.entryType === \"navigation\" ? \"document\" : /\\.(woff2?|ttf|otf|eot)(\\?|$)/i.test(e.name) ? \"font\" : (RESOURCE_TYPES[e.initiatorType] ?? \"other\");\n push({ id: randomId(12), type, method: \"GET\", url, startTime: start, endTime: Math.round(origin + e.responseEnd), durationMs: Math.round(e.duration), ...timing });\n } catch {\n /* ignore */\n }\n }\n };\n for (const type of [\"resource\", \"navigation\"]) {\n try {\n const po = new PO(onEntries);\n po.observe({ type, buffered: true });\n cleanups.push(() => po.disconnect());\n } catch {\n /* unsupported entry type */\n }\n }\n }\n\n return {\n flush,\n stop: () => {\n if (!active) return;\n flush();\n active = false;\n perfCutoff = now();\n cleanups.splice(0).forEach((fn) => {\n try {\n fn();\n } catch {\n /* ignore */\n }\n });\n },\n };\n}\n\n/** The rrweb plugin that records network activity as `{ type: 6, data: { plugin: NETWORK_PLUGIN, payload } }`. */\nexport function networkRecordPlugin(opts: NetworkCaptureOptions): { plugin: { name: string; observer: (cb: (payload: unknown) => void, win: Win) => () => void; options: unknown }; flush: () => void } {\n let capture: NetworkCapture | null = null;\n return {\n plugin: {\n name: NETWORK_PLUGIN,\n options: {},\n observer: (cb, win) => {\n // rrweb also calls plugin observers for same-origin iframes: only the top window is patched.\n if (capture || win !== (typeof window !== \"undefined\" ? window : win)) return () => {};\n const c = startNetworkCapture(win, opts, cb);\n capture = c;\n return () => {\n c.stop();\n if (capture === c) capture = null;\n };\n },\n },\n flush: () => capture?.flush(),\n };\n}\n\n/** Test hook: resets the per-page counters. */\nexport function resetNetworkCounters(): void {\n requestCount = 0;\n perfCutoff = 0;\n}\n","import { randomId } from \"@guidelytech/shared/runtime\";\nimport { backoffMs } from \"./http\";\nimport type { NetworkCaptureOptions } from \"./network\";\nimport type { RecorderHandle, RecorderStartOptions } from \"./recorder-types\";\n\n/** Recorder settings for one session (already merged from the project setting and SDK options). */\nexport interface ReplayRecordingConfig {\n maskTextSelector: string;\n blockSelector: string;\n recordConsole: boolean;\n network?: NetworkCaptureOptions | null;\n}\n\n/** Per-upload metadata (`ReplayChunk` minus `sessionId`/`windowId`/`seq`/`events`). */\nexport interface ReplayChunkMeta {\n anonymousId: string;\n userId?: string | null;\n url?: string;\n device?: { type: string; browser?: string; os?: string };\n viewport?: { width: number; height: number };\n sdk?: { name: string; version: string };\n}\n\nexport interface ReplayControllerOptions {\n /** Upload URL for `POST /api/v1/sdk/replay` (with `?key=` and the given params). */\n url: (params: Record<string, string | undefined>) => string;\n fetch: () => typeof fetch | null;\n meta: () => ReplayChunkMeta;\n /** Starts rrweb (`startRecorder` from the recorder chunk; injectable for tests). */\n startRecorder: (options: RecorderStartOptions) => RecorderHandle | null;\n /** The server answered `{ disabled: true }`: recording must stop for this session. */\n onDisabled: (sessionId: string) => void;\n onError: (err: unknown) => void;\n flushIntervalMs?: number;\n /** Buffered bytes that trigger an early upload (default ~1MB). */\n maxBufferBytes?: number;\n /** Upload backlog cap; the oldest chunks are dropped above it (default ~5MB). */\n maxQueuedBytes?: number;\n}\n\ninterface Chunk {\n sessionId: string;\n meta: ReplayChunkMeta;\n events: string[];\n bytes: number;\n /** Assigned on the first upload attempt and kept for retries (the server dedupes by window + seq). */\n seq?: number;\n}\n\n/** Largest body sent with `sendBeacon` / keepalive fetch while the page is being hidden. */\nexport const UNLOAD_MAX_BYTES = 60 * 1024;\n\nasync function gzip(text: string): Promise<Blob> {\n const stream = new Blob([text]).stream().pipeThrough(new CompressionStream(\"gzip\"));\n return new Response(stream).blob();\n}\n\n/**\n * Session-replay controller (part of the lazily loaded recorder chunk): records with rrweb, buffers events, and uploads them\n * every 5s (or once ~1MB is buffered) as gzip JSON (`?enc=gzip`; plain JSON without\n * `CompressionStream`). On page hide it sends what is left with `sendBeacon` / keepalive fetch.\n */\nexport class ReplayController {\n readonly windowId = randomId(16);\n private handle: RecorderHandle | null = null;\n private sessionId: string | null = null;\n private configKey = \"\";\n private buffer: Chunk | null = null;\n private queue: Chunk[] = [];\n private queuedBytes = 0;\n private seqs = new Map<string, number>();\n private interval: ReturnType<typeof setInterval> | null = null;\n private retryTimer: ReturnType<typeof setTimeout> | null = null;\n private attempt = 0;\n private uploading = false;\n private flushing: Promise<void> | null = null;\n private destroyed = false;\n\n constructor(private readonly opts: ReplayControllerOptions) {}\n\n /** True while rrweb is recording. */\n isRecording(): boolean {\n return !!this.handle;\n }\n\n /** The session currently being recorded, if any. */\n get activeSessionId(): string | null {\n return this.sessionId;\n }\n\n /** Starts (or keeps) recording for `sessionId`. Restarts when the session or settings changed. */\n start(sessionId: string, config: ReplayRecordingConfig): void {\n if (this.destroyed) return;\n const key = JSON.stringify(config);\n if (this.sessionId === sessionId && this.configKey === key && this.handle) return;\n this.stop();\n this.handle = this.opts.startRecorder({ ...config, emit: (e) => this.push(sessionId, e), onError: this.opts.onError });\n if (!this.handle) return;\n this.sessionId = sessionId;\n this.configKey = key;\n this.interval = setInterval(() => void this.flush(), this.opts.flushIntervalMs ?? 5000);\n }\n\n /** Stops recording. Buffered events are uploaded unless `discard` (consent withdrawn, authoring). */\n stop(options: { discard?: boolean } = {}): void {\n const handle = this.handle;\n this.handle = null;\n this.sessionId = null;\n this.configKey = \"\";\n if (this.interval) clearInterval(this.interval);\n this.interval = null;\n handle?.stop();\n if (options.discard) {\n this.buffer = null;\n this.queue = [];\n this.queuedBytes = 0;\n } else void this.flush();\n }\n\n /** Adds an rrweb custom event (Guidely event markers) while recording. */\n addCustomEvent(tag: string, payload: unknown): void {\n this.handle?.addCustomEvent(tag, payload);\n }\n\n private push(sessionId: string, event: unknown): void {\n let json: string;\n try {\n json = JSON.stringify(event);\n } catch {\n return;\n }\n if (this.buffer && this.buffer.sessionId !== sessionId) this.seal();\n const chunk = this.buffer ?? (this.buffer = { sessionId, meta: this.opts.meta(), events: [], bytes: 0 });\n chunk.events.push(json);\n chunk.bytes += json.length;\n if (chunk.bytes > (this.opts.maxBufferBytes ?? 1_000_000)) void this.flush();\n }\n\n /** Moves the buffer to the upload queue, dropping the oldest chunks above the backlog cap. */\n private seal(): void {\n const chunk = this.buffer;\n this.buffer = null;\n if (!chunk || !chunk.events.length) return;\n this.queue.push(chunk);\n this.queuedBytes += chunk.bytes;\n const max = this.opts.maxQueuedBytes ?? 5_000_000;\n while (this.queuedBytes > max && this.queue.length > 1) {\n const i = this.uploading ? 1 : 0;\n const [dropped] = this.queue.splice(i, 1);\n if (dropped) this.queuedBytes -= dropped.bytes;\n }\n }\n\n private nextSeq(chunk: Chunk): number {\n if (chunk.seq === undefined) {\n const seq = this.seqs.get(chunk.sessionId) ?? 0;\n this.seqs.set(chunk.sessionId, seq + 1);\n chunk.seq = seq;\n }\n return chunk.seq;\n }\n\n private body(chunk: Chunk): string {\n const head = JSON.stringify({ sessionId: chunk.sessionId, windowId: this.windowId, seq: this.nextSeq(chunk), ...chunk.meta });\n return `${head.slice(0, -1)},\"events\":[${chunk.events.join(\",\")}]}`;\n }\n\n private remove(chunk: Chunk): void {\n const i = this.queue.indexOf(chunk);\n if (i >= 0) {\n this.queue.splice(i, 1);\n this.queuedBytes -= chunk.bytes;\n }\n }\n\n /** Uploads the buffer and the backlog, one chunk at a time; retries with backoff on failure. */\n flush(): Promise<void> {\n this.seal();\n if (this.retryTimer || !this.queue.length) return Promise.resolve();\n // An upload loop already running may have finished its last chunk: queue another pass after it.\n if (this.flushing) return this.flushing.then(() => this.flush());\n return (this.flushing = this.drain().finally(() => {\n this.flushing = null;\n }));\n }\n\n private async drain(): Promise<void> {\n this.uploading = true;\n try {\n while (this.queue.length) {\n const chunk = this.queue[0] as Chunk;\n const result = await this.send(chunk);\n if (result === \"retry\") {\n this.attempt += 1;\n if (!this.destroyed && this.attempt < 6) {\n this.retryTimer = setTimeout(() => {\n this.retryTimer = null;\n void this.flush();\n }, backoffMs(this.attempt - 1));\n } else this.attempt = 0;\n return;\n }\n this.attempt = 0;\n this.remove(chunk);\n if (result === \"disabled\") {\n for (const c of this.queue.slice()) if (c.sessionId === chunk.sessionId) this.remove(c);\n this.opts.onDisabled(chunk.sessionId);\n }\n }\n } finally {\n this.uploading = false;\n }\n }\n\n private async send(chunk: Chunk): Promise<\"ok\" | \"drop\" | \"retry\" | \"disabled\"> {\n const f = this.opts.fetch();\n if (!f) return \"drop\";\n try {\n const json = this.body(chunk);\n let body: Blob | string = json;\n let enc: string | undefined;\n if (typeof CompressionStream === \"function\") {\n try {\n body = await gzip(json);\n enc = \"gzip\";\n } catch {\n body = json;\n }\n }\n const size = typeof body === \"string\" ? body.length : body.size;\n const res = await f(this.opts.url({ enc }), { method: \"POST\", body, headers: { \"Content-Type\": \"text/plain\" }, keepalive: size < UNLOAD_MAX_BYTES, credentials: \"omit\", mode: \"cors\" });\n if (res.ok) {\n const data = (await res.json().catch(() => null)) as { disabled?: boolean } | null;\n return data?.disabled ? \"disabled\" : \"ok\";\n }\n return res.status >= 400 && res.status < 500 && res.status !== 429 ? \"drop\" : \"retry\";\n } catch {\n return \"retry\";\n }\n }\n\n /**\n * Page is being hidden / unloaded: sends whatever is buffered synchronously as uncompressed JSON\n * (`sendBeacon` up to 60KB, keepalive fetch otherwise), splitting chunks that never went out.\n */\n flushOnHide(): void {\n this.handle?.flush?.();\n this.seal();\n const pending = this.queue.slice(this.uploading ? 1 : 0);\n for (const chunk of pending) {\n const parts = chunk.seq === undefined ? splitChunk(chunk, UNLOAD_MAX_BYTES - 2048) : [chunk];\n let sent = true;\n for (const part of parts) {\n if (!this.sendNow(this.body(part))) {\n sent = false;\n break;\n }\n if (part !== chunk) {\n chunk.events.splice(0, part.events.length);\n chunk.bytes -= part.bytes;\n this.queuedBytes -= part.bytes;\n }\n }\n if (!sent) break;\n this.remove(chunk);\n }\n }\n\n private sendNow(json: string): boolean {\n const url = this.opts.url({});\n try {\n if (json.length <= UNLOAD_MAX_BYTES && typeof navigator !== \"undefined\" && typeof navigator.sendBeacon === \"function\" && navigator.sendBeacon(url, new Blob([json], { type: \"text/plain;charset=UTF-8\" }))) return true;\n const f = this.opts.fetch();\n if (!f || json.length > 64 * 1024) return false;\n void f(url, { method: \"POST\", body: json, headers: { \"Content-Type\": \"text/plain\" }, keepalive: true, credentials: \"omit\", mode: \"cors\" }).catch(() => undefined);\n return true;\n } catch {\n return false;\n }\n }\n\n /** Stops recording and uploads what is left; no retries afterwards. */\n destroy(): void {\n this.stop();\n this.destroyed = true;\n if (this.retryTimer) clearTimeout(this.retryTimer);\n this.retryTimer = null;\n }\n}\n\n/** Splits a chunk into parts of at most `max` bytes of events (a single larger event stays whole). */\nfunction splitChunk(chunk: Chunk, max: number): Chunk[] {\n if (chunk.bytes <= max) return [chunk];\n const parts: Chunk[] = [];\n let cur: Chunk | null = null;\n for (const e of chunk.events) {\n if (!cur || (cur.bytes + e.length > max && cur.events.length)) parts.push((cur = { sessionId: chunk.sessionId, meta: chunk.meta, events: [], bytes: 0 }));\n cur.events.push(e);\n cur.bytes += e.length;\n }\n return parts;\n}\n"],"mappings":";;;;;;AAKA,SAAS,cAAc;AACvB,SAAS,8BAA8B;;;ACCvC,SAAS,gBAAgB,0BAA0B,yBAAyB,sBAAsB,4BAA4B,gBAAgH;AAavO,IAAM,iBAAiB,KAAK;AAC5B,IAAM,wBAAwB;AACrC,IAAM,aAAa;AACnB,IAAM,WAAW;AACjB,IAAM,kBAAkB;AACxB,IAAM,WAAW;AAGjB,IAAI,eAAe;AAEnB,IAAI,aAAa;AAEjB,IAAM,MAAM,MAAM,KAAK,IAAI;AAGpB,SAAS,UAAU,KAAa,OAAO,OAAO,aAAa,cAAc,SAAS,OAAO,qBAA6B;AAC3H,MAAI;AACF,UAAM,IAAI,IAAI,IAAI,KAAK,IAAI;AAC3B,MAAE,aAAa,QAAQ,CAAC,IAAI,MAAM;AAChC,UAAI,wBAAwB,KAAK,CAAC,EAAG,GAAE,aAAa,IAAI,GAAG,QAAQ;AAAA,IACrE,CAAC;AACD,WAAO,EAAE,SAAS,EAAE,QAAQ,mBAAmB,QAAQ;AAAA,EACzD,QAAQ;AACN,WAAO,OAAO,GAAG;AAAA,EACnB;AACF;AAGO,SAAS,SAAS,OAAgB,QAAQ,GAAY;AAC3D,MAAI,QAAQ,MAAM,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAC9D,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,IAAI,CAAC,MAAM,SAAS,GAAG,QAAQ,CAAC,CAAC;AACxE,QAAM,MAA+B,CAAC;AACtC,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,KAAK,EAAG,KAAI,CAAC,IAAI,wBAAwB,KAAK,CAAC,IAAI,WAAW,SAAS,GAAG,QAAQ,CAAC;AACvH,SAAO;AACT;AAEA,SAAS,IAAI,MAAc,KAAqB;AAC9C,SAAO,KAAK,SAAS,MAAM,GAAG,KAAK,MAAM,GAAG,GAAG,CAAC,gBAAgB;AAClE;AAGO,SAAS,WAAW,MAAe,aAA4D;AACpG,MAAI,QAAQ,QAAQ,SAAS,GAAI,QAAO;AACxC,QAAM,MAAM,eAAe,IAAI,YAAY;AAC3C,MAAI,OAAO,oBAAoB,eAAe,gBAAgB,gBAAiB,QAAO,WAAW,KAAK,SAAS,GAAG,mCAAmC;AACrJ,MAAI,OAAO,SAAS,SAAU,QAAO;AACrC,MAAI,MAAM,CAAC,qCAAqC,KAAK,EAAE,EAAG,QAAO;AACjE,MAAI,GAAG,SAAS,uBAAuB,GAAG;AACxC,UAAM,SAAS,IAAI,gBAAgB,IAAI;AACvC,WAAO,QAAQ,CAAC,IAAI,MAAM;AACxB,UAAI,wBAAwB,KAAK,CAAC,EAAG,QAAO,IAAI,GAAG,QAAQ;AAAA,IAC7D,CAAC;AACD,WAAO,IAAI,OAAO,SAAS,EAAE,QAAQ,mBAAmB,QAAQ,GAAG,cAAc;AAAA,EACnF;AACA,MAAI,GAAG,SAAS,MAAM,KAAM,CAAC,MAAM,WAAW,KAAK,IAAI,GAAI;AACzD,QAAI;AACF,aAAO,IAAI,KAAK,UAAU,SAAS,KAAK,MAAM,IAAI,CAAC,CAAC,GAAG,cAAc;AAAA,IACvE,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO,IAAI,MAAM,cAAc;AACjC;AAGO,SAAS,cAAc,SAA6D;AACzF,QAAM,MAA8B,CAAC;AACrC,aAAW,CAAC,GAAG,CAAC,KAAK,SAAS;AAC5B,UAAM,OAAO,EAAE,YAAY;AAC3B,QAAI,IAAI,IAAI,yBAAyB,KAAK,IAAI,IAAI,WAAW,OAAO,CAAC;AAAA,EACvE;AACA,SAAO;AACT;AAEA,SAAS,cAAc,GAAqC;AAC1D,MAAI,CAAC,EAAG,QAAO,CAAC;AAChB,MAAI,MAAM,QAAQ,CAAC,EAAG,QAAO,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;AACrE,MAAI,OAAQ,EAAc,YAAY,YAAY;AAChD,UAAM,MAA+B,CAAC;AACtC,IAAC,EAAc,QAAQ,CAAC,GAAG,MAAM,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;AACjD,WAAO;AAAA,EACT;AACA,SAAO,OAAO,QAAQ,CAA2B,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;AACnF;AAGO,SAAS,SAAS,MAAuB;AAC9C,MAAI,OAAO,SAAS,SAAU,QAAO,OAAO,gBAAgB,cAAc,IAAI,YAAY,EAAE,OAAO,IAAI,EAAE,SAAS,KAAK;AACvH,MAAI,gBAAgB,YAAa,QAAO,KAAK;AAC7C,MAAI,YAAY,OAAO,IAAI,EAAG,QAAO,KAAK;AAC1C,MAAI,OAAO,SAAS,eAAe,gBAAgB,KAAM,QAAO,KAAK;AACrE,SAAO;AACT;AAEA,SAAS,UAAU,MAAe,MAAsB;AACtD,MAAI,OAAO,SAAS,SAAU,QAAO,WAAW,IAAI;AACpD,MAAI,OAAO;AACX,MAAI;AACF,QAAI,WAAW,KAAK,IAAI,EAAG,QAAO,KAAK,UAAU,SAAS,KAAK,MAAM,IAAI,CAAC,CAAC;AAAA,EAC7E,QAAQ;AAAA,EAER;AACA,SAAO,IAAI,MAAM,oBAAoB;AACvC;AAEA,IAAM,iBAAqD,EAAE,QAAQ,UAAU,MAAM,cAAc,KAAK,cAAc,KAAK,SAAS,OAAO,SAAS,OAAO,SAAS,OAAO,SAAS,OAAO,SAAS,QAAQ,UAAU,YAAY,YAAY,QAAQ,YAAY,OAAO,WAAW;AAW7Q,SAAS,oBAAoB,KAAU,MAA6B,MAA+D;AACxI,MAAI,SAAS;AACb,QAAM,YAAY,IAAI;AACtB,MAAI,WAA6B,CAAC;AAClC,MAAI,WAA+B,CAAC;AACpC,MAAI,kBAAkB;AACtB,MAAI,kBAAkB;AACtB,MAAI,QAA8C;AAElD,QAAM,SAA2B,CAAC;AAClC,QAAM,WAA8B,CAAC;AACrC,MAAI,YAAY;AAChB,MAAI;AACF,QAAI,KAAK,OAAQ,aAAY,GAAG,IAAI,IAAI,KAAK,MAAM,EAAE,MAAM;AAAA,EAC7D,QAAQ;AAAA,EAER;AAEA,QAAM,WAAW,CAAC,QAAgB,wCAAwC,KAAK,GAAG,KAAM,CAAC,CAAC,aAAa,IAAI,QAAQ,aAAa,SAAS,EAAE,WAAW,SAAS,KAAM,uCAAuC,KAAK,GAAG;AAEpN,QAAM,QAAQ,MAAM;AAClB,QAAI,MAAO,cAAa,KAAK;AAC7B,YAAQ;AACR,QAAI,CAAC,SAAS,UAAU,CAAC,SAAS,UAAU,CAAC,mBAAmB,CAAC,gBAAiB;AAClF,UAAM,UAAgC,CAAC;AACvC,QAAI,SAAS,OAAQ,SAAQ,WAAW;AACxC,QAAI,SAAS,OAAQ,SAAQ,WAAW;AACxC,QAAI,mBAAmB,gBAAiB,SAAQ,UAAU,EAAE,GAAI,kBAAkB,EAAE,UAAU,gBAAgB,IAAI,CAAC,GAAI,GAAI,kBAAkB,EAAE,UAAU,gBAAgB,IAAI,CAAC,EAAG;AACjL,eAAW,CAAC;AACZ,eAAW,CAAC;AACZ,sBAAkB;AAClB,sBAAkB;AAClB,QAAI;AACF,WAAK,OAAO;AAAA,IACd,QAAQ;AAAA,IAER;AAAA,EACF;AACA,QAAM,WAAW,MAAM;AACrB,QAAI,SAAS,SAAS,SAAS,UAAU,WAAY,OAAM;AAAA,QACtD,mBAAU,WAAW,OAAO,QAAQ;AAAA,EAC3C;AAGA,QAAM,OAAO,CAAC,KAAqB,UAAU,UAAmB;AAC9D,QAAI,CAAC,OAAQ,QAAO;AACpB,QAAI,CAAC,SAAS;AACZ,UAAI,gBAAgB,uBAAuB;AACzC,2BAAmB;AACnB,iBAAS;AACT,eAAO;AAAA,MACT;AACA,sBAAgB;AAAA,IAClB;AAEA,QAAI,MAA6B;AACjC,QAAI,KAAK,MAAM;AACb,UAAI;AACF,cAAM,KAAK,KAAK,EAAE,GAAG,IAAI,CAAC;AAAA,MAC5B,QAAQ;AACN,cAAM;AAAA,MACR;AAAA,IACF;AACA,QAAI,CAAC,IAAK,QAAO;AACjB,aAAS,KAAK,GAAG;AACjB,aAAS;AACT,WAAO;AAAA,EACT;AACA,QAAM,SAAS,CAAC,KAAqB,WAAoC;AACvE,WAAO,OAAO,KAAK,MAAM;AACzB,QAAI,UAAU,IAAI;AAClB,QAAI,aAAa,IAAI,UAAU,IAAI;AAAA,EACrC;AACA,QAAM,WAAW,CAAC,QAAwB;AACxC,WAAO,KAAK,GAAG;AACf,QAAI,OAAO,SAAS,IAAK,QAAO,MAAM;AAAA,EACxC;AAGA,QAAM,YAAY,IAAI;AACtB,MAAI,OAAO,cAAc,YAAY;AACnC,UAAM,UAAU,SAAyB,OAA0B,MAAuC;AACxG,YAAM,IAAI,UAAU,KAAK,SAAS,UAAa,SAAS,UAAU,MAAO,MAAc,OAAO,IAAI;AAClG,UAAI,CAAC,OAAQ,QAAO;AACpB,UAAI;AACF,cAAM,QAAQ,OAAO,YAAY,eAAe,iBAAiB;AACjE,cAAM,SAAS,QAAS,MAAkB,MAAM,OAAO,iBAAiB,MAAM,MAAM,OAAO,KAAK;AAChG,cAAM,MAAM,UAAU,MAAM;AAC5B,YAAI,SAAS,GAAG,KAAK,SAAS,MAAM,EAAG,QAAO;AAC9C,cAAM,MAAsB,EAAE,IAAI,SAAS,EAAE,GAAG,MAAM,SAAS,QAAQ,OAAO,MAAM,WAAW,QAAS,MAAkB,SAAS,MAAM,EAAE,YAAY,GAAG,KAAK,WAAW,IAAI,EAAE;AAChL,YAAI,KAAK,UAAU;AACjB,gBAAM,UAAU,cAAc,MAAM,YAAY,QAAS,MAAkB,UAAU,OAAU;AAC/F,cAAI,iBAAiB,cAAc,OAAO;AAC1C,gBAAM,OAAO,WAAW,MAAM,MAAM,IAAI,eAAe,cAAc,MAAM,OAAO,MAAM,SAAS,WAAW,eAAe,OAAU;AACrI,cAAI,SAAS,OAAW,KAAI,cAAc;AAAA,QAC5C;AACA,iBAAS,GAAG;AACZ,UAAE;AAAA,UACA,CAAC,QAAQ;AACP,gBAAI;AACF,qBAAO,KAAK,EAAE,QAAQ,IAAI,OAAO,CAAC;AAClC,kBAAI,CAAC,KAAK,SAAU,QAAO,KAAK,KAAK,GAAG;AACxC,kBAAI,kBAAkB,cAAc,cAAc,IAAI,OAAO,CAAC;AAC9D,oBAAM,KAAK,IAAI,QAAQ,IAAI,cAAc;AACzC,kBAAI,CAAC,MAAM,CAAC,sDAAsD,KAAK,EAAE,KAAK,CAAC,IAAI,MAAM;AACvF,oBAAI,MAAM,IAAI,KAAM,KAAI,eAAe;AACvC,uBAAO,KAAK,KAAK,GAAG;AAAA,cACtB;AAEA,oBAAM,SAAS,IAAI,MAAM,EAAE,KAAM,UAAU;AAC3C,oBAAM,UAAU,IAAI,YAAY;AAChC,kBAAI,OAAO;AACX,oBAAM,OAAO,MAAM;AACjB,oBAAI,eAAe,WAAW,MAAM,EAAE;AACtC,qBAAK,GAAG;AAAA,cACV;AACA,oBAAM,IAAI,WAAW,MAAM,KAAK,OAAO,OAAO,EAAE,MAAM,MAAM,MAAS,GAAG,eAAe;AACvF,oBAAM,OAAO,MACX,OAAO,KAAK,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,MAAM,MAAM;AAC3C,oBAAI,MAAO,SAAQ,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;AACzD,oBAAI,IAAK;AACT,oBAAI,KAAK,SAAS,iBAAiB,EAAG,QAAO,KAAK,OAAO,OAAO,EAAE,MAAM,MAAM,MAAS;AACvF,uBAAO,KAAK;AAAA,cACd,CAAC;AACH,mBAAK,EACF,MAAM,MAAM,MAAS,EACrB,KAAK,MAAM;AACV,6BAAa,CAAC;AACd,qBAAK;AAAA,cACP,CAAC;AAAA,YACL,QAAQ;AAAA,YAER;AAAA,UACF;AAAA,UACA,CAAC,QAAiB;AAChB,gBAAI;AACF,qBAAO,KAAK,EAAE,QAAQ,GAAG,OAAO,eAAe,SAAS,IAAI,SAAS,eAAe,YAAY,OAAQ,KAAe,WAAW,GAAG,EAAE,CAAC;AACxI,mBAAK,GAAG;AAAA,YACV,QAAQ;AAAA,YAER;AAAA,UACF;AAAA,QACF;AAAA,MACF,QAAQ;AAAA,MAER;AACA,aAAO;AAAA,IACT;AACA,QAAI,QAAQ;AACZ,aAAS,KAAK,MAAM;AAClB,UAAI,IAAI,UAAW,QAA0B,KAAI,QAAQ;AAAA,IAC3D,CAAC;AAAA,EACH;AAGA,QAAM,MAAM,IAAI;AAChB,MAAI,KAAK,WAAW;AAClB,UAAM,QAAQ,IAAI;AAClB,UAAM,EAAE,MAAM,MAAM,iBAAiB,IAAI;AACzC,UAAM,QAAQ,oBAAI,QAA2F;AAC7G,UAAM,QAAQ,SAAgC,QAAgB,QAAsB,MAAiB;AACnG,UAAI;AACF,YAAI,OAAQ,OAAM,IAAI,MAAM,EAAE,QAAQ,OAAO,MAAM,EAAE,YAAY,GAAG,KAAK,OAAO,GAAG,GAAG,SAAS,CAAC,EAAE,CAAC;AAAA,MACrG,QAAQ;AAAA,MAER;AACA,aAAQ,KAAmC,KAAK,MAAM,QAAQ,KAAK,GAAG,IAAI;AAAA,IAC5E;AACA,UAAM,aAAa,SAAgC,MAAc,OAAe;AAC9E,UAAI;AACF,cAAM,IAAI,IAAI,GAAG,QAAQ,KAAK,CAAC,MAAM,KAAK,CAAC;AAAA,MAC7C,QAAQ;AAAA,MAER;AACA,aAAO,iBAAiB,KAAK,MAAM,MAAM,KAAK;AAAA,IAChD;AACA,UAAM,QAAQ,SAAgC,MAAiD;AAC7F,UAAI;AACF,cAAM,IAAI,MAAM,IAAI,IAAI;AACxB,cAAM,MAAM,IAAI,UAAU,EAAE,GAAG,IAAI;AACnC,YAAI,UAAU,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS,EAAE,GAAG,GAAG;AACrD,gBAAM,MAAsB,EAAE,IAAI,SAAS,EAAE,GAAG,MAAM,OAAO,QAAQ,EAAE,QAAQ,KAAK,WAAW,IAAI,EAAE;AACrG,cAAI,KAAK,UAAU;AACjB,gBAAI,iBAAiB,cAAc,EAAE,OAAO;AAC5C,kBAAM,IAAI,WAAW,MAAM,IAAI,eAAe,cAAc,MAAM,OAAO,SAAS,WAAW,eAAe,OAAU;AACtH,gBAAI,MAAM,OAAW,KAAI,cAAc;AAAA,UACzC;AACA,mBAAS,GAAG;AACZ,cAAI,QAAQ;AACZ,gBAAM,MAAM,CAAC,UAAmB,MAAM;AACpC,gBAAI,MAAO;AACX,oBAAQ;AACR,gBAAI;AACF,qBAAO,KAAK,EAAE,QAAQ,QAAQ,IAAI,KAAK,QAAQ,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC,EAAG,CAAC;AAC5E,kBAAI,KAAK,YAAY,CAAC,OAAO;AAC3B,sBAAM,MAAM,KAAK,sBAAsB,KAAK;AAC5C,oBAAI,kBAAkB,cAAc,IAAI,KAAK,EAAE,MAAM,SAAS,EAAE,OAAO,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,MAAM,GAAG,KAAK,QAAQ,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,MAAM,KAAK,QAAQ,GAAG,IAAI,CAAC,EAAE,KAAK,CAAC,CAAqB,CAAC;AACtM,sBAAM,KAAK,KAAK,kBAAkB,cAAc;AAChD,sBAAM,OAAO,KAAK;AAClB,sBAAM,OAAO,SAAS,MAAM,SAAS,SAAS,KAAK,eAAe,SAAS,SAAS,KAAK,UAAU,KAAK,QAAQ,IAAI;AACpH,sBAAM,YAAY,QAAQ,OAAQ,KAAK,WAAW,aAAa,SAAa,WAAW,MAAM,OAAO,SAAS,SAAS,qBAAqB,aAAa;AACxJ,oBAAI,cAAc,OAAW,KAAI,eAAe;AAAA,cAClD;AACA,mBAAK,GAAG;AAAA,YACV,QAAQ;AAAA,YAER;AAAA,UACF;AACA,eAAK,iBAAiB,QAAQ,IAAI,CAAC;AACnC,eAAK,iBAAiB,SAAS,IAAI,eAAe,CAAC;AACnD,eAAK,iBAAiB,SAAS,IAAI,SAAS,CAAC;AAC7C,eAAK,iBAAiB,WAAW,IAAI,SAAS,CAAC;AAAA,QACjD;AAAA,MACF,QAAQ;AAAA,MAER;AACA,aAAO,KAAK,KAAK,MAAM,IAAI;AAAA,IAC7B;AACA,UAAM,OAAO;AACb,UAAM,mBAAmB;AACzB,UAAM,OAAO;AACb,aAAS,KAAK,MAAM;AAClB,UAAI,MAAM,SAAU,MAAuB,OAAM,OAAO;AACxD,UAAI,MAAM,qBAAqB,WAAY,OAAM,mBAAmB;AACpE,UAAI,MAAM,SAAS,MAAO,OAAM,OAAO;AAAA,IACzC,CAAC;AAAA,EACH;AAGA,QAAM,KAAK,IAAI;AACf,MAAI,OAAO,OAAO,YAAY;AAC5B,UAAM,QAAQ,CAAC,IAAe,WAAmB;AAC/C,YAAM,MAAM,UAAU,MAAM;AAC5B,UAAI,SAAS,GAAG,EAAG;AACnB,YAAM,MAAsB,EAAE,IAAI,SAAS,EAAE,GAAG,MAAM,aAAa,QAAQ,OAAO,KAAK,WAAW,IAAI,EAAE;AACxG,YAAM,SAAS,EAAE,MAAM,GAAG,UAAU,GAAG,WAAW,GAAG,eAAe,GAAG,UAAU,EAAE;AACnF,UAAI,UAAU;AACd,UAAI,UAAU;AACd,YAAM,WAAW,CAAC,WAAoC;AACpD,YAAI,WAAW,CAAC,OAAQ;AACxB,eAAO,OAAO,KAAK,QAAQ,EAAE,UAAU,EAAE,GAAG,OAAO,EAAE,CAAC;AACtD,kBAAU,CAAC,KAAK,EAAE,GAAG,IAAI,GAAG,OAAO;AACnC,kBAAU;AAAA,MACZ;AACA,YAAM,QAAQ,CAAC,KAAsB,SAAkB;AACrD,YAAI;AACF,gBAAM,OAAO,SAAS,IAAI;AAC1B,cAAI,QAAQ,QAAQ;AAClB,mBAAO,QAAQ;AACf,mBAAO,aAAa;AAAA,UACtB,OAAO;AACL,mBAAO,YAAY;AACnB,mBAAO,iBAAiB;AAAA,UAC1B;AACA,cAAI,WAAW,CAAC,OAAQ;AACxB,cAAI,OAAO,YAAY,4BAA4B;AACjD,+BAAmB;AACnB,mBAAO,SAAS;AAAA,UAClB;AACA,iBAAO,YAAY;AACnB,gBAAM,IAAsB,EAAE,UAAU,IAAI,IAAI,KAAK,MAAM,IAAI,GAAG,KAAK;AACvE,cAAI,OAAO,SAAS,SAAU,GAAE,SAAS;AACzC,cAAI,KAAK,SAAU,GAAE,OAAO,UAAU,MAAM,IAAI;AAChD,mBAAS,KAAK,CAAC;AACf,mBAAS;AAAA,QACX,QAAQ;AAAA,QAER;AAAA,MACF;AACA,YAAM,aAAa,GAAG;AAEtB,SAAG,OAAO,SAA2B,MAAyD;AAC5F,cAAM,IAAI,WAAW,KAAK,MAAM,IAAI;AACpC,cAAM,QAAQ,IAAI;AAClB,eAAO;AAAA,MACT;AACA,SAAG,iBAAiB,QAAQ,MAAM,SAAS,EAAE,UAAU,GAAG,YAAY,OAAU,CAAC,CAAC;AAClF,SAAG,iBAAiB,WAAW,CAAC,MAAoB,MAAM,QAAQ,EAAE,IAAI,CAAC;AACzE,UAAI,UAAU;AACd,SAAG,iBAAiB,SAAS,MAAM;AACjC,kBAAU;AAAA,MACZ,CAAC;AACD,SAAG,iBAAiB,SAAS,CAAC,MAAkB;AAC9C,cAAM,UAAU,IAAI;AACpB,iBAAS,EAAE,SAAS,YAAY,UAAU,IAAI,WAAW,WAAW,EAAE,MAAM,aAAa,EAAE,UAAU,QAAW,GAAI,WAAW,EAAE,SAAS,OAAO,EAAE,OAAO,mBAAmB,IAAI,CAAC,EAAG,CAAC;AAAA,MACxL,CAAC;AAAA,IACH;AACA,UAAM,UAAU,cAAc,GAAG;AAAA,MAC/B,YAAY,KAAmB,WAA+B;AAC5D,cAAM,KAAK,SAAS;AACpB,YAAI;AACF,cAAI,OAAQ,OAAM,MAAM,OAAO,eAAe,MAAM,IAAI,OAAO,GAAG,CAAC;AAAA,QACrE,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,MACA,QAAiB,OAAO,WAAW,EAAE,GAAqB;AACxD,eAAO,aAAa;AAAA,MACtB;AAAA,IACF;AACA,WAAO,eAAe,SAAS,QAAQ,EAAE,OAAO,GAAG,KAAK,CAAC;AACzD,QAAI,YAAY;AAChB,aAAS,KAAK,MAAM;AAClB,UAAI,IAAI,cAAe,QAA8B,KAAI,YAAY;AAAA,IACvE,CAAC;AAAA,EACH;AAGA,QAAM,KAAK,IAAI;AACf,MAAI,OAAO,OAAO,YAAY;AAC5B,UAAM,SAAS,IAAI,aAAa,cAAc,aAAa,IAAI,aAAa,MAAM,KAAK;AACvF,UAAM,SAAS;AACf,UAAM,OAAO,oBAAI,IAAY;AAC7B,UAAM,YAAY,CAAC,SAAuC;AACxD,iBAAW,KAAK,KAAK,WAAW,GAAkC;AAChE,YAAI;AACF,gBAAM,QAAQ,KAAK,MAAM,SAAS,EAAE,SAAS;AAC7C,gBAAM,MAAM,GAAG,EAAE,SAAS,IAAI,EAAE,IAAI,IAAI,EAAE,SAAS;AACnD,cAAI,QAAQ,UAAU,KAAK,IAAI,GAAG,EAAG;AACrC,eAAK,IAAI,GAAG;AACZ,gBAAM,MAAM,UAAU,EAAE,IAAI;AAC5B,cAAI,SAAS,GAAG,KAAK,SAAS,EAAE,IAAI,EAAG;AACvC,gBAAM,SAAU,EAAkC;AAClD,gBAAM,SAAS,EAAE,cAAc,EAAE,cAAc,cAAc,EAAE,iBAAiB,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC,EAAG;AAC9G,cAAI,EAAE,kBAAkB,WAAW,EAAE,kBAAkB,kBAAkB;AACvE,kBAAM,QAAQ,OAAO,KAAK,CAAC,MAAM,EAAE,QAAQ,OAAO,KAAK,IAAI,EAAE,YAAY,KAAK,KAAK,EAAE;AACrF,gBAAI,MAAO,QAAO,OAAO,OAAO,EAAE,cAAc,OAAO,cAAc,cAAc,OAAO,aAAa,GAAG,MAAM,WAAW,UAAa,SAAS,EAAE,OAAO,IAAI,CAAC,CAAC;AAChK;AAAA,UACF;AACA,gBAAM,OAAO,EAAE,cAAc,eAAe,aAAa,gCAAgC,KAAK,EAAE,IAAI,IAAI,SAAU,eAAe,EAAE,aAAa,KAAK;AACrJ,eAAK,EAAE,IAAI,SAAS,EAAE,GAAG,MAAM,QAAQ,OAAO,KAAK,WAAW,OAAO,SAAS,KAAK,MAAM,SAAS,EAAE,WAAW,GAAG,YAAY,KAAK,MAAM,EAAE,QAAQ,GAAG,GAAG,OAAO,CAAC;AAAA,QACnK,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF;AACA,eAAW,QAAQ,CAAC,YAAY,YAAY,GAAG;AAC7C,UAAI;AACF,cAAM,KAAK,IAAI,GAAG,SAAS;AAC3B,WAAG,QAAQ,EAAE,MAAM,UAAU,KAAK,CAAC;AACnC,iBAAS,KAAK,MAAM,GAAG,WAAW,CAAC;AAAA,MACrC,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA,MAAM,MAAM;AACV,UAAI,CAAC,OAAQ;AACb,YAAM;AACN,eAAS;AACT,mBAAa,IAAI;AACjB,eAAS,OAAO,CAAC,EAAE,QAAQ,CAAC,OAAO;AACjC,YAAI;AACF,aAAG;AAAA,QACL,QAAQ;AAAA,QAER;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAGO,SAAS,oBAAoB,MAAoK;AACtM,MAAI,UAAiC;AACrC,SAAO;AAAA,IACL,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,SAAS,CAAC;AAAA,MACV,UAAU,CAAC,IAAI,QAAQ;AAErB,YAAI,WAAW,SAAS,OAAO,WAAW,cAAc,SAAS,KAAM,QAAO,MAAM;AAAA,QAAC;AACrF,cAAM,IAAI,oBAAoB,KAAK,MAAM,EAAE;AAC3C,kBAAU;AACV,eAAO,MAAM;AACX,YAAE,KAAK;AACP,cAAI,YAAY,EAAG,WAAU;AAAA,QAC/B;AAAA,MACF;AAAA,IACF;AAAA,IACA,OAAO,MAAM,SAAS,MAAM;AAAA,EAC9B;AACF;;;ACzgBA,SAAS,YAAAA,iBAAgB;AAkDlB,IAAM,mBAAmB,KAAK;AAErC,eAAe,KAAK,MAA6B;AAC/C,QAAM,SAAS,IAAI,KAAK,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,YAAY,IAAI,kBAAkB,MAAM,CAAC;AAClF,SAAO,IAAI,SAAS,MAAM,EAAE,KAAK;AACnC;AAOO,IAAM,mBAAN,MAAuB;AAAA,EAgB5B,YAA6B,MAA+B;AAA/B;AAf7B,wBAAS,YAAWC,UAAS,EAAE;AAC/B,wBAAQ,UAAgC;AACxC,wBAAQ,aAA2B;AACnC,wBAAQ,aAAY;AACpB,wBAAQ,UAAuB;AAC/B,wBAAQ,SAAiB,CAAC;AAC1B,wBAAQ,eAAc;AACtB,wBAAQ,QAAO,oBAAI,IAAoB;AACvC,wBAAQ,YAAkD;AAC1D,wBAAQ,cAAmD;AAC3D,wBAAQ,WAAU;AAClB,wBAAQ,aAAY;AACpB,wBAAQ,YAAiC;AACzC,wBAAQ,aAAY;AAAA,EAEyC;AAAA;AAAA,EAG7D,cAAuB;AACrB,WAAO,CAAC,CAAC,KAAK;AAAA,EAChB;AAAA;AAAA,EAGA,IAAI,kBAAiC;AACnC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,MAAM,WAAmB,QAAqC;AAC5D,QAAI,KAAK,UAAW;AACpB,UAAM,MAAM,KAAK,UAAU,MAAM;AACjC,QAAI,KAAK,cAAc,aAAa,KAAK,cAAc,OAAO,KAAK,OAAQ;AAC3E,SAAK,KAAK;AACV,SAAK,SAAS,KAAK,KAAK,cAAc,EAAE,GAAG,QAAQ,MAAM,CAAC,MAAM,KAAK,KAAK,WAAW,CAAC,GAAG,SAAS,KAAK,KAAK,QAAQ,CAAC;AACrH,QAAI,CAAC,KAAK,OAAQ;AAClB,SAAK,YAAY;AACjB,SAAK,YAAY;AACjB,SAAK,WAAW,YAAY,MAAM,KAAK,KAAK,MAAM,GAAG,KAAK,KAAK,mBAAmB,GAAI;AAAA,EACxF;AAAA;AAAA,EAGA,KAAK,UAAiC,CAAC,GAAS;AAC9C,UAAM,SAAS,KAAK;AACpB,SAAK,SAAS;AACd,SAAK,YAAY;AACjB,SAAK,YAAY;AACjB,QAAI,KAAK,SAAU,eAAc,KAAK,QAAQ;AAC9C,SAAK,WAAW;AAChB,YAAQ,KAAK;AACb,QAAI,QAAQ,SAAS;AACnB,WAAK,SAAS;AACd,WAAK,QAAQ,CAAC;AACd,WAAK,cAAc;AAAA,IACrB,MAAO,MAAK,KAAK,MAAM;AAAA,EACzB;AAAA;AAAA,EAGA,eAAe,KAAa,SAAwB;AAClD,SAAK,QAAQ,eAAe,KAAK,OAAO;AAAA,EAC1C;AAAA,EAEQ,KAAK,WAAmB,OAAsB;AACpD,QAAI;AACJ,QAAI;AACF,aAAO,KAAK,UAAU,KAAK;AAAA,IAC7B,QAAQ;AACN;AAAA,IACF;AACA,QAAI,KAAK,UAAU,KAAK,OAAO,cAAc,UAAW,MAAK,KAAK;AAClE,UAAM,QAAQ,KAAK,WAAW,KAAK,SAAS,EAAE,WAAW,MAAM,KAAK,KAAK,KAAK,GAAG,QAAQ,CAAC,GAAG,OAAO,EAAE;AACtG,UAAM,OAAO,KAAK,IAAI;AACtB,UAAM,SAAS,KAAK;AACpB,QAAI,MAAM,SAAS,KAAK,KAAK,kBAAkB,KAAY,MAAK,KAAK,MAAM;AAAA,EAC7E;AAAA;AAAA,EAGQ,OAAa;AACnB,UAAM,QAAQ,KAAK;AACnB,SAAK,SAAS;AACd,QAAI,CAAC,SAAS,CAAC,MAAM,OAAO,OAAQ;AACpC,SAAK,MAAM,KAAK,KAAK;AACrB,SAAK,eAAe,MAAM;AAC1B,UAAM,MAAM,KAAK,KAAK,kBAAkB;AACxC,WAAO,KAAK,cAAc,OAAO,KAAK,MAAM,SAAS,GAAG;AACtD,YAAM,IAAI,KAAK,YAAY,IAAI;AAC/B,YAAM,CAAC,OAAO,IAAI,KAAK,MAAM,OAAO,GAAG,CAAC;AACxC,UAAI,QAAS,MAAK,eAAe,QAAQ;AAAA,IAC3C;AAAA,EACF;AAAA,EAEQ,QAAQ,OAAsB;AACpC,QAAI,MAAM,QAAQ,QAAW;AAC3B,YAAM,MAAM,KAAK,KAAK,IAAI,MAAM,SAAS,KAAK;AAC9C,WAAK,KAAK,IAAI,MAAM,WAAW,MAAM,CAAC;AACtC,YAAM,MAAM;AAAA,IACd;AACA,WAAO,MAAM;AAAA,EACf;AAAA,EAEQ,KAAK,OAAsB;AACjC,UAAM,OAAO,KAAK,UAAU,EAAE,WAAW,MAAM,WAAW,UAAU,KAAK,UAAU,KAAK,KAAK,QAAQ,KAAK,GAAG,GAAG,MAAM,KAAK,CAAC;AAC5H,WAAO,GAAG,KAAK,MAAM,GAAG,EAAE,CAAC,cAAc,MAAM,OAAO,KAAK,GAAG,CAAC;AAAA,EACjE;AAAA,EAEQ,OAAO,OAAoB;AACjC,UAAM,IAAI,KAAK,MAAM,QAAQ,KAAK;AAClC,QAAI,KAAK,GAAG;AACV,WAAK,MAAM,OAAO,GAAG,CAAC;AACtB,WAAK,eAAe,MAAM;AAAA,IAC5B;AAAA,EACF;AAAA;AAAA,EAGA,QAAuB;AACrB,SAAK,KAAK;AACV,QAAI,KAAK,cAAc,CAAC,KAAK,MAAM,OAAQ,QAAO,QAAQ,QAAQ;AAElE,QAAI,KAAK,SAAU,QAAO,KAAK,SAAS,KAAK,MAAM,KAAK,MAAM,CAAC;AAC/D,WAAQ,KAAK,WAAW,KAAK,MAAM,EAAE,QAAQ,MAAM;AACjD,WAAK,WAAW;AAAA,IAClB,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,QAAuB;AACnC,SAAK,YAAY;AACjB,QAAI;AACF,aAAO,KAAK,MAAM,QAAQ;AACxB,cAAM,QAAQ,KAAK,MAAM,CAAC;AAC1B,cAAM,SAAS,MAAM,KAAK,KAAK,KAAK;AACpC,YAAI,WAAW,SAAS;AACtB,eAAK,WAAW;AAChB,cAAI,CAAC,KAAK,aAAa,KAAK,UAAU,GAAG;AACvC,iBAAK,aAAa,WAAW,MAAM;AACjC,mBAAK,aAAa;AAClB,mBAAK,KAAK,MAAM;AAAA,YAClB,GAAG,UAAU,KAAK,UAAU,CAAC,CAAC;AAAA,UAChC,MAAO,MAAK,UAAU;AACtB;AAAA,QACF;AACA,aAAK,UAAU;AACf,aAAK,OAAO,KAAK;AACjB,YAAI,WAAW,YAAY;AACzB,qBAAW,KAAK,KAAK,MAAM,MAAM,EAAG,KAAI,EAAE,cAAc,MAAM,UAAW,MAAK,OAAO,CAAC;AACtF,eAAK,KAAK,WAAW,MAAM,SAAS;AAAA,QACtC;AAAA,MACF;AAAA,IACF,UAAE;AACA,WAAK,YAAY;AAAA,IACnB;AAAA,EACF;AAAA,EAEA,MAAc,KAAK,OAA6D;AAC9E,UAAM,IAAI,KAAK,KAAK,MAAM;AAC1B,QAAI,CAAC,EAAG,QAAO;AACf,QAAI;AACF,YAAM,OAAO,KAAK,KAAK,KAAK;AAC5B,UAAI,OAAsB;AAC1B,UAAI;AACJ,UAAI,OAAO,sBAAsB,YAAY;AAC3C,YAAI;AACF,iBAAO,MAAM,KAAK,IAAI;AACtB,gBAAM;AAAA,QACR,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MACF;AACA,YAAM,OAAO,OAAO,SAAS,WAAW,KAAK,SAAS,KAAK;AAC3D,YAAM,MAAM,MAAM,EAAE,KAAK,KAAK,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,QAAQ,QAAQ,MAAM,SAAS,EAAE,gBAAgB,aAAa,GAAG,WAAW,OAAO,kBAAkB,aAAa,QAAQ,MAAM,OAAO,CAAC;AACtL,UAAI,IAAI,IAAI;AACV,cAAM,OAAQ,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI;AAC/C,eAAO,MAAM,WAAW,aAAa;AAAA,MACvC;AACA,aAAO,IAAI,UAAU,OAAO,IAAI,SAAS,OAAO,IAAI,WAAW,MAAM,SAAS;AAAA,IAChF,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,cAAoB;AAClB,SAAK,QAAQ,QAAQ;AACrB,SAAK,KAAK;AACV,UAAM,UAAU,KAAK,MAAM,MAAM,KAAK,YAAY,IAAI,CAAC;AACvD,eAAW,SAAS,SAAS;AAC3B,YAAM,QAAQ,MAAM,QAAQ,SAAY,WAAW,OAAO,mBAAmB,IAAI,IAAI,CAAC,KAAK;AAC3F,UAAI,OAAO;AACX,iBAAW,QAAQ,OAAO;AACxB,YAAI,CAAC,KAAK,QAAQ,KAAK,KAAK,IAAI,CAAC,GAAG;AAClC,iBAAO;AACP;AAAA,QACF;AACA,YAAI,SAAS,OAAO;AAClB,gBAAM,OAAO,OAAO,GAAG,KAAK,OAAO,MAAM;AACzC,gBAAM,SAAS,KAAK;AACpB,eAAK,eAAe,KAAK;AAAA,QAC3B;AAAA,MACF;AACA,UAAI,CAAC,KAAM;AACX,WAAK,OAAO,KAAK;AAAA,IACnB;AAAA,EACF;AAAA,EAEQ,QAAQ,MAAuB;AACrC,UAAM,MAAM,KAAK,KAAK,IAAI,CAAC,CAAC;AAC5B,QAAI;AACF,UAAI,KAAK,UAAU,oBAAoB,OAAO,cAAc,eAAe,OAAO,UAAU,eAAe,cAAc,UAAU,WAAW,KAAK,IAAI,KAAK,CAAC,IAAI,GAAG,EAAE,MAAM,2BAA2B,CAAC,CAAC,EAAG,QAAO;AACnN,YAAM,IAAI,KAAK,KAAK,MAAM;AAC1B,UAAI,CAAC,KAAK,KAAK,SAAS,KAAK,KAAM,QAAO;AAC1C,WAAK,EAAE,KAAK,EAAE,QAAQ,QAAQ,MAAM,MAAM,SAAS,EAAE,gBAAgB,aAAa,GAAG,WAAW,MAAM,aAAa,QAAQ,MAAM,OAAO,CAAC,EAAE,MAAM,MAAM,MAAS;AAChK,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA,EAGA,UAAgB;AACd,SAAK,KAAK;AACV,SAAK,YAAY;AACjB,QAAI,KAAK,WAAY,cAAa,KAAK,UAAU;AACjD,SAAK,aAAa;AAAA,EACpB;AACF;AAGA,SAAS,WAAW,OAAc,KAAsB;AACtD,MAAI,MAAM,SAAS,IAAK,QAAO,CAAC,KAAK;AACrC,QAAM,QAAiB,CAAC;AACxB,MAAI,MAAoB;AACxB,aAAW,KAAK,MAAM,QAAQ;AAC5B,QAAI,CAAC,OAAQ,IAAI,QAAQ,EAAE,SAAS,OAAO,IAAI,OAAO,OAAS,OAAM,KAAM,MAAM,EAAE,WAAW,MAAM,WAAW,MAAM,MAAM,MAAM,QAAQ,CAAC,GAAG,OAAO,EAAE,CAAE;AACxJ,QAAI,OAAO,KAAK,CAAC;AACjB,QAAI,SAAS,EAAE;AAAA,EACjB;AACA,SAAO;AACT;;;AF3RO,SAAS,cAAc,SAAsD;AAClF,QAAM,UAAU,CAAC,QAAiB;AAChC,QAAI;AACF,cAAQ,UAAU,GAAG;AAAA,IACvB,QAAQ;AAAA,IAER;AACA,WAAO;AAAA,EACT;AACA,MAAI;AACF,UAAM,UAAU,QAAQ,UAAU,oBAAoB,QAAQ,OAAO,IAAI;AACzE,UAAM,UAA0B,CAAC;AACjC,QAAI,QAAQ,cAAe,SAAQ,KAAK,uBAAuB,EAAE,OAAO,CAAC,SAAS,QAAQ,QAAQ,KAAK,GAAG,iBAAiB,IAAI,CAAC,CAAC;AACjI,QAAI,QAAS,SAAQ,KAAK,QAAQ,MAAiC;AACnE,UAAM,OAAO,OAAO;AAAA,MAClB,MAAM,CAAC,UAAU,QAAQ,KAAK,KAA4C;AAAA,MAC1E,eAAe;AAAA,MACf,kBAAkB,QAAQ,oBAAoB;AAAA,MAC9C,eAAe,QAAQ,iBAAiB;AAAA,MACxC,gBAAgB,EAAE,QAAQ,MAAM,SAAS,MAAM,aAAa,MAAM,gBAAgB,MAAM,gBAAgB,MAAM,gBAAgB,MAAM,mBAAmB,MAAM,sBAAsB,MAAM,oBAAoB,MAAM,sBAAsB,KAAK;AAAA,MAC9O,UAAU,EAAE,WAAW,IAAI,QAAQ,KAAK,OAAO,OAAO;AAAA,MACtD,kBAAkB;AAAA,MAClB,cAAc;AAAA,MACd,cAAc;AAAA,MACd,cAAc;AAAA,MACd;AAAA,MACA,cAAc;AAAA,IAChB,CAAC;AACD,QAAI,CAAC,KAAM,QAAO;AAClB,WAAO;AAAA,MACL,MAAM,MAAM;AACV,YAAI;AACF,eAAK;AAAA,QACP,SAAS,KAAK;AACZ,kBAAQ,GAAG;AAAA,QACb;AAAA,MACF;AAAA,MACA,gBAAgB,CAAC,KAAK,YAAY;AAChC,YAAI;AACF,iBAAO,eAAe,KAAK,OAAO;AAAA,QACpC,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,MACA,OAAO,MAAM,SAAS,MAAM;AAAA,IAC9B;AAAA,EACF,SAAS,KAAK;AACZ,YAAQ,GAAG;AACX,WAAO;AAAA,EACT;AACF;AAGO,IAAM,WAAwB,EAAE,eAAe,cAAc,CAAC,YAAY,IAAI,iBAAiB,EAAE,GAAG,SAAS,cAAc,CAAC,EAAE;","names":["randomId","randomId"]}
|
|
1
|
+
{"version":3,"sources":["../src/recorder.ts","../src/network.ts","../src/replay.ts"],"sourcesContent":["/**\n * Session-replay chunk: the only module that imports rrweb. Built as its own entry and loaded on\n * demand (`import(\"./recorder\")`) together with the replay controller, so the base SDK pays for\n * neither. The script-tag build ships it as `guidely-recorder.js` (assigns `window.__guidelyRecorder`).\n */\nimport { record } from \"@rrweb/record\";\nimport { getRecordConsolePlugin } from \"@rrweb/rrweb-plugin-console-record\";\nimport type { RecordPlugin } from \"@rrweb/types\";\nimport type { RecorderApi, RecorderHandle, RecorderStartOptions } from \"./recorder-types\";\nimport { networkRecordPlugin } from \"./network\";\nimport { ReplayController } from \"./replay\";\n\nexport type { RecorderApi, RecorderHandle, RecorderStartOptions } from \"./recorder-types\";\nexport { ReplayController } from \"./replay\";\nexport type { ReplayControllerOptions, ReplayRecordingConfig, ReplayChunkMeta } from \"./replay\";\nexport type { NetworkCaptureOptions } from \"./network\";\n\n/** Starts an rrweb recording with Guidely's privacy defaults (inputs always masked, no canvas, no fonts). */\nexport function startRecorder(options: RecorderStartOptions): RecorderHandle | null {\n const onError = (err: unknown) => {\n try {\n options.onError?.(err);\n } catch {\n /* ignore */\n }\n return true; // handled: never rethrow into the host page\n };\n try {\n const network = options.network ? networkRecordPlugin(options.network) : null;\n const plugins: RecordPlugin[] = [];\n if (options.recordConsole) plugins.push(getRecordConsolePlugin({ level: [\"error\", \"warn\", \"info\", \"log\"], lengthThreshold: 200 }));\n if (network) plugins.push(network.plugin as unknown as RecordPlugin);\n const stop = record({\n emit: (event) => options.emit(event as { type: number; timestamp: number }),\n maskAllInputs: true,\n maskTextSelector: options.maskTextSelector || undefined,\n blockSelector: options.blockSelector || undefined,\n slimDOMOptions: { script: true, comment: true, headFavicon: true, headWhitespace: true, headMetaSocial: true, headMetaRobots: true, headMetaHttpEquiv: true, headMetaVerification: true, headMetaAuthorship: true, headMetaDescKeywords: true },\n sampling: { mousemove: 50, scroll: 150, input: \"last\" },\n inlineStylesheet: true,\n recordCanvas: false,\n collectFonts: false,\n inlineImages: false,\n plugins,\n errorHandler: onError,\n });\n if (!stop) return null;\n return {\n stop: () => {\n try {\n stop();\n } catch (err) {\n onError(err);\n }\n },\n addCustomEvent: (tag, payload) => {\n try {\n record.addCustomEvent(tag, payload);\n } catch {\n /* not recording */\n }\n },\n flush: () => network?.flush(),\n };\n } catch (err) {\n onError(err);\n return null;\n }\n}\n\n/** The recorder chunk's API (the script-tag build substitutes a loader that resolves to it). */\nexport const recorder: RecorderApi = { startRecorder, createReplay: (options) => new ReplayController({ ...options, startRecorder }) };\n","/**\n * Network recording for session replays (part of the lazily loaded recorder chunk, never the base\n * SDK): patches `fetch`, `XMLHttpRequest` and `WebSocket` while recording, observes resource timing,\n * and emits batched `NetworkPluginPayload`s as rrweb plugin events (`NETWORK_PLUGIN`).\n * The page's own requests behave exactly as before: the original promise / XHR / socket is returned\n * untouched, bodies are read from clones, and every recording step is wrapped in try/catch.\n */\nimport { NETWORK_PLUGIN, SENSITIVE_HEADER_PATTERN, SENSITIVE_PARAM_PATTERN, WS_MAX_MESSAGE_BYTES, WS_MAX_MESSAGES_PER_SOCKET, randomId, type NetworkPluginPayload, type NetworkRequest, type NetworkRequestType, type WebSocketMessage } from \"@guidelytech/shared/runtime\";\n\n/** Network recording settings (merged from the project setting and SDK options by the client). */\nexport interface NetworkCaptureOptions {\n /** Record headers and text bodies (`recordNetworkPayloads`). */\n payloads: boolean;\n /** Guidely API base URL: requests to `<origin>/api/v1/sdk/` are never recorded. */\n apiUrl?: string;\n /** Last say over each request: return a modified copy, or null to drop it (exceptions drop it too). */\n mask?: (request: NetworkRequest) => NetworkRequest | null;\n}\n\n/** Bodies above this many characters are cut and marked `[truncated]`. */\nexport const MAX_BODY_CHARS = 10 * 1024;\nexport const MAX_REQUESTS_PER_PAGE = 500;\nconst BATCH_SIZE = 20;\nconst BATCH_MS = 1000;\nconst BODY_TIMEOUT_MS = 2000;\nconst REDACTED = \"[redacted]\";\n\n// Per page load (the chunk is loaded once per page).\nlet requestCount = 0;\n/** Resource-timing entries that started before this time were already reported by an earlier recording. */\nlet perfCutoff = 0;\n\nconst now = () => Date.now();\n\n/** Absolute URL with sensitive query parameter values replaced by `[redacted]`. */\nexport function redactUrl(raw: string, base = typeof location !== \"undefined\" ? location.href : \"http://localhost/\"): string {\n try {\n const u = new URL(raw, base);\n u.searchParams.forEach((_v, k) => {\n if (SENSITIVE_PARAM_PATTERN.test(k)) u.searchParams.set(k, REDACTED);\n });\n return u.toString().replace(/%5Bredacted%5D/g, REDACTED);\n } catch {\n return String(raw);\n }\n}\n\n/** Recursively masks values of sensitive-looking keys. */\nexport function maskJson(value: unknown, depth = 0): unknown {\n if (depth > 20 || !value || typeof value !== \"object\") return value;\n if (Array.isArray(value)) return value.map((v) => maskJson(v, depth + 1));\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value)) out[k] = SENSITIVE_PARAM_PATTERN.test(k) ? REDACTED : maskJson(v, depth + 1);\n return out;\n}\n\nfunction cap(text: string, max: number): string {\n return text.length > max ? `${text.slice(0, max)}[truncated]` : text;\n}\n\n/** Body as recorded: text/JSON/form only (JSON keys masked), ≤10KB; `[binary]` otherwise. */\nexport function formatBody(body: unknown, contentType: string | null | undefined): string | undefined {\n if (body == null || body === \"\") return undefined;\n const ct = (contentType || \"\").toLowerCase();\n if (typeof URLSearchParams !== \"undefined\" && body instanceof URLSearchParams) return formatBody(body.toString(), \"application/x-www-form-urlencoded\");\n if (typeof body !== \"string\") return \"[binary]\";\n if (ct && !/^text\\/|json|x-www-form-urlencoded/.test(ct)) return \"[binary]\";\n if (ct.includes(\"x-www-form-urlencoded\")) {\n const params = new URLSearchParams(body);\n params.forEach((_v, k) => {\n if (SENSITIVE_PARAM_PATTERN.test(k)) params.set(k, REDACTED);\n });\n return cap(params.toString().replace(/%5Bredacted%5D/g, REDACTED), MAX_BODY_CHARS);\n }\n if (ct.includes(\"json\") || (!ct && /^\\s*[[{]/.test(body))) {\n try {\n return cap(JSON.stringify(maskJson(JSON.parse(body))), MAX_BODY_CHARS);\n } catch {\n /* not JSON after all */\n }\n }\n return cap(body, MAX_BODY_CHARS);\n}\n\n/** Lower-cased header names; sensitive values `[redacted]`. */\nexport function formatHeaders(entries: Iterable<[string, string]>): Record<string, string> {\n const out: Record<string, string> = {};\n for (const [k, v] of entries) {\n const name = k.toLowerCase();\n out[name] = SENSITIVE_HEADER_PATTERN.test(name) ? REDACTED : String(v);\n }\n return out;\n}\n\nfunction headerEntries(h: unknown): Array<[string, string]> {\n if (!h) return [];\n if (Array.isArray(h)) return h.map(([k, v]) => [String(k), String(v)]);\n if (typeof (h as Headers).forEach === \"function\") {\n const out: Array<[string, string]> = [];\n (h as Headers).forEach((v, k) => out.push([k, v]));\n return out;\n }\n return Object.entries(h as Record<string, string>).map(([k, v]) => [k, String(v)]);\n}\n\n/** UTF-8 byte length of a string, or the size of binary data. */\nexport function byteSize(data: unknown): number {\n if (typeof data === \"string\") return typeof TextEncoder !== \"undefined\" ? new TextEncoder().encode(data).length : data.length;\n if (data instanceof ArrayBuffer) return data.byteLength;\n if (ArrayBuffer.isView(data)) return data.byteLength;\n if (typeof Blob !== \"undefined\" && data instanceof Blob) return data.size;\n return 0;\n}\n\nfunction frameData(data: unknown, size: number): string {\n if (typeof data !== \"string\") return `[binary ${size} B]`;\n let text = data;\n try {\n if (/^\\s*[[{]/.test(text)) text = JSON.stringify(maskJson(JSON.parse(text)));\n } catch {\n /* plain text */\n }\n return cap(text, WS_MAX_MESSAGE_BYTES);\n}\n\nconst RESOURCE_TYPES: Record<string, NetworkRequestType> = { script: \"script\", link: \"stylesheet\", css: \"stylesheet\", img: \"image\", image: \"image\", audio: \"media\", video: \"media\", track: \"media\", beacon: \"beacon\", navigation: \"document\", iframe: \"document\", frame: \"document\" };\n\n/** A running capture: `flush()` emits pending items now; `stop()` flushes and restores everything. */\nexport interface NetworkCapture {\n flush(): void;\n stop(): void;\n}\n\ntype Win = Window & typeof globalThis;\n\n/** Starts recording the window's network activity; `emit` receives batched payloads. */\nexport function startNetworkCapture(win: Win, opts: NetworkCaptureOptions, emit: (payload: NetworkPluginPayload) => void): NetworkCapture {\n let active = true;\n const startedAt = now();\n let requests: NetworkRequest[] = [];\n let messages: WebSocketMessage[] = [];\n let droppedRequests = 0;\n let droppedMessages = 0;\n let timer: ReturnType<typeof setTimeout> | null = null;\n /** Recent fetch/XHR requests, for enriching with resource timing. */\n const recent: NetworkRequest[] = [];\n const cleanups: Array<() => void> = [];\n let apiPrefix = \"\";\n try {\n if (opts.apiUrl) apiPrefix = `${new URL(opts.apiUrl).origin}/api/v1/sdk/`;\n } catch {\n /* invalid apiUrl */\n }\n\n const excluded = (url: string) => /^(data|blob|about|chrome-extension):/i.test(url) || (!!apiPrefix && url.replace(/^ws(s?):/i, \"http$1:\").startsWith(apiPrefix)) || /\\/guidely-(recorder|capture)(\\.min)?\\.js(\\?|$)/.test(url);\n\n const flush = () => {\n if (timer) clearTimeout(timer);\n timer = null;\n if (!requests.length && !messages.length && !droppedRequests && !droppedMessages) return;\n const payload: NetworkPluginPayload = {};\n if (requests.length) payload.requests = requests;\n if (messages.length) payload.messages = messages;\n if (droppedRequests || droppedMessages) payload.dropped = { ...(droppedRequests ? { requests: droppedRequests } : {}), ...(droppedMessages ? { messages: droppedMessages } : {}) };\n requests = [];\n messages = [];\n droppedRequests = 0;\n droppedMessages = 0;\n try {\n emit(payload);\n } catch {\n /* recorder stopped */\n }\n };\n const schedule = () => {\n if (requests.length + messages.length >= BATCH_SIZE) flush();\n else timer ??= setTimeout(flush, BATCH_MS);\n };\n\n /** Applies the mask hook; null drops. `counted` requests (re-emissions) skip the per-page cap. */\n const push = (req: NetworkRequest, counted = false): boolean => {\n if (!active) return false;\n if (!counted) {\n if (requestCount >= MAX_REQUESTS_PER_PAGE) {\n droppedRequests += 1;\n schedule();\n return false;\n }\n requestCount += 1;\n }\n // Pushed as-is (resource timing may still enrich it before the batch is sent); the hook gets a copy.\n let out: NetworkRequest | null = req;\n if (opts.mask) {\n try {\n out = opts.mask({ ...req });\n } catch {\n out = null;\n }\n }\n if (!out) return false;\n requests.push(out);\n schedule();\n return true;\n };\n const finish = (req: NetworkRequest, fields: Partial<NetworkRequest>) => {\n Object.assign(req, fields);\n req.endTime = now();\n req.durationMs = req.endTime - req.startTime;\n };\n const remember = (req: NetworkRequest) => {\n recent.push(req);\n if (recent.length > 100) recent.shift();\n };\n\n // --- fetch -------------------------------------------------------------------------------------\n const origFetch = win.fetch;\n if (typeof origFetch === \"function\") {\n const patched = function (this: unknown, input: RequestInfo | URL, init?: RequestInit): Promise<Response> {\n const p = origFetch.call(this === undefined || this === patched ? win : (this as Win), input, init);\n if (!active) return p;\n try {\n const isReq = typeof Request !== \"undefined\" && input instanceof Request;\n const rawUrl = isReq ? (input as Request).url : String(input instanceof URL ? input.href : input);\n const url = redactUrl(rawUrl);\n if (excluded(url) || excluded(rawUrl)) return p;\n const req: NetworkRequest = { id: randomId(12), type: \"fetch\", method: String(init?.method || (isReq ? (input as Request).method : \"GET\")).toUpperCase(), url, startTime: now() };\n if (opts.payloads) {\n const headers = headerEntries(init?.headers ?? (isReq ? (input as Request).headers : undefined));\n req.requestHeaders = formatHeaders(headers);\n const body = formatBody(init?.body, req.requestHeaders[\"content-type\"] ?? (typeof init?.body === \"string\" ? \"text/plain\" : undefined));\n if (body !== undefined) req.requestBody = body;\n }\n remember(req);\n p.then(\n (res) => {\n try {\n finish(req, { status: res.status });\n if (!opts.payloads) return void push(req);\n req.responseHeaders = formatHeaders(headerEntries(res.headers));\n const ct = res.headers.get(\"content-type\");\n if (!ct || !/^text\\/(?!event-stream)|json|x-www-form-urlencoded/i.test(ct) || !res.body) {\n if (ct && res.body) req.responseBody = \"[binary]\";\n return void push(req);\n }\n // Read a clone (never the app's stream), at most ~10KB and 2s, then send.\n const reader = res.clone().body!.getReader();\n const decoder = new TextDecoder();\n let text = \"\";\n const done = () => {\n req.responseBody = formatBody(text, ct);\n push(req);\n };\n const t = setTimeout(() => void reader.cancel().catch(() => undefined), BODY_TIMEOUT_MS);\n const read = (): Promise<void> =>\n reader.read().then(({ done: end, value }) => {\n if (value) text += decoder.decode(value, { stream: true });\n if (end) return;\n if (text.length > MAX_BODY_CHARS * 4) return void reader.cancel().catch(() => undefined);\n return read();\n });\n read()\n .catch(() => undefined)\n .then(() => {\n clearTimeout(t);\n done();\n });\n } catch {\n /* never affect the app */\n }\n },\n (err: unknown) => {\n try {\n finish(req, { status: 0, error: err instanceof Error && err.name === \"AbortError\" ? \"aborted\" : String((err as Error)?.message || err) });\n push(req);\n } catch {\n /* ignore */\n }\n },\n );\n } catch {\n /* never affect the app */\n }\n return p;\n };\n win.fetch = patched as typeof fetch;\n cleanups.push(() => {\n if (win.fetch === (patched as typeof fetch)) win.fetch = origFetch;\n });\n }\n\n // --- XMLHttpRequest ----------------------------------------------------------------------------\n const XHR = win.XMLHttpRequest;\n if (XHR?.prototype) {\n const proto = XHR.prototype;\n const { open, send, setRequestHeader } = proto;\n const state = new WeakMap<XMLHttpRequest, { method: string; url: string; headers: Array<[string, string]> }>();\n const pOpen = function (this: XMLHttpRequest, method: string, url: string | URL, ...rest: unknown[]) {\n try {\n if (active) state.set(this, { method: String(method).toUpperCase(), url: String(url), headers: [] });\n } catch {\n /* ignore */\n }\n return (open as (...a: unknown[]) => void).call(this, method, url, ...rest);\n };\n const pSetHeader = function (this: XMLHttpRequest, name: string, value: string) {\n try {\n state.get(this)?.headers.push([name, value]);\n } catch {\n /* ignore */\n }\n return setRequestHeader.call(this, name, value);\n };\n const pSend = function (this: XMLHttpRequest, body?: Document | XMLHttpRequestBodyInit | null) {\n try {\n const s = state.get(this);\n const url = s ? redactUrl(s.url) : \"\";\n if (active && s && !excluded(url) && !excluded(s.url)) {\n const req: NetworkRequest = { id: randomId(12), type: \"xhr\", method: s.method, url, startTime: now() };\n if (opts.payloads) {\n req.requestHeaders = formatHeaders(s.headers);\n const b = formatBody(body, req.requestHeaders[\"content-type\"] ?? (typeof body === \"string\" ? \"text/plain\" : undefined));\n if (b !== undefined) req.requestBody = b;\n }\n remember(req);\n let ended = false;\n const end = (error?: string) => () => {\n if (ended) return;\n ended = true;\n try {\n finish(req, { status: error ? 0 : this.status, ...(error ? { error } : {}) });\n if (opts.payloads && !error) {\n const raw = this.getAllResponseHeaders() || \"\";\n req.responseHeaders = formatHeaders(raw.trim().split(/[\\r\\n]+/).filter(Boolean).map((line) => [line.slice(0, line.indexOf(\":\")).trim(), line.slice(line.indexOf(\":\") + 1).trim()] as [string, string]));\n const ct = this.getResponseHeader(\"content-type\");\n const type = this.responseType;\n const text = type === \"\" || type === \"text\" ? this.responseText : type === \"json\" ? JSON.stringify(this.response) : null;\n const formatted = text == null ? (this.response ? \"[binary]\" : undefined) : formatBody(text, ct ?? (type === \"json\" ? \"application/json\" : \"text/plain\"));\n if (formatted !== undefined) req.responseBody = formatted;\n }\n push(req);\n } catch {\n /* ignore */\n }\n };\n this.addEventListener(\"load\", end());\n this.addEventListener(\"error\", end(\"network error\"));\n this.addEventListener(\"abort\", end(\"aborted\"));\n this.addEventListener(\"timeout\", end(\"timeout\"));\n }\n } catch {\n /* never affect the app */\n }\n return send.call(this, body);\n };\n proto.open = pOpen as typeof open;\n proto.setRequestHeader = pSetHeader;\n proto.send = pSend;\n cleanups.push(() => {\n if (proto.open === (pOpen as typeof open)) proto.open = open;\n if (proto.setRequestHeader === pSetHeader) proto.setRequestHeader = setRequestHeader;\n if (proto.send === pSend) proto.send = send;\n });\n }\n\n // --- WebSocket ---------------------------------------------------------------------------------\n const WS = win.WebSocket;\n if (typeof WS === \"function\") {\n const track = (ws: WebSocket, rawUrl: string) => {\n const url = redactUrl(rawUrl);\n if (excluded(url)) return;\n const req: NetworkRequest = { id: randomId(12), type: \"websocket\", method: \"GET\", url, startTime: now() };\n const totals = { sent: 0, received: 0, sentBytes: 0, receivedBytes: 0, recorded: 0 };\n let dropped = false;\n let counted = false;\n const emitConn = (fields: Partial<NetworkRequest>) => {\n if (dropped || !active) return;\n Object.assign(req, fields, { messages: { ...totals } });\n dropped = !push({ ...req }, counted);\n counted = true;\n };\n const frame = (dir: \"send\" | \"recv\", data: unknown) => {\n try {\n const size = byteSize(data);\n if (dir === \"send\") {\n totals.sent += 1;\n totals.sentBytes += size;\n } else {\n totals.received += 1;\n totals.receivedBytes += size;\n }\n if (dropped || !active) return;\n if (totals.recorded >= WS_MAX_MESSAGES_PER_SOCKET) {\n droppedMessages += 1;\n return schedule();\n }\n totals.recorded += 1;\n const m: WebSocketMessage = { socketId: req.id, dir, time: now(), size };\n if (typeof data !== \"string\") m.binary = true;\n if (opts.payloads) m.data = frameData(data, size);\n messages.push(m);\n schedule();\n } catch {\n /* ignore */\n }\n };\n const nativeSend = ws.send;\n // Instance-level wrapper: the socket keeps working after recording stops (it just stops being recorded).\n ws.send = function (this: WebSocket, data: string | ArrayBufferLike | Blob | ArrayBufferView) {\n const r = nativeSend.call(this, data); // throws (unrecorded) when the socket is not open\n frame(\"send\", data);\n return r;\n };\n ws.addEventListener(\"open\", () => emitConn({ protocol: ws.protocol || undefined }));\n ws.addEventListener(\"message\", (e: MessageEvent) => frame(\"recv\", e.data));\n let errored = false;\n ws.addEventListener(\"error\", () => {\n errored = true;\n });\n ws.addEventListener(\"close\", (e: CloseEvent) => {\n const endTime = now();\n emitConn({ endTime, durationMs: endTime - req.startTime, closeCode: e.code, closeReason: e.reason || undefined, ...(errored || e.code === 1006 ? { error: \"connection error\" } : {}) });\n });\n };\n const Patched = class extends WS {\n constructor(url: string | URL, protocols?: string | string[]) {\n super(url, protocols);\n try {\n if (active) track(this, String(url instanceof URL ? url.href : url));\n } catch {\n /* never affect the app */\n }\n }\n static override [Symbol.hasInstance](x: unknown): boolean {\n return x instanceof WS;\n }\n };\n Object.defineProperty(Patched, \"name\", { value: WS.name });\n win.WebSocket = Patched as typeof WebSocket;\n cleanups.push(() => {\n if (win.WebSocket === (Patched as typeof WebSocket)) win.WebSocket = WS;\n });\n }\n\n // --- Resource timing -------------------------------------------------------------------------\n const PO = win.PerformanceObserver;\n if (typeof PO === \"function\") {\n const origin = win.performance?.timeOrigin ?? startedAt - (win.performance?.now?.() ?? 0);\n const cutoff = perfCutoff;\n const seen = new Set<string>(); // browsers re-deliver the navigation entry once the load completes\n const onEntries = (list: PerformanceObserverEntryList) => {\n for (const e of list.getEntries() as PerformanceResourceTiming[]) {\n try {\n const start = Math.round(origin + e.startTime);\n const key = `${e.entryType}|${e.name}|${e.startTime}`;\n if (start < cutoff || seen.has(key)) continue;\n seen.add(key);\n const url = redactUrl(e.name);\n if (excluded(url) || excluded(e.name)) continue;\n const status = (e as { responseStatus?: number }).responseStatus;\n const timing = { transferSize: e.transferSize, responseSize: e.decodedBodySize, ...(status ? { status } : {}) };\n if (e.initiatorType === \"fetch\" || e.initiatorType === \"xmlhttprequest\") {\n const match = recent.find((r) => r.url === url && Math.abs(r.startTime - start) <= 50);\n if (match) Object.assign(match, { transferSize: timing.transferSize, responseSize: timing.responseSize }, match.status === undefined && status ? { status } : {});\n continue;\n }\n const type = e.entryType === \"navigation\" ? \"document\" : /\\.(woff2?|ttf|otf|eot)(\\?|$)/i.test(e.name) ? \"font\" : (RESOURCE_TYPES[e.initiatorType] ?? \"other\");\n push({ id: randomId(12), type, method: \"GET\", url, startTime: start, endTime: Math.round(origin + e.responseEnd), durationMs: Math.round(e.duration), ...timing });\n } catch {\n /* ignore */\n }\n }\n };\n for (const type of [\"resource\", \"navigation\"]) {\n try {\n const po = new PO(onEntries);\n po.observe({ type, buffered: true });\n cleanups.push(() => po.disconnect());\n } catch {\n /* unsupported entry type */\n }\n }\n }\n\n return {\n flush,\n stop: () => {\n if (!active) return;\n flush();\n active = false;\n perfCutoff = now();\n cleanups.splice(0).forEach((fn) => {\n try {\n fn();\n } catch {\n /* ignore */\n }\n });\n },\n };\n}\n\n/** The rrweb plugin that records network activity as `{ type: 6, data: { plugin: NETWORK_PLUGIN, payload } }`. */\nexport function networkRecordPlugin(opts: NetworkCaptureOptions): { plugin: { name: string; observer: (cb: (payload: unknown) => void, win: Win) => () => void; options: unknown }; flush: () => void } {\n let capture: NetworkCapture | null = null;\n return {\n plugin: {\n name: NETWORK_PLUGIN,\n options: {},\n observer: (cb, win) => {\n // rrweb also calls plugin observers for same-origin iframes: only the top window is patched.\n if (capture || win !== (typeof window !== \"undefined\" ? window : win)) return () => {};\n const c = startNetworkCapture(win, opts, cb);\n capture = c;\n return () => {\n c.stop();\n if (capture === c) capture = null;\n };\n },\n },\n flush: () => capture?.flush(),\n };\n}\n\n/** Test hook: resets the per-page counters. */\nexport function resetNetworkCounters(): void {\n requestCount = 0;\n perfCutoff = 0;\n}\n","import { randomId } from \"@guidelytech/shared/runtime\";\nimport { backoffMs } from \"./http\";\nimport type { NetworkCaptureOptions } from \"./network\";\nimport type { RecorderHandle, RecorderStartOptions } from \"./recorder-types\";\n\n/** Recorder settings for one session (already merged from the project setting and SDK options). */\nexport interface ReplayRecordingConfig {\n maskTextSelector: string;\n blockSelector: string;\n recordConsole: boolean;\n network?: NetworkCaptureOptions | null;\n}\n\n/** Per-upload metadata (`ReplayChunk` minus `sessionId`/`windowId`/`seq`/`events`). */\nexport interface ReplayChunkMeta {\n anonymousId: string;\n userId?: string | null;\n url?: string;\n device?: { type: string; browser?: string; os?: string };\n viewport?: { width: number; height: number };\n sdk?: { name: string; version: string };\n}\n\nexport interface ReplayControllerOptions {\n /** Upload URL for `POST /api/v1/sdk/replay` (with `?key=` and the given params). */\n url: (params: Record<string, string | undefined>) => string;\n fetch: () => typeof fetch | null;\n meta: () => ReplayChunkMeta;\n /** Starts rrweb (`startRecorder` from the recorder chunk; injectable for tests). */\n startRecorder: (options: RecorderStartOptions) => RecorderHandle | null;\n /** The server answered `{ disabled: true }`: recording must stop for this session. */\n onDisabled: (sessionId: string) => void;\n onError: (err: unknown) => void;\n flushIntervalMs?: number;\n /** Buffered bytes that trigger an early upload (default ~1MB). */\n maxBufferBytes?: number;\n /** Upload backlog cap; the oldest chunks are dropped above it (default ~5MB). */\n maxQueuedBytes?: number;\n}\n\ninterface Chunk {\n sessionId: string;\n meta: ReplayChunkMeta;\n events: string[];\n bytes: number;\n /** Assigned on the first upload attempt and kept for retries (the server dedupes by window + seq). */\n seq?: number;\n}\n\n/** Largest body sent with `sendBeacon` / keepalive fetch while the page is being hidden. */\nexport const UNLOAD_MAX_BYTES = 60 * 1024;\n\nasync function gzip(text: string): Promise<Blob> {\n const stream = new Blob([text]).stream().pipeThrough(new CompressionStream(\"gzip\"));\n return new Response(stream).blob();\n}\n\n/**\n * Session-replay controller (part of the lazily loaded recorder chunk): records with rrweb, buffers events, and uploads them\n * every 5s (or once ~1MB is buffered) as gzip JSON (`?enc=gzip`; plain JSON without\n * `CompressionStream`). On page hide it sends what is left with `sendBeacon` / keepalive fetch.\n */\nexport class ReplayController {\n readonly windowId = randomId(16);\n private handle: RecorderHandle | null = null;\n private sessionId: string | null = null;\n private configKey = \"\";\n private buffer: Chunk | null = null;\n private queue: Chunk[] = [];\n private queuedBytes = 0;\n private seqs = new Map<string, number>();\n private interval: ReturnType<typeof setInterval> | null = null;\n private retryTimer: ReturnType<typeof setTimeout> | null = null;\n private attempt = 0;\n private uploading = false;\n private flushing: Promise<void> | null = null;\n private destroyed = false;\n\n constructor(private readonly opts: ReplayControllerOptions) {}\n\n /** True while rrweb is recording. */\n isRecording(): boolean {\n return !!this.handle;\n }\n\n /** The session currently being recorded, if any. */\n get activeSessionId(): string | null {\n return this.sessionId;\n }\n\n /** Starts (or keeps) recording for `sessionId`. Restarts when the session or settings changed. */\n start(sessionId: string, config: ReplayRecordingConfig): void {\n if (this.destroyed) return;\n const key = JSON.stringify(config);\n if (this.sessionId === sessionId && this.configKey === key && this.handle) return;\n this.stop();\n this.handle = this.opts.startRecorder({ ...config, emit: (e) => this.push(sessionId, e), onError: this.opts.onError });\n if (!this.handle) return;\n this.sessionId = sessionId;\n this.configKey = key;\n this.interval = setInterval(() => void this.flush(), this.opts.flushIntervalMs ?? 5000);\n }\n\n /** Stops recording. Buffered events are uploaded unless `discard` (consent withdrawn, authoring). */\n stop(options: { discard?: boolean } = {}): void {\n const handle = this.handle;\n this.handle = null;\n this.sessionId = null;\n this.configKey = \"\";\n if (this.interval) clearInterval(this.interval);\n this.interval = null;\n handle?.stop();\n if (options.discard) {\n this.buffer = null;\n this.queue = [];\n this.queuedBytes = 0;\n } else void this.flush();\n }\n\n /** Adds an rrweb custom event (Guidely event markers) while recording. */\n addCustomEvent(tag: string, payload: unknown): void {\n this.handle?.addCustomEvent(tag, payload);\n }\n\n private push(sessionId: string, event: unknown): void {\n let json: string;\n try {\n json = JSON.stringify(event);\n } catch {\n return;\n }\n if (this.buffer && this.buffer.sessionId !== sessionId) this.seal();\n const chunk = this.buffer ?? (this.buffer = { sessionId, meta: this.opts.meta(), events: [], bytes: 0 });\n chunk.events.push(json);\n chunk.bytes += json.length;\n if (chunk.bytes > (this.opts.maxBufferBytes ?? 1_000_000)) void this.flush();\n }\n\n /** Moves the buffer to the upload queue, dropping the oldest chunks above the backlog cap. */\n private seal(): void {\n const chunk = this.buffer;\n this.buffer = null;\n if (!chunk || !chunk.events.length) return;\n this.queue.push(chunk);\n this.queuedBytes += chunk.bytes;\n const max = this.opts.maxQueuedBytes ?? 5_000_000;\n while (this.queuedBytes > max && this.queue.length > 1) {\n const i = this.uploading ? 1 : 0;\n const [dropped] = this.queue.splice(i, 1);\n if (dropped) this.queuedBytes -= dropped.bytes;\n }\n }\n\n private nextSeq(chunk: Chunk): number {\n if (chunk.seq === undefined) {\n const seq = this.seqs.get(chunk.sessionId) ?? 0;\n this.seqs.set(chunk.sessionId, seq + 1);\n chunk.seq = seq;\n }\n return chunk.seq;\n }\n\n private body(chunk: Chunk): string {\n const head = JSON.stringify({ sessionId: chunk.sessionId, windowId: this.windowId, seq: this.nextSeq(chunk), ...chunk.meta });\n return `${head.slice(0, -1)},\"events\":[${chunk.events.join(\",\")}]}`;\n }\n\n private remove(chunk: Chunk): void {\n const i = this.queue.indexOf(chunk);\n if (i >= 0) {\n this.queue.splice(i, 1);\n this.queuedBytes -= chunk.bytes;\n }\n }\n\n /** Uploads the buffer and the backlog, one chunk at a time; retries with backoff on failure. */\n flush(): Promise<void> {\n this.seal();\n if (this.retryTimer || !this.queue.length) return Promise.resolve();\n // An upload loop already running may have finished its last chunk: queue another pass after it.\n if (this.flushing) return this.flushing.then(() => this.flush());\n return (this.flushing = this.drain().finally(() => {\n this.flushing = null;\n }));\n }\n\n private async drain(): Promise<void> {\n this.uploading = true;\n try {\n while (this.queue.length) {\n const chunk = this.queue[0] as Chunk;\n const result = await this.send(chunk);\n if (result === \"retry\") {\n this.attempt += 1;\n if (!this.destroyed && this.attempt < 6) {\n this.retryTimer = setTimeout(() => {\n this.retryTimer = null;\n void this.flush();\n }, backoffMs(this.attempt - 1));\n } else this.attempt = 0;\n return;\n }\n this.attempt = 0;\n this.remove(chunk);\n if (result === \"disabled\") {\n for (const c of this.queue.slice()) if (c.sessionId === chunk.sessionId) this.remove(c);\n this.opts.onDisabled(chunk.sessionId);\n }\n }\n } finally {\n this.uploading = false;\n }\n }\n\n private async send(chunk: Chunk): Promise<\"ok\" | \"drop\" | \"retry\" | \"disabled\"> {\n const f = this.opts.fetch();\n if (!f) return \"drop\";\n try {\n const json = this.body(chunk);\n let body: Blob | string = json;\n let enc: string | undefined;\n if (typeof CompressionStream === \"function\") {\n try {\n body = await gzip(json);\n enc = \"gzip\";\n } catch {\n body = json;\n }\n }\n const size = typeof body === \"string\" ? body.length : body.size;\n const res = await f(this.opts.url({ enc }), { method: \"POST\", body, headers: { \"Content-Type\": \"text/plain\" }, keepalive: size < UNLOAD_MAX_BYTES, credentials: \"omit\", mode: \"cors\" });\n if (res.ok) {\n const data = (await res.json().catch(() => null)) as { disabled?: boolean } | null;\n return data?.disabled ? \"disabled\" : \"ok\";\n }\n return res.status >= 400 && res.status < 500 && res.status !== 429 ? \"drop\" : \"retry\";\n } catch {\n return \"retry\";\n }\n }\n\n /**\n * Page is being hidden / unloaded: sends whatever is buffered synchronously as uncompressed JSON\n * (`sendBeacon` up to 60KB, keepalive fetch otherwise), splitting chunks that never went out.\n */\n flushOnHide(): void {\n this.handle?.flush?.();\n this.seal();\n const pending = this.queue.slice(this.uploading ? 1 : 0);\n for (const chunk of pending) {\n const parts = chunk.seq === undefined ? splitChunk(chunk, UNLOAD_MAX_BYTES - 2048) : [chunk];\n let sent = true;\n for (const part of parts) {\n if (!this.sendNow(this.body(part))) {\n sent = false;\n break;\n }\n if (part !== chunk) {\n chunk.events.splice(0, part.events.length);\n chunk.bytes -= part.bytes;\n this.queuedBytes -= part.bytes;\n }\n }\n if (!sent) break;\n this.remove(chunk);\n }\n }\n\n private sendNow(json: string): boolean {\n const url = this.opts.url({});\n try {\n if (json.length <= UNLOAD_MAX_BYTES && typeof navigator !== \"undefined\" && typeof navigator.sendBeacon === \"function\" && navigator.sendBeacon(url, new Blob([json], { type: \"text/plain;charset=UTF-8\" }))) return true;\n const f = this.opts.fetch();\n if (!f || json.length > 64 * 1024) return false;\n void f(url, { method: \"POST\", body: json, headers: { \"Content-Type\": \"text/plain\" }, keepalive: true, credentials: \"omit\", mode: \"cors\" }).catch(() => undefined);\n return true;\n } catch {\n return false;\n }\n }\n\n /** Stops recording and uploads what is left; no retries afterwards. */\n destroy(): void {\n this.stop();\n this.destroyed = true;\n if (this.retryTimer) clearTimeout(this.retryTimer);\n this.retryTimer = null;\n }\n}\n\n/** Splits a chunk into parts of at most `max` bytes of events (a single larger event stays whole). */\nfunction splitChunk(chunk: Chunk, max: number): Chunk[] {\n if (chunk.bytes <= max) return [chunk];\n const parts: Chunk[] = [];\n let cur: Chunk | null = null;\n for (const e of chunk.events) {\n if (!cur || (cur.bytes + e.length > max && cur.events.length)) parts.push((cur = { sessionId: chunk.sessionId, meta: chunk.meta, events: [], bytes: 0 }));\n cur.events.push(e);\n cur.bytes += e.length;\n }\n return parts;\n}\n"],"mappings":"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","names":["randomId","randomId"]}
|
|
@@ -1,32 +1,5 @@
|
|
|
1
1
|
import { SdkEvent, PublishedGuide, FrequencyConfig, AutocaptureSettings, NetworkRequest, ProjectTheme, SdkConfigResponse, ChecklistTask, MessengerMessage, MessengerConversation } from '@guidelytech/shared';
|
|
2
|
-
|
|
3
|
-
type StorageKind = "local" | "session" | "memory";
|
|
4
|
-
/** Minimal synchronous key/value store; never throws (private mode, quota, SSR). */
|
|
5
|
-
interface KeyValueStore {
|
|
6
|
-
get(key: string): string | null;
|
|
7
|
-
set(key: string, value: string): void;
|
|
8
|
-
remove(key: string): void;
|
|
9
|
-
}
|
|
10
|
-
declare function memoryStore(): KeyValueStore;
|
|
11
|
-
/** Creates a store of the requested kind, degrading to memory when unavailable. */
|
|
12
|
-
declare function createStorage(kind?: StorageKind): KeyValueStore;
|
|
13
|
-
declare function readJson<T>(store: KeyValueStore, key: string, fallback: T): T;
|
|
14
|
-
declare function writeJson(store: KeyValueStore, key: string, value: unknown): void;
|
|
15
|
-
/** Storage keys used by the SDK (documented for integrators). */
|
|
16
|
-
declare const KEYS: {
|
|
17
|
-
readonly anon: "guidely:anon";
|
|
18
|
-
readonly seen: "guidely:seen";
|
|
19
|
-
readonly config: (key: string) => string;
|
|
20
|
-
readonly etag: (key: string) => string;
|
|
21
|
-
readonly state: (key: string) => string;
|
|
22
|
-
readonly user: (key: string) => string;
|
|
23
|
-
readonly queue: (key: string) => string;
|
|
24
|
-
readonly session: "guidely:session";
|
|
25
|
-
readonly active: "guidely:active";
|
|
26
|
-
readonly authoring: "guidely:authoring";
|
|
27
|
-
/** Messenger conversations this browser can read: [{ id, token }]. */
|
|
28
|
-
readonly conversations: (key: string) => string;
|
|
29
|
-
};
|
|
2
|
+
import { b as KeyValueStore, S as StorageKind } from './http-47tiAlOM.js';
|
|
30
3
|
|
|
31
4
|
interface EventQueueOptions {
|
|
32
5
|
store: KeyValueStore;
|
|
@@ -85,6 +58,8 @@ interface GuideLocalState {
|
|
|
85
58
|
completedAt?: string | null;
|
|
86
59
|
dismissedAt?: string | null;
|
|
87
60
|
lastShownAt?: string | null;
|
|
61
|
+
/** Last step reached in a multi-step flow (local only), so the messenger can offer to resume it. */
|
|
62
|
+
lastStepId?: string | null;
|
|
88
63
|
}
|
|
89
64
|
/**
|
|
90
65
|
* Pure frequency evaluation. `shownThisSession` comes from session storage.
|
|
@@ -168,7 +143,7 @@ interface GuidelyOptions {
|
|
|
168
143
|
user?: GuidelyUser;
|
|
169
144
|
/** Custom fetch (tests / polyfills). */
|
|
170
145
|
fetch?: typeof fetch;
|
|
171
|
-
/** Disable the authoring-mode URL parameter detection. */
|
|
146
|
+
/** Disable the authoring-mode and how-to capture URL parameter detection. */
|
|
172
147
|
disableAuthoring?: boolean;
|
|
173
148
|
}
|
|
174
149
|
interface ChecklistProgressState {
|
|
@@ -241,7 +216,11 @@ interface GuidelyEvents {
|
|
|
241
216
|
authoring: (info: {
|
|
242
217
|
active: boolean;
|
|
243
218
|
}) => void;
|
|
219
|
+
/** How-to capture (recording steps for a how-to guide) started or ended in this tab. */
|
|
220
|
+
capture: (info: {
|
|
221
|
+
active: boolean;
|
|
222
|
+
}) => void;
|
|
244
223
|
}
|
|
245
224
|
type GuidelyEventName = keyof GuidelyEvents;
|
|
246
225
|
|
|
247
|
-
export { type ChecklistProgressState as C, EventQueue as E, type GuideLocalState as G,
|
|
226
|
+
export { type ChecklistProgressState as C, EventQueue as E, type GuideLocalState as G, type PersistedState as P, type SessionState as S, type ConsentState as a, type EventQueueOptions as b, type GuidelyAccount as c, type GuidelyEnvironment as d, type GuidelyUser as e, areMutuallyExclusive as f, isFrequencyEligible as g, isWithinSchedule as h, isExclusiveGuide as i, type GuidelyOptions as j, type GuidelyEvents as k, type GuideStepInfo as l, type GuidelyEventName as m };
|
|
@@ -1,32 +1,5 @@
|
|
|
1
1
|
import { SdkEvent, PublishedGuide, FrequencyConfig, AutocaptureSettings, NetworkRequest, ProjectTheme, SdkConfigResponse, ChecklistTask, MessengerMessage, MessengerConversation } from '@guidelytech/shared';
|
|
2
|
-
|
|
3
|
-
type StorageKind = "local" | "session" | "memory";
|
|
4
|
-
/** Minimal synchronous key/value store; never throws (private mode, quota, SSR). */
|
|
5
|
-
interface KeyValueStore {
|
|
6
|
-
get(key: string): string | null;
|
|
7
|
-
set(key: string, value: string): void;
|
|
8
|
-
remove(key: string): void;
|
|
9
|
-
}
|
|
10
|
-
declare function memoryStore(): KeyValueStore;
|
|
11
|
-
/** Creates a store of the requested kind, degrading to memory when unavailable. */
|
|
12
|
-
declare function createStorage(kind?: StorageKind): KeyValueStore;
|
|
13
|
-
declare function readJson<T>(store: KeyValueStore, key: string, fallback: T): T;
|
|
14
|
-
declare function writeJson(store: KeyValueStore, key: string, value: unknown): void;
|
|
15
|
-
/** Storage keys used by the SDK (documented for integrators). */
|
|
16
|
-
declare const KEYS: {
|
|
17
|
-
readonly anon: "guidely:anon";
|
|
18
|
-
readonly seen: "guidely:seen";
|
|
19
|
-
readonly config: (key: string) => string;
|
|
20
|
-
readonly etag: (key: string) => string;
|
|
21
|
-
readonly state: (key: string) => string;
|
|
22
|
-
readonly user: (key: string) => string;
|
|
23
|
-
readonly queue: (key: string) => string;
|
|
24
|
-
readonly session: "guidely:session";
|
|
25
|
-
readonly active: "guidely:active";
|
|
26
|
-
readonly authoring: "guidely:authoring";
|
|
27
|
-
/** Messenger conversations this browser can read: [{ id, token }]. */
|
|
28
|
-
readonly conversations: (key: string) => string;
|
|
29
|
-
};
|
|
2
|
+
import { b as KeyValueStore, S as StorageKind } from './http-47tiAlOM.cjs';
|
|
30
3
|
|
|
31
4
|
interface EventQueueOptions {
|
|
32
5
|
store: KeyValueStore;
|
|
@@ -85,6 +58,8 @@ interface GuideLocalState {
|
|
|
85
58
|
completedAt?: string | null;
|
|
86
59
|
dismissedAt?: string | null;
|
|
87
60
|
lastShownAt?: string | null;
|
|
61
|
+
/** Last step reached in a multi-step flow (local only), so the messenger can offer to resume it. */
|
|
62
|
+
lastStepId?: string | null;
|
|
88
63
|
}
|
|
89
64
|
/**
|
|
90
65
|
* Pure frequency evaluation. `shownThisSession` comes from session storage.
|
|
@@ -168,7 +143,7 @@ interface GuidelyOptions {
|
|
|
168
143
|
user?: GuidelyUser;
|
|
169
144
|
/** Custom fetch (tests / polyfills). */
|
|
170
145
|
fetch?: typeof fetch;
|
|
171
|
-
/** Disable the authoring-mode URL parameter detection. */
|
|
146
|
+
/** Disable the authoring-mode and how-to capture URL parameter detection. */
|
|
172
147
|
disableAuthoring?: boolean;
|
|
173
148
|
}
|
|
174
149
|
interface ChecklistProgressState {
|
|
@@ -241,7 +216,11 @@ interface GuidelyEvents {
|
|
|
241
216
|
authoring: (info: {
|
|
242
217
|
active: boolean;
|
|
243
218
|
}) => void;
|
|
219
|
+
/** How-to capture (recording steps for a how-to guide) started or ended in this tab. */
|
|
220
|
+
capture: (info: {
|
|
221
|
+
active: boolean;
|
|
222
|
+
}) => void;
|
|
244
223
|
}
|
|
245
224
|
type GuidelyEventName = keyof GuidelyEvents;
|
|
246
225
|
|
|
247
|
-
export { type ChecklistProgressState as C, EventQueue as E, type GuideLocalState as G,
|
|
226
|
+
export { type ChecklistProgressState as C, EventQueue as E, type GuideLocalState as G, type PersistedState as P, type SessionState as S, type ConsentState as a, type EventQueueOptions as b, type GuidelyAccount as c, type GuidelyEnvironment as d, type GuidelyUser as e, areMutuallyExclusive as f, isFrequencyEligible as g, isWithinSchedule as h, isExclusiveGuide as i, type GuidelyOptions as j, type GuidelyEvents as k, type GuideStepInfo as l, type GuidelyEventName as m };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@guidelytech/sdk-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Framework-independent Guidely browser SDK",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -23,6 +23,11 @@
|
|
|
23
23
|
"types": "./dist/recorder.d.ts",
|
|
24
24
|
"import": "./dist/recorder.js",
|
|
25
25
|
"require": "./dist/recorder.cjs"
|
|
26
|
+
},
|
|
27
|
+
"./capture": {
|
|
28
|
+
"types": "./dist/capture.d.ts",
|
|
29
|
+
"import": "./dist/capture.js",
|
|
30
|
+
"require": "./dist/capture.cjs"
|
|
26
31
|
}
|
|
27
32
|
},
|
|
28
33
|
"files": [
|
|
@@ -33,8 +38,8 @@
|
|
|
33
38
|
"@rrweb/record": "^2.1.7",
|
|
34
39
|
"@rrweb/rrweb-plugin-console-record": "^2.1.7",
|
|
35
40
|
"@rrweb/types": "^2.1.7",
|
|
36
|
-
"@guidelytech/
|
|
37
|
-
"@guidelytech/
|
|
41
|
+
"@guidelytech/guide-runtime": "0.6.1",
|
|
42
|
+
"@guidelytech/shared": "0.6.1"
|
|
38
43
|
},
|
|
39
44
|
"devDependencies": {
|
|
40
45
|
"@eslint/js": "^9.39.5",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/http.ts"],"sourcesContent":["export interface ApiResponse<T> {\n status: number;\n ok: boolean;\n data: T | null;\n etag: string | null;\n}\n\nexport type FetchLike = typeof fetch;\n\n/** Thin HTTP client for the public SDK API. Adds the public key as query + header. */\nexport class Api {\n constructor(\n private readonly baseUrl: string,\n private readonly key: string,\n private readonly fetchImpl?: FetchLike,\n ) {}\n\n url(path: string, params: Record<string, string | undefined> = {}): string {\n // Built by hand rather than with URL/searchParams, which React Native does not implement.\n const query = Object.entries({ key: this.key, ...params })\n .filter((e): e is [string, string] => e[1] != null)\n .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)\n .join(\"&\");\n return `${this.baseUrl.replace(/\\/$/, \"\")}/api/v1/sdk${path}?${query}`;\n }\n\n private get fetcher(): FetchLike | null {\n if (this.fetchImpl) return this.fetchImpl;\n return typeof fetch === \"function\" ? fetch.bind(globalThis) : null;\n }\n\n async request<T>(method: \"GET\" | \"POST\", path: string, options: { body?: unknown; etag?: string | null; params?: Record<string, string | undefined>; keepalive?: boolean; timeoutMs?: number; headers?: Record<string, string> } = {}): Promise<ApiResponse<T>> {\n const f = this.fetcher;\n if (!f) throw new Error(\"fetch is not available\");\n const headers: Record<string, string> = { ...options.headers, \"X-Guidely-Key\": this.key, Accept: \"application/json\" };\n if (options.body !== undefined) headers[\"Content-Type\"] = \"application/json\";\n if (options.etag) headers[\"If-None-Match\"] = options.etag;\n const ac = typeof AbortController !== \"undefined\" ? new AbortController() : null;\n const timer = ac ? setTimeout(() => ac.abort(), options.timeoutMs ?? 10000) : null;\n try {\n const res = await f(this.url(path, options.params), {\n method,\n headers,\n body: options.body === undefined ? undefined : JSON.stringify(options.body),\n keepalive: options.keepalive,\n signal: ac?.signal,\n credentials: \"omit\",\n mode: \"cors\",\n });\n let data: T | null = null;\n if (res.status !== 304 && res.status !== 204) {\n try {\n data = (await res.json()) as T;\n } catch {\n data = null;\n }\n }\n return { status: res.status, ok: res.ok, data, etag: res.headers?.get?.(\"etag\") ?? null };\n } finally {\n if (timer) clearTimeout(timer);\n }\n }\n\n get<T>(path: string, options?: { etag?: string | null; params?: Record<string, string | undefined>; headers?: Record<string, string> }): Promise<ApiResponse<T>> {\n return this.request<T>(\"GET\", path, options);\n }\n\n post<T>(path: string, body: unknown, options?: { params?: Record<string, string | undefined>; keepalive?: boolean; headers?: Record<string, string> }): Promise<ApiResponse<T>> {\n return this.request<T>(\"POST\", path, { body, ...options });\n }\n\n /**\n * POST that may answer with server-sent events. Each event is passed to `onEvent`; a JSON answer is\n * returned instead. No timeout: Scout replies can take a while and the server keeps the stream alive.\n */\n async stream<T>(path: string, body: unknown, onEvent: (event: string, data: unknown) => void, headers: Record<string, string> = {}): Promise<ApiResponse<T>> {\n const f = this.fetcher;\n if (!f) throw new Error(\"fetch is not available\");\n const res = await f(this.url(path), {\n method: \"POST\",\n headers: { ...headers, \"X-Guidely-Key\": this.key, Accept: \"text/event-stream, application/json\", \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n credentials: \"omit\",\n mode: \"cors\",\n });\n const type = res.headers?.get?.(\"content-type\") ?? \"\";\n if (!type.includes(\"text/event-stream\") || !res.body) {\n let data: T | null = null;\n try {\n data = (await res.json()) as T;\n } catch {\n data = null;\n }\n return { status: res.status, ok: res.ok, data, etag: null };\n }\n await readEventStream(res.body, onEvent);\n return { status: res.status, ok: res.ok, data: null, etag: null };\n }\n\n /** `navigator.sendBeacon` when available (page unload); returns false when it could not be queued. */\n beacon(path: string, body: unknown): boolean {\n try {\n if (typeof navigator === \"undefined\" || typeof navigator.sendBeacon !== \"function\") return false;\n // text/plain is a CORS-safelisted type: cross-origin beacons need no preflight (which\n // browsers drop during unload). The API parses the body regardless of content type.\n const blob = new Blob([JSON.stringify(body)], { type: \"text/plain;charset=UTF-8\" });\n return navigator.sendBeacon(this.url(path), blob);\n } catch {\n return false;\n }\n }\n}\n\nexport const sleep = (ms: number) => new Promise<void>((r) => setTimeout(r, ms));\n\n/** Exponential backoff helper: 1s → 2s → 4s … capped. */\nexport function backoffMs(attempt: number, baseMs = 1000, maxMs = 30000): number {\n return Math.min(maxMs, baseMs * 2 ** Math.max(0, attempt));\n}\n\n/** Fire-and-forget POST with bounded retries (used for state sync; never blocks UI). */\nexport async function postWithRetry<T>(api: Api, path: string, body: unknown, attempts = 3, baseMs = 500): Promise<ApiResponse<T> | null> {\n for (let i = 0; i < attempts; i++) {\n try {\n const res = await api.post<T>(path, body);\n if (res.ok || (res.status >= 400 && res.status < 500 && res.status !== 429)) return res;\n } catch {\n /* network error → retry */\n }\n if (i < attempts - 1) await sleep(backoffMs(i, baseMs, 8000));\n }\n return null;\n}\n\n/** Parses a `text/event-stream` body (`event:` + single-line JSON `data:`; comment lines ignored). */\nexport async function readEventStream(body: ReadableStream<Uint8Array>, onEvent: (event: string, data: unknown) => void): Promise<void> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n const dispatch = (raw: string) => {\n let event = \"message\";\n const data: string[] = [];\n for (const line of raw.split(\"\\n\")) {\n if (!line || line.startsWith(\":\")) continue;\n const i = line.indexOf(\":\");\n const field = i < 0 ? line : line.slice(0, i);\n const value = i < 0 ? \"\" : line.slice(i + 1).replace(/^ /, \"\");\n if (field === \"event\") event = value;\n else if (field === \"data\") data.push(value);\n }\n if (!data.length) return;\n try {\n onEvent(event, JSON.parse(data.join(\"\\n\")));\n } catch {\n /* ignore malformed events */\n }\n };\n for (;;) {\n const { done, value } = await reader.read();\n if (value) buffer += decoder.decode(value, { stream: true }).replace(/\\r\\n?/g, \"\\n\");\n let sep: number;\n while ((sep = buffer.indexOf(\"\\n\\n\")) >= 0) {\n dispatch(buffer.slice(0, sep));\n buffer = buffer.slice(sep + 2);\n }\n if (done) break;\n }\n if (buffer.trim()) dispatch(buffer);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/storage.ts","../src/queue.ts","../src/emitter.ts","../src/frequency.ts"],"sourcesContent":["export type StorageKind = \"local\" | \"session\" | \"memory\";\n\n/** Minimal synchronous key/value store; never throws (private mode, quota, SSR). */\nexport interface KeyValueStore {\n get(key: string): string | null;\n set(key: string, value: string): void;\n remove(key: string): void;\n}\n\nexport function memoryStore(): KeyValueStore {\n const map = new Map<string, string>();\n return { get: (k) => map.get(k) ?? null, set: (k, v) => void map.set(k, v), remove: (k) => void map.delete(k) };\n}\n\nfunction webStore(area: \"localStorage\" | \"sessionStorage\"): KeyValueStore | null {\n try {\n if (typeof window === \"undefined\") return null;\n const s = window[area];\n if (!s) return null;\n const probe = \"__guidely_probe__\";\n s.setItem(probe, \"1\");\n s.removeItem(probe);\n return {\n get: (k) => {\n try {\n return s.getItem(k);\n } catch {\n return null;\n }\n },\n set: (k, v) => {\n try {\n s.setItem(k, v);\n } catch {\n /* quota / private mode */\n }\n },\n remove: (k) => {\n try {\n s.removeItem(k);\n } catch {\n /* ignore */\n }\n },\n };\n } catch {\n return null;\n }\n}\n\n/** Creates a store of the requested kind, degrading to memory when unavailable. */\nexport function createStorage(kind: StorageKind = \"local\"): KeyValueStore {\n if (kind === \"memory\") return memoryStore();\n return webStore(kind === \"local\" ? \"localStorage\" : \"sessionStorage\") ?? memoryStore();\n}\n\nexport function readJson<T>(store: KeyValueStore, key: string, fallback: T): T {\n const raw = store.get(key);\n if (raw == null) return fallback;\n try {\n return JSON.parse(raw) as T;\n } catch {\n return fallback;\n }\n}\n\nexport function writeJson(store: KeyValueStore, key: string, value: unknown): void {\n try {\n store.set(key, JSON.stringify(value));\n } catch {\n /* ignore */\n }\n}\n\n/** Storage keys used by the SDK (documented for integrators). */\nexport const KEYS = {\n anon: \"guidely:anon\",\n seen: \"guidely:seen\",\n config: (key: string) => `guidely:config:${key}`,\n etag: (key: string) => `guidely:etag:${key}`,\n state: (key: string) => `guidely:state:${key}`,\n user: (key: string) => `guidely:user:${key}`,\n queue: (key: string) => `guidely:queue:${key}`,\n session: \"guidely:session\",\n active: \"guidely:active\",\n authoring: \"guidely:authoring\",\n /** Messenger conversations this browser can read: [{ id, token }]. */\n conversations: (key: string) => `guidely:conversations:${key}`,\n} as const;\n","import type { SdkEvent } from \"@guidelytech/shared\";\nimport { backoffMs } from \"./http\";\nimport { readJson, writeJson, type KeyValueStore } from \"./storage\";\n\nexport interface EventQueueOptions {\n store: KeyValueStore;\n storageKey: string;\n /** Sends one batch. Resolve `{ ok: true }` on success, `{ ok: false, retry }` otherwise. */\n send: (events: SdkEvent[]) => Promise<{ ok: boolean; retry?: boolean }>;\n /** Synchronous best-effort send used on page hide. */\n beacon?: (events: SdkEvent[]) => boolean;\n /** Should events be flushed right now? (analytics enabled + consent granted). */\n enabled: () => boolean;\n flushIntervalMs?: number;\n maxQueue?: number;\n batchSize?: number;\n maxAttempts?: number;\n onError?: (err: unknown) => void;\n}\n\n/**\n * Bounded, persisted event queue. Batches ≤50 events every 2s (or on demand), dedupes by\n * `eventId`, retries with exponential backoff (1s→30s, 5 attempts) and keeps failed events for\n * the next flush. Drops the oldest events once the queue exceeds 200.\n */\nexport class EventQueue {\n private events: SdkEvent[] = [];\n private timer: ReturnType<typeof setTimeout> | null = null;\n private retryTimer: ReturnType<typeof setTimeout> | null = null;\n private inflight = false;\n private inflightIds = new Set<string>();\n private attempt = 0;\n private destroyed = false;\n private readonly flushIntervalMs: number;\n private readonly maxQueue: number;\n private readonly batchSize: number;\n private readonly maxAttempts: number;\n\n constructor(private readonly opts: EventQueueOptions) {\n this.flushIntervalMs = opts.flushIntervalMs ?? 2000;\n this.maxQueue = opts.maxQueue ?? 200;\n this.batchSize = opts.batchSize ?? 50;\n this.maxAttempts = opts.maxAttempts ?? 5;\n const persisted = readJson<SdkEvent[]>(opts.store, opts.storageKey, []);\n if (Array.isArray(persisted)) this.events = persisted.filter((e) => e && typeof e.eventId === \"string\").slice(-this.maxQueue);\n // Events left over from a previous page load are flushed on the next tick.\n if (this.events.length) this.schedule();\n }\n\n get size(): number {\n return this.events.length;\n }\n\n /** Pending events (read-only copy, for debugging). */\n peek(): SdkEvent[] {\n return this.events.slice();\n }\n\n enqueue(event: SdkEvent): void {\n if (this.destroyed) return;\n if (this.events.some((e) => e.eventId === event.eventId)) return;\n this.events.push(event);\n if (this.events.length > this.maxQueue) this.events.splice(0, this.events.length - this.maxQueue);\n this.persist();\n this.schedule();\n }\n\n private persist(): void {\n writeJson(this.opts.store, this.opts.storageKey, this.events);\n }\n\n private schedule(): void {\n if (this.timer || this.retryTimer || this.destroyed) return;\n this.timer = setTimeout(() => {\n this.timer = null;\n void this.flush();\n }, this.flushIntervalMs);\n }\n\n /** Sends queued events now (one batch at a time). Safe to call at any time. */\n async flush(): Promise<void> {\n if (this.destroyed || this.inflight || !this.events.length || !this.opts.enabled()) return;\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n this.inflight = true;\n const batch = this.events.slice(0, this.batchSize);\n this.inflightIds = new Set(batch.map((e) => e.eventId));\n let ok = false;\n let retry = true;\n try {\n const res = await this.opts.send(batch);\n ok = res.ok;\n retry = res.retry ?? !res.ok;\n } catch (err) {\n this.opts.onError?.(err);\n }\n this.inflight = false;\n this.inflightIds.clear();\n if (this.destroyed) return;\n if (ok || !retry) {\n const ids = new Set(batch.map((e) => e.eventId));\n this.events = this.events.filter((e) => !ids.has(e.eventId));\n this.attempt = 0;\n this.persist();\n if (this.events.length) this.schedule();\n return;\n }\n this.attempt += 1;\n if (this.attempt >= this.maxAttempts) {\n // Give up for now; events stay queued until the next flush trigger (navigation, timer, unload).\n this.attempt = 0;\n return;\n }\n this.retryTimer = setTimeout(() => {\n this.retryTimer = null;\n void this.flush();\n }, backoffMs(this.attempt - 1));\n }\n\n /** Best-effort synchronous flush on unload using `sendBeacon`. */\n flushSync(): void {\n if (this.destroyed || !this.opts.enabled() || !this.opts.beacon) return;\n // Events already in flight (fetch with keepalive) survive unload; only beacon the rest to avoid duplicate batches.\n const batch = this.events.filter((e) => !this.inflightIds.has(e.eventId)).slice(0, this.batchSize);\n if (!batch.length) return;\n if (this.opts.beacon(batch)) {\n const ids = new Set(batch.map((e) => e.eventId));\n this.events = this.events.filter((e) => !ids.has(e.eventId));\n this.persist();\n }\n }\n\n clear(): void {\n this.events = [];\n this.persist();\n }\n\n destroy(): void {\n this.destroyed = true;\n if (this.timer) clearTimeout(this.timer);\n if (this.retryTimer) clearTimeout(this.retryTimer);\n this.timer = null;\n this.retryTimer = null;\n this.persist();\n }\n}\n","type AnyFn = (...args: any[]) => void;\n\n/** Tiny typed event emitter. Listener errors are reported, never thrown into the caller. */\nexport class Emitter<Events extends { [K in keyof Events]: AnyFn }> {\n private listeners = new Map<keyof Events, Set<AnyFn>>();\n constructor(private readonly onError?: (err: unknown) => void) {}\n\n on<K extends keyof Events>(event: K, cb: Events[K]): () => void {\n let set = this.listeners.get(event);\n if (!set) {\n set = new Set();\n this.listeners.set(event, set);\n }\n set.add(cb);\n return () => this.off(event, cb);\n }\n\n off<K extends keyof Events>(event: K, cb: Events[K]): void {\n this.listeners.get(event)?.delete(cb);\n }\n\n emit<K extends keyof Events>(event: K, ...args: Parameters<Events[K]>): void {\n const set = this.listeners.get(event);\n if (!set) return;\n for (const cb of Array.from(set)) {\n try {\n cb(...args);\n } catch (err) {\n this.onError?.(err);\n }\n }\n }\n\n clear(): void {\n this.listeners.clear();\n }\n}\n","import type { FrequencyConfig, PublishedGuide } from \"@guidelytech/shared\";\n\n/** Per-guide state kept locally and merged with the server's `IdentifyResponse.guideState`. */\nexport interface GuideLocalState {\n impressions: number;\n completedAt?: string | null;\n dismissedAt?: string | null;\n lastShownAt?: string | null;\n}\n\nconst HOUR = 3600 * 1000;\n\n/**\n * Pure frequency evaluation. `shownThisSession` comes from session storage.\n * - `once`: a single impression ever (unless `reshowAfterDismiss` and it was only dismissed).\n * - `once_per_session`: at most once per browser session.\n * - `every_time`: on every trigger (still blocked by completion/dismissal rules below).\n * - `interval`: at most once every `intervalHours`.\n * `maxImpressions` caps total impressions; dismissal blocks re-showing unless `reshowAfterDismiss`\n * (then `cooldownHours` must have elapsed); completion blocks unless mode is `every_time`.\n */\nexport function isFrequencyEligible(freq: FrequencyConfig, state: GuideLocalState | undefined, shownThisSession: boolean, now: number = Date.now()): boolean {\n const s = state ?? { impressions: 0 };\n if (freq.maxImpressions != null && s.impressions >= freq.maxImpressions) return false;\n if (s.completedAt && freq.mode !== \"every_time\") return false;\n if (s.dismissedAt) {\n if (!freq.reshowAfterDismiss) return false;\n if (freq.cooldownHours > 0 && now - Date.parse(s.dismissedAt) < freq.cooldownHours * HOUR) return false;\n }\n switch (freq.mode) {\n case \"once\":\n return s.impressions === 0 || (!!s.dismissedAt && freq.reshowAfterDismiss);\n case \"once_per_session\":\n return !shownThisSession;\n case \"interval\": {\n if (!s.lastShownAt) return true;\n const hours = freq.intervalHours ?? 24;\n return now - Date.parse(s.lastShownAt) >= hours * HOUR;\n }\n case \"every_time\":\n default:\n return true;\n }\n}\n\n/**\n * Exclusive guides (flows, modals, tooltips, announcements, surveys) run one at a time.\n * Banners, checklists and hotspot-only tooltips can coexist with an active flow.\n */\nexport function isExclusiveGuide(guide: PublishedGuide): boolean {\n if (guide.kind === \"banner\" || guide.kind === \"checklist\") return false;\n const steps = guide.config.steps;\n if (steps.length > 0 && steps.every((s) => s.type === \"hotspot\" || s.type === \"banner\")) return false;\n return true;\n}\n\n/** Is `a` mutually exclusive with `b` (by id or key, in either direction)? */\nexport function areMutuallyExclusive(a: PublishedGuide, b: PublishedGuide): boolean {\n const la = a.config.frequency.mutuallyExclusiveWith;\n const lb = b.config.frequency.mutuallyExclusiveWith;\n return la.includes(b.id) || la.includes(b.key) || lb.includes(a.id) || lb.includes(a.key);\n}\n\n/** Survey scheduling window. */\nexport function isWithinSchedule(guide: PublishedGuide, now: number = Date.now()): boolean {\n const s = guide.config.survey;\n if (!s) return true;\n if (s.startAt && now < Date.parse(s.startAt)) return false;\n if (s.endAt && now > Date.parse(s.endAt)) return false;\n return true;\n}\n"],"mappings":"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","names":[]}
|