@guidelytech/shared 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils.ts","../src/platform.ts","../src/widgets.ts","../src/targeting-eval.ts","../src/events-util.ts","../src/errors.ts","../src/complaints-limits.ts","../src/network.ts","../src/media.ts","../src/resource-center.ts","../src/howto-limits.ts","../src/page.ts"],"sourcesContent":["/** FNV-1a 32-bit hash; deterministic across SDK and server for rollout bucketing. */\nexport function fnv1a(input: string): number {\n let h = 0x811c9dc5;\n for (let i = 0; i < input.length; i++) {\n h ^= input.charCodeAt(i);\n h = Math.imul(h, 0x01000193) >>> 0;\n }\n return h >>> 0;\n}\n\n/** Deterministic 0-99 bucket for percentage rollouts. */\nexport function rolloutBucket(subject: string, salt: string): number {\n return fnv1a(`${salt}:${subject}`) % 100;\n}\n\nexport function slugify(input: string): string {\n return input\n .toLowerCase()\n .trim()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, 64) || \"untitled\";\n}\n\nconst ALPHABET = \"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\";\n/** URL-safe random id. Uses crypto when available, falls back to Math.random (ids are not secrets). */\nexport function randomId(size = 16): string {\n const g: any = typeof globalThis !== \"undefined\" ? globalThis : {};\n let bytes: Uint8Array;\n if (g.crypto && typeof g.crypto.getRandomValues === \"function\") {\n bytes = new Uint8Array(size);\n g.crypto.getRandomValues(bytes);\n } else {\n bytes = new Uint8Array(size);\n for (let i = 0; i < size; i++) bytes[i] = Math.floor(Math.random() * 256);\n }\n let out = \"\";\n for (let i = 0; i < size; i++) out += ALPHABET[(bytes[i] as number) % ALPHABET.length];\n return out;\n}\n\nexport function isPlainObject(v: unknown): v is Record<string, unknown> {\n return typeof v === \"object\" && v !== null && !Array.isArray(v);\n}\n\n/** Safe nested getter: get(obj, \"account.plan\") */\nexport function getPath(obj: unknown, path: string): unknown {\n if (!path) return obj;\n let cur: any = obj;\n for (const part of path.split(\".\")) {\n if (cur == null) return undefined;\n cur = cur[part];\n }\n return cur;\n}\n\nexport function clamp(n: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, n));\n}\n\n/** Glob-style URL pattern match: `*` matches any chars except `/`, `**` matches anything. */\nexport function matchUrlPattern(pattern: string, url: string): boolean {\n if (!pattern) return true;\n if (pattern === url) return true;\n const escaped = pattern\n .replace(/[.+?^${}()|[\\]\\\\]/g, \"\\\\$&\")\n .replace(/\\*\\*/g, \"__GLOBSTAR__\")\n .replace(/\\*/g, \"[^/]*\")\n .replace(/__GLOBSTAR__/g, \".*\");\n const re = new RegExp(`^${escaped}$`);\n return re.test(url) || re.test(stripOrigin(url));\n}\n\nexport function stripOrigin(url: string): string {\n try {\n const u = new URL(url);\n return u.pathname + u.search + u.hash;\n } catch {\n return url;\n }\n}\n\n/** Safe regexp construction with a length cap to limit ReDoS exposure. */\nexport function safeRegExp(source: string, flags = \"i\"): RegExp | null {\n if (!source || source.length > 200) return null;\n try {\n return new RegExp(source, flags);\n } catch {\n return null;\n }\n}\n","/**\n * Cross-platform helpers shared by the web SDK, the React Native SDK and (as a reference for) the\n * native iOS/Android SDKs. Zod-free so it can ship in runtime bundles.\n */\n\n/** Platforms a guide can be delivered to. */\nexport const PLATFORMS = [\"web\", \"ios\", \"android\"] as const;\nexport type Platform = (typeof PLATFORMS)[number];\n\n/** Guides published before platforms existed have no `platforms` field and are web-only. */\nexport const DEFAULT_PLATFORMS: Platform[] = [\"web\"];\n\n/** Platforms a guide config targets (missing field → web only). */\nexport function guidePlatforms(config: { platforms?: readonly string[] | null }): string[] {\n return Array.isArray(config.platforms) ? [...config.platforms] : [...DEFAULT_PLATFORMS];\n}\n\nexport function supportsPlatform(config: { platforms?: readonly string[] | null }, platform: string): boolean {\n return guidePlatforms(config).includes(platform);\n}\n\n/** Attribute web apps put on elements to share a target id with their mobile apps. */\nexport const GUIDELY_ID_ATTRIBUTE = \"data-guidely-id\";\n\n/**\n * Extracts a Guidely target id from a trigger / advance / checklist value so the same guide works on\n * web and mobile. Accepts a bare id (`new-deal`), `#new-deal`, or `[data-guidely-id=\"new-deal\"]`\n * (also `data-testid` / `data-tour`). Returns null for selectors that cannot name a single id.\n */\nexport function guidelyIdFromSelector(value: string | null | undefined): string | null {\n const v = (value ?? \"\").trim();\n if (!v) return null;\n if (/^[A-Za-z0-9_][A-Za-z0-9_\\-:.]*$/.test(v)) return v;\n const hash = v.match(/^#([A-Za-z0-9_][A-Za-z0-9_\\-:.]*)$/);\n if (hash) return hash[1] as string;\n const attr = v.match(/^(?:[a-z][a-z0-9]*)?\\[\\s*data-(?:guidely-id|testid|tour)\\s*=\\s*([\"']?)([^\"'\\]]+)\\1\\s*\\]$/i);\n if (attr) return (attr[2] as string).trim();\n return null;\n}\n\n/**\n * Mobile SDKs have screens instead of URLs. URL-based features (URL patterns, `url_match` triggers,\n * multi-page steps, `url_visited` checklist tasks) match against the screen path `/<ScreenName>`.\n */\nexport function screenPath(screen: string | null | undefined): string {\n const s = (screen ?? \"\").trim();\n if (!s) return \"/\";\n return s.startsWith(\"/\") ? s : `/${s}`;\n}\n\n/**\n * Compares dotted version strings numerically (`2.10.0` > `2.9.3`). Non-numeric parts compare as\n * strings; a missing part counts as 0 (`2.1` == `2.1.0`). Returns -1, 0 or 1, or null when either\n * side is empty.\n */\nexport function compareVersions(a: unknown, b: unknown): -1 | 0 | 1 | null {\n const sa = a == null ? \"\" : String(a).trim().replace(/^v/i, \"\");\n const sb = b == null ? \"\" : String(b).trim().replace(/^v/i, \"\");\n if (!sa || !sb) return null;\n const pa = sa.split(/[.+-]/);\n const pb = sb.split(/[.+-]/);\n const n = Math.max(pa.length, pb.length);\n for (let i = 0; i < n; i++) {\n const x = pa[i] ?? \"0\";\n const y = pb[i] ?? \"0\";\n const nx = /^\\d+$/.test(x) ? Number(x) : NaN;\n const ny = /^\\d+$/.test(y) ? Number(y) : NaN;\n if (!Number.isNaN(nx) && !Number.isNaN(ny)) {\n if (nx !== ny) return nx < ny ? -1 : 1;\n } else if (x !== y) {\n return x < y ? -1 : 1;\n }\n }\n return 0;\n}\n","/**\n * Pure checklist / survey logic shared by every renderer (web runtime, React Native, and the\n * reference for the native SDKs). Zod-free; uses structural types so it stays in the runtime entry.\n */\n\nexport type SurveyAnswer = string | number | boolean | string[];\nexport type SurveyAnswers = Record<string, SurveyAnswer>;\n\nexport interface ShowWhen {\n questionId: string;\n op: \"equals\" | \"lte\" | \"gte\" | \"in\";\n value: string | number | string[];\n}\n\n/** Evaluates a question's `showWhen` condition against current answers. */\nexport function isQuestionVisible(q: { showWhen?: ShowWhen }, answers: SurveyAnswers): boolean {\n if (!q.showWhen) return true;\n const actual = answers[q.showWhen.questionId];\n if (actual === undefined) return false;\n const expected = q.showWhen.value;\n switch (q.showWhen.op) {\n case \"equals\":\n return Array.isArray(actual) ? actual.includes(String(expected)) : String(actual) === String(expected);\n case \"lte\":\n return Number(actual) <= Number(expected);\n case \"gte\":\n return Number(actual) >= Number(expected);\n case \"in\": {\n const list = Array.isArray(expected) ? expected : [String(expected)];\n return Array.isArray(actual) ? actual.some((a) => list.includes(a)) : list.includes(String(actual));\n }\n default:\n return false;\n }\n}\n\n/** Are all required tasks done? */\nexport function isChecklistComplete(config: { tasks: Array<{ id: string; required: boolean }> }, progress: { completedTaskIds: string[] }): boolean {\n const done = new Set(progress.completedTaskIds);\n return config.tasks.filter((t) => t.required).every((t) => done.has(t.id));\n}\n","/**\n * Zod-free targeting evaluation. Imported by the browser SDK via `@guidelytech/shared/runtime`\n * so the runtime bundle does not pull in zod. Schemas live in ./targeting.ts.\n */\nimport { compareVersions } from \"./platform\";\nimport { getPath, matchUrlPattern, rolloutBucket, safeRegExp } from \"./utils\";\n\nexport const RULE_OPERATORS = [\n \"equals\",\n \"not_equals\",\n \"contains\",\n \"not_contains\",\n \"starts_with\",\n \"ends_with\",\n \"exists\",\n \"not_exists\",\n \"gt\",\n \"gte\",\n \"lt\",\n \"lte\",\n \"in\",\n \"not_in\",\n \"regex\",\n \"is_true\",\n \"is_false\",\n \"before\",\n \"after\",\n] as const;\nexport type RuleOperator = (typeof RULE_OPERATORS)[number];\n\nexport const RULE_FIELDS = [\n \"user.attribute\",\n \"account.attribute\",\n \"user.role\",\n \"user.plan\",\n \"user.identified\",\n \"url.path\",\n \"url.full\",\n \"url.query\",\n \"device.type\",\n \"device.browser\",\n \"device.os\",\n \"visitor.firstVisit\",\n \"visitor.returning\",\n \"event.seen\",\n \"guide.completed\",\n \"guide.dismissed\",\n \"guide.seen\",\n \"checklist.completed\",\n \"audience.member\",\n \"datetime.now\",\n \"datetime.dayOfWeek\",\n \"datetime.hour\",\n \"rollout.percentage\",\n \"environment\",\n \"locale\",\n \"app.platform\",\n \"app.screen\",\n \"app.version\",\n] as const;\nexport type RuleField = (typeof RULE_FIELDS)[number];\n\nexport interface Rule {\n id: string;\n field: RuleField;\n /** For attribute/query/event fields: the attribute key, query param name, event name, guide id... */\n key?: string;\n op: RuleOperator;\n value?: string | number | boolean | string[];\n /** When true, the rule is negated (exclusion rule). */\n negate?: boolean;\n}\n\nexport type RuleGroup = {\n id: string;\n op: \"and\" | \"or\";\n rules: Array<Rule | RuleGroup>;\n negate?: boolean;\n};\n\nexport function isRuleGroup(r: Rule | RuleGroup): r is RuleGroup {\n return (r as RuleGroup).rules !== undefined;\n}\n\nexport function emptyRuleGroup(id = \"root\"): RuleGroup {\n return { id, op: \"and\", rules: [] };\n}\n\n/** Everything targeting evaluation can see. Built by the SDK (browser) or the server. */\nexport interface TargetingContext {\n userId?: string | null;\n anonymousId: string;\n userAttributes: Record<string, unknown>;\n accountAttributes: Record<string, unknown>;\n url: string;\n path: string;\n query: Record<string, string>;\n device: { type: \"desktop\" | \"tablet\" | \"mobile\"; browser?: string; os?: string };\n firstVisit: boolean;\n eventsSeen: Set<string> | string[];\n completedGuides: Set<string> | string[];\n dismissedGuides: Set<string> | string[];\n seenGuides: Set<string> | string[];\n completedChecklists: Set<string> | string[];\n audienceIds: Set<string> | string[];\n now: Date;\n environment: string;\n locale?: string;\n /** `web`, `ios` or `android`. Missing means web. */\n platform?: string;\n /** Current screen name (mobile SDKs; set via `screen()`). */\n screen?: string;\n /** Host app version (mobile SDKs), e.g. `2.4.1`. */\n appVersion?: string;\n /** Salt for rollout (guide id). Set per guide before evaluating. */\n rolloutSalt?: string;\n}\n\nfunction has(set: Set<string> | string[] | undefined, v: string): boolean {\n if (!set) return false;\n return set instanceof Set ? set.has(v) : set.includes(v);\n}\n\nfunction resolveField(rule: Rule, ctx: TargetingContext): unknown {\n switch (rule.field) {\n case \"user.attribute\":\n return rule.key ? getPath(ctx.userAttributes, rule.key) : undefined;\n case \"account.attribute\":\n return rule.key ? getPath(ctx.accountAttributes, rule.key) : undefined;\n case \"user.role\":\n return ctx.userAttributes.role;\n case \"user.plan\":\n return ctx.userAttributes.plan ?? ctx.accountAttributes.plan;\n case \"user.identified\":\n return !!ctx.userId;\n case \"url.path\":\n return ctx.path;\n case \"url.full\":\n return ctx.url;\n case \"url.query\":\n return rule.key ? ctx.query[rule.key] : undefined;\n case \"device.type\":\n return ctx.device.type;\n case \"device.browser\":\n return ctx.device.browser;\n case \"device.os\":\n return ctx.device.os;\n case \"visitor.firstVisit\":\n return ctx.firstVisit;\n case \"visitor.returning\":\n return !ctx.firstVisit;\n case \"event.seen\":\n return rule.key ? has(ctx.eventsSeen, rule.key) : false;\n case \"guide.completed\":\n return rule.key ? has(ctx.completedGuides, rule.key) : false;\n case \"guide.dismissed\":\n return rule.key ? has(ctx.dismissedGuides, rule.key) : false;\n case \"guide.seen\":\n return rule.key ? has(ctx.seenGuides, rule.key) : false;\n case \"checklist.completed\":\n return rule.key ? has(ctx.completedChecklists, rule.key) : false;\n case \"audience.member\":\n return rule.key ? has(ctx.audienceIds, rule.key) : false;\n case \"datetime.now\":\n return ctx.now.toISOString();\n case \"datetime.dayOfWeek\":\n return ctx.now.getDay();\n case \"datetime.hour\":\n return ctx.now.getHours();\n case \"rollout.percentage\":\n return rolloutBucket(ctx.userId || ctx.anonymousId, ctx.rolloutSalt || \"guidely\");\n case \"environment\":\n return ctx.environment;\n case \"locale\":\n return ctx.locale;\n case \"app.platform\":\n return ctx.platform ?? \"web\";\n case \"app.screen\":\n return ctx.screen;\n case \"app.version\":\n return ctx.appVersion;\n default:\n return undefined;\n }\n}\n\nfunction toNumber(v: unknown): number | null {\n if (typeof v === \"number\") return v;\n if (typeof v === \"string\" && v.trim() !== \"\" && !Number.isNaN(Number(v))) return Number(v);\n if (v instanceof Date) return v.getTime();\n return null;\n}\n\nfunction toDateMs(v: unknown): number | null {\n if (v instanceof Date) return v.getTime();\n if (typeof v === \"string\") {\n const t = Date.parse(v);\n return Number.isNaN(t) ? null : t;\n }\n if (typeof v === \"number\") return v;\n return null;\n}\n\nexport function compare(op: RuleOperator, actual: unknown, expected: Rule[\"value\"]): boolean {\n switch (op) {\n case \"exists\":\n return actual !== undefined && actual !== null && actual !== \"\";\n case \"not_exists\":\n return actual === undefined || actual === null || actual === \"\";\n case \"is_true\":\n return actual === true || actual === \"true\";\n case \"is_false\":\n return actual === false || actual === \"false\";\n case \"equals\":\n if (typeof actual === \"boolean\") return String(actual) === String(expected);\n return String(actual ?? \"\") === String(expected ?? \"\");\n case \"not_equals\":\n return String(actual ?? \"\") !== String(expected ?? \"\");\n case \"contains\":\n return typeof actual === \"string\" && actual.toLowerCase().includes(String(expected ?? \"\").toLowerCase());\n case \"not_contains\":\n return !(typeof actual === \"string\" && actual.toLowerCase().includes(String(expected ?? \"\").toLowerCase()));\n case \"starts_with\":\n return typeof actual === \"string\" && actual.startsWith(String(expected ?? \"\"));\n case \"ends_with\":\n return typeof actual === \"string\" && actual.endsWith(String(expected ?? \"\"));\n case \"gt\":\n case \"gte\":\n case \"lt\":\n case \"lte\": {\n const a = toNumber(actual);\n const b = toNumber(expected);\n if (a === null || b === null) return false;\n return op === \"gt\" ? a > b : op === \"gte\" ? a >= b : op === \"lt\" ? a < b : a <= b;\n }\n case \"in\": {\n const list = Array.isArray(expected) ? expected : String(expected ?? \"\").split(\",\").map((s) => s.trim());\n return list.includes(String(actual ?? \"\"));\n }\n case \"not_in\": {\n const list = Array.isArray(expected) ? expected : String(expected ?? \"\").split(\",\").map((s) => s.trim());\n return !list.includes(String(actual ?? \"\"));\n }\n case \"regex\": {\n const re = safeRegExp(String(expected ?? \"\"));\n return !!re && typeof actual === \"string\" && re.test(actual);\n }\n case \"before\": {\n const a = toDateMs(actual);\n const b = toDateMs(expected);\n return a !== null && b !== null && a < b;\n }\n case \"after\": {\n const a = toDateMs(actual);\n const b = toDateMs(expected);\n return a !== null && b !== null && a > b;\n }\n default:\n return false;\n }\n}\n\nexport function evaluateRule(rule: Rule, ctx: TargetingContext): boolean {\n let result: boolean;\n if (rule.field === \"url.path\" || rule.field === \"url.full\" || rule.field === \"app.screen\") {\n const actual = String(resolveField(rule, ctx) ?? \"\");\n if (rule.op === \"equals\") result = matchUrlPattern(String(rule.value ?? \"\"), actual);\n else if (rule.op === \"not_equals\") result = !matchUrlPattern(String(rule.value ?? \"\"), actual);\n else result = compare(rule.op, actual, rule.value);\n } else if (rule.field === \"app.version\" && [\"equals\", \"not_equals\", \"gt\", \"gte\", \"lt\", \"lte\"].includes(rule.op)) {\n const c = compareVersions(resolveField(rule, ctx), rule.value);\n if (c === null) result = rule.op === \"not_equals\";\n else result = rule.op === \"equals\" ? c === 0 : rule.op === \"not_equals\" ? c !== 0 : rule.op === \"gt\" ? c > 0 : rule.op === \"gte\" ? c >= 0 : rule.op === \"lt\" ? c < 0 : c <= 0;\n } else {\n result = compare(rule.op, resolveField(rule, ctx), rule.value);\n }\n return rule.negate ? !result : result;\n}\n\n/** Evaluate a rule tree. An empty group matches everyone. */\nexport function evaluateRules(group: RuleGroup | null | undefined, ctx: TargetingContext): boolean {\n if (!group || group.rules.length === 0) return group?.negate ? false : true;\n const results = group.rules.map((r) => (isRuleGroup(r) ? evaluateRules(r, ctx) : evaluateRule(r, ctx)));\n const value = group.op === \"and\" ? results.every(Boolean) : results.some(Boolean);\n return group.negate ? !value : value;\n}\n\n/** Fields whose evaluation requires server-held data. Used by docs/UI to annotate rules. */\nexport const SERVER_ASSISTED_FIELDS: RuleField[] = [\"audience.member\", \"guide.completed\", \"guide.dismissed\", \"guide.seen\", \"checklist.completed\"];\n\n/** Who a guide is shown to, before its targeting rules: everyone, only signed-in (identified) users, or only anonymous visitors. */\nexport const GUIDE_VISIBILITIES = [\"everyone\", \"identified\", \"anonymous\"] as const;\nexport type GuideVisibility = (typeof GUIDE_VISIBILITIES)[number];\n\n/**\n * Targeting with the visibility folded in as a `user.identified` rule. The server sends guides to SDKs\n * this way, so every SDK applies it through the rules it already evaluates (SDKs that predate the field\n * resolve it as undefined and show the guide to nobody rather than to everyone).\n */\nexport function withVisibility(targeting: RuleGroup, visibility: GuideVisibility | undefined): RuleGroup {\n if (!visibility || visibility === \"everyone\") return targeting;\n return { id: \"visibility\", op: \"and\", rules: [{ id: \"visibility-rule\", field: \"user.identified\", op: visibility === \"identified\" ? \"is_true\" : \"is_false\" }, targeting] };\n}\n\nexport function describeRule(rule: Rule): string {\n const subject = rule.key ? `${rule.field}[${rule.key}]` : rule.field;\n const v = Array.isArray(rule.value) ? rule.value.join(\", \") : rule.value === undefined ? \"\" : String(rule.value);\n return `${rule.negate ? \"NOT \" : \"\"}${subject} ${rule.op.replace(/_/g, \" \")}${v ? ` \"${v}\"` : \"\"}`;\n}\n","/** Reserved property names that are stripped from `properties` to avoid collecting sensitive data. */\nexport const SENSITIVE_PROPERTY_PATTERN = /(password|passwd|secret|token|credit|card|cvv|ssn|iban)/i;\n\nexport const MAX_PROPERTY_BYTES = 8 * 1024;\n\nexport function sanitizeProperties(props: Record<string, unknown> | undefined): Record<string, unknown> {\n if (!props) return {};\n const out: Record<string, unknown> = {};\n let size = 0;\n for (const [k, v] of Object.entries(props)) {\n if (SENSITIVE_PROPERTY_PATTERN.test(k)) continue;\n if (typeof v === \"function\" || typeof v === \"symbol\") continue;\n const str = typeof v === \"string\" ? v : JSON.stringify(v ?? null);\n size += k.length + (str?.length ?? 0);\n if (size > MAX_PROPERTY_BYTES) break;\n out[k.slice(0, 100)] = typeof v === \"string\" ? v.slice(0, 1000) : v;\n }\n return out;\n}\n","import { fnv1a } from \"./utils\";\n\n/** One parsed stack frame. `file` is the script URL (or path) as the browser reported it. */\nexport interface StackFrame {\n fn?: string;\n file?: string;\n line?: number;\n col?: number;\n}\n\n/** Properties carried by an `exception` event (see `docs/product-analytics.md`). */\nexport interface ExceptionProperties {\n type: string;\n message: string;\n frames: StackFrame[];\n /** `onerror` | `unhandledrejection` | `manual` (captureException) | `console`. */\n source?: string;\n handled?: boolean;\n /** App version or commit (SDK option `release`); selects the matching uploaded source maps. */\n release?: string;\n /** Set by the server at ingestion; groups occurrences into an issue. */\n fingerprint?: string;\n}\n\nexport const MAX_STACK_FRAMES = 30;\n\n// Chrome / Edge / Node: \" at fn (https://x/app.js:1:2)\" or \" at https://x/app.js:1:2\"\nconst V8 = /^\\s*at (?:(.+?) \\()?((?:[a-z][\\w+.-]*:)?[^\\s()]+?):(\\d+):(\\d+)\\)?\\s*$/i;\n// Firefox / Safari: \"fn@https://x/app.js:1:2\" or \"@https://x/app.js:1:2\"\nconst GECKO = /^\\s*(.*?)@(.+?):(\\d+):(\\d+)\\s*$/;\n\n/** Parses `Error.stack` from V8, SpiderMonkey and JavaScriptCore into frames (innermost first). */\nexport function parseStack(stack: string | undefined | null, max = MAX_STACK_FRAMES): StackFrame[] {\n if (!stack || typeof stack !== \"string\") return [];\n const frames: StackFrame[] = [];\n for (const raw of stack.split(\"\\n\")) {\n if (frames.length >= max) break;\n const line = raw.slice(0, 1000);\n const m = V8.exec(line) ?? GECKO.exec(line);\n if (!m) continue;\n const fn = (m[1] || \"\").trim();\n frames.push({ fn: fn ? fn.slice(0, 200) : undefined, file: m[2]?.slice(0, 500), line: Number(m[3]), col: Number(m[4]) });\n }\n return frames;\n}\n\n/** Replaces volatile parts of a message (numbers, ids, quoted values) so repeats group together. */\nexport function normalizeErrorMessage(message: string): string {\n return (message || \"\")\n .slice(0, 500)\n .replace(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi, \"<uuid>\")\n .replace(/([\"'`]).*?\\1/g, \"<str>\")\n .replace(/\\b0x[0-9a-f]+\\b/gi, \"<hex>\")\n .replace(/\\d+(\\.\\d+)?/g, \"<n>\")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\n/** Strips origin, query, hash and build hashes (`main.3f2a1b9c.js` → `main.js`) from a script URL. */\nexport function normalizeFrameFile(file: string | undefined): string {\n if (!file) return \"\";\n let path = file;\n try {\n path = new URL(file).pathname;\n } catch {\n path = file.replace(/[?#].*$/, \"\");\n }\n return path.replace(/[.-][0-9a-f]{6,}(?=\\.\\w+$)/i, \"\").replace(/\\/\\d+\\//g, \"/<n>/\");\n}\n\n/** Source map upload limits (`POST /api/v1/sourcemaps/upload`). */\nexport const SOURCEMAP_MAX_FILE_BYTES = 25 * 1024 * 1024;\nexport const SOURCEMAP_MAX_FILES_PER_UPLOAD = 200;\n\n/**\n * Stable issue key for an exception: error type + normalised message + the innermost frame that\n * has a file (path without build hash + function name, or its original source + name when a source\n * map resolved it). Line/column numbers are left out because they change with every deploy of a\n * minified bundle.\n */\nexport function fingerprintError(input: { type?: string; message?: string; frames?: StackFrame[]; original?: { source: string; name?: string | null } | null }): string {\n const top = (input.frames ?? []).find((f) => f.file);\n // With a source map the original file and function name identify the frame across deploys, even\n // when a minifier renames functions between builds.\n const frameKey = input.original ? `src:${input.original.source}:${input.original.name || \"?\"}` : top ? `${normalizeFrameFile(top.file)}:${top.fn || \"?\"}` : \"\";\n const key = [input.type || \"Error\", normalizeErrorMessage(input.message || \"\"), frameKey].join(\"|\");\n const a = fnv1a(key).toString(16).padStart(8, \"0\");\n const b = fnv1a(`guidely:${key}`).toString(16).padStart(8, \"0\");\n return a + b;\n}\n","/** Complaint types and limits shared by the server and the messenger runtime (zod-free). */\n\n/** Where a complaint is in its handling; the messenger shows it as Received → In review → Resolved. */\nexport const COMPLAINT_STATUSES = [\"open\", \"in_review\", \"resolved\"] as const;\nexport type ComplaintStatus = (typeof COMPLAINT_STATUSES)[number];\n\n/** Events a complaint webhook can fire on. */\nexport const COMPLAINT_EVENTS = [\"complaint.filed\", \"complaint.status_changed\", \"complaint.replied\", \"complaint.resolved\"] as const;\nexport type ComplaintEvent = (typeof COMPLAINT_EVENTS)[number];\n\n/** Extra questions a category asks for. */\nexport const COMPLAINT_FIELD_TYPES = [\"text\", \"choice\"] as const;\nexport type ComplaintFieldType = (typeof COMPLAINT_FIELD_TYPES)[number];\n\nexport const MAX_COMPLAINT_FIELDS = 8;\nexport const MAX_COMPLAINT_CHOICES = 12;\nexport const MAX_COMPLAINT_LENGTH = 4000;\nexport const MAX_COMPLAINT_ANSWER_LENGTH = 500;\n/** Largest screenshot the messenger may attach (after it scales the image down). */\nexport const MAX_COMPLAINT_ATTACHMENT_BYTES = 2 * 1024 * 1024;\n\nexport interface ComplaintField {\n /** Stable id (`invoice_number`), used as the answer's key in webhook payloads. */\n id: string;\n label: string;\n type: ComplaintFieldType;\n /** Choices for `choice` questions. */\n options?: string[];\n required: boolean;\n}\n\n/** \"CMP-1042\" for complaint number 1042. */\nexport const complaintReference = (number: number) => `CMP-${number}`;\n\n/** A category as the messenger offers it. */\nexport interface MessengerComplaintCategory {\n id: string;\n name: string;\n description: string;\n color: string;\n fields: ComplaintField[];\n}\n\n/** Complaints block of `MessengerConfig`; absent when the project doesn't take complaints. */\nexport interface MessengerComplaintsConfig {\n categories: MessengerComplaintCategory[];\n /** Visitors the app hasn't identified may file one (with an email address). */\n anonymous: boolean;\n}\n\n/** A complaint as the person who filed it sees it (on its conversation). */\nexport interface MessengerComplaint {\n id: string;\n reference: string;\n status: ComplaintStatus;\n categoryName: string;\n createdAt: string;\n}\n\n/** Scout offered the complaint form in a reply, with this category picked. */\nexport interface MessengerComplaintOffer {\n categoryId: string;\n name: string;\n}\n","/* Network requests recorded into session replays (zod-free: imported by the SDK recorder). */\n\n/** rrweb plugin name for network events in recordings (event type 6, `data.plugin`). */\nexport const NETWORK_PLUGIN = \"guidely/network@1\";\n\n/** Request kinds, from `fetch`/`XMLHttpRequest` patching or `PerformanceResourceTiming.initiatorType`. */\nexport type NetworkRequestType = \"fetch\" | \"xhr\" | \"websocket\" | \"document\" | \"script\" | \"stylesheet\" | \"image\" | \"font\" | \"media\" | \"beacon\" | \"other\";\n\n/**\n * One network request in a recording: the payload of an rrweb plugin event\n * `{ type: 6, data: { plugin: NETWORK_PLUGIN, payload: NetworkPluginPayload } }`, batched.\n * Times are epoch milliseconds (same clock as rrweb event timestamps).\n *\n * A WebSocket is a request with `type: \"websocket\"` and `method: \"GET\"`, emitted when it opens\n * (`startTime`) and again, with the same `id`, when it closes (`endTime`, `closeCode`, `closeReason`,\n * totals in `messages`). Its frames are `WebSocketMessage`s referencing that `id`.\n */\nexport interface NetworkRequest {\n /** Stable id within the recording. */\n id: string;\n type: NetworkRequestType;\n method: string;\n /** Absolute URL; values of sensitive-looking query parameters replaced with `[redacted]`. */\n url: string;\n startTime: number;\n endTime?: number;\n durationMs?: number;\n /** HTTP status; 0 for network errors / blocked / aborted. Absent when the browser doesn't expose it. */\n status?: number;\n /** Set for failed requests: network error, abort or timeout message. */\n error?: string;\n /** Encoded bytes transferred / decoded response size, when known. */\n transferSize?: number;\n responseSize?: number;\n /** Only when `recordNetworkPayloads` is on; sensitive headers are `[redacted]`. */\n requestHeaders?: Record<string, string>;\n responseHeaders?: Record<string, string>;\n /** Text/JSON bodies, ≤10KB, sensitive JSON fields masked; `[binary]` / `[truncated]` markers otherwise. */\n requestBody?: string;\n responseBody?: string;\n /** WebSocket only: negotiated subprotocol, close details, and frame totals (all frames, including ones not recorded). */\n protocol?: string;\n closeCode?: number;\n closeReason?: string;\n messages?: { sent: number; received: number; sentBytes: number; receivedBytes: number; recorded: number };\n}\n\n/** One WebSocket frame. At most `WS_MAX_MESSAGES_PER_SOCKET` are recorded per connection per page load. */\nexport interface WebSocketMessage {\n /** The `NetworkRequest.id` of the connection. */\n socketId: string;\n dir: \"send\" | \"recv\";\n time: number;\n /** Bytes (UTF-8 length for text frames). */\n size: number;\n binary?: boolean;\n /** Only with `recordNetworkPayloads`: text ≤ 2KB (JSON with sensitive keys masked), else `[truncated]`; binary frames are `[binary <n> B]`. */\n data?: string;\n}\n\nexport interface NetworkPluginPayload {\n requests?: NetworkRequest[];\n messages?: WebSocketMessage[];\n /** Requests/frames dropped since the previous payload because a cap was reached. */\n dropped?: { requests?: number; messages?: number };\n}\n\nexport const WS_MAX_MESSAGES_PER_SOCKET = 200;\nexport const WS_MAX_MESSAGE_BYTES = 2048;\n\n/** Header names always replaced with `[redacted]` when payload capture is on. */\nexport const SENSITIVE_HEADER_PATTERN = /^(authorization|proxy-authorization|cookie|set-cookie|x-api-key|x-auth-token|x-csrf-token|x-xsrf-token|x-guidely-key|x-guidely-conversation)$/i;\n\n/** Query parameter and JSON field names whose values are redacted. */\nexport const SENSITIVE_PARAM_PATTERN = /(token|key|secret|password|passwd|pwd|auth|session|code|sig|signature|credential|ssn|card|cvv|iban)/i;\n","/**\n * Media in article bodies: uploaded images, videos and PDFs, and video embeds from a fixed set of hosts.\n * Sanitizers (server, browser runtime) only keep an `<iframe>` whose `src` is the canonical embed URL\n * that `toEmbedUrl` produces, so authors can never point a frame anywhere else.\n *\n * An uploaded PDF reads inline, as its pages: the dashboard renders each page to an image when the\n * PDF is uploaded, and the body holds plain HTML every renderer already understands:\n *\n * <div class=\"gd-pdf\">\n * <a class=\"gd-file\" href=\"…/{id}.pdf\" target=\"_blank\">{file name}</a>\n * <div class=\"gd-pdf-pages\"><img src=\"…/{page}.webp\" alt=\"{file name}, page 1 of 12\"> …</div>\n * </div>\n *\n * Renderers that know the classes draw a swipeable page viewer with the PDF one tap away; older ones\n * show a link and the pages as images. A PDF whose pages couldn't be rendered (password-protected,\n * damaged) is just the `gd-file` link, drawn as a file card.\n */\n\nexport type EmbedProvider = \"youtube\" | \"vimeo\" | \"loom\" | \"wistia\";\n\nexport interface VideoEmbed {\n provider: EmbedProvider;\n /** Canonical player URL, safe to use as an iframe `src`. */\n src: string;\n /** Page a viewer can open instead (native SDKs show a link rather than a player). */\n watchUrl: string;\n /** Accessible iframe title. */\n title: string;\n}\n\nexport const MEDIA_IMAGE_TYPES = [\"image/png\", \"image/jpeg\", \"image/gif\", \"image/webp\"] as const;\nexport const MEDIA_VIDEO_TYPES = [\"video/mp4\", \"video/webm\"] as const;\nexport const MEDIA_FILE_TYPES = [\"application/pdf\"] as const;\nexport type MediaContentType = (typeof MEDIA_IMAGE_TYPES)[number] | (typeof MEDIA_VIDEO_TYPES)[number] | (typeof MEDIA_FILE_TYPES)[number];\nexport type MediaKind = \"image\" | \"video\" | \"file\";\nexport const MEDIA_IMAGE_MAX_BYTES = 10 * 1024 * 1024;\nexport const MEDIA_VIDEO_MAX_BYTES = 200 * 1024 * 1024;\nexport const MEDIA_FILE_MAX_BYTES = 50 * 1024 * 1024;\n/** The largest upload of any type. */\nexport const MEDIA_MAX_BYTES = Math.max(MEDIA_IMAGE_MAX_BYTES, MEDIA_VIDEO_MAX_BYTES, MEDIA_FILE_MAX_BYTES);\n\nexport function isMediaContentType(type: string): type is MediaContentType {\n return mediaKind(type) !== null;\n}\nexport function isVideoContentType(type: string): boolean {\n return (MEDIA_VIDEO_TYPES as readonly string[]).includes(type);\n}\nexport function mediaKind(type: string): MediaKind | null {\n if ((MEDIA_IMAGE_TYPES as readonly string[]).includes(type)) return \"image\";\n if ((MEDIA_VIDEO_TYPES as readonly string[]).includes(type)) return \"video\";\n if ((MEDIA_FILE_TYPES as readonly string[]).includes(type)) return \"file\";\n return null;\n}\nexport function mediaMaxBytes(type: string): number {\n const kind = mediaKind(type);\n return kind === \"video\" ? MEDIA_VIDEO_MAX_BYTES : kind === \"file\" ? MEDIA_FILE_MAX_BYTES : MEDIA_IMAGE_MAX_BYTES;\n}\n\n/** Class marking a link as an attached file (an uploaded PDF), drawn as a file card. */\nexport const FILE_LINK_CLASS = \"gd-file\";\n/** Class of the block holding a PDF's link and its rendered pages. */\nexport const PDF_BLOCK_CLASS = \"gd-pdf\";\n/** Class of the element wrapping a PDF's page images. */\nexport const PDF_PAGES_CLASS = \"gd-pdf-pages\";\n/** Pages rendered inline per PDF; readers open the PDF itself for the rest. */\nexport const PDF_MAX_PAGES = 40;\n\n/** True for an `<a>` with the file class and an absolute http(s) `href`: render it as a file card. */\nexport function isFileLink(className: string | null | undefined, href: string | null | undefined): boolean {\n return !!className && className.split(/\\s+/).includes(FILE_LINK_CLASS) && isSafeMediaUrl(href);\n}\n\nconst YOUTUBE_ID = /^[A-Za-z0-9_-]{11}$/;\nconst VIMEO_ID = /^\\d{1,12}$/;\nconst VIMEO_HASH = /^[0-9a-f]{6,20}$/i;\nconst LOOM_ID = /^[0-9a-f]{32}$/i;\nconst WISTIA_ID = /^[a-z0-9]{10}$/i;\n\nfunction parseUrl(input: string): URL | null {\n const s = input.trim();\n if (!s) return null;\n try {\n const u = new URL(/^[a-z][a-z0-9+.-]*:/i.test(s) ? s : `https://${s}`);\n if (u.protocol !== \"https:\" && u.protocol !== \"http:\") return null;\n if (u.username || u.password || (u.port && u.port !== \"443\" && u.port !== \"80\")) return null;\n return u;\n } catch {\n return null;\n }\n}\n\n/** \"1h2m3s\", \"90s\", \"90\" → seconds. */\nfunction parseStart(v: string | null): number {\n if (!v) return 0;\n if (/^\\d+$/.test(v)) return Math.min(Number(v), 86400);\n const m = /^(?:(\\d+)h)?(?:(\\d+)m)?(?:(\\d+)s)?$/.exec(v);\n if (!m || !m[0]) return 0;\n return Math.min(Number(m[1] ?? 0) * 3600 + Number(m[2] ?? 0) * 60 + Number(m[3] ?? 0), 86400);\n}\n\nfunction youtube(id: string, start: number): VideoEmbed | null {\n if (!YOUTUBE_ID.test(id)) return null;\n return {\n provider: \"youtube\",\n src: `https://www.youtube-nocookie.com/embed/${id}${start ? `?start=${start}` : \"\"}`,\n watchUrl: `https://www.youtube.com/watch?v=${id}${start ? `&t=${start}s` : \"\"}`,\n title: \"YouTube video\",\n };\n}\n\nfunction vimeo(id: string, hash: string | null): VideoEmbed | null {\n if (!VIMEO_ID.test(id) || (hash !== null && !VIMEO_HASH.test(hash))) return null;\n const h = hash ? hash.toLowerCase() : null;\n return { provider: \"vimeo\", src: `https://player.vimeo.com/video/${id}${h ? `?h=${h}` : \"\"}`, watchUrl: `https://vimeo.com/${id}${h ? `/${h}` : \"\"}`, title: \"Vimeo video\" };\n}\n\n/**\n * Turns a pasted video page or player URL from YouTube, Vimeo, Loom or Wistia into its canonical\n * embed. Returns null for anything else, including lookalike hosts.\n */\nexport function toEmbedUrl(input: string): VideoEmbed | null {\n const u = parseUrl(input);\n if (!u) return null;\n const host = u.hostname.toLowerCase().replace(/^www\\./, \"\");\n const parts = u.pathname.split(\"/\").filter(Boolean);\n switch (host) {\n case \"youtube.com\":\n case \"m.youtube.com\":\n case \"youtube-nocookie.com\": {\n const start = parseStart(u.searchParams.get(\"start\") ?? u.searchParams.get(\"t\"));\n if (parts[0] === \"watch\" && parts.length === 1) return youtube(u.searchParams.get(\"v\") ?? \"\", start);\n if ((parts[0] === \"embed\" || parts[0] === \"shorts\" || parts[0] === \"live\") && parts.length === 2) return youtube(parts[1]!, start);\n return null;\n }\n case \"youtu.be\":\n return parts.length === 1 ? youtube(parts[0]!, parseStart(u.searchParams.get(\"t\") ?? u.searchParams.get(\"start\"))) : null;\n case \"vimeo.com\":\n if (parts.length === 1) return vimeo(parts[0]!, null);\n if (parts.length === 2 && VIMEO_ID.test(parts[0]!)) return vimeo(parts[0]!, parts[1]!);\n return null;\n case \"player.vimeo.com\":\n return parts[0] === \"video\" && parts.length === 2 ? vimeo(parts[1]!, u.searchParams.get(\"h\")) : null;\n case \"loom.com\":\n if ((parts[0] === \"share\" || parts[0] === \"embed\") && parts.length === 2 && LOOM_ID.test(parts[1]!)) {\n const id = parts[1]!.toLowerCase();\n return { provider: \"loom\", src: `https://www.loom.com/embed/${id}`, watchUrl: `https://www.loom.com/share/${id}`, title: \"Loom video\" };\n }\n return null;\n default: {\n let id: string | undefined;\n if (host === \"fast.wistia.net\" || host === \"fast.wistia.com\") id = parts[0] === \"embed\" && parts[1] === \"iframe\" && parts.length === 3 ? parts[2] : undefined;\n else if (host === \"wi.st\" || host === \"wistia.com\" || /^[a-z0-9-]+\\.wistia\\.com$/.test(host)) id = parts[0] === \"medias\" && parts.length === 2 ? parts[1] : undefined;\n if (!id || !WISTIA_ID.test(id)) return null;\n return { provider: \"wistia\", src: `https://fast.wistia.net/embed/iframe/${id}`, watchUrl: `https://fast.wistia.net/embed/iframe/${id}`, title: \"Wistia video\" };\n }\n }\n}\n\n/** True only for a canonical embed URL (exactly what `toEmbedUrl` returns), so an iframe can't be pointed elsewhere. */\nexport function isAllowedEmbedSrc(src: string | null | undefined): boolean {\n if (!src) return false;\n return toEmbedUrl(src)?.src === src;\n}\n\n/** Fixed iframe attributes for an embed; sanitizers overwrite whatever the author's HTML carried. */\nexport const EMBED_IFRAME_ATTRS: Readonly<Record<string, string>> = {\n allow: \"fullscreen; picture-in-picture; encrypted-media\",\n allowfullscreen: \"\",\n loading: \"lazy\",\n referrerpolicy: \"strict-origin-when-cross-origin\",\n sandbox: \"allow-scripts allow-same-origin allow-presentation allow-popups allow-popups-to-escape-sandbox\",\n};\n\n/** Fixed `<video>` attributes: player controls, no autoplay or loop. */\nexport const VIDEO_ATTRS: Readonly<Record<string, string>> = { controls: \"\", preload: \"metadata\", playsinline: \"\" };\n\n/** `<video src>` / `poster` must be plain http(s): no data:, blob: or relative URLs (bodies render on customer sites). */\nexport function isSafeMediaUrl(url: string | null | undefined): boolean {\n if (!url) return false;\n try {\n const u = new URL(url);\n return (u.protocol === \"https:\" || u.protocol === \"http:\") && !u.username && !u.password;\n } catch {\n return false;\n }\n}\n","/**\n * Resource center structure shared by every SDK: nested collections and audience targeting.\n *\n * Audience targeting decides what the help widget lists; it is not access control. The published\n * config (and every article body) is the same for all visitors of an environment, and end-user\n * identities are self-asserted, so never rely on it to keep an article private.\n */\n\n/** Collections nest at most this deep (a collection, its sections, and their sub-sections). */\nexport const MAX_COLLECTION_DEPTH = 3;\n\nexport interface RcCollectionLike {\n id: string;\n name: string;\n order: number;\n /** Parent collection; null/absent for a top-level collection. */\n parentId?: string | null;\n /** Shown only to members of any of these audiences; empty/absent means everyone. */\n audienceIds?: string[];\n}\n\nexport interface RcArticleLike {\n id: string;\n collectionId?: string | null;\n audienceIds?: string[];\n}\n\nexport interface RcSection<C extends RcCollectionLike, A extends RcArticleLike> {\n /** null for articles outside any (known) collection. */\n collection: C | null;\n /** Collection names from the top level down, e.g. [\"Billing\", \"Invoices\"]. */\n path: string[];\n /** 0 for a top-level collection. */\n depth: number;\n articles: A[];\n}\n\ntype Membership = ReadonlySet<string> | readonly string[] | undefined | null;\n\n/** True when an item targeted at `required` audiences is shown to someone in `member`. */\nexport function matchesAudiences(required: readonly string[] | undefined | null, member: Membership): boolean {\n if (!required || required.length === 0) return true;\n if (!member) return false;\n const has = member instanceof Set ? (id: string) => member.has(id) : (id: string) => (member as readonly string[]).includes(id);\n return required.some(has);\n}\n\n/**\n * Article sections in display order: each collection (depth first, siblings by `order`) with the\n * visible articles filed directly in it, then the uncategorized articles. A collection the viewer\n * isn't targeted by hides its whole subtree, and sections without visible articles are left out\n * (their sub-collections still appear). Unknown parents count as top level, so an old config or a\n * dangling reference never hides content.\n */\nexport function resourceCenterSections<C extends RcCollectionLike, A extends RcArticleLike>(collections: readonly C[], articles: readonly A[], member?: Membership): Array<RcSection<C, A>> {\n const byId = new Map(collections.map((c) => [c.id, c]));\n const children = new Map<string | null, C[]>();\n for (const c of collections) {\n const parent = c.parentId && c.parentId !== c.id && byId.has(c.parentId) ? c.parentId : null;\n const list = children.get(parent) ?? [];\n list.push(c);\n children.set(parent, list);\n }\n for (const list of children.values()) list.sort((a, b) => a.order - b.order || a.name.localeCompare(b.name));\n const direct = new Map<string, A[]>();\n const uncategorized: A[] = [];\n for (const a of articles) {\n if (!matchesAudiences(a.audienceIds, member)) continue;\n if (a.collectionId && byId.has(a.collectionId)) {\n const list = direct.get(a.collectionId) ?? [];\n list.push(a);\n direct.set(a.collectionId, list);\n } else uncategorized.push(a);\n }\n\n const sections: Array<RcSection<C, A>> = [];\n const seen = new Set<string>();\n const visit = (c: C, path: string[]) => {\n // A parent cycle in bad data would otherwise recurse forever; cycle members simply don't show.\n if (seen.has(c.id)) return;\n seen.add(c.id);\n if (!matchesAudiences(c.audienceIds, member)) return;\n const here = [...path, c.name];\n const list = direct.get(c.id);\n if (list?.length) sections.push({ collection: c, path: here, depth: here.length - 1, articles: list });\n for (const child of children.get(c.id) ?? []) visit(child, here);\n };\n for (const root of children.get(null) ?? []) visit(root, []);\n if (uncategorized.length) sections.push({ collection: null, path: [], depth: 0, articles: uncategorized });\n return sections;\n}\n\n/**\n * Whether one article is listed for a viewer: its own audiences and those of every collection above\n * it must match. For articles outside a loaded list (e.g. server search results).\n */\nexport function isArticleListed(collections: readonly RcCollectionLike[], article: RcArticleLike, member?: Membership): boolean {\n if (!matchesAudiences(article.audienceIds, member)) return false;\n const byId = new Map(collections.map((c) => [c.id, c]));\n let cur = article.collectionId ? byId.get(article.collectionId) : undefined;\n for (let guard = 0; cur && guard <= MAX_COLLECTION_DEPTH * 4; guard++) {\n if (!matchesAudiences(cur.audienceIds, member)) return false;\n cur = cur.parentId ? byId.get(cur.parentId) : undefined;\n }\n return true;\n}\n\n// ---------------------------------------------------------------------------\n// Logged-in only articles\n// ---------------------------------------------------------------------------\n\n/** Who an article is listed for, besides its audiences: everyone, or logged-in (identified) visitors only. */\nexport const ARTICLE_VISIBILITIES = [\"everyone\", \"identified\"] as const;\nexport type ArticleVisibility = (typeof ARTICLE_VISIBILITIES)[number];\n\n/**\n * Reserved audience every logged-in visitor belongs to. The server folds an article's visibility into\n * the audiences it serves and adds this one to identified visitors' memberships, so every SDK (old ones\n * too) applies \"logged-in only\" through the audience matching it already does. Anonymous visitors never\n * identify, so their membership is empty and these articles stay hidden.\n */\nexport const LOGGED_IN_AUDIENCE = \"$identified\";\n\n/**\n * The audiences an article is served with. Logged-in only with no audiences becomes the reserved\n * audience; with audiences it is already implied, since audience membership only comes from identify.\n */\nexport function servedArticleAudiences(audienceIds: readonly string[], visibility: ArticleVisibility | string | null | undefined): string[] {\n return visibility === \"identified\" && audienceIds.length === 0 ? [LOGGED_IN_AUDIENCE] : [...audienceIds];\n}\n\n/** A visitor's memberships as matched against served articles: logged-in visitors are also in `LOGGED_IN_AUDIENCE`. */\nexport function visitorAudiences(audienceIds: readonly string[], identified: boolean): string[] {\n const ids = audienceIds.filter((id) => id !== LOGGED_IN_AUDIENCE);\n return identified ? [...ids, LOGGED_IN_AUDIENCE] : ids;\n}\n\n/** Ids of the articles a viewer sees (for filtering server search results the same way). */\nexport function visibleArticleIds(collections: readonly RcCollectionLike[], articles: readonly RcArticleLike[], member?: Membership): Set<string> {\n return new Set(resourceCenterSections(collections, articles, member).flatMap((s) => s.articles.map((a) => a.id)));\n}\n\n// ---------------------------------------------------------------------------\n// \"Show me\" prompts in help articles\n// ---------------------------------------------------------------------------\n\n/**\n * A \"Show me\" prompt in an article body is a plain link, `<p class=\"gd-show-me\"><a href=\"#guidely-show-me:<guide key>\">Show me how</a></p>`,\n * so it survives every sanitizer and reads as a harmless link anywhere else. The widgets turn it into a\n * button that starts the walkthrough (a live flow guide), or hide it when that guide isn't available.\n */\nexport const SHOW_ME_CLASS = \"gd-show-me\";\nexport const SHOW_ME_HREF_PREFIX = \"#guidely-show-me:\";\nconst SHOW_ME_KEY = /^[A-Za-z0-9_-]{1,64}$/;\n\nexport function showMeHref(guideKey: string): string {\n return `${SHOW_ME_HREF_PREFIX}${guideKey}`;\n}\n\n/** The guide key of a Show me link's `href`, or null. */\nexport function showMeKeyFromHref(href: string | null | undefined): string | null {\n if (!href || !href.startsWith(SHOW_ME_HREF_PREFIX)) return null;\n const key = href.slice(SHOW_ME_HREF_PREFIX.length);\n return SHOW_ME_KEY.test(key) ? key : null;\n}\n\n/** Guide keys of the Show me prompts in an article body, in order, without repeats (at most 10). */\nexport function showMeKeys(html: string | null | undefined): string[] {\n const out: string[] = [];\n for (const m of (html ?? \"\").matchAll(/href=\"#guidely-show-me:([A-Za-z0-9_-]{1,64})\"/g)) if (!out.includes(m[1]!)) out.push(m[1]!);\n return out.slice(0, 10);\n}\n\n/** The article body plays a video (uploaded, or embedded from YouTube, Vimeo, Loom or Wistia). */\nexport function hasVideoMedia(html: string | null | undefined): boolean {\n return /<(video|iframe)\\b/i.test(html ?? \"\");\n}\n\nconst words = (s: string) => s.toLowerCase().match(/[\\p{L}\\p{N}]+/gu) ?? [];\n\n/**\n * Help search on the device: every word of the query starts a word of the text, in any order, or the\n * query appears as typed. \"add user\" finds \"Add a new user\" and \"Users › Add\".\n */\nexport function matchesHelpQuery(text: string, query: string): boolean {\n const q = query.trim().toLowerCase();\n if (!q) return true;\n const hay = text.toLowerCase();\n if (hay.includes(q)) return true;\n const terms = words(q);\n if (!terms.length) return false;\n const have = words(hay);\n return terms.every((t) => have.some((w) => w.startsWith(t) || (t.length > 3 && t.endsWith(\"s\") && w.startsWith(t.slice(0, -1)))));\n}\n","/** How-to capture constants the browser SDK needs without zod (also exported from `@guidelytech/shared/runtime`). */\n\n/** Query parameter that starts capture mode in the SDK (separate from `guidely_authoring`, so SDKs without capture ignore it). */\nexport const CAPTURE_PARAM = \"guidely_capture\";\n/** Largest screenshot the capture endpoint accepts. */\nexport const CAPTURE_SCREENSHOT_MAX_BYTES = 8 * 1024 * 1024;\n","/**\n * Page helpers shared by the SDK (page-aware messenger) and the server (page matching, analytics).\n * Pure and zod-free.\n */\n\n/**\n * One path segment that looks like an identifier: digits, a UUID, or a long hex string (hashes,\n * Mongo ids). Written so the same source works as a JS RegExp and a Postgres ARE (`regexp_replace`\n * with flags `gi`), which keeps id folding identical in SQL and in code. A segment may be followed\n * by the page query parameters a project keeps in its paths (`/contacts/42?tab=notes`).\n */\nexport const PATH_ID_PATTERN = \"/([0-9]+|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|[0-9a-f]{12,})(?=[/?]|$)\";\n\n/** `/contacts/42/notes/9f8e…` → `/contacts/:id/notes/:id`; `/contacts/42?tab=notes` → `/contacts/:id?tab=notes`. */\nexport function foldPathIds(path: string): string {\n return path.replace(new RegExp(PATH_ID_PATTERN, \"gi\"), \"/:id\");\n}\n\n/** Path segments that say nothing about what a page is for. */\nconst GENERIC_SEGMENTS = new Set([\"\", \":id\", \"app\", \"apps\", \"dashboard\", \"home\", \"index\", \"main\", \"new\", \"edit\", \"view\", \"show\", \"details\", \"detail\", \"overview\", \"list\", \"all\", \"page\", \"pages\", \"en\", \"v1\", \"v2\"]);\n\nfunction segmentsOf(path: string): string[] {\n const clean = foldPathIds((path.split(/[?#]/)[0] || \"/\").toLowerCase());\n return clean\n .split(\"/\")\n .map((s) => s.replace(/\\.(html?|php|aspx?)$/, \"\"))\n .filter((s) => !GENERIC_SEGMENTS.has(s) && /[a-z]/.test(s) && s.length <= 40);\n}\n\n/** Words describing a page, from its path (`/settings/billing-plans/42` → `settings billing plans`). */\nexport function pageTerms(path: string): string[] {\n const words = segmentsOf(path).flatMap((s) => s.split(/[-_.+\\s]+/).filter((w) => w.length > 1 && /[a-z]/.test(w)));\n return [...new Set(words)].slice(0, 8);\n}\n\n/** A short human name for a page (`/app/invoices/42` → `Invoices`), or null for the root and unnamed pages. */\nexport function pageLabel(path: string): string | null {\n const last = segmentsOf(path).at(-1);\n if (!last) return null;\n const words = last.split(/[-_.+\\s]+/).filter(Boolean);\n if (!words.length) return null;\n const label = words.join(\" \");\n return label.charAt(0).toUpperCase() + label.slice(1);\n}\n\n/** The first name in a user's traits (`firstName`, `first_name`, or the first word of `name`), or null. */\nexport function firstNameFromTraits(traits: Record<string, unknown> | null | undefined): string | null {\n if (!traits) return null;\n const pick = (v: unknown) => (typeof v === \"string\" ? v.trim() : \"\");\n const first = pick(traits.firstName) || pick(traits.first_name) || pick(traits.firstname) || pick(traits.name).split(/\\s+/)[0] || \"\";\n // Never greet someone by an email address or an id.\n if (!first || first.length > 40 || first.includes(\"@\") || !/\\p{L}/u.test(first)) return null;\n return first;\n}\n"],"mappings":"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","names":["has","words"]}