@monetize.software/sdk-extension 3.0.0-alpha.1 → 3.0.0-alpha.2
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/chunks/{chrome-port-BXHR4SOG.js → chrome-port-DPFUj1MP.js} +144 -98
- package/dist/chunks/chrome-port-DPFUj1MP.js.map +1 -0
- package/dist/chunks/{chrome-port-EtYqHf3p.js → chrome-port-MoMohiHB.js} +2 -2
- package/dist/chunks/chrome-port-MoMohiHB.js.map +1 -0
- package/dist/content.cjs +2 -2
- package/dist/content.cjs.map +1 -1
- package/dist/content.js +37 -17
- package/dist/content.js.map +1 -1
- package/dist/offscreen.cjs +1 -1
- package/dist/offscreen.js +1 -1
- package/package.json +1 -1
- package/dist/chunks/chrome-port-BXHR4SOG.js.map +0 -1
- package/dist/chunks/chrome-port-EtYqHf3p.js.map +0 -1
package/dist/content.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content.js","sources":["../../sdk/src/ui/mount.ts","../../sdk/src/ui/Modal.tsx","../../sdk/src/ui/renderer/blocks/AuthPanel.tsx","../../sdk/src/ui/AuthGate.tsx","../../sdk/src/ui/AnonGate.tsx","../../sdk/src/ui/SupportGate.tsx","../../sdk/src/ui/renderer/blocks/CtaButton.tsx","../../sdk/src/ui/renderer/blocks/CurrentSession.tsx","../../sdk/src/ui/renderer/blocks/FeaturesList.tsx","../../sdk/src/ui/renderer/blocks/Heading.tsx","../../sdk/src/ui/renderer/blocks/PriceGrid.tsx","../../sdk/src/ui/renderer/blocks/Text.tsx","../../sdk/src/ui/renderer/blocks/TokenizationGate.tsx","../../sdk/src/ui/renderer/registry.ts","../../sdk/src/ui/renderer/Renderer.tsx","../../sdk/src/ui/PaywallRoot.tsx","../../sdk/src/ui/UserWatcher.ts","../../sdk/src/ui/PaywallUI.ts","../src/content/RemoteTrialStore.ts","../src/content/RemoteBillingClient.ts","../src/content/RemoteAuthClient.ts","../src/content/RemoteEventTracker.ts","../src/shared/transport-client.ts","../src/content/transport.ts","../src/content/PaywallUI.ts"],"sourcesContent":["import { h, render, type ComponentType } from 'preact';\nimport cssText from './styles.css?inline';\n\nexport interface MountHandle {\n update: (props: Record<string, unknown>) => void;\n unmount: () => void;\n shadowRoot: ShadowRoot;\n}\n\n// Tailwind v4 определяет утилиты вроде `.border` через `border-style: var(--tw-border-style)`,\n// где значение `solid` задаётся реестровой проперти `@property --tw-border-style { initial-value: solid }`.\n// В Chromium `@property`-объявления внутри shadow root не регистрируются document-wide, переменная\n// остаётся пустой → IACVT → border-style: none → used border-width: 0. Чтобы шорткаты работали в\n// shadow scope, регистрируем те же `@property` на уровне document один раз. `inherits: false`\n// держит изоляцию: имя проперти видно глобально, но значения на host-страницу не утекают.\nlet twPropertiesRegistered = false;\nfunction ensureTwPropertiesRegistered(): void {\n if (twPropertiesRegistered) return;\n twPropertiesRegistered = true;\n if (typeof CSS === 'undefined' || typeof CSS.registerProperty !== 'function') return;\n let rules: CSSRuleList;\n try {\n const sheet = new CSSStyleSheet();\n sheet.replaceSync(cssText);\n rules = sheet.cssRules;\n } catch {\n return;\n }\n for (const rule of rules) {\n if (rule.constructor.name !== 'CSSPropertyRule') continue;\n const r = rule as CSSRule & { name: string; syntax: string; inherits: boolean; initialValue: string | null };\n try {\n CSS.registerProperty({\n name: r.name,\n syntax: r.syntax,\n inherits: r.inherits,\n ...(r.initialValue != null ? { initialValue: r.initialValue } : {})\n });\n } catch {\n // Уже зарегистрирована другим инстансом SDK на той же странице — ок.\n }\n }\n}\n\nexport function mountShadow<P extends object>(\n Component: ComponentType<P>,\n props: P,\n options: { host?: HTMLElement; injectCss?: string; shadowMode?: 'open' | 'closed' } = {}\n): MountHandle {\n if (typeof document === 'undefined') {\n throw new Error('mountShadow called in non-DOM environment');\n }\n\n ensureTwPropertiesRegistered();\n\n const host = options.host ?? document.createElement('div');\n host.setAttribute('data-paywall-host', '');\n host.style.cssText = 'all: initial; position: fixed; inset: 0; z-index: 2147483647; pointer-events: none;';\n if (!host.isConnected) document.body.appendChild(host);\n\n // Дефолт `closed` — изоляция от хост-страницы. В e2e/demo тестах\n // включаем `open` через опцию, иначе Playwright не проходит через\n // shadow boundary accessibility-снапшотом и не кликает по внутренним кнопкам.\n const shadow = host.attachShadow({ mode: options.shadowMode ?? 'closed' });\n\n // Защита от протечки наследуемых свойств (color, font, letter-spacing, text-transform,\n // cursor, visibility) с host-страницы внутрь shadow через host-элемент. `!important`\n // в shadow перебивает внешний `!important` на host (спека CSS Scoping).\n // Рендер-фильтры (filter, transform, opacity) на ancestors защитить нельзя —\n // они применяются на уровне композитинга.\n const hostReset = `\n:host {\n all: initial !important;\n display: block !important;\n color: #111827 !important;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif !important;\n font-size: 16px !important;\n font-weight: 400 !important;\n font-style: normal !important;\n line-height: 1.5 !important;\n letter-spacing: normal !important;\n text-transform: none !important;\n text-decoration: none !important;\n text-align: left !important;\n direction: ltr !important;\n cursor: auto !important;\n visibility: visible !important;\n}\n`;\n\n const style = document.createElement('style');\n style.textContent = hostReset + cssText + (options.injectCss ?? '');\n shadow.appendChild(style);\n\n const mountPoint = document.createElement('div');\n mountPoint.style.pointerEvents = 'auto';\n shadow.appendChild(mountPoint);\n\n let currentProps = props;\n render(h(Component as ComponentType<object>, currentProps), mountPoint);\n\n return {\n shadowRoot: shadow,\n update(nextProps) {\n currentProps = { ...currentProps, ...nextProps } as P;\n render(h(Component as ComponentType<object>, currentProps), mountPoint);\n },\n unmount() {\n render(null, mountPoint);\n host.remove();\n }\n };\n}\n","import type { ComponentChildren } from 'preact';\nimport { useEffect, useRef } from 'preact/hooks';\n\nconst FOCUSABLE =\n 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n\nexport interface ModalProps {\n open: boolean;\n onClose: () => void;\n labelledBy?: string;\n brandColor?: string | null;\n /** Пейвол в тестовом режиме эквайринга — рисуем предупреждающую полоску сверху. */\n testMode?: boolean;\n /** Можно ли закрыть модалку: ESC, клик по overlay, крестик. По умолчанию true.\n * false — модалка остаётся открытой до явного host-close() / success-purchase. */\n allowClose?: boolean;\n children: ComponentChildren;\n}\n\nexport function Modal({\n open,\n onClose,\n labelledBy,\n brandColor,\n testMode,\n allowClose = true,\n children\n}: ModalProps) {\n const dialogRef = useRef<HTMLDivElement | null>(null);\n const previouslyFocused = useRef<HTMLElement | null>(null);\n\n useEffect(() => {\n if (!open) return;\n previouslyFocused.current = (document.activeElement as HTMLElement) ?? null;\n\n const dialog = dialogRef.current;\n if (dialog) {\n const first = dialog.querySelector<HTMLElement>(FOCUSABLE);\n (first ?? dialog).focus({ preventScroll: true });\n }\n\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n if (!allowClose) return;\n e.stopPropagation();\n onClose();\n return;\n }\n if (e.key !== 'Tab' || !dialogRef.current) return;\n const focusables = Array.from(\n dialogRef.current.querySelectorAll<HTMLElement>(FOCUSABLE)\n ).filter((el) => !el.hasAttribute('disabled') && el.tabIndex !== -1);\n if (focusables.length === 0) {\n e.preventDefault();\n return;\n }\n const first = focusables[0];\n const last = focusables[focusables.length - 1];\n const active = document.activeElement as HTMLElement | null;\n if (e.shiftKey && active === first) {\n e.preventDefault();\n last.focus();\n } else if (!e.shiftKey && active === last) {\n e.preventDefault();\n first.focus();\n }\n };\n\n document.addEventListener('keydown', onKey, true);\n const prevOverflow = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n\n return () => {\n document.removeEventListener('keydown', onKey, true);\n document.body.style.overflow = prevOverflow;\n previouslyFocused.current?.focus?.({ preventScroll: true });\n };\n }, [open, onClose, allowClose]);\n\n if (!open) return null;\n\n const onBackdrop = (e: MouseEvent) => {\n if (!allowClose) return;\n if (e.target === e.currentTarget) onClose();\n };\n\n const accent = brandColor ?? '#3b82f6';\n\n return (\n <div\n class=\"fixed inset-0 z-[2147483647] flex items-center justify-center bg-slate-950/50 p-2 sm:p-4 backdrop-blur-md animate-[pw-fade-in_180ms_ease-out]\"\n onClick={onBackdrop}\n data-pw-root\n >\n <div\n ref={dialogRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={labelledBy}\n tabIndex={-1}\n // max-h ограничивает высоту вьюпортом (использует dvh для мобильных\n // safe-area), flex-col + overflow на children даёт внутренний скролл\n // когда контент выше viewport'а — критично для extension popup'ов\n // (max 600px высоты) и узких контейнеров на сайтах.\n class=\"relative flex max-h-[calc(100dvh-1rem)] sm:max-h-[calc(100dvh-2rem)] w-full max-w-md flex-col overflow-hidden rounded-3xl bg-white outline-none ring-1 ring-black/5 animate-[pw-scale-in_220ms_cubic-bezier(0.16,1,0.3,1)]\"\n style={\n {\n '--pw-accent': accent,\n boxShadow:\n '0 1px 2px rgba(15,23,42,0.04), 0 12px 32px -8px rgba(15,23,42,0.18), 0 24px 64px -16px rgba(15,23,42,0.22)'\n } as unknown as Record<string, string>\n }\n >\n {testMode && (\n <div\n class=\"flex items-center justify-center gap-1.5 bg-gradient-to-r from-amber-300 to-amber-400 px-3 py-1.5 text-[11px] font-semibold uppercase tracking-wider text-amber-950\"\n role=\"status\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"M8 1L15 14H1L8 1Z\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linejoin=\"round\"\n />\n <path d=\"M8 6v3\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n <circle cx=\"8\" cy=\"11.5\" r=\"0.5\" fill=\"currentColor\" />\n </svg>\n Test mode — no real charge\n </div>\n )}\n <div class=\"flex-1 overflow-y-auto p-7\">\n {children}\n </div>\n {allowClose ? (\n <button\n type=\"button\"\n onClick={onClose}\n aria-label=\"Close\"\n // Absolute относительно dialog'а (не scrollable area) — кнопка\n // всегда в правом верхнем углу dialog'а, не двигается со скроллом\n // и не влияет на flow контента. top-10 при testMode чтобы не\n // залезть на жёлтый банер (~28px высотой).\n class={`absolute right-3 ${testMode ? 'top-10' : 'top-3'} z-10 flex h-8 w-8 items-center justify-center rounded-full bg-white/80 text-gray-400 backdrop-blur-sm transition-colors hover:bg-gray-100 hover:text-gray-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--pw-accent)]`}\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"M3 3l10 10M13 3L3 13\"\n stroke=\"currentColor\"\n stroke-width=\"1.75\"\n stroke-linecap=\"round\"\n />\n </svg>\n </button>\n ) : null}\n </div>\n\n <style>{`\n @keyframes pw-fade-in { from { opacity: 0 } to { opacity: 1 } }\n @keyframes pw-scale-in {\n from { opacity: 0; transform: translateY(12px) scale(0.96) }\n to { opacity: 1; transform: none }\n }\n `}</style>\n </div>\n );\n}\n","import { useState } from 'preact/hooks';\nimport type { OAuthProvider } from '../../../core/auth';\nimport type { LayoutBlock } from '../../../core/types';\nimport { PaywallError } from '../../../core/types';\nimport type { BlockProps } from '../types';\n\ntype AuthPanelBlock = Extract<LayoutBlock, { type: 'auth_panel' }>;\n\ntype Mode = 'signin' | 'signup' | 'forgot' | 'reset_sent' | 'reset_verify';\n\nconst PROVIDER_LABEL: Record<OAuthProvider, string> = {\n google: 'Continue with Google',\n apple: 'Continue with Apple',\n github: 'Continue with GitHub',\n facebook: 'Continue with Facebook'\n};\n\nexport function AuthPanel({ block, ctx }: BlockProps<AuthPanelBlock>) {\n const auth = ctx.auth;\n const session = ctx.authSession;\n const allowSignup = block.allow_signup !== false;\n const allowReset = block.allow_password_reset !== false;\n const hideWhenAuthed = block.hide_when_authenticated !== false;\n\n // Без AuthClient рендерим заметный fallback в dev, в проде ничего: блок\n // в layout не попадает по ошибке только если host забыл передать auth-опцию.\n if (!auth) {\n if (typeof console !== 'undefined') {\n console.warn('[paywall] auth_panel rendered without AuthClient — pass `auth: true` to PaywallUI');\n }\n return null;\n }\n\n // Анон-сессия в UI пейвола — это «нет авторизации»: анон нужен только для\n // api-gateway-токена, для покупки/restore юзеру всё равно надо реально\n // залогиниться. hide_when_authenticated анон тоже игнорирует (иначе блок\n // схлопнется, и юзер останется без формы).\n const realSession = session && !session.user.is_anonymous ? session : null;\n\n // Реально залогинен и block явно не просит показать summary — скрываемся целиком.\n if (realSession && hideWhenAuthed) return null;\n\n if (realSession) {\n return <SignedIn email={realSession.user.email ?? ''} onSignOut={() => auth.signOut().catch(() => {})} />;\n }\n\n return (\n <AuthForm\n block={block}\n allowSignup={allowSignup}\n allowReset={allowReset}\n ctx={ctx}\n />\n );\n}\n\nfunction SignedIn({ email, onSignOut }: { email: string; onSignOut: () => void }) {\n return (\n <div class=\"flex items-center justify-between gap-3 rounded-2xl border border-gray-200 bg-gray-50/60 px-4 py-3\">\n <div class=\"flex flex-col\">\n <span class=\"text-[10px] font-semibold uppercase tracking-wider text-gray-500\">Signed in</span>\n <span class=\"text-sm font-medium text-gray-900\">{email}</span>\n </div>\n <button\n type=\"button\"\n onClick={onSignOut}\n class=\"rounded-md px-1.5 py-0.5 text-xs font-medium text-gray-600 transition-colors hover:bg-white hover:text-gray-900 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--pw-accent)]\"\n >\n Sign out\n </button>\n </div>\n );\n}\n\ninterface FormProps {\n block: AuthPanelBlock;\n allowSignup: boolean;\n allowReset: boolean;\n ctx: BlockProps<AuthPanelBlock>['ctx'];\n}\n\nfunction AuthForm({ block, allowSignup, allowReset, ctx }: FormProps) {\n const auth = ctx.auth!;\n const providers = block.providers ?? [];\n\n const [mode, setMode] = useState<Mode>('signin');\n const [email, setEmail] = useState('');\n const [password, setPassword] = useState('');\n const [otpCode, setOtpCode] = useState('');\n const [busy, setBusy] = useState<null | OAuthProvider | 'email' | 'reset'>(null);\n const [error, setError] = useState<string | null>(null);\n const [info, setInfo] = useState<string | null>(null);\n\n // Успешный auth не эмитит через ctx.onAction — single source of truth для\n // signin/signout-аналитики = AuthClient.onAuthChange (PaywallUI на него\n // подписан и сам пишет в трекер). Failed-кейсы тоже не эмитим: показываем\n // юзеру inline-error, для funnel'а это пока не критично.\n const onSubmit = async (e: Event) => {\n e.preventDefault();\n if (busy) return;\n setBusy('email');\n setError(null);\n setInfo(null);\n try {\n if (mode === 'signin') {\n await auth.signInWithEmail({ email, password });\n } else if (mode === 'signup') {\n const res = await auth.signUp({ email, password });\n if (res.kind === 'confirmation_required') {\n // Сервер требует email confirm — переключаемся в OTP-verify, юзер\n // вводит 6-значный код из письма. После verify — мы сразу залогинены\n // (verifyOtp сам поставит session), AuthForm схлопнется.\n setMode('reset_verify');\n setInfo('Check your email for a confirmation code.');\n }\n } else if (mode === 'forgot') {\n await auth.requestPasswordReset({ email });\n setMode('reset_sent');\n setInfo('If that email exists, a reset code has been sent.');\n } else if (mode === 'reset_verify') {\n // Используется и для signup-confirm, и для recovery — оба flow одинаковые\n // (verifyOtp выдаёт session). Differentiator — какой type послали.\n await auth.verifyOtp({\n email,\n token: otpCode,\n type: password ? 'recovery' : 'email'\n });\n if (password) {\n // Recovery flow: после verify мы получили session, теперь меняем пароль.\n await auth.updatePassword({ password });\n }\n }\n } catch (e) {\n const msg = e instanceof PaywallError ? e.message : 'Something went wrong';\n setError(msg);\n } finally {\n setBusy(null);\n }\n };\n\n const onOAuth = async (provider: OAuthProvider) => {\n if (busy) return;\n setBusy(provider);\n setError(null);\n setInfo(null);\n try {\n // Лоадер снимаем сразу после открытия popup'а — дальше судьба флоу\n // в руках юзера. Юзер закрыл вкладку / отвлёкся / COOP-severance не дал\n // нам поймать closed → promise тихо дойдёт до oauth_timeout, но кнопка\n // не висит, юзер просто кликнет ещё раз.\n await auth.signInWithOAuth({\n provider,\n onPopupOpened: () => setBusy(null)\n });\n } catch (e) {\n // popup_blocked показываем (юзер должен включить popup'ы); cancelled/\n // timeout — нормальный ход событий, не считаем ошибкой.\n if (e instanceof PaywallError) {\n if (e.code === 'oauth_cancelled' || e.code === 'oauth_timeout') return;\n setError(e.message);\n } else {\n setError('Sign-in failed');\n }\n } finally {\n setBusy(null);\n }\n };\n\n return (\n <div class=\"flex flex-col gap-3\">\n {block.heading ? (\n <h2 class=\"text-lg font-semibold tracking-tight text-gray-900\">{block.heading}</h2>\n ) : null}\n\n {providers.length > 0 && (mode === 'signin' || mode === 'signup') ? (\n <div class=\"flex flex-col gap-2\">\n {providers.map((p) => (\n <button\n key={p}\n type=\"button\"\n onClick={() => onOAuth(p)}\n disabled={busy !== null}\n class=\"flex h-11 w-full items-center justify-center gap-2 rounded-xl border border-gray-200 bg-white px-4 text-sm font-medium text-gray-900 shadow-[0_1px_0_rgba(15,23,42,0.04)] transition-all hover:-translate-y-px hover:border-gray-300 hover:bg-gray-50 hover:shadow-sm disabled:cursor-not-allowed disabled:opacity-60 disabled:hover:translate-y-0 disabled:hover:shadow-[0_1px_0_rgba(15,23,42,0.04)] focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--pw-accent)]\"\n >\n {busy === p ? (\n <span class=\"inline-block h-4 w-4 animate-spin rounded-full border-2 border-gray-300 border-t-gray-700\" />\n ) : (\n <ProviderIcon provider={p} />\n )}\n <span>{PROVIDER_LABEL[p]}</span>\n </button>\n ))}\n <Divider />\n </div>\n ) : null}\n\n <form onSubmit={onSubmit} class=\"flex flex-col gap-2\">\n {(mode === 'signin' || mode === 'signup' || mode === 'forgot') && (\n <Field\n type=\"email\"\n label=\"Email\"\n value={email}\n onInput={setEmail}\n autocomplete=\"email\"\n required\n />\n )}\n\n {(mode === 'signin' || mode === 'signup') && (\n <Field\n type=\"password\"\n label=\"Password\"\n value={password}\n onInput={setPassword}\n autocomplete={mode === 'signin' ? 'current-password' : 'new-password'}\n required\n />\n )}\n\n {mode === 'reset_verify' && (\n <>\n <Field\n type=\"text\"\n label=\"Confirmation code\"\n value={otpCode}\n onInput={setOtpCode}\n autocomplete=\"one-time-code\"\n inputMode=\"numeric\"\n required\n />\n <Field\n type=\"password\"\n label=\"New password (optional — only for password reset)\"\n value={password}\n onInput={setPassword}\n autocomplete=\"new-password\"\n />\n </>\n )}\n\n {mode === 'reset_sent' && info && (\n <p class=\"rounded-lg bg-gray-50 px-3 py-2 text-xs text-gray-600\">{info}</p>\n )}\n\n {error && <p class=\"text-xs text-red-600\">{error}</p>}\n {info && mode !== 'reset_sent' && (\n <p class=\"text-xs text-gray-500\">{info}</p>\n )}\n\n {mode !== 'reset_sent' && (\n <button\n type=\"submit\"\n disabled={busy !== null}\n class=\"flex h-11 w-full items-center justify-center rounded-xl px-4 text-sm font-semibold tracking-tight text-white transition-all hover:-translate-y-px hover:brightness-105 disabled:cursor-not-allowed disabled:opacity-60 disabled:hover:translate-y-0 disabled:hover:brightness-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--pw-accent)]\"\n style={{\n background:\n 'linear-gradient(180deg, color-mix(in srgb, var(--pw-accent) 92%, white), var(--pw-accent))',\n boxShadow:\n '0 1px 2px rgba(15,23,42,0.08), 0 6px 14px -4px color-mix(in srgb, var(--pw-accent) 50%, transparent)'\n }}\n >\n {busy === 'email' ? (\n <span class=\"inline-block h-4 w-4 animate-spin rounded-full border-2 border-white/40 border-t-white\" />\n ) : (\n submitLabel(mode)\n )}\n </button>\n )}\n </form>\n\n <div class=\"flex flex-wrap items-center justify-between gap-x-3 gap-y-1 text-xs text-gray-500\">\n {mode === 'signin' && allowSignup && (\n <button type=\"button\" onClick={() => switchMode(setMode, setError, setInfo, 'signup')} class=\"font-medium text-gray-700 hover:underline\">\n Create account\n </button>\n )}\n {mode === 'signup' && (\n <button type=\"button\" onClick={() => switchMode(setMode, setError, setInfo, 'signin')} class=\"font-medium text-gray-700 hover:underline\">\n I already have an account\n </button>\n )}\n {mode === 'signin' && allowReset && (\n <button type=\"button\" onClick={() => switchMode(setMode, setError, setInfo, 'forgot')} class=\"hover:underline\">\n Forgot password?\n </button>\n )}\n {(mode === 'forgot' || mode === 'reset_sent' || mode === 'reset_verify') && (\n <button type=\"button\" onClick={() => switchMode(setMode, setError, setInfo, 'signin')} class=\"hover:underline\">\n Back to sign in\n </button>\n )}\n {mode === 'reset_sent' && (\n <button type=\"button\" onClick={() => switchMode(setMode, setError, setInfo, 'reset_verify')} class=\"font-medium text-gray-700 hover:underline\">\n I have a code\n </button>\n )}\n </div>\n </div>\n );\n}\n\nfunction submitLabel(mode: Mode): string {\n switch (mode) {\n case 'signin':\n return 'Sign in';\n case 'signup':\n return 'Create account';\n case 'forgot':\n return 'Send reset code';\n case 'reset_verify':\n return 'Verify';\n default:\n return 'Continue';\n }\n}\n\nfunction switchMode(\n setMode: (m: Mode) => void,\n setError: (s: string | null) => void,\n setInfo: (s: string | null) => void,\n next: Mode\n): void {\n setMode(next);\n setError(null);\n setInfo(null);\n}\n\ninterface FieldProps {\n type: 'email' | 'password' | 'text';\n label: string;\n value: string;\n onInput: (v: string) => void;\n autocomplete?: string;\n inputMode?: 'numeric' | 'text' | 'email';\n required?: boolean;\n}\n\nfunction Field({ type, label, value, onInput, autocomplete, inputMode, required }: FieldProps) {\n return (\n <label class=\"flex flex-col gap-1.5\">\n <span class=\"text-xs font-medium text-gray-700\">{label}</span>\n <input\n type={type}\n value={value}\n onInput={(e) => onInput((e.target as HTMLInputElement).value)}\n autocomplete={autocomplete}\n inputMode={inputMode}\n required={required}\n class=\"h-11 w-full rounded-xl border border-gray-200 bg-white px-3.5 text-sm text-gray-900 shadow-[0_1px_0_rgba(15,23,42,0.02)] outline-none transition-all placeholder:text-gray-400 hover:border-gray-300 focus:border-[var(--pw-accent)] focus:shadow-[0_0_0_3px_color-mix(in_srgb,var(--pw-accent)_18%,transparent)]\"\n />\n </label>\n );\n}\n\nfunction Divider() {\n return (\n <div class=\"flex items-center gap-2 py-1 text-[10px] uppercase tracking-[0.14em] text-gray-400\">\n <div class=\"h-px flex-1 bg-gradient-to-r from-gray-200 to-transparent\" />\n <span>or</span>\n <div class=\"h-px flex-1 bg-gradient-to-r from-transparent to-gray-200\" />\n </div>\n );\n}\n\n// Минималистичные SVG-иконки провайдеров. CWS требует bundled-ассеты, поэтому\n// никаких CDN-картинок. По цвету рисуем только Google (брендовый), остальные\n// чёрно-белые — это нейтрально и подходит под любой brand_color пейвола.\nfunction ProviderIcon({ provider }: { provider: OAuthProvider }) {\n if (provider === 'google') {\n return (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 18 18\" aria-hidden=\"true\">\n <path fill=\"#4285F4\" d=\"M17.64 9.2c0-.64-.06-1.25-.16-1.84H9v3.49h4.84a4.14 4.14 0 0 1-1.79 2.71v2.26h2.9c1.7-1.56 2.69-3.87 2.69-6.62Z\" />\n <path fill=\"#34A853\" d=\"M9 18c2.43 0 4.47-.8 5.96-2.18l-2.9-2.26c-.8.54-1.83.86-3.06.86-2.36 0-4.36-1.59-5.07-3.74H.92v2.33A9 9 0 0 0 9 18Z\" />\n <path fill=\"#FBBC05\" d=\"M3.93 10.68a5.4 5.4 0 0 1 0-3.36V4.99H.92a9 9 0 0 0 0 8.02l3-2.33Z\" />\n <path fill=\"#EA4335\" d=\"M9 3.58c1.32 0 2.5.45 3.44 1.34l2.58-2.58A9 9 0 0 0 .92 4.99l3.01 2.33C4.64 5.17 6.64 3.58 9 3.58Z\" />\n </svg>\n );\n }\n if (provider === 'apple') {\n return (\n <svg width=\"14\" height=\"16\" viewBox=\"0 0 14 16\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M11.4 8.5c0-2 1.6-3 1.7-3-.9-1.3-2.4-1.5-2.9-1.5-1.2-.1-2.4.7-3 .7-.6 0-1.6-.7-2.6-.7-1.3 0-2.6.8-3.3 2C-.4 8.4.7 12.5 2.2 14.7c.7 1.1 1.6 2.3 2.7 2.3 1.1 0 1.5-.7 2.8-.7 1.3 0 1.7.7 2.8.7 1.2 0 1.9-1.1 2.6-2.2.6-.9 1-1.8 1.1-2.7-1.4-.5-2.8-1.7-2.8-3.6Zm-2-6.5C10 1.3 10.4.4 10.3 0c-.7 0-1.6.5-2.1 1.1-.5.5-1 1.4-.9 2.2.7 0 1.5-.4 2.1-1.3Z\" />\n </svg>\n );\n }\n if (provider === 'github') {\n return (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M8 0C3.6 0 0 3.6 0 8a8 8 0 0 0 5.5 7.6c.4.1.5-.2.5-.4v-1.5c-2.2.5-2.7-1-2.7-1-.4-.9-.9-1.2-.9-1.2-.7-.5.1-.5.1-.5.8.1 1.2.8 1.2.8.7 1.2 1.9.9 2.4.7 0-.5.3-.9.5-1.1-1.8-.2-3.6-.9-3.6-4 0-.9.3-1.6.8-2.1-.1-.2-.4-1 .1-2.1 0 0 .7-.2 2.2.8a7.6 7.6 0 0 1 4 0c1.5-1 2.2-.8 2.2-.8.4 1.1.2 1.9.1 2.1.5.5.8 1.2.8 2.1 0 3.1-1.9 3.7-3.6 3.9.3.3.6.8.6 1.6V15c0 .2.1.5.6.4A8 8 0 0 0 16 8c0-4.4-3.6-8-8-8Z\" />\n </svg>\n );\n }\n return (\n <svg width=\"14\" height=\"16\" viewBox=\"0 0 14 16\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M14 2.7C14 1.2 12.8 0 11.3 0H2.7C1.2 0 0 1.2 0 2.7v10.6C0 14.8 1.2 16 2.7 16h4V9.8H4.7v-2H6.7V6.4c0-2 1.2-3.1 3-3.1.9 0 1.7.1 2 .2V5h-1.4c-.8 0-1 .4-1 1v1.5h2.4l-.3 2H9.3V16h2c1.5 0 2.7-1.2 2.7-2.7V2.7Z\" />\n </svg>\n );\n}\n","import type { AuthClient, AuthSession } from '../core/auth';\nimport type { LayoutBlock, PaywallBootstrap } from '../core/types';\nimport { AuthPanel } from './renderer/blocks/AuthPanel';\nimport type { BlockContext } from './renderer/types';\n\ntype AuthPanelBlock = Extract<LayoutBlock, { type: 'auth_panel' }>;\n\nexport interface AuthGateProps {\n block: AuthPanelBlock;\n bootstrap: PaywallBootstrap;\n auth: AuthClient;\n authSession: AuthSession | null;\n onBack: () => void;\n /** Показывать кнопку «← Back». Для preauth/restore-flow — true (юзер\n * пришёл сюда из layout, должен иметь возможность вернуться к тарифам).\n * Для standalone openAuth() — false: модалка открыта только ради signin'а,\n * ESC и X-кнопка модалки уже закрывают её, дублирующая Back путает. */\n showBack?: boolean;\n}\n\n// Полноэкранная обёртка над AuthPanel для preauth-gate flow. AuthPanel сам по\n// себе — layout-блок и не знает про \"вернуться к тарифам\"; gate добавляет\n// Back-кнопку + конструирует stub BlockContext (AuthPanel из контекста читает\n// только auth/authSession, остальные поля не использует).\nexport function AuthGate({\n block,\n bootstrap,\n auth,\n authSession,\n onBack,\n showBack = true\n}: AuthGateProps) {\n const ctx: BlockContext = {\n bootstrap,\n selectedPriceId: null,\n setSelectedPriceId: () => {},\n onAction: () => {},\n auth,\n authSession\n };\n\n return (\n <div class=\"flex flex-col gap-3\">\n {showBack ? (\n <button\n type=\"button\"\n onClick={onBack}\n class=\"-ml-1 self-start rounded-md px-1.5 py-0.5 text-xs font-medium text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--pw-accent)]\"\n >\n ← Back\n </button>\n ) : null}\n <AuthPanel block={block} ctx={ctx} />\n </div>\n );\n}\n","import { useEffect, useRef, useState } from 'preact/hooks';\nimport type { AuthClient, AuthSession } from '../core/auth';\n\n// Anonymous sign-in gate. После удаления Turnstile-iframe'а (captcha в Supabase\n// выключена, защита держится на Supabase rate-limit per real-IP + CF Bot Fight\n// Mode) экран превратился в индикатор прогресса:\n// 1. Mount → auth.signInAnonymously() — внутри AuthClient'а сначала проверит\n// idempotent (если уже анон — instant return) и resume (если есть\n// сохранённый refresh_token — silent restore), потом fresh signin.\n// 2. Success → onSuccess(session). Gate схлопывается через PaywallRoot.\n// 3. Error → отображаем сообщение + кнопку «Try again».\n//\n// `forceCaptcha` имя сохранили в API ради host backward-compat (на самом деле\n// — «принудительно создать нового anon-юзера, не resume'ить»).\n\nexport interface AnonGateProps {\n auth: AuthClient;\n /** Вызывается после успешного signin'а (любым путём — idempotent / resume / fresh). */\n onSuccess: (session: AuthSession) => void;\n /** Кнопка «← Back». Опциональна — когда AnonGate смонтирован как\n * standalone (paywallUI.openAnonGate()), `onBack` приведёт к закрытию\n * модалки целиком; для inline-варианта в layout — возврат к тарифам. */\n onBack?: () => void;\n heading?: string;\n description?: string;\n}\n\ntype Phase =\n | { kind: 'signing-in' }\n | { kind: 'error'; message: string };\n\nexport function AnonGate({\n auth,\n onSuccess,\n onBack,\n heading = 'Continue as guest',\n description = 'Setting up your guest session…'\n}: AnonGateProps) {\n const [phase, setPhase] = useState<Phase>({ kind: 'signing-in' });\n // Защита от race: если юзер закрыл модалку посреди signin'а, не зовём\n // onSuccess из устаревшего промиса.\n const aliveRef = useRef(true);\n useEffect(() => {\n return () => {\n aliveRef.current = false;\n };\n }, []);\n\n const run = (): void => {\n setPhase({ kind: 'signing-in' });\n void (async () => {\n try {\n const session = await auth.signInAnonymously();\n if (!aliveRef.current) return;\n onSuccess(session);\n } catch (e) {\n if (!aliveRef.current) return;\n setPhase({\n kind: 'error',\n message: e instanceof Error ? e.message : 'Anonymous sign-in failed'\n });\n }\n })();\n };\n\n // Один автозапуск на mount. Зависимости — пустые: повторное срабатывание\n // привело бы к лишним сетевым запросам на каждом ре-рендере родителя.\n useEffect(() => {\n run();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return (\n <div class=\"flex flex-col gap-3\">\n {onBack ? (\n <button\n type=\"button\"\n onClick={onBack}\n class=\"-ml-1 self-start rounded-md px-1.5 py-0.5 text-xs font-medium text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--pw-accent)]\"\n >\n ← Back\n </button>\n ) : null}\n\n <div class=\"flex flex-col gap-1\">\n <h2 class=\"text-xl font-semibold text-gray-900\">{heading}</h2>\n <p class=\"text-sm text-gray-500\">{description}</p>\n </div>\n\n {phase.kind === 'signing-in' ? (\n <div class=\"flex items-center justify-center py-6\">\n <Spinner />\n </div>\n ) : null}\n\n {phase.kind === 'error' ? (\n <div class=\"flex flex-col gap-3\">\n <div class=\"rounded-lg bg-red-50 px-3 py-2 text-sm text-red-700\">\n {phase.message}\n </div>\n <button\n type=\"button\"\n onClick={run}\n class=\"self-start rounded-md bg-[var(--pw-accent)] px-3 py-1.5 text-sm font-medium text-white hover:opacity-90 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--pw-accent)] focus-visible:ring-offset-2\"\n >\n Try again\n </button>\n </div>\n ) : null}\n </div>\n );\n}\n\nfunction Spinner() {\n return (\n <svg class=\"h-5 w-5 animate-spin text-[var(--pw-accent)]\" viewBox=\"0 0 24 24\" fill=\"none\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"3\" stroke-opacity=\"0.2\" />\n <path d=\"M22 12a10 10 0 0 0-10-10\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\" />\n </svg>\n );\n}\n","import { useMemo, useRef, useState } from 'preact/hooks';\nimport type { BillingClient } from '../core/BillingClient';\nimport type { AuthSession } from '../core/auth';\nimport { PaywallError } from '../core/types';\n\nexport interface SupportGateProps {\n client: BillingClient;\n authSession: AuthSession | null;\n // 'standalone' — модалка открыта только для саппорта (paywall.openSupport()),\n // Back/Done закрывают её. 'layout' — пришли из current_session-блока,\n // Back/Done возвращают в layout (и пейвол с тарифами остаётся открытым).\n origin: 'layout' | 'standalone';\n onBack: () => void;\n}\n\nconst SUBJECT_MIN = 3;\nconst SUBJECT_MAX = 200;\nconst CONTENT_MAX = 5000;\nconst MAX_FILES = 5;\nconst MAX_FILE_SIZE = 10 * 1024 * 1024;\nconst ACCEPTED_MIME = ['image/jpeg', 'image/png', 'image/webp'];\nconst EMAIL_RE = /.+@.+\\..+/;\n\nexport function SupportGate({ client, authSession, origin, onBack }: SupportGateProps) {\n const sessionEmail = authSession?.user.email ?? '';\n // Если есть сессия — email фиксируем из неё, форма его не редактирует.\n const lockedEmail = sessionEmail ? sessionEmail : null;\n const [email, setEmail] = useState<string>(sessionEmail);\n const [subject, setSubject] = useState('');\n const [message, setMessage] = useState('');\n const [files, setFiles] = useState<File[]>([]);\n const [submitting, setSubmitting] = useState(false);\n const [submittedEmail, setSubmittedEmail] = useState<string | null>(null);\n const [errors, setErrors] = useState<{\n subject?: string;\n email?: string;\n message?: string;\n files?: string;\n submit?: string;\n }>({});\n\n const isValid = useMemo(() => {\n const e = (lockedEmail ?? email).trim().toLowerCase();\n const s = subject.trim();\n const m = message.trim();\n return (\n EMAIL_RE.test(e) &&\n s.length >= SUBJECT_MIN &&\n s.length <= SUBJECT_MAX &&\n m.length >= 1 &&\n m.length <= CONTENT_MAX\n );\n }, [lockedEmail, email, subject, message]);\n\n const validate = (): boolean => {\n const next: typeof errors = {};\n const e = (lockedEmail ?? email).trim();\n const s = subject.trim();\n const m = message.trim();\n if (!e) next.email = 'Required';\n else if (!EMAIL_RE.test(e.toLowerCase())) next.email = 'Invalid email';\n if (s.length < SUBJECT_MIN || s.length > SUBJECT_MAX) {\n next.subject = `${SUBJECT_MIN}–${SUBJECT_MAX} characters`;\n }\n if (m.length < 1 || m.length > CONTENT_MAX) {\n next.message = `1–${CONTENT_MAX} characters`;\n }\n setErrors(next);\n return Object.keys(next).length === 0;\n };\n\n const onSubmit = async (e: Event): Promise<void> => {\n e.preventDefault();\n if (submitting) return;\n if (!validate()) return;\n setSubmitting(true);\n setErrors((prev) => ({ ...prev, submit: undefined }));\n try {\n const finalEmail = (lockedEmail ?? email).trim();\n await client.createSupportTicket({\n subject: subject.trim(),\n content: message.trim(),\n email: finalEmail || undefined,\n files: files.length > 0 ? files : undefined\n });\n setSubmittedEmail(finalEmail);\n } catch (err) {\n const msg =\n err instanceof PaywallError\n ? err.message || 'Failed to send. Please try again.'\n : 'Failed to send. Please try again.';\n setErrors((prev) => ({ ...prev, submit: msg }));\n } finally {\n setSubmitting(false);\n }\n };\n\n const resetForm = (): void => {\n setSubject('');\n setMessage('');\n setFiles([]);\n setErrors({});\n setSubmittedEmail(null);\n };\n\n if (submittedEmail) {\n return (\n <div class=\"flex flex-col items-center gap-4 py-2 text-center\">\n <div\n class=\"flex h-14 w-14 items-center justify-center rounded-full\"\n style={{\n background:\n 'linear-gradient(135deg, color-mix(in srgb, var(--pw-accent) 85%, white), var(--pw-accent))',\n color: '#fff',\n boxShadow:\n '0 0 0 8px color-mix(in srgb, var(--pw-accent) 12%, transparent), 0 8px 20px -6px color-mix(in srgb, var(--pw-accent) 45%, transparent)'\n }}\n aria-hidden=\"true\"\n >\n <svg viewBox=\"0 0 24 24\" class=\"h-7 w-7\">\n <path\n fill=\"currentColor\"\n d=\"M12 0a12 12 0 1 0 0 24 12 12 0 0 0 0-24Zm6.93 8.2-6.85 9.29a1.01 1.01 0 0 1-1.43.19L5.76 13.77a1 1 0 1 1 1.25-1.56l4.08 3.26 6.23-8.45a1 1 0 1 1 1.61 1.18Z\"\n />\n </svg>\n </div>\n <div class=\"text-lg font-semibold tracking-tight text-gray-900\">Request submitted</div>\n <div class=\"max-w-[320px] text-sm leading-relaxed text-gray-500\">\n We've received your message and will respond to{' '}\n <b class=\"text-gray-700\">{submittedEmail}</b>.\n </div>\n <div class=\"mt-2 flex items-center justify-center gap-3\">\n <button\n type=\"button\"\n onClick={onBack}\n class=\"rounded-xl px-3 py-2 text-sm font-medium text-gray-600 transition-colors hover:bg-gray-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--pw-accent)]\"\n >\n {origin === 'standalone' ? 'Done' : 'Back'}\n </button>\n <button\n type=\"button\"\n onClick={resetForm}\n class=\"flex h-10 items-center justify-center rounded-xl px-4 text-sm font-semibold text-white transition-all hover:-translate-y-px hover:brightness-105 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--pw-accent)]\"\n style={{\n background:\n 'linear-gradient(180deg, color-mix(in srgb, var(--pw-accent) 92%, white), var(--pw-accent))',\n boxShadow:\n '0 1px 2px rgba(15,23,42,0.08), 0 6px 14px -4px color-mix(in srgb, var(--pw-accent) 50%, transparent)'\n }}\n >\n Send another request\n </button>\n </div>\n </div>\n );\n }\n\n return (\n <div class=\"flex flex-col gap-3\">\n <div class=\"flex items-center justify-between\">\n <button\n type=\"button\"\n onClick={onBack}\n class=\"-ml-1 rounded-md px-1.5 py-0.5 text-xs font-medium text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--pw-accent)]\"\n >\n ← {origin === 'standalone' ? 'Close' : 'Back'}\n </button>\n </div>\n <h2 class=\"text-lg font-semibold tracking-tight text-gray-900\">Contact Support</h2>\n <p class=\"text-xs leading-relaxed text-gray-500\">\n Fill out the form below and we'll get back to you.\n </p>\n\n <form onSubmit={onSubmit} class=\"flex flex-col gap-3\">\n {!lockedEmail ? (\n <Field\n type=\"email\"\n label=\"Your email\"\n value={email}\n onInput={setEmail}\n error={errors.email}\n autocomplete=\"email\"\n required\n />\n ) : (\n <div class=\"rounded-xl border border-gray-200 bg-gray-50/60 px-3 py-2 text-xs text-gray-500\">\n Sending as <b class=\"font-medium text-gray-700\">{lockedEmail}</b>\n </div>\n )}\n <Field\n type=\"text\"\n label=\"Subject\"\n value={subject}\n onInput={setSubject}\n error={errors.subject}\n required\n />\n <TextareaField\n label=\"Message\"\n value={message}\n onInput={setMessage}\n error={errors.message}\n required\n />\n\n <Dropzone files={files} onChange={setFiles} disabled={submitting} />\n\n {errors.submit && <p class=\"text-xs text-red-600\">{errors.submit}</p>}\n\n <div class=\"mt-1 flex items-center justify-end gap-2\">\n <button\n type=\"button\"\n onClick={onBack}\n disabled={submitting}\n class=\"rounded-xl px-3 py-2 text-sm font-medium text-gray-600 transition-colors hover:bg-gray-100 disabled:cursor-not-allowed disabled:opacity-60 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--pw-accent)]\"\n >\n {origin === 'standalone' ? 'Close' : 'Back'}\n </button>\n <button\n type=\"submit\"\n disabled={!isValid || submitting}\n class=\"flex h-10 items-center justify-center rounded-xl px-4 text-sm font-semibold text-white transition-all hover:-translate-y-px hover:brightness-105 disabled:cursor-not-allowed disabled:opacity-60 disabled:hover:translate-y-0 disabled:hover:brightness-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--pw-accent)]\"\n style={{\n background:\n 'linear-gradient(180deg, color-mix(in srgb, var(--pw-accent) 92%, white), var(--pw-accent))',\n boxShadow:\n '0 1px 2px rgba(15,23,42,0.08), 0 6px 14px -4px color-mix(in srgb, var(--pw-accent) 50%, transparent)'\n }}\n >\n {submitting ? (\n <span class=\"inline-block h-4 w-4 animate-spin rounded-full border-2 border-white/40 border-t-white\" />\n ) : (\n 'Send'\n )}\n </button>\n </div>\n </form>\n </div>\n );\n}\n\ninterface FieldProps {\n type: 'email' | 'text';\n label: string;\n value: string;\n onInput: (v: string) => void;\n error?: string;\n autocomplete?: string;\n required?: boolean;\n}\n\nfunction Field({ type, label, value, onInput, error, autocomplete, required }: FieldProps) {\n return (\n <label class=\"flex flex-col gap-1.5\">\n <span class=\"text-xs font-medium text-gray-700\">{label}</span>\n <input\n type={type}\n value={value}\n onInput={(e) => onInput((e.target as HTMLInputElement).value)}\n autocomplete={autocomplete}\n required={required}\n class={`h-11 w-full rounded-xl border bg-white px-3.5 text-sm text-gray-900 shadow-[0_1px_0_rgba(15,23,42,0.02)] outline-none transition-all placeholder:text-gray-400 ${\n error\n ? 'border-red-400 focus:border-red-500 focus:shadow-[0_0_0_3px_rgba(239,68,68,0.18)]'\n : 'border-gray-200 hover:border-gray-300 focus:border-[var(--pw-accent)] focus:shadow-[0_0_0_3px_color-mix(in_srgb,var(--pw-accent)_18%,transparent)]'\n }`}\n />\n {error && <span class=\"text-xs text-red-600\">{error}</span>}\n </label>\n );\n}\n\ninterface TextareaFieldProps {\n label: string;\n value: string;\n onInput: (v: string) => void;\n error?: string;\n required?: boolean;\n}\n\nfunction TextareaField({ label, value, onInput, error, required }: TextareaFieldProps) {\n return (\n <label class=\"flex flex-col gap-1.5\">\n <span class=\"text-xs font-medium text-gray-700\">{label}</span>\n <textarea\n value={value}\n onInput={(e) => onInput((e.target as HTMLTextAreaElement).value)}\n required={required}\n rows={4}\n class={`min-h-[104px] w-full rounded-xl border bg-white px-3.5 py-2.5 text-sm leading-relaxed text-gray-900 shadow-[0_1px_0_rgba(15,23,42,0.02)] outline-none transition-all placeholder:text-gray-400 ${\n error\n ? 'border-red-400 focus:border-red-500 focus:shadow-[0_0_0_3px_rgba(239,68,68,0.18)]'\n : 'border-gray-200 hover:border-gray-300 focus:border-[var(--pw-accent)] focus:shadow-[0_0_0_3px_color-mix(in_srgb,var(--pw-accent)_18%,transparent)]'\n }`}\n />\n {error && <span class=\"text-xs text-red-600\">{error}</span>}\n </label>\n );\n}\n\ninterface DropzoneProps {\n files: File[];\n onChange: (next: File[]) => void;\n disabled?: boolean;\n}\n\nfunction Dropzone({ files, onChange, disabled }: DropzoneProps) {\n const inputRef = useRef<HTMLInputElement | null>(null);\n const [dragOver, setDragOver] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const handleFiles = (incoming: FileList | null): void => {\n if (!incoming || disabled) return;\n setError(null);\n const arr = Array.from(incoming);\n if (files.length + arr.length > MAX_FILES) {\n setError(`Up to ${MAX_FILES} files`);\n return;\n }\n const valid = arr.filter(\n (f) => ACCEPTED_MIME.includes(f.type) && f.size <= MAX_FILE_SIZE\n );\n if (valid.length !== arr.length) {\n setError('Only JPEG/PNG/WebP, ≤ 10MB each');\n return;\n }\n onChange([...files, ...valid]);\n };\n\n return (\n <div>\n <span class=\"text-xs font-medium text-gray-700\">Attachments (optional)</span>\n <div\n role=\"button\"\n tabIndex={0}\n aria-label=\"Attachments upload\"\n onClick={() => !disabled && inputRef.current?.click()}\n onDragOver={(e) => {\n e.preventDefault();\n if (!disabled) setDragOver(true);\n }}\n onDragLeave={() => setDragOver(false)}\n onDrop={(e) => {\n e.preventDefault();\n setDragOver(false);\n handleFiles(e.dataTransfer?.files ?? null);\n }}\n class={`mt-1.5 cursor-pointer rounded-2xl border border-dashed p-3.5 text-center transition-all ${\n dragOver\n ? 'border-[var(--pw-accent)] bg-[color-mix(in_srgb,var(--pw-accent)_6%,white)]'\n : 'border-gray-300 hover:border-gray-400 hover:bg-gray-50/60'\n } ${disabled ? 'cursor-not-allowed opacity-60' : ''}`}\n >\n <div class=\"text-xs text-gray-500\">Drop images here or click to select</div>\n <div class=\"mt-0.5 text-[11px] text-gray-400\">\n JPEG/PNG/WebP, up to {MAX_FILES} files, ≤ 10MB each\n </div>\n </div>\n <input\n ref={inputRef}\n type=\"file\"\n multiple\n accept={ACCEPTED_MIME.join(',')}\n class=\"hidden\"\n onChange={(e) => {\n handleFiles((e.target as HTMLInputElement).files);\n (e.currentTarget as HTMLInputElement).value = '';\n }}\n />\n {error && <p class=\"mt-1 text-xs text-red-600\">{error}</p>}\n {files.length > 0 && (\n <ul class=\"mt-2 flex flex-col gap-1\">\n {files.map((f, i) => (\n <li\n key={`${f.name}-${f.size}-${i}`}\n class=\"flex items-center justify-between gap-2 rounded bg-gray-50 px-2 py-1 text-xs\"\n >\n <span class=\"truncate text-gray-700\">{f.name}</span>\n <button\n type=\"button\"\n onClick={() => {\n const next = [...files];\n next.splice(i, 1);\n onChange(next);\n }}\n disabled={disabled}\n class=\"text-gray-500 hover:text-red-600 disabled:cursor-not-allowed disabled:opacity-60\"\n aria-label={`Remove ${f.name}`}\n >\n ✕\n </button>\n </li>\n ))}\n </ul>\n )}\n </div>\n );\n}\n","import { useState } from 'preact/hooks';\nimport type { LayoutBlock } from '../../../core/types';\nimport type { BlockProps } from '../types';\n\ntype CtaBlock = Extract<LayoutBlock, { type: 'cta_button' }>;\n\nexport function CtaButton({ block, ctx }: BlockProps<CtaBlock>) {\n const [busy, setBusy] = useState(false);\n const priceId = block.priceId ?? ctx.selectedPriceId;\n const disabled = busy || (block.action === 'checkout' && !priceId);\n\n const onClick = async () => {\n if (disabled) return;\n setBusy(true);\n try {\n await ctx.onAction(block.action, { priceId });\n } finally {\n setBusy(false);\n }\n };\n\n return (\n <button\n type=\"button\"\n disabled={disabled}\n onClick={onClick}\n class=\"relative flex h-12 w-full items-center justify-center overflow-hidden rounded-2xl px-4 text-sm font-semibold tracking-tight text-white transition-all duration-150 hover:-translate-y-px hover:brightness-105 active:translate-y-0 active:brightness-95 disabled:cursor-not-allowed disabled:opacity-60 disabled:hover:translate-y-0 disabled:hover:brightness-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--pw-accent)]\"\n style={{\n background:\n 'linear-gradient(180deg, color-mix(in srgb, var(--pw-accent) 92%, white), var(--pw-accent))',\n boxShadow:\n '0 1px 0 rgba(255,255,255,0.25) inset, 0 1px 2px rgba(15,23,42,0.08), 0 8px 20px -6px color-mix(in srgb, var(--pw-accent) 50%, transparent)'\n }}\n >\n {busy ? (\n <span class=\"inline-block h-4 w-4 animate-spin rounded-full border-2 border-white/40 border-t-white\" />\n ) : (\n block.label\n )}\n </button>\n );\n}\n","import { useState } from 'preact/hooks';\nimport type { LayoutBlock } from '../../../core/types';\nimport type { BlockProps } from '../types';\n\ntype CurrentSessionBlock = Extract<LayoutBlock, { type: 'current_session' }>;\n\n// Footer под cta_button. Зеркалит legacy v2 PaywallCurrentSession:\n// - залогинен → \"Signed in as <email>\" + Sign out (вызывает auth.signOut())\n// + Contact Support\n// - гость → \"Restore purchases\" + Contact Support\n// Без AuthClient в managed-режиме — рендерим только Restore + Support\n// (sign out нечему делать, restore без auth-клиента no-op'нет в handleAction).\n// Анон-сессия (is_anonymous=true) трактуется как «нет логина»: анон существует\n// только ради api-gateway-токена, у юзера нет email и UX-смысла «Signed in».\nexport function CurrentSession({ ctx }: BlockProps<CurrentSessionBlock>) {\n const session = ctx.authSession;\n const auth = ctx.auth;\n const [signingOut, setSigningOut] = useState(false);\n\n const onSupport = (): void => ctx.onAction('support');\n\n if (session && !session.user.is_anonymous) {\n const onSignOut = async (): Promise<void> => {\n if (!auth || signingOut) return;\n setSigningOut(true);\n try {\n await auth.signOut();\n } catch {\n /* signOut ошибки безшумные — onAuthChange всё равно отработает на refresh-fail */\n } finally {\n setSigningOut(false);\n }\n };\n\n return (\n <div class=\"mt-2 text-center text-xs text-gray-500\">\n <span>Signed in as </span>\n <b class=\"font-medium text-gray-700\">{session.user.email}</b>\n <div class=\"mt-1 flex items-center justify-center gap-3\">\n <button\n type=\"button\"\n onClick={onSignOut}\n disabled={!auth || signingOut}\n class=\"font-medium text-gray-600 underline-offset-2 hover:underline disabled:cursor-not-allowed disabled:opacity-60 focus:outline-none focus-visible:underline\"\n >\n {signingOut ? 'Signing out…' : 'Sign out'}\n </button>\n <Dot />\n <SupportLink onClick={onSupport} />\n </div>\n </div>\n );\n }\n\n return (\n <div class=\"mt-2 flex items-center justify-center gap-3 text-center text-xs text-gray-500\">\n <button\n type=\"button\"\n onClick={() => ctx.onAction('restore')}\n class=\"font-medium text-gray-600 underline-offset-2 hover:underline focus:outline-none focus-visible:underline\"\n >\n Restore purchases\n </button>\n <Dot />\n <SupportLink onClick={onSupport} />\n </div>\n );\n}\n\nfunction SupportLink({ onClick }: { onClick: () => void }) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n class=\"font-medium text-gray-600 underline-offset-2 hover:underline focus:outline-none focus-visible:underline\"\n >\n Contact Support\n </button>\n );\n}\n\nfunction Dot() {\n return <span class=\"h-1 w-1 rounded-full bg-gray-300\" aria-hidden=\"true\" />;\n}\n","import type { LayoutBlock } from '../../../core/types';\nimport type { BlockProps } from '../types';\n\ntype FeaturesListBlock = Extract<LayoutBlock, { type: 'features_list' }>;\n\nexport function FeaturesList({ block }: BlockProps<FeaturesListBlock>) {\n if (!block.items.length) return null;\n return (\n <ul class=\"flex flex-col gap-2.5\" role=\"list\">\n {block.items.map((item) => (\n <li key={item.id} class=\"flex items-start gap-3 text-sm text-gray-700\">\n <span\n class=\"mt-px flex h-5 w-5 flex-shrink-0 items-center justify-center rounded-full\"\n style={{\n background:\n 'color-mix(in srgb, var(--pw-accent) 12%, white)',\n color: 'var(--pw-accent)'\n }}\n aria-hidden=\"true\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 20 20\" fill=\"none\">\n <path\n d=\"M5 10l3 3 7-7\"\n stroke=\"currentColor\"\n stroke-width=\"2.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </span>\n <div class=\"flex flex-col gap-0.5\">\n <span class=\"font-medium leading-snug text-gray-900\">{item.name}</span>\n {item.desc ? (\n <span class=\"text-xs leading-relaxed text-gray-500\">{item.desc}</span>\n ) : null}\n </div>\n </li>\n ))}\n </ul>\n );\n}\n","import { useEffect, useRef } from 'preact/hooks';\nimport type { LayoutBlock } from '../../../core/types';\nimport type { BlockProps } from '../types';\n\ntype HeadingBlock = Extract<LayoutBlock, { type: 'heading' }>;\n\nconst BASE_FONT_PX = 26; // соответствует text-[1.625rem] у h1\nconst MIN_FONT_PX = 14;\nconst MAX_LINES = 2;\n\n// Авто-fit: если заголовок не вмещается в `MAX_LINES` строк при базовом размере,\n// уменьшаем font-size шагом 1px до тех пор, пока влезает или не упёрлись в\n// `MIN_FONT_PX`. Используется только для h1 — h2/h3 это подзаголовки, им\n// клиппинг не нужен. Считаем по реальной высоте элемента (scrollHeight) после\n// рендера — иначе пришлось бы держать canvas-измеритель.\nfunction fitHeading(el: HTMLElement, lineHeight: number): void {\n const maxHeight = lineHeight * MAX_LINES;\n let size = BASE_FONT_PX;\n el.style.fontSize = `${size}px`;\n while (el.scrollHeight > maxHeight && size > MIN_FONT_PX) {\n size -= 1;\n el.style.fontSize = `${size}px`;\n }\n}\n\nexport function Heading({ block, ctx }: BlockProps<HeadingBlock>) {\n const level = block.level ?? 1;\n const Tag = (`h${level}` as 'h1' | 'h2' | 'h3');\n const className =\n level === 1\n ? 'text-[1.625rem] font-semibold leading-tight text-gray-900 tracking-[-0.02em]'\n : level === 2\n ? 'text-xl font-semibold leading-snug text-gray-900 tracking-tight'\n : 'text-base font-medium text-gray-900';\n\n const ref = useRef<HTMLHeadingElement | null>(null);\n const autoFit = level === 1 && !!ctx.bootstrap.settings.title_auto_fit;\n\n useEffect(() => {\n if (!autoFit || !ref.current) return;\n // line-height у text-2xl = 1.5 (Tailwind дефолт). Считаем от текущего\n // computed line-height — устойчиво к будущим CSS-изменениям.\n const cs = getComputedStyle(ref.current);\n const lh = parseFloat(cs.lineHeight) || BASE_FONT_PX * 1.5;\n fitHeading(ref.current, lh);\n }, [autoFit, block.text]);\n\n return (\n <Tag ref={ref} class={className}>\n {block.text}\n </Tag>\n );\n}\n","import type { LayoutBlock, PaywallPrice } from '../../../core/types';\nimport type { BlockProps } from '../types';\n\ntype PriceGridBlock = Extract<LayoutBlock, { type: 'price_grid' }>;\n\nfunction formatPrice(price: PaywallPrice): string {\n const display = price.local ?? { currency: price.currency, amount: price.amount };\n try {\n return new Intl.NumberFormat(undefined, {\n style: 'currency',\n currency: display.currency,\n maximumFractionDigits: display.amount % 1 === 0 ? 0 : 2\n }).format(display.amount);\n } catch {\n return `${display.amount} ${display.currency}`;\n }\n}\n\nfunction intervalLabel(price: PaywallPrice): string {\n if (!price.interval || price.interval === 'lifetime') return 'one-time';\n const n = price.interval_count ?? 1;\n if (n === 1) return `per ${price.interval}`;\n return `every ${n} ${price.interval}s`;\n}\n\nexport function PriceGrid({ block, ctx }: BlockProps<PriceGridBlock>) {\n const filter = block.priceIds && block.priceIds.length > 0 ? new Set(block.priceIds) : null;\n const prices = ctx.bootstrap.prices.filter((p) => !filter || filter.has(p.id));\n\n if (prices.length === 0) {\n return <p class=\"text-sm text-gray-500\">No prices available.</p>;\n }\n\n const horizontal = block.view === 'horizontal';\n const popularLabel = block.popular_label ?? 'Most popular';\n\n // Horizontal раскладывает карточки в ряд через CSS grid. Кол-во колонок\n // = min(N,3) — задаём через inline-style, потому что Tailwind purge не\n // переживает runtime-конкатенацию `grid-cols-${N}`. Карточки в горизонтали\n // выкладывают цену снизу, не справа — иначе при N≥3 не хватает ширины.\n const cols = horizontal ? Math.min(prices.length, 3) : 1;\n\n return (\n <div\n class={horizontal ? 'grid gap-2.5' : 'flex flex-col gap-2.5'}\n style={horizontal ? { gridTemplateColumns: `repeat(${cols}, minmax(0, 1fr))` } : undefined}\n role=\"radiogroup\"\n aria-label=\"Plans\"\n >\n {prices.map((price) => {\n const selected = ctx.selectedPriceId === price.id;\n const isPopular = block.popular_price_id === price.id;\n return (\n <button\n key={price.id}\n type=\"button\"\n role=\"radio\"\n aria-checked={selected}\n onClick={() => {\n ctx.setSelectedPriceId(price.id);\n ctx.onAction('price_selected', { priceId: price.id, price });\n }}\n class={[\n 'group relative rounded-2xl border px-4 py-3.5 text-left transition-all duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--pw-accent)]',\n horizontal\n ? 'flex w-full flex-col items-start gap-1'\n : 'flex w-full items-center justify-between gap-3',\n selected\n ? 'border-[var(--pw-accent)] bg-[color-mix(in_srgb,var(--pw-accent)_6%,white)] shadow-[0_0_0_3px_color-mix(in_srgb,var(--pw-accent)_12%,transparent)]'\n : 'border-gray-200 bg-white hover:border-gray-300 hover:shadow-sm',\n isPopular ? 'mt-2.5' : ''\n ].join(' ')}\n >\n {isPopular ? (\n <span\n class=\"absolute -top-2.5 left-4 rounded-full px-2.5 py-0.5 text-[10px] font-semibold uppercase tracking-wider text-white shadow-sm\"\n style={{\n background:\n 'linear-gradient(135deg, var(--pw-accent), color-mix(in srgb, var(--pw-accent) 70%, black))'\n }}\n >\n {popularLabel}\n </span>\n ) : null}\n <div class={horizontal ? 'flex w-full items-start gap-2.5' : 'flex flex-1 items-start gap-2.5'}>\n <span\n class={[\n 'mt-0.5 flex h-4 w-4 flex-shrink-0 items-center justify-center rounded-full border transition-colors',\n selected\n ? 'border-[var(--pw-accent)] bg-[var(--pw-accent)]'\n : 'border-gray-300 bg-white group-hover:border-gray-400'\n ].join(' ')}\n aria-hidden=\"true\"\n >\n {selected ? <span class=\"h-1.5 w-1.5 rounded-full bg-white\" /> : null}\n </span>\n <div class=\"flex flex-col\">\n <span class=\"text-sm font-semibold text-gray-900\">{price.label ?? intervalLabel(price)}</span>\n {price.description ? (\n <span class=\"text-xs leading-relaxed text-gray-500\">{price.description}</span>\n ) : null}\n {price.trial_days ? (\n <span class=\"text-xs font-medium text-[var(--pw-accent)]\">\n {price.trial_days}-day free trial\n </span>\n ) : null}\n </div>\n </div>\n <div class={horizontal ? 'mt-1 flex flex-col items-start' : 'flex flex-col items-end'}>\n <span class=\"text-base font-semibold tracking-tight text-gray-900\">{formatPrice(price)}</span>\n <span class=\"text-[11px] text-gray-500\">{intervalLabel(price)}</span>\n </div>\n </button>\n );\n })}\n </div>\n );\n}\n","import type { LayoutBlock } from '../../../core/types';\nimport type { BlockProps } from '../types';\n\ntype TextBlock = Extract<LayoutBlock, { type: 'text' }>;\n\nexport function Text({ block }: BlockProps<TextBlock>) {\n return <p class=\"text-[0.9375rem] leading-relaxed text-gray-600\">{block.text}</p>;\n}\n","import type { LayoutBlock, PaywallPrice } from '../../../core/types';\nimport type { BlockProps } from '../types';\n\ntype TokenizationGateBlock = Extract<LayoutBlock, { type: 'tokenization_gate' }>;\n\nconst INTERVAL_MULTIPLIER: Record<string, number> = {\n week: 0.25,\n month: 1,\n year: 12\n};\n\nfunction intervalNoun(interval: PaywallPrice['interval']): string {\n if (!interval) return 'period';\n return interval;\n}\n\nexport function TokenizationGate({ block, ctx }: BlockProps<TokenizationGateBlock>) {\n if (!block.queries.length) return null;\n\n const selectedPrice = ctx.bootstrap.prices.find((p) => p.id === ctx.selectedPriceId);\n const interval = selectedPrice?.interval ?? null;\n const multiplier = interval ? INTERVAL_MULTIPLIER[interval] : undefined;\n\n return (\n <div class=\"flex flex-col gap-2\">\n <div class=\"text-sm font-semibold text-gray-800\">\n Included per <span>{intervalNoun(interval)}</span>:\n </div>\n <ul class=\"flex flex-col gap-2\" role=\"list\">\n {block.queries.map((q) => {\n const rawCount = Number.isFinite(q.count as number) ? (q.count as number) : 0;\n const amount =\n multiplier !== undefined ? Math.round(rawCount * multiplier) : rawCount;\n return (\n <li key={q.id} class={`flex gap-2 ${q.desc ? '' : 'items-center'}`}>\n <span\n class={`flex h-5 w-5 flex-shrink-0 items-center justify-center rounded-full ${q.desc ? 'mt-0.5' : ''}`}\n style={{\n background: 'color-mix(in srgb, var(--pw-accent) 12%, white)',\n color: 'var(--pw-accent)'\n }}\n aria-hidden=\"true\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 20 20\" fill=\"none\">\n <path\n d=\"M5 10l3 3 7-7\"\n stroke=\"currentColor\"\n stroke-width=\"2.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </span>\n <div>\n <span class=\"font-semibold text-gray-900 text-sm\">{amount}</span>{' '}\n <span class=\"text-sm text-gray-800\">{q.name}</span>\n {q.desc ? (\n <>\n <br />\n <span class=\"text-xs text-gray-500\">{q.desc}</span>\n </>\n ) : null}\n </div>\n </li>\n );\n })}\n </ul>\n </div>\n );\n}\n","import type { LayoutBlock } from '../../core/types';\nimport type { BlockComponent } from './types';\nimport { AuthPanel } from './blocks/AuthPanel';\nimport { CtaButton } from './blocks/CtaButton';\nimport { CurrentSession } from './blocks/CurrentSession';\nimport { FeaturesList } from './blocks/FeaturesList';\nimport { Heading } from './blocks/Heading';\nimport { PriceGrid } from './blocks/PriceGrid';\nimport { Text } from './blocks/Text';\nimport { TokenizationGate } from './blocks/TokenizationGate';\n\nexport const blockRegistry: Record<LayoutBlock['type'], BlockComponent<any>> = {\n heading: Heading,\n text: Text,\n price_grid: PriceGrid,\n cta_button: CtaButton,\n auth_panel: AuthPanel,\n current_session: CurrentSession,\n features_list: FeaturesList,\n tokenization_gate: TokenizationGate\n};\n","import { useMemo, useState } from 'preact/hooks';\nimport type { AuthClient, AuthSession } from '../../core/auth';\nimport type { Layout, PaywallBootstrap } from '../../core/types';\nimport { blockRegistry } from './registry';\nimport type { BlockContext } from './types';\n\nexport interface RendererProps {\n layout: Layout;\n bootstrap: PaywallBootstrap;\n onAction: (action: string, payload?: unknown) => void;\n auth?: AuthClient;\n authSession: AuthSession | null;\n}\n\nexport function Renderer({ layout, bootstrap, onAction, auth, authSession }: RendererProps) {\n const defaultPriceId = useMemo(() => bootstrap.prices[0]?.id ?? null, [bootstrap.prices]);\n const [selectedPriceId, setSelectedPriceId] = useState<string | null>(defaultPriceId);\n\n const ctx: BlockContext = {\n bootstrap,\n selectedPriceId,\n setSelectedPriceId,\n onAction,\n auth,\n authSession\n };\n\n return (\n <div class=\"flex flex-col gap-4\">\n {layout.blocks.map((block, i) => {\n const Cmp = blockRegistry[block.type];\n if (!Cmp) {\n if (typeof console !== 'undefined') {\n console.warn(`[paywall] unknown block type: ${block.type}`);\n }\n return null;\n }\n return <Cmp key={i} block={block as never} ctx={ctx} />;\n })}\n </div>\n );\n}\n","import { useEffect, useRef, useState } from 'preact/hooks';\nimport type { BillingClient } from '../core/BillingClient';\nimport type { AuthSession } from '../core/auth';\nimport type { LayoutBlock, PaywallBootstrap } from '../core/types';\nimport { PaywallError } from '../core/types';\nimport { Modal } from './Modal';\nimport { AuthGate } from './AuthGate';\nimport { AnonGate } from './AnonGate';\nimport { SupportGate } from './SupportGate';\nimport { Renderer } from './renderer/Renderer';\n\nexport type PaywallView = 'layout' | 'support' | 'auth' | 'anon';\n\n/**\n * Публичный snapshot состояния PaywallUI для host'а. Производится из internal\n * LoadState + GateState + open/purchased флагов. Каждое реальное изменение —\n * один call onState; повторы (`useSyncExternalStore`-friendly).\n */\nexport interface PaywallStateSnapshot {\n /** Модалка отрендерена и видна. False — closed (или ещё не открывалась). */\n open: boolean;\n /** Что показывается в модалке. null когда `open=false`. */\n view:\n | 'loading'\n | 'error'\n | 'layout'\n | 'auth'\n | 'anon'\n | 'support'\n | 'awaiting_payment'\n | 'popup_blocked'\n | 'purchased'\n | null;\n /** Заполнено только когда `view === 'error'`. */\n error: PaywallError | null;\n}\n\n// 'anon' — отдельная вью для signInAnonymously (silent resume / fresh signin).\n// Не сливается с 'auth', потому что UX разный: auth — формы email/oauth,\n// anon — спиннер без интеракции; и flow завершения тоже разный (анон не идёт\n// через has_active_subscription pre-check после signin'а).\n\nexport interface PaywallRootProps {\n client: BillingClient;\n open: boolean;\n onClose: () => void;\n onEvent: (event: string, payload?: unknown) => void;\n /** Какой view показать при open=true. 'support' стартует сразу с саппорт-формой,\n * Back/Done закрывают модалку (origin='standalone'). По умолчанию 'layout'. */\n initialView?: PaywallView;\n /** Server-confirmed покупка — показать success-вью с кнопкой Continue.\n * Управляется снаружи (PaywallUI выставляет true из watcher.onActive),\n * сбрасывается на open()/close(). Перебивает любые другие view. */\n purchased?: boolean;\n /** Renewal/upgrade flow. true — пропускаем все has_active_subscription\n * pre-check'и (bootstrap-time + post-auth), и при checkout передаём\n * `ignoreActivePurchase: true` на бэк, чтобы /start-checkout не вернул\n * 409 для уже подписанного юзера. См. OpenOptions.renew. */\n renew?: boolean;\n /** Публичный state-machine notify. PaywallUI прокидывает сюда колбек, который\n * кэширует snapshot и эмитит свой `onStateChange`. Если не передан —\n * state-tracking отключён (нет оверхеда для host'ов, которым не нужно). */\n onState?: (snapshot: PaywallStateSnapshot) => void;\n}\n\ntype LoadState =\n | { status: 'idle' }\n | { status: 'loading' }\n | { status: 'ready'; data: PaywallBootstrap }\n | { status: 'error'; error: PaywallError };\n\ntype GateState =\n | { kind: 'layout' }\n // pendingCheckout=undefined, origin='layout' — gate открыт через \"Restore purchases\"\n // (без последующего checkout-а), после signIn схлопываемся в layout. С\n // pendingCheckout — gate открыт по preauth-flow от cta_button и после signIn\n // auto-resume createCheckout. origin='standalone' — paywall.openAuth(): модалка\n // открыта только для логина, после signIn / Back закрываем модалку, layout\n // вообще не показываем.\n | {\n kind: 'auth_gate';\n pendingCheckout?: { priceId: string };\n origin?: 'layout' | 'standalone';\n }\n // origin='standalone' — paywall.openAnonGate(): модалка открыта только ради\n // анонимного логина, после signin'а закрываем модалку. origin='layout' — пока\n // не используется (анон-блока в layout нет), но оставляем для симметрии с\n // auth_gate на случай будущего inline-варианта.\n | {\n kind: 'anon_gate';\n origin: 'layout' | 'standalone';\n }\n // origin='layout' — пришли из current_session-блока, Back возвращает в layout.\n // origin='standalone' — модалка открыта только для саппорта (paywall.openSupport()),\n // Back закрывает модалку.\n | { kind: 'support'; origin: 'layout' | 'standalone' }\n // window.open отдал handle — checkout открылся в новой вкладке. Пейвол остаётся\n // как индикатор: «оплати в той вкладке». priceId храним, чтобы кнопка retry\n // могла пересоздать checkout (URL'ы у Stripe/Paddle expire'ятся). url храним,\n // чтобы fallback-ссылка «Didn't open? Click here» переоткрывала тот же URL без\n // повторного похода в createCheckout — нужно для случая когда window.open\n // отдал handle, но реально таб заблокирован (агрессивные мобильные блокеры).\n | { kind: 'awaiting_payment'; priceId: string; url: string }\n // window.open вернул null — попап заблокирован (бывает после async-резюма\n // post-auth, когда transient activation истёк). НЕ редиректим текущую вкладку:\n // пейвол должен остаться. URL уже выписан — кнопка «Open checkout» дёрнет\n // window.open под фреш-гестуром, без второго похода в createCheckout.\n | { kind: 'popup_blocked'; priceId: string; url: string }\n // Юзер уже залогинен и has_active_subscription — показываем success-view.\n // Срабатывает либо после auth-resume (поллим getUser сразу после signIn),\n // либо когда /start-checkout вернул 409 hasActivePurchase. restored=true\n // меняет текст PurchaseSuccessView на «Subscription restored».\n | { kind: 'purchase_success'; restored: boolean }\n // После signIn ждём getUser({force:true}), пока не узнаем есть ли уже\n // active subscription. Без этого промежуточного state'а юзер видит\n // несколько секунд auth_gate'овский «серый экран» с уже скрытой формой.\n | { kind: 'verifying' };\n\ntype AuthPanelBlock = Extract<LayoutBlock, { type: 'auth_panel' }>;\n\nfunction computePaywallSnapshot(\n open: boolean,\n state: LoadState,\n gate: GateState,\n purchased: boolean | undefined\n): PaywallStateSnapshot {\n if (!open) return { open: false, view: null, error: null };\n if (purchased) return { open: true, view: 'purchased', error: null };\n if (state.status === 'idle' || state.status === 'loading') {\n return { open: true, view: 'loading', error: null };\n }\n if (state.status === 'error') {\n return { open: true, view: 'error', error: state.error };\n }\n if (gate.kind === 'support') return { open: true, view: 'support', error: null };\n if (gate.kind === 'auth_gate') return { open: true, view: 'auth', error: null };\n if (gate.kind === 'anon_gate') return { open: true, view: 'anon', error: null };\n if (gate.kind === 'awaiting_payment') {\n return { open: true, view: 'awaiting_payment', error: null };\n }\n if (gate.kind === 'popup_blocked') {\n return { open: true, view: 'popup_blocked', error: null };\n }\n if (gate.kind === 'purchase_success') {\n return { open: true, view: 'purchased', error: null };\n }\n if (gate.kind === 'verifying') {\n return { open: true, view: 'loading', error: null };\n }\n return { open: true, view: 'layout', error: null };\n}\n\nfunction sameSnapshot(a: PaywallStateSnapshot, b: PaywallStateSnapshot): boolean {\n return a.open === b.open && a.view === b.view && a.error === b.error;\n}\n\nexport function PaywallRoot({\n client,\n open,\n onClose,\n onEvent,\n initialView,\n purchased,\n renew,\n onState\n}: PaywallRootProps) {\n const [state, setState] = useState<LoadState>({ status: 'idle' });\n // session держим в state, чтобы блоки (auth_panel) ре-рендерились на login/logout.\n // Без этого AuthPanel прочитал бы snapshot один раз и не схлопнулся бы после\n // успешного signin'а.\n const [authSession, setAuthSession] = useState<AuthSession | null>(\n () => client.auth?.getCachedSession() ?? null\n );\n const [gate, setGate] = useState<GateState>(() => {\n if (initialView === 'support') return { kind: 'support', origin: 'standalone' };\n if (initialView === 'auth') return { kind: 'auth_gate', origin: 'standalone' };\n if (initialView === 'anon') return { kind: 'anon_gate', origin: 'standalone' };\n return { kind: 'layout' };\n });\n // Защита от двойного auto-resume: useEffect ниже зависит от authSession,\n // и подписка onAuthChange может прислать одну и ту же сессию повторно\n // (refresh) — без флага мы дважды дёрнем createCheckout.\n const resumingRef = useRef(false);\n\n // State-machine bridge: эмитим snapshot когда меняется любое из\n // (open, state, gate, purchased). sameSnapshot гасит no-op'ы — например\n // переход loading→error меняет state.status, но если мы уже в error-вью\n // (иначе невозможно), эмит не повторится.\n const lastSnapshotRef = useRef<PaywallStateSnapshot | null>(null);\n useEffect(() => {\n if (!onState) return;\n const next = computePaywallSnapshot(open, state, gate, purchased);\n const prev = lastSnapshotRef.current;\n if (prev && sameSnapshot(prev, next)) return;\n lastSnapshotRef.current = next;\n onState(next);\n }, [open, state, gate, purchased, onState]);\n\n useEffect(() => {\n if (!client.auth) return;\n return client.auth.onAuthChange((s) => setAuthSession(s));\n }, [client.auth]);\n\n useEffect(() => {\n if (!open) return;\n if (state.status === 'ready' || state.status === 'loading') return;\n\n let cancelled = false;\n setState({ status: 'loading' });\n client\n .bootstrap()\n .then((data) => {\n if (cancelled) return;\n setState({ status: 'ready', data });\n onEvent('ready', data);\n // Юзер уже подписан — host вызвал open() вслепую (без getAccess pre-check'а),\n // или просто из popup'а «Open paywall». Не показываем тарифы — переключаемся\n // в restored success-view. Эмитим purchase_completed чтобы host получил\n // согласованный сигнал, как из любых других путей (UserWatcher, 409 в\n // checkout, auth-resume). renew=true пропускает эту проверку — host явно\n // показывает «Renew»/«Upgrade», тарифы должны быть видны.\n if (data.user?.has_active_subscription && !renew) {\n onEvent('purchase_completed', {\n priceId: null,\n sessionId: null,\n restored: true\n });\n setGate({ kind: 'purchase_success', restored: true });\n }\n })\n .catch((error: unknown) => {\n if (cancelled) return;\n const err =\n error instanceof PaywallError\n ? error\n : new PaywallError('unknown', 'Failed to load paywall', { cause: error });\n setState({ status: 'error', error: err });\n onEvent('error', err);\n });\n return () => {\n cancelled = true;\n };\n }, [open, client]);\n\n // Закрытие/повторное открытие модалки сбрасывает gate. Standalone-flows\n // (openSupport / openAuth) PaywallUI вызывает на уже смонтированном компоненте\n // через handle.update({initialView: 'support'|'auth'}) — useState-initializer\n // отрабатывает только на первом mount'е, поэтому без этого useEffect'а gate\n // оставался бы 'layout' (с тарифами) при последующих standalone open'ах.\n useEffect(() => {\n if (!open) {\n setGate({ kind: 'layout' });\n resumingRef.current = false;\n return;\n }\n if (initialView === 'support') {\n setGate({ kind: 'support', origin: 'standalone' });\n } else if (initialView === 'auth') {\n setGate({ kind: 'auth_gate', origin: 'standalone' });\n } else if (initialView === 'anon') {\n setGate({ kind: 'anon_gate', origin: 'standalone' });\n }\n }, [open, initialView]);\n\n const runCheckout = async (priceId: string) => {\n try {\n const result = await client.createCheckout({\n priceId,\n ignoreActivePurchase: renew === true\n });\n onEvent('checkout_started', { priceId, url: result.url, acquiring: result.acquiring });\n if (typeof window === 'undefined' || !result.url) return;\n // Без `noopener,noreferrer` в фичах: эти флаги заставляют window.open\n // ВСЕГДА вернуть null (даже когда попап реально открылся), и мы не\n // могли отличить «успех» от «заблокирован». Severance делаем вручную\n // через popup.opener=null после успеха — на checkout-домене (Stripe/\n // Paddle) opener-доступ всё равно cross-origin-restricted, но явный\n // null безопаснее.\n const popup = window.open(result.url, '_blank');\n if (popup) {\n try {\n popup.opener = null;\n } catch {\n /* cross-origin already — ok */\n }\n setGate({ kind: 'awaiting_payment', priceId, url: result.url });\n } else {\n // Попап заблокирован — обычно из-за stale transient activation\n // (auto-resume после async signin). НЕ уносим юзера через\n // location.assign: пейвол должен остаться открытым. Показываем\n // inline retry; клик по кнопке — fresh gesture, попап откроется.\n setGate({ kind: 'popup_blocked', priceId, url: result.url });\n }\n } catch (error) {\n // 409 hasActivePurchase от бэка — это не ошибка чекаута, это «у юзера\n // уже есть активная подписка». Освежаем cache (host'овский userChange\n // должен увидеть has_active_subscription=true), эмитим purchase_completed\n // с restored=true и переключаемся в success-view. Не setGate в layout,\n // не onEvent('error') — иначе host увидит false-negative.\n if (error instanceof PaywallError && error.code === 'already_purchased') {\n try {\n await client.getUser({ force: true });\n } catch {\n /* offline / 401 — host'у getUser сам отрапортует, тут это не блокирует success-view */\n }\n onEvent('purchase_completed', { priceId, sessionId: null, restored: true });\n setGate({ kind: 'purchase_success', restored: true });\n return;\n }\n const err =\n error instanceof PaywallError\n ? error\n : new PaywallError('checkout_failed', 'Checkout failed', { cause: error });\n onEvent('error', err);\n // На ошибке возвращаем юзера в layout — иначе застрянем в auth_gate\n // (если пришли через preauth-flow) с уже залогиненной сессией.\n setGate({ kind: 'layout' });\n }\n };\n\n const reopenCheckout = (priceId: string, url: string) => {\n if (typeof window === 'undefined') return;\n const popup = window.open(url, '_blank');\n if (popup) {\n try {\n popup.opener = null;\n } catch {\n /* ignore */\n }\n setGate({ kind: 'awaiting_payment', priceId, url });\n }\n // Если и сейчас null — оставляем popup_blocked, юзер кликнет ещё раз.\n };\n\n // Auto-resume: появилась сессия в открытом gate → продолжаем флоу.\n // Pending preauth-checkout — НЕ схлопываем gate в layout до runCheckout:\n // иначе юзер видит мигание тарифов между submit'ом auth-формы и открытием\n // чекаут-вкладки. runCheckout сам переведёт gate в awaiting_payment /\n // popup_blocked / layout (на ошибке). Restore-flow без pendingCheckout —\n // просто возвращаемся в layout. resumingRef защищает от повторного запуска,\n // если authChange сработает несколько раз за один gate-цикл (refresh).\n useEffect(() => {\n if (gate.kind !== 'auth_gate') return;\n // Анон-сессия не считается логином: юзер пришёл в auth_gate реально\n // залогиниться. Иначе openAuth() при существующем анон-токене мгновенно\n // закрывал бы модалку через auto-resume, и формы он бы не увидел.\n if (!authSession || authSession.user.is_anonymous) return;\n if (resumingRef.current) return;\n resumingRef.current = true;\n const pending = gate.pendingCheckout;\n const origin = gate.origin;\n // Сразу переключаемся в verifying — иначе модалка висит в auth_gate с\n // уже залогиненным юзером (~3с пока getUser ходит к бэку), и юзер видит\n // «пустой серый экран» вместо progress'а. Loader честнее показывает что\n // SDK что-то делает.\n setGate({ kind: 'verifying' });\n void (async () => {\n // Прежде чем продолжать flow (runCheckout / возврат в layout / закрытие\n // модалки), проверяем — может, у юзера уже есть active subscription.\n // Сценарии: Restore-кнопка (он уже платил с другого аккаунта); preauth\n // signIn (юзер вспомнил, что подписка есть); standalone openAuth.\n // Без этой проверки юзер увидел бы тарифы и кликнул Buy → 409 от бэка\n // → fallback на already_purchased. Лучше не давать ему этот шаг.\n // renew=true пропускает проверку — host явно делает renewal-flow.\n if (!renew) {\n try {\n const user = await client.getUser({ force: true });\n if (user.has_active_subscription) {\n onEvent('purchase_completed', {\n priceId: pending?.priceId ?? null,\n sessionId: null,\n restored: true\n });\n setGate({ kind: 'purchase_success', restored: true });\n return;\n }\n } catch {\n /* getUser упал — продолжаем обычный flow, юзер увидит тарифы */\n }\n }\n if (!pending) {\n // openAuth standalone: после signIn закрываем модалку, layout не показываем.\n // Restore-flow (origin='layout' или undefined): возвращаемся в layout.\n if (origin === 'standalone') {\n onClose();\n } else {\n setGate({ kind: 'layout' });\n }\n return;\n }\n await runCheckout(pending.priceId);\n })().finally(() => {\n resumingRef.current = false;\n });\n }, [authSession, gate]);\n\n const handleAction = async (action: string, payload?: unknown) => {\n if (action === 'close') {\n onClose();\n return;\n }\n if (action === 'price_selected') {\n // Пробрасываем как есть — блок уже собрал { priceId, price }.\n onEvent('price_selected', payload);\n return;\n }\n if (action === 'restore') {\n // CurrentSession-блок: гость кликнул \"Restore purchases\". Открываем\n // gate без pendingCheckout — после signIn auto-resume просто схлопнётся.\n // Без AuthClient'а ничего не делаем (managed-auth не подключён).\n if (!client.auth) return;\n if (client.auth.getCachedSession()) return;\n setGate({ kind: 'auth_gate' });\n return;\n }\n if (action === 'support') {\n // CurrentSession-блок: открыть саппорт-форму. Видна и гостю, и залогиненному.\n // Из layout — Back возвращает к тарифам.\n setGate({ kind: 'support', origin: 'layout' });\n return;\n }\n if (action === 'checkout' && state.status === 'ready') {\n const priceId = (payload as { priceId?: string } | undefined)?.priceId;\n if (!priceId) {\n onEvent('error', new PaywallError('no_price', 'No price selected'));\n return;\n }\n const mode = state.data.settings.checkout_mode ?? 'guest';\n const needsAuth = mode === 'preauth' && !!client.auth && !client.auth.getCachedSession();\n if (needsAuth) {\n setGate({ kind: 'auth_gate', pendingCheckout: { priceId } });\n return;\n }\n await runCheckout(priceId);\n }\n };\n\n const brand = state.status === 'ready' ? state.data.settings.brand_color : null;\n const testMode = state.status === 'ready' ? !!state.data.settings.is_test_mode : false;\n // allow_close=undefined трактуем как true (default до bootstrap'а — пейвол\n // должен быть закрываемым во время loading/error, иначе юзера запрёт). После\n // ready settings.allow_close=false запретит ESC/overlay/крестик.\n const allowClose =\n state.status === 'ready' ? state.data.settings.allow_close !== false : true;\n\n const gateBlock: AuthPanelBlock = {\n type: 'auth_panel',\n heading: 'Sign in to continue',\n allow_signup: true,\n allow_password_reset: true,\n // Не скрываем при наличии сессии — auto-resume useEffect отрабатывает быстрее,\n // чем хотим показывать \"Signed in as ...\" промежуточным экраном.\n hide_when_authenticated: false,\n providers: state.status === 'ready' ? state.data.settings.auth_providers : undefined\n };\n\n // Support-view имеет приоритет над bootstrap-state: standalone-открытие\n // (paywall.openSupport()) должно работать даже если bootstrap ещё грузится\n // или упал — сама форма от settings/prices не зависит. Из layout-режима\n // Back возвращает к тарифам, из standalone — закрывает модалку.\n const supportView =\n gate.kind === 'support' ? (\n <SupportGate\n client={client}\n authSession={authSession}\n origin={gate.origin}\n onBack={() => {\n if (gate.origin === 'standalone') onClose();\n else setGate({ kind: 'layout' });\n }}\n />\n ) : null;\n\n return (\n <Modal\n open={open}\n onClose={onClose}\n brandColor={brand}\n testMode={testMode}\n allowClose={allowClose}\n labelledBy=\"pw-title\"\n >\n {purchased ? (\n <PurchaseSuccessView onContinue={onClose} />\n ) : gate.kind === 'purchase_success' ? (\n <PurchaseSuccessView restored={gate.restored} onContinue={onClose} />\n ) : supportView ? (\n supportView\n ) : state.status === 'loading' || state.status === 'idle' || gate.kind === 'verifying' ? (\n <div class=\"flex flex-col items-center justify-center gap-3 py-12\">\n <span class=\"inline-block h-7 w-7 animate-spin rounded-full border-[2.5px] border-gray-200 border-t-[var(--pw-accent)]\" />\n <span class=\"text-xs font-medium tracking-wide text-gray-500\">\n {gate.kind === 'verifying' ? 'Checking your subscription…' : 'Loading…'}\n </span>\n </div>\n ) : state.status === 'error' ? (\n <div class=\"flex flex-col items-center gap-2 py-8 text-center\">\n <div class=\"flex h-11 w-11 items-center justify-center rounded-full bg-red-50\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"M10 6v5M10 14h.01\"\n stroke=\"#dc2626\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n />\n <circle cx=\"10\" cy=\"10\" r=\"8\" stroke=\"#dc2626\" stroke-width=\"1.75\" />\n </svg>\n </div>\n <p class=\"text-sm font-semibold tracking-tight text-gray-900\">Something went wrong</p>\n <p class=\"text-xs leading-relaxed text-gray-500\">{state.error.message}</p>\n </div>\n ) : gate.kind === 'auth_gate' && client.auth ? (\n <AuthGate\n block={gateBlock}\n bootstrap={state.data}\n auth={client.auth}\n authSession={authSession}\n // standalone (paywall.openAuth()) — модалка открыта только ради\n // signin'а, Back-кнопка дублирует ESC/X. Скрываем. Для preauth/\n // restore-flow Back ведёт обратно в layout — оставляем.\n showBack={gate.origin !== 'standalone'}\n onBack={() => {\n if (gate.origin === 'standalone') onClose();\n else setGate({ kind: 'layout' });\n }}\n />\n ) : gate.kind === 'anon_gate' && client.auth ? (\n <AnonGate\n auth={client.auth}\n // standalone — pure anon-flow (paywall.openAnonGate()). Закрываем\n // модалку после signin'а: host подцепит свежую session через\n // onAuthChange/onUserChange. Для layout-варианта возвращаемся в\n // тарифы; pendingCheckout анону не выписываем (анон по дизайну\n // не покупает — он юзает api-gateway без email).\n onSuccess={() => {\n if (gate.origin === 'standalone') onClose();\n else setGate({ kind: 'layout' });\n }}\n onBack={\n gate.origin === 'standalone'\n ? undefined\n : () => setGate({ kind: 'layout' })\n }\n />\n ) : gate.kind === 'awaiting_payment' ? (\n <AwaitingPaymentView\n client={client}\n onBack={() => setGate({ kind: 'layout' })}\n onReopen={() => {\n if (typeof window === 'undefined') return;\n const popup = window.open(gate.url, '_blank');\n if (popup) {\n try {\n popup.opener = null;\n } catch {\n /* ignore */\n }\n }\n }}\n onRetry={() => runCheckout(gate.priceId)}\n />\n ) : gate.kind === 'popup_blocked' ? (\n <div class=\"flex flex-col items-center gap-3 py-8 text-center\">\n <div\n class=\"flex h-11 w-11 items-center justify-center rounded-full\"\n style={{ background: 'color-mix(in srgb, var(--pw-accent) 12%, white)', color: 'var(--pw-accent)' }}\n aria-hidden=\"true\"\n >\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\">\n <path\n d=\"M4 5h12v10H4z\"\n stroke=\"currentColor\"\n stroke-width=\"1.75\"\n stroke-linejoin=\"round\"\n />\n <path d=\"M7 9l3 3 4-5\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </div>\n <p class=\"text-sm font-semibold tracking-tight text-gray-900\">Allow popups to continue</p>\n <p class=\"max-w-[18rem] text-xs leading-relaxed text-gray-500\">\n Your browser blocked the checkout tab. Click below to open it.\n </p>\n <button\n type=\"button\"\n onClick={() => reopenCheckout(gate.priceId, gate.url)}\n class=\"mt-1 rounded-xl px-4 py-2 text-xs font-semibold text-white transition-all hover:-translate-y-px hover:brightness-105 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--pw-accent)]\"\n style={{\n background:\n 'linear-gradient(180deg, color-mix(in srgb, var(--pw-accent) 92%, white), var(--pw-accent))',\n boxShadow:\n '0 1px 2px rgba(15,23,42,0.08), 0 6px 14px -4px color-mix(in srgb, var(--pw-accent) 50%, transparent)'\n }}\n >\n Open checkout\n </button>\n </div>\n ) : (\n <Renderer\n layout={state.data.layout!}\n bootstrap={state.data}\n onAction={handleAction}\n auth={client.auth}\n authSession={authSession}\n />\n )}\n </Modal>\n );\n}\n\n// Экран ожидания после window.open(checkoutUrl). UserWatcher в PaywallUI уже\n// poll'ит user-state раз в 5s (visible вкладка) — этот экран только UI-обёртка.\n//\n// «I've paid» — для нетерпеливых: форсим getUser({force:true}), чтобы cache\n// обновился сразу, и постим внутрь окна 'paywall_purchase' message — этого\n// ждёт UserWatcher.handleMessage и сразу же тригерит свой check(). Если\n// подписка ещё не активна (webhook не дошёл), показываем inline-таймаут на 5s.\n//\n// «Open checkout again» — fallback для случая «window.open отдал handle, но таб\n// заблокирован» (агрессивные мобильные блокеры). Дёргает existing URL без\n// похода в createCheckout, не сбивая awaiting_payment state.\n//\n// «Tab closed? Try again» — крайний случай: URL у Stripe/Paddle/etc. может\n// expire'нуться, поэтому пересоздаём checkout. Менее prominent кнопка.\nfunction AwaitingPaymentView({\n client,\n onBack,\n onReopen,\n onRetry\n}: {\n client: BillingClient;\n onBack: () => void;\n onReopen: () => void;\n onRetry: () => void;\n}) {\n const [checking, setChecking] = useState(false);\n const [stillPending, setStillPending] = useState(false);\n const stillPendingTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n return () => {\n if (stillPendingTimerRef.current !== null) {\n clearTimeout(stillPendingTimerRef.current);\n }\n };\n }, []);\n\n const handleVerify = async () => {\n if (checking) return;\n setChecking(true);\n setStillPending(false);\n try {\n const user = await client.getUser({ force: true });\n if (user.has_active_subscription) {\n // Будит UserWatcher — он сразу же сделает check(), увидит fresh active\n // user из cache и эмитит purchase_completed (PaywallUI переведёт в\n // PurchaseSuccessView). Не эмитим purchase_completed напрямую отсюда —\n // single source of truth остаётся в watcher.onActive.\n if (typeof window !== 'undefined') {\n window.postMessage({ type: 'paywall_purchase' }, '*');\n }\n return;\n }\n // Webhook ещё не дошёл — показываем подсказку и через 5s сворачиваем,\n // чтобы юзер мог нажать ещё раз. setTimeout cancel'нется на unmount.\n setStillPending(true);\n if (stillPendingTimerRef.current !== null) {\n clearTimeout(stillPendingTimerRef.current);\n }\n stillPendingTimerRef.current = setTimeout(() => {\n setStillPending(false);\n stillPendingTimerRef.current = null;\n }, 5000);\n } catch {\n setStillPending(true);\n } finally {\n setChecking(false);\n }\n };\n\n return (\n <div class=\"flex flex-col gap-3\">\n <button\n type=\"button\"\n onClick={onBack}\n class=\"-ml-1 self-start rounded-md px-1.5 py-0.5 text-xs font-medium text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--pw-accent)]\"\n >\n ← Back\n </button>\n <div class=\"flex flex-col items-center gap-3 py-6 text-center\">\n <div class=\"relative flex h-12 w-12 items-center justify-center\">\n <span\n class=\"absolute inset-0 animate-ping rounded-full opacity-40\"\n style={{ background: 'color-mix(in srgb, var(--pw-accent) 30%, transparent)' }}\n aria-hidden=\"true\"\n />\n <span class=\"relative inline-block h-7 w-7 animate-spin rounded-full border-[2.5px] border-gray-200 border-t-[var(--pw-accent)]\" />\n </div>\n <p class=\"text-sm font-semibold tracking-tight text-gray-900\">Complete payment in the new tab</p>\n <p class=\"max-w-[20rem] text-xs leading-relaxed text-gray-500\">\n We'll detect your payment automatically — or click below once you're done.\n </p>\n <button\n type=\"button\"\n onClick={handleVerify}\n disabled={checking}\n class=\"mt-1 rounded-xl px-5 py-2.5 text-sm font-semibold text-white transition-all hover:-translate-y-px hover:brightness-105 disabled:cursor-not-allowed disabled:opacity-60 disabled:hover:translate-y-0 disabled:hover:brightness-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--pw-accent)]\"\n style={{\n background:\n 'linear-gradient(180deg, color-mix(in srgb, var(--pw-accent) 92%, white), var(--pw-accent))',\n boxShadow:\n '0 1px 2px rgba(15,23,42,0.08), 0 6px 14px -4px color-mix(in srgb, var(--pw-accent) 50%, transparent)'\n }}\n >\n {checking ? 'Checking…' : \"I've paid\"}\n </button>\n {stillPending ? (\n <p class=\"text-xs leading-relaxed text-gray-500\">\n Payment is still being processed. Please try again in a moment.\n </p>\n ) : null}\n </div>\n <div class=\"rounded-2xl border border-gray-200 bg-gray-50/60 p-3.5\">\n <p class=\"text-xs leading-relaxed text-gray-600\">\n Checkout window didn't open or got blocked? Click here to open it again.\n </p>\n <button\n type=\"button\"\n onClick={onReopen}\n class=\"mt-2.5 w-full rounded-xl border border-gray-200 bg-white px-3 py-2 text-xs font-semibold text-gray-700 transition-colors hover:border-gray-300 hover:bg-gray-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--pw-accent)]\"\n >\n Open checkout again\n </button>\n </div>\n <button\n type=\"button\"\n onClick={onRetry}\n class=\"self-center rounded-md px-2 py-1 text-xs text-gray-500 underline-offset-2 hover:text-gray-900 hover:underline focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--pw-accent)]\"\n >\n Tab closed? Try again\n </button>\n </div>\n );\n}\n\nfunction PurchaseSuccessView({\n onContinue,\n restored = false\n}: {\n onContinue: () => void;\n /** true — у юзера уже была активная подписка на момент попытки checkout\n * (или после signIn выяснилось, что подписка есть). Меняет heading на\n * «Subscription restored» — без этого юзер думает, что только что\n * оплатил. */\n restored?: boolean;\n}) {\n return (\n <div class=\"flex flex-col items-center gap-3 py-8 text-center\">\n <div\n class=\"flex h-14 w-14 items-center justify-center rounded-full ring-8\"\n style={{\n background: 'linear-gradient(135deg, #4ade80, #16a34a)',\n color: '#fff',\n // emerald ring with low alpha for a halo effect\n boxShadow: '0 0 0 8px rgba(74,222,128,0.12), 0 8px 20px -6px rgba(22,163,74,0.45)'\n }}\n aria-hidden=\"true\"\n >\n <svg width=\"28\" height=\"28\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path\n d=\"M5 13l4 4L19 7\"\n stroke=\"currentColor\"\n stroke-width=\"2.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </div>\n <p id=\"pw-title\" class=\"mt-1 text-lg font-semibold tracking-tight text-gray-900\">\n {restored ? 'Subscription restored' : 'Payment received'}\n </p>\n <p class=\"text-sm leading-relaxed text-gray-500\">\n {restored\n ? 'Welcome back — your subscription is already active.'\n : 'Your subscription is now active.'}\n </p>\n <button\n type=\"button\"\n onClick={onContinue}\n class=\"mt-3 rounded-xl px-5 py-2.5 text-sm font-semibold text-white transition-all hover:-translate-y-px hover:brightness-105 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--pw-accent)]\"\n style={{\n background:\n 'linear-gradient(180deg, color-mix(in srgb, var(--pw-accent) 92%, white), var(--pw-accent))',\n boxShadow:\n '0 1px 2px rgba(15,23,42,0.08), 0 8px 20px -6px color-mix(in srgb, var(--pw-accent) 50%, transparent)'\n }}\n >\n Continue\n </button>\n </div>\n );\n}\n","import type { BillingClient } from '../core/BillingClient';\nimport type { PaywallUser } from '../core/types';\n\n// Параметры по умолчанию подобраны под \"юзер платит ~60-90с после клика\n// Continue, иногда ходит за чашкой 5-10 минут\". См. обсуждение в TODO.md\n// (фаза \"Что это меняет в архитектуре\").\nexport interface UserWatcherOptions {\n client: BillingClient;\n /** Дёрнут, когда впервые увидели has_active_subscription === true. */\n onActive: (user: PaywallUser) => void;\n /** Полный таймаут наблюдения. По истечении — стоп без onActive. */\n onTimeout?: () => void;\n timeoutMs?: number;\n /** Интервал polling, когда вкладка видимая. */\n visibleIntervalMs?: number;\n /** Интервал polling, когда вкладка скрыта (браузер троттлит таймеры). */\n hiddenIntervalMs?: number;\n}\n\nconst DEFAULT_TIMEOUT_MS = 10 * 60_000;\nconst DEFAULT_VISIBLE_INTERVAL_MS = 5_000;\nconst DEFAULT_HIDDEN_INTERVAL_MS = 30_000;\n\n// Polling после checkout_started.\n//\n// Источники сигнала \"проверить сейчас\":\n// 1. visibility change → visible (юзер вернулся в исходную вкладку).\n// 2. window focus.\n// 3. postMessage вида { type: 'paywall_purchase' } от success-страницы\n// (acceleration: success_url на нашем origin делает window.opener.postMessage).\n// 4. Регулярный таймер с visibility-aware расписанием.\n//\n// Стоп: либо has_active_subscription === true, либо таймаут.\n//\n// Runtime detection: см. shouldRunUserWatcher() — extension popup отбрасывается,\n// он не доживает до возврата с checkout. Background service worker отсекается\n// проверкой `typeof document` в start().\nexport class UserWatcher {\n private opts: Required<Omit<UserWatcherOptions, 'client'>> & { client: BillingClient };\n private timer: ReturnType<typeof setTimeout> | null = null;\n private timeoutTimer: ReturnType<typeof setTimeout> | null = null;\n private visibilityHandler: (() => void) | null = null;\n private focusHandler: (() => void) | null = null;\n private messageHandler: ((e: MessageEvent) => void) | null = null;\n private stopped = false;\n private checking = false;\n\n constructor(opts: UserWatcherOptions) {\n this.opts = {\n client: opts.client,\n onActive: opts.onActive,\n onTimeout: opts.onTimeout ?? (() => {}),\n timeoutMs: opts.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n visibleIntervalMs: opts.visibleIntervalMs ?? DEFAULT_VISIBLE_INTERVAL_MS,\n hiddenIntervalMs: opts.hiddenIntervalMs ?? DEFAULT_HIDDEN_INTERVAL_MS\n };\n }\n\n start(): void {\n if (this.stopped) return;\n if (typeof document === 'undefined' || typeof window === 'undefined') return;\n\n void this.check();\n this.scheduleNext();\n\n this.visibilityHandler = () => this.handleVisibilityChange();\n document.addEventListener('visibilitychange', this.visibilityHandler);\n\n this.focusHandler = () => void this.check();\n window.addEventListener('focus', this.focusHandler);\n\n this.messageHandler = (e: MessageEvent) => this.handleMessage(e);\n window.addEventListener('message', this.messageHandler);\n\n this.timeoutTimer = setTimeout(() => {\n if (this.stopped) return;\n this.stop();\n this.opts.onTimeout();\n }, this.opts.timeoutMs);\n }\n\n stop(): void {\n this.stopped = true;\n if (this.timer !== null) clearTimeout(this.timer);\n this.timer = null;\n if (this.timeoutTimer !== null) clearTimeout(this.timeoutTimer);\n this.timeoutTimer = null;\n if (typeof document !== 'undefined' && this.visibilityHandler) {\n document.removeEventListener('visibilitychange', this.visibilityHandler);\n }\n if (typeof window !== 'undefined') {\n if (this.focusHandler) window.removeEventListener('focus', this.focusHandler);\n if (this.messageHandler) window.removeEventListener('message', this.messageHandler);\n }\n this.visibilityHandler = null;\n this.focusHandler = null;\n this.messageHandler = null;\n }\n\n private async check(): Promise<void> {\n if (this.stopped || this.checking) return;\n this.checking = true;\n try {\n const user = await this.opts.client.getUser({ force: true });\n if (this.stopped) return;\n if (user.has_active_subscription) {\n this.stop();\n this.opts.onActive(user);\n }\n } catch {\n /* транзиентные ошибки — пропустим один тик, поллер дёрнет ещё */\n } finally {\n this.checking = false;\n }\n }\n\n private scheduleNext(): void {\n if (this.stopped) return;\n const visible =\n typeof document !== 'undefined' && document.visibilityState === 'visible';\n const interval = visible\n ? this.opts.visibleIntervalMs\n : this.opts.hiddenIntervalMs;\n this.timer = setTimeout(async () => {\n await this.check();\n this.scheduleNext();\n }, interval);\n }\n\n private handleVisibilityChange(): void {\n if (typeof document === 'undefined') return;\n if (document.visibilityState === 'visible') void this.check();\n // Перепланируем таймер с интервалом нового состояния.\n if (this.timer !== null) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n this.scheduleNext();\n }\n\n private handleMessage(e: MessageEvent): void {\n const data = e.data as { type?: string } | null;\n if (!data || typeof data !== 'object') return;\n if (data.type !== 'paywall_purchase') return;\n void this.check();\n }\n}\n\n// Решаем, имеет ли смысл вообще запускать watcher в текущем рантайме.\n// false → код, который должен закрывать пейвол на оплату, полагается на\n// другой путь (bootstrap при следующем открытии для extension popup;\n// отсутствие document — для service worker).\nexport function shouldRunUserWatcher(): boolean {\n if (typeof document === 'undefined') return false;\n if (typeof window === 'undefined') return false;\n // Chrome extension popup живёт только пока открыт. window.open()\n // checkout-провайдера сразу ест фокус → popup закрывается → весь JS-context\n // (включая SDK и watcher) уничтожается. Polling тут бесполезен.\n if (typeof location !== 'undefined' && location.protocol === 'chrome-extension:') {\n return false;\n }\n return true;\n}\n","import { AuthClient, type AuthClientOptions, type AuthSession } from '../core/auth';\nimport { BillingClient, type BillingClientOptions } from '../core/BillingClient';\nimport { EventTracker } from '../core/EventTracker';\nimport { createTrialStore, type TrialStore } from '../core/trial';\nimport type {\n Acquiring,\n Identity,\n PaywallBootstrap,\n PaywallError,\n PaywallPrice,\n PaywallUser,\n TrialConfig,\n TrialStatus,\n UserLanguageInfo,\n VisibilityStatus\n} from '../core/types';\nimport { mountShadow, type MountHandle } from './mount';\nimport {\n PaywallRoot,\n type PaywallRootProps,\n type PaywallStateSnapshot,\n type PaywallView\n} from './PaywallRoot';\nimport { UserWatcher, shouldRunUserWatcher } from './UserWatcher';\n\ntype PaywallStateListener = (state: PaywallStateSnapshot) => void;\n\nconst CLOSED_STATE: PaywallStateSnapshot = { open: false, view: null, error: null };\n\n// Контракт событий SDK. Клиент подписывается через paywall.on(event, handler).\n// Каждый event строго типизирован — IDE даёт автокомплит на payload.\nexport interface PaywallEventPayloads {\n /** Модалка открыта (запрос на открытие — данные могут ещё грузиться). */\n open: void;\n /** Модалка закрыта. */\n close: void;\n /** Bootstrap загружен, модалка показывает контент. Подходит для impression-метрик. */\n ready: PaywallBootstrap;\n /** Любая ошибка SDK (bootstrap, checkout). */\n error: PaywallError;\n /** Юзер выбрал тариф (клик по плану), ещё не инициировал checkout. */\n price_selected: { priceId: string; price: PaywallPrice };\n /** Checkout URL получен с бэка и открыт в новой вкладке. `acquiring` —\n * имя платёжного процессора, на который ушёл checkout (для конверсии\n * по эквайрингам в host-аналитике). */\n checkout_started: { priceId: string; url: string; acquiring?: Acquiring };\n /** Юзер вернулся с успешной оплатой (через URL-маркеры или postMessage),\n * либо после signIn / попытки checkout-а выяснилось, что подписка уже\n * активна (`restored: true`). priceId = null когда payment-интент не\n * был привязан к конкретной цене (UserWatcher-tick, restore-flow). */\n purchase_completed: {\n priceId: string | null;\n sessionId: string | null;\n /** true — это не свежая оплата, а активная подписка, которую SDK обнаружил\n * и показал juзеру success/restored view. Hostу полезно различать (для\n * metrics — «restore» vs «new purchase»). */\n restored?: boolean;\n };\n /** Юзер вернулся с ошибкой/cancel от провайдера. */\n purchase_failed: { reason: string | null };\n /** User-state изменился (bootstrap snapshot, getUser refresh, watcher tick).\n * Дёргается также сразу с last-known user после первой подписки. */\n userChange: PaywallUser;\n /** Auth-session изменилась (signin / signup / refresh / signout / 401-revoke).\n * null = разлогинен. Дёргается сразу с last-known session после первой подписки. */\n authChange: AuthSession | null;\n /** Триал заблокировал показ модалки. payload содержит свежий статус (после\n * recordBlock). Для `mode: 'time'` — startedAt/expiresAt/remainingMs;\n * для `mode: 'opens'` — remainingActions/totalActions. Хост может\n * использовать payload для показа собственного UI («осталось 3 показа»). */\n trial_blocked: TrialStatus;\n /** Триал истёк, паывол показывается впервые после истечения. Эмитится\n * раз за жизнь PaywallUI-инстанса (не персистится между перезагрузками\n * страницы — на каждом page-load событие может стрельнуть один раз). */\n trial_expired: void;\n /** Targeting не сошёлся — паывол не открывается. payload содержит\n * server-computed snapshot из bootstrap (visible=false + reason + country +\n * tier). Хост может показать собственный fallback («сервис недоступен в\n * вашей стране») или просто залогировать impression для аналитики. */\n visibility_blocked: VisibilityStatus;\n}\n\nexport type PaywallEvent = keyof PaywallEventPayloads;\n\nexport type PaywallEventHandler<E extends PaywallEvent = PaywallEvent> = (\n payload: PaywallEventPayloads[E]\n) => void;\n\n// Вспомогательный тип: `void` payload эмитится без аргумента (`emit('open')`),\n// непустой — с аргументом (`emit('ready', bootstrap)`).\ntype EmitArgs<E extends PaywallEvent> = PaywallEventPayloads[E] extends void\n ? []\n : [PaywallEventPayloads[E]];\n\nexport interface AnalyticsOptions {\n enabled?: boolean;\n /** Полный URL до /events. По умолчанию — `${apiOrigin}/api/v1/paywall/${id}/events`. */\n endpoint?: string;\n flushIntervalMs?: number;\n maxBufferSize?: number;\n /** Тестовый override fetch'а (jsdom/Vitest). */\n fetch?: typeof fetch;\n /** Тестовый override sendBeacon'а. */\n sendBeacon?: (url: string, data: BodyInit) => boolean;\n}\n\n/**\n * Managed-auth конфиг. Передай `auth: true` — PaywallUI создаёт `AuthClient`\n * сам (с тем же `paywallId/apiOrigin/storage`, как BillingClient). Передай\n * объект — те же дефолты + override опций. Передай готовый `AuthClient` —\n * PaywallUI просто прокинет его в BillingClient (полезно, если хост хочет\n * иметь общий AuthClient на несколько пейволов / делать manual signIn/signOut\n * из своего UI до открытия модалки).\n *\n * Без `auth` опции SDK работает в hybrid-режиме: identity передаётся снаружи\n * через `opts.identity` или `paywall.open({identity})`.\n */\nexport type AuthOption = true | AuthClient | Partial<Omit<AuthClientOptions, 'paywallId'>>;\n\nexport interface PaywallUIOptions extends Omit<BillingClientOptions, 'auth'> {\n client?: BillingClient;\n host?: HTMLElement;\n /** Подключить managed-auth слой. См. {@link AuthOption}. */\n auth?: AuthOption;\n /**\n * Автоматически парсить URL при создании PaywallUI, чтобы поймать возврат\n * с checkout-провайдера (?paywall_status=paid|failed|cancelled). Дефолт: true.\n * Эмитит purchase_completed / purchase_failed через microtask — подпишись синхронно.\n */\n autoDetectReturn?: boolean;\n /**\n * Режим shadow DOM. По умолчанию `closed` — полная изоляция от хоста.\n * Для e2e тестов (Playwright) и live-preview в админке передавать `open`.\n */\n shadowMode?: 'open' | 'closed';\n /**\n * Аналитика SDK 3.0. По умолчанию включена. Передай `false` для полного\n * отключения (ничего не шлётся на бэк). Принимает объект с настройками\n * batch'а или endpoint-override.\n */\n analytics?: boolean | AnalyticsOptions;\n /**\n * Когда bootstrap не в кеше — модалку рендерить **сразу** со спиннером и\n * прогонять gates (visibility/trial) после получения данных, или **ждать**\n * bootstrap и монтировать только если gates прошли. Дефолт `true` —\n * snappy open, кнопка «открыть» отзывается мгновенно.\n *\n * Trade-off: при `true` и блокирующем gate'е модалка моргнёт (открылась\n * → закрылась через ~200-500мс). На extension'ах и сайтах с включённым\n * targeting-fallback'ом это редкий путь, поэтому дефолт оптимизирован\n * под основной 99%-кейс. Передай `false`, если для вашего use-case'а\n * флеш на blocked-странах/устройствах хуже воспринимаемой латентности.\n */\n mountThenLoad?: boolean;\n}\n\n/**\n * Результат `paywall.getAccess()` — отвечает на главный вопрос хоста: «нужно\n * ли блокировать фичу для этого юзера?». Без побочных эффектов: на trial-storage\n * `recordBlock` не вызывается (счётчики не двигаются), модалка не монтируется.\n *\n * Семантика `access`:\n * - `granted` — фичу НЕ блокировать. Один из сценариев:\n * - `has_subscription` — у юзера активная подписка/покупка;\n * - `visibility_blocked` — таргетинг (страна/девайс/visibility-флаг) не\n * сошёлся, юзер вне monetization-scope'а пейвола → монетизация неприменима;\n * - `trial_blocked` — пре-пейвольный триал ещё активен.\n * - `blocked` — фичу заблокировать и вызвать `paywall.open()`. Reason всегда\n * `no_subscription`.\n *\n * Discriminated union по `access`: type-narrowing на `result.access === 'blocked'`\n * сужает `reason` до `'no_subscription'`, на `'granted'` — до трёх granted-вариантов.\n */\nexport type PaywallAccessResult =\n | {\n access: 'granted';\n reason: 'has_subscription' | 'visibility_blocked' | 'trial_blocked';\n visibility: VisibilityStatus | null;\n trial: TrialStatus | null;\n user: PaywallUser | null;\n }\n | {\n access: 'blocked';\n reason: 'no_subscription';\n visibility: VisibilityStatus | null;\n trial: TrialStatus | null;\n user: PaywallUser | null;\n };\n\nexport interface GetAccessOptions {\n skipTrial?: boolean;\n skipVisibility?: boolean;\n signal?: AbortSignal;\n}\n\nexport interface OpenOptions {\n identity?: Identity;\n /** Принудительно открыть, минуя pre-paywall trial check. По умолчанию SDK\n * читает `bootstrap.settings.trial` и блокирует open(), пока триал активен.\n * Эскейп-хатч для случаев типа «host решил показать всё-таки» или дев-режим. */\n skipTrial?: boolean;\n /** Принудительно открыть, минуя targeting-gate. По умолчанию SDK читает\n * `bootstrap.settings.visibility` и эмитит `visibility_blocked` без\n * открытия модалки, если visible=false (страна/девайс/visibility-флаг\n * не сошлись). Эскейп-хатч для дев-отладки. */\n skipVisibility?: boolean;\n /** Renewal/upgrade flow. По умолчанию (false) SDK после bootstrap'а или\n * signIn проверяет `user.has_active_subscription` и переключается в\n * restored success-view, не показывая тарифы — open() для уже подписанного\n * юзера превращается в подтверждение «у вас уже есть подписка». С\n * `renew: true` все эти проверки пропускаются: тарифы показываются всегда,\n * и при checkout SDK передаёт `ignoreActivePurchase: true` на бэк, чтобы\n * /start-checkout не вернул 409. Использовать когда host-UI явно\n * показывает кнопку «Renew»/«Upgrade plan». */\n renew?: boolean;\n}\n\n// Маркеры в URL, по которым SDK определяет результат checkout.\n// Контракт общий с бэком — online добавляет их в success/cancel URLs.\nconst URL_MARKERS = {\n status: 'paywall_status',\n priceId: 'paywall_price_id',\n sessionId: 'paywall_session_id'\n} as const;\n\nexport class PaywallUI {\n readonly billing: BillingClient;\n /** AuthClient (managed-auth) или undefined в hybrid-режиме. Доступен публично:\n * host может вызывать `paywall.auth?.signOut()`, читать `getCachedSession()`,\n * подписываться на `onAuthChange` напрямую. */\n readonly auth: AuthClient | undefined;\n private ownsAuth: boolean;\n private host?: HTMLElement;\n private shadowMode: 'open' | 'closed';\n private handle: MountHandle | null = null;\n private isOpen = false;\n private listeners = new Map<PaywallEvent, Set<PaywallEventHandler>>();\n private userUnsub: (() => void) | null = null;\n private authUnsub: (() => void) | null = null;\n private watcher: UserWatcher | null = null;\n private tracker: EventTracker | null = null;\n private purchased = false;\n /** Lazy-инстанс TrialStore. Резолвится при первом open(), когда уже знаем\n * `bootstrap.settings.trial`. null — триал отключён в конфиге пейвола. */\n private trialStore: TrialStore | null = null;\n /** Конфиг, под который создан текущий trialStore — пересобираем, если он\n * поменялся между bootstrap-фетчами (например, владелец переключил режим\n * в админке между сессиями SDK). */\n private trialStoreConfig: TrialConfig | null = null;\n /** In-memory snapshot последнего check() — для синхронного getTrialStatus(). */\n private lastTrialStatus: TrialStatus | null = null;\n /** Флаг dedupe для `trial_expired` события в рамках жизни инстанса. */\n private trialExpiredFired = false;\n /** In-memory snapshot последнего bootstrap'а — для синхронного getVisibility(). */\n private lastVisibility: VisibilityStatus | null = null;\n /** Поведение open() при холодном bootstrap'е. См. PaywallUIOptions.mountThenLoad. */\n private mountThenLoad: boolean;\n /** Текущий snapshot UI state-machine. Обновляется PaywallRoot'ом через\n * `onState` prop; при close сбрасывается обратно в CLOSED_STATE. */\n private currentState: PaywallStateSnapshot = CLOSED_STATE;\n private stateListeners = new Set<PaywallStateListener>();\n\n constructor(opts: PaywallUIOptions) {\n // Резолвим AuthClient: готовый инстанс / managed-конфиг (true|object) /\n // undefined. ownsAuth=true → сами создавали и должны прибрать в destroy().\n const { auth, ownsAuth } = resolveAuth(opts);\n this.auth = auth;\n this.ownsAuth = ownsAuth;\n\n // Если auth есть — прокидываем в BillingClient (он сам подключит Bearer\n // и auto-sync identity через onAuthChange). client из opts побеждает —\n // считаем, что хост уже сконфигурировал его сам, не лезем перетирать auth.\n this.billing =\n opts.client ?? new BillingClient({ ...opts, auth: this.auth });\n this.host = opts.host;\n this.shadowMode = opts.shadowMode ?? 'closed';\n this.mountThenLoad = opts.mountThenLoad ?? true;\n\n // Форвардим user-change события из BillingClient на public-API PaywallUI.\n // Один источник правды (BillingClient cache) — два consumer'а (host через\n // paywall.onUserChange и сам watcher через billing.onUserChange).\n this.userUnsub = this.billing.onUserChange((user) => {\n this.emit('userChange', user);\n });\n\n if (this.auth) {\n this.authUnsub = this.auth.onAuthChange((session) => {\n this.emit('authChange', session);\n });\n }\n\n this.initTracker(opts.analytics);\n\n if (opts.autoDetectReturn !== false && typeof window !== 'undefined') {\n // Microtask — клиент успевает подписаться синхронно после конструктора,\n // до того как событие действительно стрельнёт.\n queueMicrotask(() => this.checkReturn());\n }\n }\n\n private initTracker(analytics: PaywallUIOptions['analytics']): void {\n if (analytics === false) return;\n const cfg: AnalyticsOptions =\n typeof analytics === 'object' && analytics !== null ? analytics : {};\n if (cfg.enabled === false) return;\n\n const endpoint =\n cfg.endpoint ?? `${this.billing.apiOrigin}/api/v1/paywall/${this.billing.paywallId}/events`;\n\n this.tracker = new EventTracker({\n endpoint,\n paywallId: this.billing.paywallId,\n capabilities: this.billing.capabilities,\n getVisitorId: () => this.billing.getVisitorId(),\n getCachedVisitorId: () => this.billing.getCachedVisitorId(),\n getUserId: () => this.billing.getIdentity()?.userId ?? null,\n flushIntervalMs: cfg.flushIntervalMs,\n maxBufferSize: cfg.maxBufferSize,\n fetch: cfg.fetch,\n sendBeacon: cfg.sendBeacon\n });\n\n // Биндим внутренние SDK-события на аналитический транспорт. Один эмиттер,\n // один потребитель (трекер) — никто кроме трекера не должен трогать\n // эти имена событий за пределами PaywallUI.\n this.on('open', () => this.tracker?.track('paywall_opened'));\n this.on('ready', (b) =>\n this.tracker?.track('paywall_viewed', {\n is_test_mode: b.settings.is_test_mode,\n prices_count: b.prices.length,\n offers_count: b.offers.length\n })\n );\n this.on('price_selected', (p) =>\n this.tracker?.track('price_selected', { price_id: p.priceId })\n );\n this.on('checkout_started', (p) =>\n this.tracker?.track('checkout_started', {\n price_id: p.priceId,\n acquiring: p.acquiring\n })\n );\n this.on('purchase_completed', (p) =>\n this.tracker?.track('purchase_completed', {\n price_id: p.priceId,\n session_id: p.sessionId\n })\n );\n this.on('purchase_failed', (p) =>\n this.tracker?.track('purchase_failed', { reason: p.reason })\n );\n this.on('close', () => this.tracker?.track('paywall_closed'));\n this.on('trial_blocked', (s) =>\n this.tracker?.track('trial_blocked', {\n mode: s.mode,\n ...(s.mode === 'time'\n ? { remaining_ms: s.remainingMs, total_ms: s.totalMs }\n : s.mode === 'opens'\n ? { remaining_actions: s.remainingActions, total_actions: s.totalActions }\n : {})\n })\n );\n this.on('trial_expired', () => this.tracker?.track('trial_expired'));\n this.on('visibility_blocked', (v) =>\n this.tracker?.track('visibility_blocked', {\n reason: v.reason,\n country: v.country,\n tier: v.tier\n })\n );\n this.on('error', (e) =>\n this.tracker?.track('error', { code: e.code, message: e.message })\n );\n // auth_signin_success / auth_signout пока не фаерим: authChange эмитится\n // и на гидрации сессии (UI поднимает кеш из storage), и на token refresh,\n // и при параллельных consumer'ах одного auth-state — даёт ложные signin'ы.\n // Реальные login-события нужно ловить через прямые вызовы\n // signInWithEmail/signUp/signInWithOAuth/signOut, а не через authChange.\n }\n\n /**\n * Отправить произвольное аналитическое событие. Имена из системного whitelist'а\n * (`app_opened`, `paywall_viewed`, ...) разрешены как есть. Кастомные —\n * с префиксом `host:` (например `host:user_clicked_upgrade`). Сервер\n * дропает события с неразрешёнными именами.\n *\n * Самый частый кейс — `track('app_opened')` от хоста сразу после загрузки\n * приложения, чтобы зафиксировать воронку до открытия пейвола.\n */\n track(name: string, props?: Record<string, unknown>): void {\n this.tracker?.track(name, props);\n }\n\n /**\n * Удобный шорткат вместо `paywall.on('userChange', cb)` — самый частый\n * паттерн в host-коде, поэтому отдельный named метод. Колбек получает\n * last-known user из кеша синхронно через microtask, если он есть.\n */\n onUserChange(handler: PaywallEventHandler<'userChange'>): () => void {\n return this.on('userChange', handler);\n }\n\n on<E extends PaywallEvent>(event: E, handler: PaywallEventHandler<E>): () => 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(handler as PaywallEventHandler);\n return () => set!.delete(handler as PaywallEventHandler);\n }\n\n off<E extends PaywallEvent>(event: E, handler: PaywallEventHandler<E>): void {\n this.listeners.get(event)?.delete(handler as PaywallEventHandler);\n }\n\n private emit<E extends PaywallEvent>(event: E, ...args: EmitArgs<E>): void {\n const set = this.listeners.get(event);\n if (!set) return;\n const payload = args[0] as PaywallEventPayloads[E];\n for (const handler of set) {\n try {\n (handler as PaywallEventHandler<E>)(payload);\n } catch (error) {\n if (typeof console !== 'undefined') console.error('[paywall] listener error', error);\n }\n }\n }\n\n open(opts: OpenOptions = {}): void {\n this.openInternal('layout', opts);\n }\n\n /**\n * Прогревает bootstrap-кеш и balance-кеш заранее, без открытия модалки.\n * Полезно когда host знает, что юзер скоро откроет paywall (hover на CTA,\n * mount компонента) — первый `open()` рендерится мгновенно, без loading-flash.\n *\n * Не throw'ает: если сеть упала, тихо игнорирует (повторный open() сделает\n * fresh-bootstrap с error-state как обычно). `signal` для отмены — например,\n * если хост размонтирует компонент быстрее, чем bootstrap вернётся.\n *\n * Вызывать можно сколько угодно раз — последующие вызовы возвращают cached\n * Promise (BillingClient уже дедуплицирует).\n */\n async preload(opts: { signal?: AbortSignal } = {}): Promise<void> {\n try {\n await this.billing.bootstrap({ signal: opts.signal });\n // Балансы — best-effort: пейволы без `tokenization` отдают пустой\n // массив, и getBalances не делает сетевого запроса для unauth-юзера.\n if (this.billing.auth) {\n await this.billing.getBalances({ signal: opts.signal });\n }\n } catch {\n /* preload best-effort — open() сам покажет error-state */\n }\n }\n\n /**\n * Открывает модалку сразу с саппорт-формой (минуя layout с тарифами).\n * Полезно, когда host-приложение хочет дать юзеру кнопку «Help / Support»,\n * не связанную с пейволом-апгрейдом. Back/Done в саппорт-форме закрывают\n * модалку (не возвращают к тарифам), потому что юзер пришёл сюда напрямую.\n *\n * Из обычного `paywall.open()`-flow саппорт всё равно доступен через\n * Contact Support-ссылку в `current_session`-блоке (там Back возвращает\n * к layout).\n */\n openSupport(opts: OpenOptions = {}): void {\n this.openInternal('support', opts);\n }\n\n /**\n * Открывает модалку сразу с auth-gate (логин/регистрация), без layout с\n * тарифами. Сценарий: returning customer уже купил, ему просто нужно\n * залогиниться, чтобы SDK подцепил его purchases. После signIn модалка\n * закрывается; Back тоже закрывает (юзер пришёл только за логином).\n *\n * Без `auth` (managed-auth не подключён) метод — no-op: некому делать\n * signIn. Если юзер уже залогинен — модалка всё равно откроется и\n * закроется через auto-resume в auth_gate effect'е (мгновение).\n *\n * Триал не блокирует этот флоу — auth не connect'ится с trial-механикой.\n */\n openAuth(opts: OpenOptions = {}): void {\n if (!this.auth) return;\n this.openInternal('auth', { ...opts, skipTrial: true });\n }\n\n /**\n * Открывает модалку с anonymous-gate. AnonGate сразу зовёт\n * `auth.signInAnonymously()`:\n * - если в storage есть `anonRefreshToken` — silent resume через\n * /auth/refresh, модалка схлопывается мгновенно (юзер видит лёгкий\n * спиннер ~100мс);\n * - иначе — fresh POST /auth/anonymous/signin без captcha (защита от\n * abuse держится на Supabase per-real-IP rate-limit + CF Bot Fight Mode).\n *\n * После успешного signIn'а модалка закрывается; host подхватывает свежую\n * session через `auth.onAuthChange` или `paywall.onUserChange`. Для UX\n * \"просто залогиниться чтобы api-gateway работал, без покупок\".\n *\n * Без managed-auth (`auth` не подключён) метод — no-op. Триал и\n * visibility-таргетинг этот flow обходит: анон-логин не должен зависеть\n * от страны юзера или trial-стейджа.\n */\n openAnonGate(opts: OpenOptions = {}): void {\n if (!this.auth) return;\n this.openInternal('anon', { ...opts, skipTrial: true, skipVisibility: true });\n }\n\n private openInternal(view: PaywallView, opts: OpenOptions): void {\n if (opts.identity) this.billing.setIdentity(opts.identity);\n // Сбрасываем флаг success-вью — повторное открытие должно стартовать\n // с обычного layout, а не с прошлого \"Payment received\".\n this.purchased = false;\n\n // support и auth-standalone флоу обходят оба гейта (триал и таргетинг):\n // юзер пришёл за саппортом или за логином к уже купленной подписке —\n // блокировать его по trial-stage'у или таргетингу неуместно. openAuth\n // дополнительно передаёт skipTrial:true для совместимости с прежней\n // семантикой; здесь skip-флаги нормализуем единообразно.\n const skipTrial = opts.skipTrial === true || view === 'support';\n const skipVisibility =\n opts.skipVisibility === true ||\n view === 'support' ||\n view === 'auth' ||\n view === 'anon';\n const renew = opts.renew === true;\n\n if (skipTrial && skipVisibility) {\n this.mountAndShow(view, { renew });\n return;\n }\n\n // Cache hit — sync путь, gates до mount как раньше. Никаких компромиссов:\n // когда bootstrap уже в памяти, мы знаем за один tick можно открывать\n // или нет, без флеша.\n const cached = this.billing.getCachedBootstrap();\n if (cached) {\n this.runOpenGates(view, cached, { skipTrial, skipVisibility, renew });\n return;\n }\n\n // Cold bootstrap. Два режима:\n //\n // mountThenLoad=true (default): монтируем модалку немедленно — юзер видит\n // спиннер, кнопка отзывается мгновенно. Bootstrap идёт параллельно.\n // Когда придёт — гоняем gates, и если блокирует, закрываем модалку с\n // эмиссией *_blocked. Цена — флеш «открылась → закрылась» в редком\n // случае visibility/trial-блока. Для extension'ов и сайтов с включённым\n // targeting'ом большинство open()'ов проходят, флеш — edge case.\n //\n // mountThenLoad=false (legacy): ждём bootstrap до mount'а. Гарантия\n // отсутствия флеша на блоке, но кнопка кажется «мёртвой» 200-500мс\n // на холодном кеше.\n if (this.mountThenLoad) {\n this.mountAndShow(view, { renew });\n this.billing\n .bootstrap()\n .then((b) => this.runDelayedGates(b, { skipTrial, skipVisibility }))\n .catch(() => {\n // Bootstrap упал — модалка уже открыта, PaywallRoot сам в error-state.\n });\n return;\n }\n\n this.billing\n .bootstrap()\n .then((b) => this.runOpenGates(view, b, { skipTrial, skipVisibility, renew }))\n .catch(() => {\n // Bootstrap упал — открываем без gates; PaywallRoot покажет error.\n this.mountAndShow(view, { renew });\n });\n }\n\n /** Применить gates ПОСЛЕ того, как модалка уже смонтирована (mount-then-load\n * путь). Если gate блокирует — close() + emit. Если юзер уже сам закрыл\n * модалку до резолва bootstrap'а — no-op (isOpen=false). */\n private runDelayedGates(\n bootstrap: PaywallBootstrap,\n flags: { skipTrial: boolean; skipVisibility: boolean }\n ): void {\n if (!this.isOpen) return;\n\n if (!flags.skipVisibility) {\n const v = bootstrap.settings.visibility;\n if (v) {\n this.lastVisibility = v;\n if (!v.visible) {\n this.close();\n this.emit('visibility_blocked', v);\n return;\n }\n }\n }\n\n if (flags.skipTrial) return;\n\n const trialCfg = bootstrap.settings.trial;\n if (!trialCfg) return;\n const store = this.ensureTrialStore(trialCfg);\n void store\n .check()\n .then(async (status) => {\n if (!this.isOpen) return;\n this.lastTrialStatus = status;\n if (status.mode === 'none') return;\n if (status.blocked) {\n const updated = await store.recordBlock();\n this.lastTrialStatus = updated;\n if (!this.isOpen) return;\n this.close();\n this.emit('trial_blocked', updated);\n return;\n }\n if (!this.trialExpiredFired) {\n this.trialExpiredFired = true;\n this.emit('trial_expired');\n }\n })\n .catch((e) => {\n if (typeof console !== 'undefined') console.warn('[paywall] trial check failed', e);\n });\n }\n\n // Порядок гейтов: visibility → trial. Country-mismatch ≠ trial-block, и\n // вести trial-стейт «осталось N показов» под юзером, который вообще не\n // должен увидеть пейвол по таргетингу — бессмысленно: при возврате в\n // правильную страну он окажется со «слипшимся» триал-счётчиком.\n private runOpenGates(\n view: PaywallView,\n bootstrap: PaywallBootstrap,\n flags: { skipTrial: boolean; skipVisibility: boolean; renew: boolean }\n ): void {\n if (!flags.skipVisibility) {\n const v = bootstrap.settings.visibility;\n if (v) {\n this.lastVisibility = v;\n if (!v.visible) {\n this.emit('visibility_blocked', v);\n return;\n }\n }\n }\n\n if (flags.skipTrial) {\n this.mountAndShow(view, { renew: flags.renew });\n return;\n }\n this.gateThroughTrial(view, bootstrap, flags.renew);\n }\n\n private gateThroughTrial(view: PaywallView, bootstrap: PaywallBootstrap, renew: boolean): void {\n const trialCfg = bootstrap.settings.trial;\n if (!trialCfg) {\n this.mountAndShow(view, { renew });\n return;\n }\n const store = this.ensureTrialStore(trialCfg);\n void store\n .check()\n .then(async (status) => {\n this.lastTrialStatus = status;\n if (status.mode === 'none') {\n this.mountAndShow(view, { renew });\n return;\n }\n if (status.blocked) {\n // recordBlock делает запись (init firstOpen / inc skipTimes) и\n // возвращает обновлённый snapshot — его и эмитим, чтобы хост\n // получил актуальный счётчик.\n const updated = await store.recordBlock();\n this.lastTrialStatus = updated;\n this.emit('trial_blocked', updated);\n return;\n }\n // Триал в конфиге, но не блокирует → истёк. Эмитим один раз за\n // сессию, дальше открываем как обычно.\n if (!this.trialExpiredFired) {\n this.trialExpiredFired = true;\n this.emit('trial_expired');\n }\n this.mountAndShow(view, { renew });\n })\n .catch((e) => {\n // Storage недоступен (privacy mode, quota) — не блокируем юзера,\n // открываем модалку и не теряем продажу.\n if (typeof console !== 'undefined') console.warn('[paywall] trial check failed', e);\n this.mountAndShow(view, { renew });\n });\n }\n\n private ensureTrialStore(config: TrialConfig): TrialStore {\n if (this.trialStore && this.trialStoreConfig && sameTrialConfig(this.trialStoreConfig, config)) {\n return this.trialStore;\n }\n this.trialStoreConfig = config;\n // Duck-type: если billing-клиент предоставляет свой factory (extension'овский\n // RemoteBillingClient — атомарный TrialStore через offscreen + navigator.locks),\n // используем его. Иначе — обычный path через storage-adapter.\n const factoryFn = (this.billing as { createTrialStore?: (cfg: TrialConfig) => TrialStore })\n .createTrialStore;\n this.trialStore =\n typeof factoryFn === 'function'\n ? factoryFn.call(this.billing, config)\n : createTrialStore(this.billing.getStorage(), this.billing.paywallId, config);\n return this.trialStore;\n }\n\n private mountAndShow(view: PaywallView, mountOpts: { renew?: boolean } = {}): void {\n const renew = mountOpts.renew === true;\n if (this.handle) {\n this.isOpen = true;\n this.handle.update({ open: true, initialView: view, purchased: false, renew });\n this.emit('open');\n return;\n }\n\n this.isOpen = true;\n this.handle = mountShadow<PaywallRootProps>(\n PaywallRoot,\n {\n client: this.billing,\n open: true,\n initialView: view,\n purchased: false,\n renew,\n onClose: () => this.close(),\n onEvent: (event, payload) => {\n this.emit(event as PaywallEvent, payload as never);\n // Поднимаем watcher как только начался checkout — отсюда уже\n // полагаемся на server-confirmed flow, а не URL-маркеры.\n if (event === 'checkout_started') this.startUserWatcher();\n },\n onState: (snapshot) => this.applyState(snapshot)\n },\n { host: this.host, shadowMode: this.shadowMode }\n );\n this.emit('open');\n }\n\n private applyState(snapshot: PaywallStateSnapshot): void {\n if (sameStateSnapshot(this.currentState, snapshot)) return;\n this.currentState = snapshot;\n for (const cb of this.stateListeners) {\n try {\n cb(snapshot);\n } catch (e) {\n console.warn('[paywall] onStateChange listener threw', e);\n }\n }\n }\n\n /**\n * Sync-snapshot текущего состояния модалки. Подходит для `useSyncExternalStore`\n * в React (`useSyncExternalStore(paywall.onStateChange, paywall.getState)`)\n * и для одноразовых проверок («открыт ли пейвол сейчас?»).\n *\n * Snapshot стабилен — пока state не изменился, повторный getState() вернёт\n * `===`-равный объект (важно для useSyncExternalStore чтобы не ре-рендерить).\n */\n getState(): PaywallStateSnapshot {\n return this.currentState;\n }\n\n /**\n * Подписка на изменения state. Колбек вызывается при каждом реальном\n * изменении (closed → loading → ready → ...). По умолчанию initial snapshot\n * отдаётся через microtask после подписки; через `{immediate: 'sync'|'none'}`\n * можно сделать sync-доставку (для useSyncExternalStore — там она не нужна,\n * snapshot читается через getSnapshot отдельно) или вовсе пропустить\n * initial.\n *\n * Возвращает unsubscribe.\n */\n onStateChange(\n cb: PaywallStateListener,\n opts: { immediate?: 'microtask' | 'sync' | 'none' } = {}\n ): () => void {\n this.stateListeners.add(cb);\n const mode = opts.immediate ?? 'microtask';\n if (mode !== 'none') {\n const snapshot = this.currentState;\n if (mode === 'sync') {\n try {\n cb(snapshot);\n } catch (e) {\n console.warn('[paywall] onStateChange initial sync threw', e);\n }\n } else {\n queueMicrotask(() => {\n if (this.stateListeners.has(cb)) cb(snapshot);\n });\n }\n }\n return () => {\n this.stateListeners.delete(cb);\n };\n }\n\n /** Sync-доступ к последнему известному статусу триала. null — `paywall.open()`\n * ещё не вызывался либо триал отключён в конфиге пейвола. Удобно для\n * собственного UI хоста («осталось 3 показа», «триал истечёт через 2ч»). */\n getTrialStatus(): TrialStatus | null {\n return this.lastTrialStatus;\n }\n\n /** Sync-доступ к последнему server-computed visibility-статусу. null —\n * bootstrap ещё не загружен или сервер не отдаёт `settings.visibility`\n * (например, старая версия online без targeting-патча). Хост может\n * использовать для собственного fallback'а: «сервис недоступен в вашей\n * стране». Обновляется на каждом open(), который проходит через gate. */\n getVisibility(): VisibilityStatus | null {\n return this.lastVisibility;\n }\n\n /**\n * Цены пейвола — шорткат над `bootstrap()`. Локали уже применены, кэш и\n * stale-while-revalidate идентичны `billing.bootstrap()`. Подходит для\n * pricing-страниц/карточек на сайте, где host хочет показать те же цены,\n * что и в модалке, не вытаскивая bootstrap руками.\n */\n getPrices(opts: { force?: boolean; signal?: AbortSignal } = {}): Promise<PaywallPrice[]> {\n return this.billing.getPrices(opts);\n }\n\n /** Sync-снимок цен. null — bootstrap ещё не загружали. */\n getCachedPrices(): PaywallPrice[] | null {\n return this.billing.getCachedPrices();\n }\n\n /** Снимок текущего «языка юзера» — proxy над `billing.getUserLanguage()`.\n * Используй, чтобы синхронизировать i18n host'а с тем, что фактически\n * показывает пейвол. См. подробности в `BillingClient.getUserLanguage`. */\n getUserLanguage(): UserLanguageInfo {\n return this.billing.getUserLanguage();\n }\n\n /**\n * Решает, нужно ли блокировать фичу для текущего юзера. Без побочных эффектов\n * (на trial-storage `recordBlock` не вызывается, модалка не монтируется).\n *\n * Порядок проверок (первый сработавший — финальный):\n * 1. `has_active_subscription` — самый сильный сигнал, перебивает остальные.\n * Юзер с подпиской получает доступ независимо от visibility/trial.\n * 2. `visibility` (страна/девайс/disabled-флаг) — юзер вне monetization-scope'а\n * пейвола, гейтить нельзя.\n * 3. `trial` — пре-пейвольный бесплатный период активен.\n * 4. Иначе — `blocked`, host лочит фичу и зовёт `paywall.open()`.\n *\n * Bootstrap кешируется в BillingClient — `getAccess()` можно дёргать на\n * каждый рендер host-компонента, /bootstrap не дублируется. При упавшей сети\n * fallback на persistent-cached user из storage: юзер с прошлой подпиской\n * получает `granted` офлайн, иначе `blocked` (host покажет пейвол с\n * error-state, юзер сможет ретрайнуть). Side-эффект: обновляются\n * `lastVisibility` / `lastTrialStatus`, чтобы синхронные геттеры\n * `getVisibility()` / `getTrialStatus()` видели свежие данные после первого\n * `getAccess()`, а не только после первого `open()`.\n */\n async getAccess(opts: GetAccessOptions = {}): Promise<PaywallAccessResult> {\n let bootstrap = this.billing.getCachedBootstrap();\n if (!bootstrap) {\n try {\n bootstrap = await this.billing.bootstrap({ signal: opts.signal });\n } catch {\n // Сеть упала. Fallback на persistent-cached user (TTL 30 мин в storage).\n // Юзер с прошлой подпиской → granted (офлайн-friendly), иначе → blocked\n // (open() покажет пейвол с error-state, юзер ретрайнет).\n const cached = this.billing.getCachedUser();\n if (cached?.has_active_subscription) {\n return {\n access: 'granted',\n reason: 'has_subscription',\n visibility: null,\n trial: null,\n user: cached\n };\n }\n return {\n access: 'blocked',\n reason: 'no_subscription',\n visibility: null,\n trial: null,\n user: cached\n };\n }\n }\n\n const user = bootstrap.user ?? null;\n\n if (user?.has_active_subscription) {\n return {\n access: 'granted',\n reason: 'has_subscription',\n visibility: bootstrap.settings.visibility ?? null,\n trial: null,\n user\n };\n }\n\n let visibility: VisibilityStatus | null = null;\n if (!opts.skipVisibility) {\n const v = bootstrap.settings.visibility;\n if (v) {\n visibility = v;\n this.lastVisibility = v;\n if (!v.visible) {\n return { access: 'granted', reason: 'visibility_blocked', visibility, trial: null, user };\n }\n }\n }\n\n let trial: TrialStatus | null = null;\n if (!opts.skipTrial) {\n const trialCfg = bootstrap.settings.trial;\n if (trialCfg) {\n try {\n const store = this.ensureTrialStore(trialCfg);\n trial = await store.check();\n this.lastTrialStatus = trial;\n if (trial.blocked) {\n return { access: 'granted', reason: 'trial_blocked', visibility, trial, user };\n }\n } catch (e) {\n if (typeof console !== 'undefined') console.warn('[paywall] getAccess: trial check failed', e);\n }\n }\n }\n\n return { access: 'blocked', reason: 'no_subscription', visibility, trial, user };\n }\n\n /** Сбросить состояние триала в storage. Полезно для дев-режима / админ-кнопки\n * «прогнать сценарий заново». В проде хост обычно не дёргает. */\n async resetTrial(): Promise<void> {\n if (!this.trialStore) return;\n await this.trialStore.reset();\n this.lastTrialStatus = null;\n this.trialExpiredFired = false;\n }\n\n // Запускает polling user-state до has_active_subscription=true либо до\n // таймаута. Идемпотентен: повторный вызов на уже работающем watcher'е —\n // no-op (юзер мог нажать Continue повторно после возврата).\n //\n // В extension popup runtime — no-op (popup не доживёт). Там полагаемся на\n // bootstrap при следующем открытии.\n private startUserWatcher(): void {\n if (this.watcher) return;\n if (!shouldRunUserWatcher()) return;\n\n this.watcher = new UserWatcher({\n client: this.billing,\n onActive: (user) => {\n this.watcher = null;\n // Серверная правда — эмитим финальный purchase_completed\n // (server-confirmed), чтобы host получил согласованный сигнал\n // независимо от того, был ли URL-маркер. userChange эмитит сам\n // billing-listener.\n this.emit('purchase_completed', { priceId: null, sessionId: null });\n // success_redirect_url из settings — host явно попросил отправить\n // юзера в свой apps-flow после оплаты. Редирект имеет приоритет\n // над PurchaseSuccessView: рисовать success ради 200мс перед\n // переходом — мерцание. Берём snapshot из cached bootstrap\n // (он гарантированно загружен — иначе watcher не запустился бы).\n const redirect = this.billing\n .getCachedBootstrap()\n ?.settings.success_redirect_url;\n if (redirect && typeof window !== 'undefined') {\n try {\n window.location.assign(redirect);\n return;\n } catch {\n /* navigation заблокирована — fallback на success-view */\n }\n }\n // Если пейвол открыт — переключаем во вью «Payment received» с\n // кнопкой Continue. Молчаливое закрытие сбивало юзера с толку:\n // окно просто исчезало, без подтверждения, что оплата прошла.\n // Если пейвол закрыт — событие уже эмитнуто, host решит сам.\n if (this.isOpen && this.handle) {\n this.purchased = true;\n this.handle.update({ purchased: true });\n }\n void user; // shape доступен через paywall.billing.getCachedUser()\n },\n onTimeout: () => {\n this.watcher = null;\n }\n });\n this.watcher.start();\n }\n\n close(): void {\n if (!this.isOpen || !this.handle) return;\n this.isOpen = false;\n this.purchased = false;\n this.handle.update({ open: false, purchased: false });\n // PaywallRoot эмитит onState с open=false при handle.update, но из-за\n // microtask'ов хост может прочитать getState() до того, как PaywallRoot\n // useEffect отстреляет. Применяем закрытое состояние сразу.\n this.applyState(CLOSED_STATE);\n this.emit('close');\n }\n\n /**\n * Сканирует текущий URL на маркеры возврата с checkout и эмитит\n * purchase_completed / purchase_failed. Маркеры удаляются из URL\n * через history.replaceState. Ищет и в hash, и в search (hash приоритетнее —\n * защита от клиентских SPA-роутеров, перехватывающих query).\n */\n checkReturn(): void {\n if (typeof window === 'undefined') return;\n const url = new URL(window.location.href);\n\n const hashMarkers = parseMarkers(url.hash.replace(/^#/, ''));\n const searchMarkers = parseMarkers(url.search.replace(/^\\?/, ''));\n const markers = hashMarkers ?? searchMarkers;\n if (!markers) return;\n\n if (markers.status === 'paid') {\n this.emit('purchase_completed', {\n priceId: markers.priceId,\n sessionId: markers.sessionId\n });\n // Acceleration: если страница загружена в новой вкладке от исходного\n // приложения (typical Stripe success_url flow), шлём opener'у postMessage.\n // Watcher в исходной вкладке среагирует мгновенно, не дожидаясь focus\n // event. Если opener'а нет (юзер закрыл/не было) — fallback на polling.\n notifyOpenerOfPurchase(markers);\n } else if (markers.status === 'failed' || markers.status === 'cancelled') {\n this.emit('purchase_failed', { reason: markers.status });\n }\n\n stripMarkersFromUrl(url);\n }\n\n destroy(): void {\n this.tracker?.destroy();\n this.tracker = null;\n this.listeners.clear();\n this.stateListeners.clear();\n this.watcher?.stop();\n this.watcher = null;\n this.userUnsub?.();\n this.userUnsub = null;\n this.authUnsub?.();\n this.authUnsub = null;\n // Если AuthClient был передан хостом — его жизненный цикл не наш,\n // ничего не дёргаем. Если создавали мы — отписываемся через BillingClient\n // (он сам держит listener на onAuthChange) и оставляем session в storage,\n // чтобы следующее открытие подхватило её через hydrate.\n if (this.ownsAuth && this.auth) {\n // Если AuthClient создавали мы — destroy сами, чтобы snapshot listener\n // отписался и не висел дальше. Externally-supplied auth не трогаем.\n this.auth.destroy?.();\n }\n this.ownsAuth = false;\n this.billing.destroy?.();\n this.handle?.unmount();\n this.handle = null;\n this.isOpen = false;\n this.currentState = CLOSED_STATE;\n }\n}\n\nfunction resolveAuth(opts: PaywallUIOptions): {\n auth: AuthClient | undefined;\n ownsAuth: boolean;\n} {\n if (!opts.auth) return { auth: undefined, ownsAuth: false };\n // Duck-typing: AuthClient ИЛИ структурный совместимец (RemoteAuthClient из\n // @monetize/sdk-extension). Проверяем по public-методам, которые\n // PaywallUI использует — если все на месте, доверяем. Это позволяет host'у\n // подставить proxy-реализацию (offscreen-architecture) без изменений в\n // PaywallUI. instanceof не подходит — runtime в content-script'е и в\n // sdk-extension'е разные, классы не nominally равны.\n if (opts.auth instanceof AuthClient || isAuthClientLike(opts.auth)) {\n return { auth: opts.auth as AuthClient, ownsAuth: false };\n }\n // true | partial-options → создаём свой AuthClient. apiOrigin/storage/fetch\n // подхватываем из общих опций PaywallUI, чтобы конфиг был \"одно поле — вся\n // система\". Юзер может перебить точечно через opts.auth = { apiOrigin: ... }.\n const cfg = opts.auth === true ? {} : opts.auth;\n return {\n auth: new AuthClient({\n paywallId: opts.paywallId,\n apiOrigin: cfg.apiOrigin ?? opts.apiOrigin,\n storage: cfg.storage ?? opts.storage,\n fetch: cfg.fetch ?? opts.fetch,\n openPopup: cfg.openPopup\n }),\n ownsAuth: true\n };\n}\n\n// Проверяет «AuthClient-подобность» переданного объекта по public-методам,\n// которые PaywallUI трогает (`onAuthChange`, `getCachedSession`, `signOut`).\n// Partial<AuthClientOptions> этих методов не имеет — пересечения с этим\n// объединением нет, ложноположительных не будет.\nfunction isAuthClientLike(value: unknown): value is AuthClient {\n if (typeof value !== 'object' || value === null) return false;\n const v = value as Record<string, unknown>;\n return (\n typeof v.onAuthChange === 'function' &&\n typeof v.getCachedSession === 'function' &&\n typeof v.signOut === 'function'\n );\n}\n\nfunction sameStateSnapshot(\n a: PaywallStateSnapshot,\n b: PaywallStateSnapshot\n): boolean {\n return a.open === b.open && a.view === b.view && a.error === b.error;\n}\n\nfunction sameTrialConfig(a: TrialConfig, b: TrialConfig): boolean {\n return a.mode === b.mode && a.payload === b.payload && a.storage === b.storage;\n}\n\nfunction parseMarkers(\n segment: string\n): { status: string; priceId: string | null; sessionId: string | null } | null {\n if (!segment) return null;\n const params = new URLSearchParams(segment);\n const status = params.get(URL_MARKERS.status);\n if (!status) return null;\n return {\n status,\n priceId: params.get(URL_MARKERS.priceId),\n sessionId: params.get(URL_MARKERS.sessionId)\n };\n}\n\n// Контракт сообщения должен совпадать с UserWatcher.handleMessage:\n// `{ type: 'paywall_purchase' }`. opener — исходная вкладка хоста, на ней живёт\n// PaywallUI с активным watcher'ом, ждущим этого сигнала.\nfunction notifyOpenerOfPurchase(markers: {\n status: string;\n priceId: string | null;\n sessionId: string | null;\n}): void {\n if (typeof window === 'undefined' || !window.opener) return;\n try {\n window.opener.postMessage(\n {\n type: 'paywall_purchase',\n status: markers.status,\n priceId: markers.priceId,\n sessionId: markers.sessionId\n },\n '*'\n );\n } catch {\n /* opener из другого origin или закрыт — watcher через focus подхватит */\n }\n}\n\nfunction stripMarkersFromUrl(url: URL): void {\n const clean = (raw: string, prefix: '?' | '#'): string => {\n if (!raw) return '';\n const p = new URLSearchParams(raw.replace(/^[?#]/, ''));\n p.delete(URL_MARKERS.status);\n p.delete(URL_MARKERS.priceId);\n p.delete(URL_MARKERS.sessionId);\n const out = p.toString();\n return out ? prefix + out : '';\n };\n const next = url.pathname + clean(url.search, '?') + clean(url.hash, '#');\n window.history.replaceState(null, '', next);\n}\n","// RemoteTrialStore — TrialStore-совместимый proxy. check / recordBlock /\n// reset идут через transport в offscreen, где реальный TrialStore работает\n// под navigator.locks — два таба не могут одновременно read-modify-write\n// один и тот же counter, drift'а нет.\n\nimport type { TrialStore } from '@sdk/core/trial';\nimport type { TrialConfig, TrialStatus } from '@sdk/core/types';\nimport type { TransportClient } from '../shared/transport-client';\n\nexport class RemoteTrialStore implements TrialStore {\n constructor(\n private readonly transport: TransportClient,\n private readonly paywallId: string,\n private readonly config: TrialConfig\n ) {}\n\n async check(): Promise<TrialStatus> {\n return this.transport.request('trial.check', {\n paywallId: this.paywallId,\n config: this.config\n });\n }\n\n async recordBlock(): Promise<TrialStatus> {\n return this.transport.request('trial.recordBlock', {\n paywallId: this.paywallId,\n config: this.config\n });\n }\n\n async reset(): Promise<void> {\n await this.transport.request('trial.reset', {\n paywallId: this.paywallId,\n config: this.config\n });\n }\n}\n","// RemoteBillingClient — структурный совместимец BillingClient, который\n// проксирует все методы в offscreen через TransportClient. Public API\n// идентичен (host пишет тот же код, что для @monetize.software/sdk), реализация\n// другая. Sync-getCached* остаются sync — ходят в локальный mirror, который\n// обновляется (a) ответами на async-методы и (b) broadcast-событиями\n// userChange/balancesChange.\n\nimport type {\n Balance,\n CheckoutResult,\n Identity,\n PaywallBootstrap,\n PaywallPrice,\n PaywallPurchaseDetailed,\n PaywallUser,\n TrialConfig\n} from '@sdk/core/types';\nimport type { StorageAdapter } from '@sdk/core/storage';\nimport type { TrialStore } from '@sdk/core/trial';\nimport { TransportClient } from '../shared/transport-client';\nimport { RemoteTrialStore } from './RemoteTrialStore';\n\nexport type UserListener = (user: PaywallUser) => void;\nexport type BalanceListener = (balances: Balance[]) => void;\n\nexport interface RemoteBillingClientOptions {\n paywallId: string;\n apiOrigin?: string;\n}\n\nexport class RemoteBillingClient {\n readonly paywallId: string;\n readonly apiOrigin: string | undefined;\n\n // Локальные mirror'ы. Источник правды — offscreen; mirror нужен только\n // чтобы getCached* оставались sync. Updated после каждого async-ответа +\n // на каждом broadcast-событии.\n private cachedBootstrap: PaywallBootstrap | null = null;\n private cachedUser: PaywallUser | null = null;\n private cachedBalances: Balance[] | null = null;\n private identity: Identity | null = null;\n /** Storage proxy через transport: get/set/remove идут в offscreen'овский\n * StorageAdapter (single source of truth для всех вкладок). Trial-state\n * PaywallUI пишет сюда — все табы видят один и тот же counter, не\n * drift'ит между вкладками.\n *\n * Race-окно read-modify-write всё ещё существует (две вкладки одновременно\n * читают N → пишут N-1, drift 1). Для exact atomicity нужен Phase 9:\n * TrialStore целиком переехать в offscreen и делать recordBlock как\n * single-handler с одной atomic-операцией. Это редкий edge case\n * (одновременное открытие пейвола в нескольких вкладках в рамках мс). */\n private remoteStorageAdapter: StorageAdapter;\n\n private userListeners = new Set<UserListener>();\n private balanceListeners = new Set<BalanceListener>();\n private unsubUserBroadcast: (() => void) | null = null;\n private unsubBalancesBroadcast: (() => void) | null = null;\n\n constructor(\n private readonly transport: TransportClient,\n opts: RemoteBillingClientOptions\n ) {\n this.paywallId = opts.paywallId;\n this.apiOrigin = opts.apiOrigin;\n\n this.remoteStorageAdapter = {\n getItem: (key) => this.transport.request('storage.get', { key }),\n setItem: async (key, value) => {\n await this.transport.request('storage.set', { key, value });\n },\n removeItem: async (key) => {\n await this.transport.request('storage.remove', { key });\n }\n // watch не реализуем — для cross-context уведомлений consumer'ы (AuthClient,\n // TrialStore) подписываются на broadcast-events напрямую через transport.\n // Если когда-то понадобится — добавим storage.watch broadcast.\n };\n\n this.unsubUserBroadcast = this.transport.on('userChange', (user) => {\n this.applyUser(user);\n });\n\n this.unsubBalancesBroadcast = this.transport.on('balancesChange', (balances) => {\n this.applyBalances([...balances]);\n });\n }\n\n // === Bootstrap ===\n\n async bootstrap(opts: { force?: boolean; signal?: AbortSignal } = {}): Promise<PaywallBootstrap> {\n const result = await this.transport.request(\n 'billing.bootstrap',\n { force: opts.force },\n { signal: opts.signal }\n );\n this.cachedBootstrap = result;\n if (result.user) this.applyUser(result.user);\n return result;\n }\n\n getCachedBootstrap(): PaywallBootstrap | null {\n return this.cachedBootstrap;\n }\n\n /** Шорткат над `bootstrap()` — возвращает цены пейвола (locale-оверрайды\n * уже применены в offscreen'е). Те же кэш-семантики, что у `bootstrap()`. */\n async getPrices(opts: { force?: boolean; signal?: AbortSignal } = {}): Promise<PaywallPrice[]> {\n const b = await this.bootstrap(opts);\n return b.prices;\n }\n\n /** Sync-снимок цен из локального mirror'а bootstrap'а. null = ещё не грузили. */\n getCachedPrices(): PaywallPrice[] | null {\n return this.cachedBootstrap?.prices ?? null;\n }\n\n // === Visitor ===\n\n async getVisitorId(): Promise<string> {\n return this.transport.request('billing.getVisitorId', undefined);\n }\n\n // === User ===\n\n async getUser(opts: { force?: boolean; signal?: AbortSignal } = {}): Promise<PaywallUser> {\n const result = await this.transport.request(\n 'billing.getUser',\n { force: opts.force },\n { signal: opts.signal }\n );\n this.applyUser(result);\n return result;\n }\n\n getCachedUser(): PaywallUser | null {\n return this.cachedUser;\n }\n\n /** Подписка на user-state. Mirror'имся на broadcast'ы offscreen'а; initial\n * snapshot отдаётся через microtask из локального cache (если есть) —\n * ровно как в BillingClient.onUserChange. Возвращает функцию отписки. */\n onUserChange(\n cb: UserListener,\n opts: { immediate?: 'microtask' | 'sync' | 'none' } = {}\n ): () => void {\n this.userListeners.add(cb);\n const mode = opts.immediate ?? 'microtask';\n if (this.cachedUser && mode !== 'none') {\n const snapshot = this.cachedUser;\n if (mode === 'sync') {\n try {\n cb(snapshot);\n } catch (e) {\n console.warn('[paywall] onUserChange initial sync threw', e);\n }\n } else {\n queueMicrotask(() => {\n if (this.userListeners.has(cb)) cb(snapshot);\n });\n }\n }\n return () => {\n this.userListeners.delete(cb);\n };\n }\n\n // === Balances ===\n\n async getBalances(opts: { force?: boolean; signal?: AbortSignal } = {}): Promise<Balance[]> {\n const result = await this.transport.request(\n 'billing.getBalances',\n { force: opts.force },\n { signal: opts.signal }\n );\n const arr = [...result];\n this.applyBalances(arr);\n return arr;\n }\n\n getCachedBalances(): Balance[] | null {\n return this.cachedBalances;\n }\n\n onBalanceChange(\n cb: BalanceListener,\n opts: { immediate?: 'microtask' | 'sync' | 'none' } = {}\n ): () => void {\n this.balanceListeners.add(cb);\n const mode = opts.immediate ?? 'microtask';\n if (this.cachedBalances && mode !== 'none') {\n const snapshot = this.cachedBalances;\n if (mode === 'sync') {\n try {\n cb(snapshot);\n } catch (e) {\n console.warn('[paywall] onBalanceChange initial sync threw', e);\n }\n } else {\n queueMicrotask(() => {\n if (this.balanceListeners.has(cb)) cb(snapshot);\n });\n }\n }\n return () => {\n this.balanceListeners.delete(cb);\n };\n }\n\n // === Checkout ===\n\n async createCheckout(params: {\n priceId: string;\n successUrl?: string;\n errorUrl?: string;\n shopUrl?: string;\n trialDays?: number;\n idempotencyKey?: string;\n ignoreActivePurchase?: boolean;\n signal?: AbortSignal;\n }): Promise<CheckoutResult> {\n const { signal, ...payload } = params;\n return this.transport.request('billing.createCheckout', payload, { signal });\n }\n\n // === Customer portal: list/cancel purchases ===\n\n /** Rich-shape список покупок юзера (с ценой, валютой, interval, discount,\n * cancel-метаданными). Через offscreen — там настоящий BillingClient\n * ходит на `/api/v1/paywall/[id]/user` с Bearer'ом. Полезно для\n * customer-portal UI: cards + Cancel/Renew кнопки. */\n async listPurchases(opts: { signal?: AbortSignal } = {}): Promise<PaywallPurchaseDetailed[]> {\n const result = await this.transport.request('billing.listPurchases', undefined, {\n signal: opts.signal\n });\n return [...result];\n }\n\n /** Отменить подписку через бэк. По умолчанию cancel в конце текущего\n * периода (юзер сохраняет access до renewal date'ы). reason обязательна\n * (валидируется бэком) — собирается через select причин в host-UI. */\n async cancelSubscription(params: {\n subscriptionId: string;\n reason: string;\n signal?: AbortSignal;\n }): Promise<{\n subscription: {\n status: string | null;\n canceled_at: string | null;\n cancel_at: string | null;\n cancel_at_period_end: boolean | null;\n };\n }> {\n const { signal, ...payload } = params;\n return this.transport.request('billing.cancelSubscription', payload, { signal });\n }\n\n // === Storage ===\n\n /** PaywallUI просит storage у billing-клиента для TrialStore и других\n * consumer'ов. Возвращает proxy: get/set/remove идут через transport\n * в offscreen'овский storage = single source of truth для всех вкладок. */\n getStorage(): StorageAdapter {\n return this.remoteStorageAdapter;\n }\n\n /** Factory-метод для PaywallUI: вместо локального createTrialStore'а на\n * storage-proxy, возвращаем RemoteTrialStore — он шлёт каждую операцию\n * одним атомарным RPC в offscreen, где navigator.locks сериализуют\n * read-modify-write. PaywallUI duck-types этот метод и предпочитает его\n * локальной фабрике, если он есть. */\n createTrialStore(config: TrialConfig): TrialStore {\n return new RemoteTrialStore(this.transport, this.paywallId, config);\n }\n\n // === Identity ===\n\n getIdentity(): Identity | null {\n return this.identity;\n }\n\n async setIdentity(identity: Identity | null): Promise<void> {\n this.identity = identity;\n await this.transport.request('billing.setIdentity', { identity });\n }\n\n /** Подгрузить identity с offscreen'а. Используется при первом подключении\n * content-script'а — если другая вкладка уже залогинила юзера, текущая\n * тут же подхватит identity без ожидания authChange. */\n async syncIdentity(): Promise<Identity | null> {\n const result = await this.transport.request('billing.getIdentity', undefined);\n this.identity = result;\n return result;\n }\n\n destroy(): void {\n this.unsubUserBroadcast?.();\n this.unsubBalancesBroadcast?.();\n this.unsubUserBroadcast = null;\n this.unsubBalancesBroadcast = null;\n this.userListeners.clear();\n this.balanceListeners.clear();\n this.cachedBootstrap = null;\n this.cachedUser = null;\n this.cachedBalances = null;\n this.identity = null;\n }\n\n /** Обновить mirror user'а и эмитнуть listener'ам если он реально изменился.\n * Используется и для self-инициированных RPC (bootstrap/getUser), и для\n * broadcast'ов от offscreen — чтобы host'овский onUserChange handler\n * получил signal независимо от того, кто триггернул обновление. */\n private applyUser(user: PaywallUser): void {\n if (sameUser(this.cachedUser, user)) return;\n this.cachedUser = user;\n this.fireUserListeners(user);\n }\n\n private applyBalances(balances: Balance[]): void {\n if (sameBalances(this.cachedBalances, balances)) return;\n this.cachedBalances = balances;\n this.fireBalanceListeners(balances);\n }\n\n private fireUserListeners(user: PaywallUser): void {\n for (const cb of [...this.userListeners]) {\n try {\n cb(user);\n } catch (e) {\n console.warn('[paywall] onUserChange listener threw', e);\n }\n }\n }\n\n private fireBalanceListeners(balances: Balance[]): void {\n for (const cb of [...this.balanceListeners]) {\n try {\n cb(balances);\n } catch (e) {\n console.warn('[paywall] onBalanceChange listener threw', e);\n }\n }\n }\n}\n\nfunction sameUser(a: PaywallUser | null, b: PaywallUser | null): boolean {\n if (a === b) return true;\n if (!a || !b) return false;\n return (\n a.has_active_subscription === b.has_active_subscription &&\n (a.purchases?.length ?? 0) === (b.purchases?.length ?? 0)\n );\n}\n\nfunction sameBalances(a: Balance[] | null, b: Balance[] | null): boolean {\n if (a === b) return true;\n if (!a || !b) return false;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i].type !== b[i].type || a[i].count !== b[i].count) return false;\n }\n return true;\n}\n","// RemoteAuthClient — структурный совместимец AuthClient. Public методы\n// идентичны, под капотом — async-прокси через TransportClient в offscreen,\n// где живёт реальная сессия и storage.\n//\n// Sync-getCachedSession поддерживается через локальный mirror, обновляемый\n// (a) на каждом ответе async-метода, (b) на broadcast'е authChange.\n//\n// OAuth (signInWithOAuth) пока бросает not-implemented — требует publicного\n// split-API в @sdk/core/auth (Phase 4.5). Для email/password/refresh/signOut\n// и прочей сетевой части — всё работает.\n\nimport type { AuthSession, AuthUser, OAuthProvider, OtpVerifyType, SignUpResult } from '@sdk/core/auth';\nimport { waitForOAuthCode } from '@sdk/core/auth';\nimport { PaywallError } from '@sdk/core/types';\nimport { TransportClient } from '../shared/transport-client';\n\nexport type AuthChangeListener = (session: AuthSession | null) => void;\n\nexport interface RemoteAuthClientOptions {\n paywallId: string;\n apiOrigin?: string;\n}\n\nexport class RemoteAuthClient {\n readonly paywallId: string;\n readonly apiOrigin: string | undefined;\n\n private session: AuthSession | null = null;\n private listeners = new Set<AuthChangeListener>();\n private unsubBroadcast: (() => void) | null = null;\n private hydrated: Promise<void>;\n\n constructor(\n private readonly transport: TransportClient,\n opts: RemoteAuthClientOptions\n ) {\n this.paywallId = opts.paywallId;\n this.apiOrigin = opts.apiOrigin;\n\n this.unsubBroadcast = this.transport.on('authChange', (session) => {\n this.applySession(session);\n });\n\n // Initial sync с offscreen'а — чтобы getCachedSession() мог отдать что-то\n // не-null уже на первом тике после ready().\n this.hydrated = this.transport\n .request('auth.getCachedSession', undefined)\n .then((session) => {\n if (this.session === null && session !== null) {\n this.applySession(session);\n }\n })\n .catch(() => {\n /* offscreen не готов или транспорт упал — getCachedSession отдаст null */\n });\n }\n\n /** Promise, который резолвится после первичной синхронизации session с\n * offscreen'а. Аналог AuthClient.ready(). */\n ready(): Promise<void> {\n return this.hydrated;\n }\n\n getCachedSession(): AuthSession | null {\n return this.session;\n }\n\n getCachedUser(): AuthUser | null {\n return this.session?.user ?? null;\n }\n\n onAuthChange(cb: AuthChangeListener): () => void {\n this.listeners.add(cb);\n // Initial snapshot через microtask — match AuthClient.onAuthChange UX.\n if (this.session) {\n const snapshot = this.session;\n queueMicrotask(() => {\n if (this.listeners.has(cb)) cb(snapshot);\n });\n }\n return () => {\n this.listeners.delete(cb);\n };\n }\n\n // === Email/password ===\n\n async signInWithEmail(input: { email: string; password: string }): Promise<AuthSession> {\n const session = await this.transport.request('auth.signInWithEmail', input);\n this.applySession(session);\n return session;\n }\n\n async signUp(input: {\n email: string;\n password: string;\n userMeta?: Record<string, string>;\n }): Promise<SignUpResult> {\n const result = await this.transport.request('auth.signUp', input);\n if (result.kind === 'signed_in') this.applySession(result.session);\n return result;\n }\n\n async signOut(): Promise<void> {\n await this.transport.request('auth.signOut', undefined);\n // Broadcast authChange придёт от offscreen'а с session=null, applySession\n // там уже отработает. Тут ничего не делаем, чтобы не дёрнуть listener'ы\n // дважды.\n }\n\n async refresh(): Promise<AuthSession | null> {\n const session = await this.transport.request('auth.refresh', undefined);\n this.applySession(session);\n return session;\n }\n\n // === OTP / password reset / confirmation ===\n\n async sendOtp(input: {\n email: string;\n createUser?: boolean;\n userMeta?: Record<string, unknown>;\n }): Promise<void> {\n await this.transport.request('auth.sendOtp', input);\n }\n\n async verifyOtp(input: {\n email: string;\n token: string;\n type: OtpVerifyType;\n }): Promise<AuthSession> {\n const session = await this.transport.request('auth.verifyOtp', input);\n this.applySession(session);\n return session;\n }\n\n async resendConfirmation(input: { email: string }): Promise<void> {\n await this.transport.request('auth.resendConfirmation', input);\n }\n\n async requestPasswordReset(input: { email: string }): Promise<void> {\n await this.transport.request('auth.requestPasswordReset', input);\n }\n\n async updatePassword(input: { password: string }): Promise<void> {\n await this.transport.request('auth.updatePassword', input);\n }\n\n async revokeAllSessions(): Promise<void> {\n await this.transport.request('auth.revokeAllSessions', undefined);\n }\n\n // === Anonymous sign-in ===\n\n /** Анонимный sign-in (Supabase user без email). Логика (idempotent-check +\n * resume через сохранённый refresh_token + fresh signin) живёт в\n * offscreen-AuthClient'е — content только проксирует. captchaToken и\n * forceCaptcha — pass-through для forward-compat / switch-account flow. */\n async signInAnonymously(input: {\n captchaToken?: string;\n userMeta?: Record<string, string>;\n forceCaptcha?: boolean;\n } = {}): Promise<AuthSession> {\n const session = await this.transport.request('auth.signInAnonymously', {\n captchaToken: input.captchaToken,\n userMeta: input.userMeta,\n forceCaptcha: input.forceCaptcha\n });\n this.applySession(session);\n return session;\n }\n\n /** Текущий access token (lazy-refreshable в offscreen'е). content/popup\n * использует для Bearer'а в внешние fetch'и — ApiGatewayClient в\n * content-script'е, прямые запросы из demo-UI. null если разлогинен или\n * offscreen'овский AuthClient не смог рефрешнуть. */\n async getAccessToken(): Promise<string | null> {\n return this.transport.request('auth.getAccessToken', undefined);\n }\n\n // === OAuth (web-flow через split-API) ===\n\n /** OAuth через web-вариант: window.open в content-script'е, provider\n * redirect, callback page постит code в opener. Под капотом split на\n * два request'а в offscreen — startOAuthFlow (дёргаем /init, получаем\n * authorize_url) → открываем popup → waitForOAuthCode → exchange.\n *\n * PKCE verifier живёт ТОЛЬКО в offscreen'е (внутри AuthClient'а), через\n * runtime-границу не идёт. Content получает только authorize_url и state.\n *\n * Popup-gesture: `window.open(authorize_url, ...)` идёт в том же synchronous\n * flow'е, что startOAuthFlow ответ; user-gesture сохраняется потому что\n * content-script unloaded не за этот tick (gesture сохраняется через все\n * microtask'и одного call stack'а). Если в каком-то браузере gesture\n * всё-таки теряется — host получит `popup_blocked` (тот же что в @monetize.software/sdk).\n */\n async signInWithOAuth(input: {\n provider: OAuthProvider;\n scopes?: string;\n userMeta?: Record<string, string>;\n onPopupOpened?: () => void;\n }): Promise<AuthSession> {\n if (typeof window === 'undefined') {\n throw new PaywallError('oauth_unavailable', 'window is required for OAuth');\n }\n\n // Открываем popup СИНХРОННО — user-gesture сохраняется только в том же\n // synchronous frame, что click-handler. Async `await` на transport.request\n // до window.open съедает gesture, и Chrome открывает popup с пустым URL'ом\n // / блокирует совсем.\n //\n // about:blank вместо data:text/html (которым раньше показывали inline-loader):\n // data:-URL'ы триггерят static-сканеры CWS и EDR'ы как подозрительные. Вместо\n // этого открываем about:blank (наследует origin opener'а) и инжектим loader-DOM\n // через document.createElement + textContent — ровно то же UX, без data:-URL'а.\n const tempName = `pw-oauth-pending-${Math.random().toString(36).slice(2, 10)}`;\n const popup = window.open('about:blank', tempName, 'width=480,height=640,popup=yes');\n if (!popup) {\n throw new PaywallError(\n 'popup_blocked',\n 'browser blocked auth popup — call from a user gesture'\n );\n }\n injectLoaderUI(popup, input.provider);\n\n try {\n // Async-часть: дёргаем offscreen за authorize_url и state. Popup пока\n // показывает about:blank.\n const { authorizeUrl, state } = await this.transport.request('auth.oauthStart', {\n provider: input.provider,\n scopes: input.scopes,\n userMeta: input.userMeta\n });\n\n // Перед navigate'ом меняем имя popup'а на формат, который ожидает\n // callback page (pw-oauth-<state>) — name переживает cross-origin\n // редиректы (Google → Supabase → наш callback). callback page\n // читает window.name → извлекает state → posts back.\n popup.name = `pw-oauth-${state}`;\n popup.location.replace(authorizeUrl);\n\n input.onPopupOpened?.();\n\n const code = await waitForOAuthCode(popup, state);\n const session = await this.transport.request('auth.oauthExchange', { state, code });\n this.applySession(session);\n return session;\n } catch (e) {\n try {\n popup.close();\n } catch {\n /* ignore */\n }\n throw e;\n }\n }\n\n destroy(): void {\n this.unsubBroadcast?.();\n this.unsubBroadcast = null;\n this.listeners.clear();\n this.session = null;\n }\n\n private applySession(next: AuthSession | null): void {\n if (sameSession(this.session, next)) return;\n this.session = next;\n for (const cb of [...this.listeners]) {\n try {\n cb(next);\n } catch (e) {\n console.warn('[paywall] onAuthChange listener threw', e);\n }\n }\n }\n}\n\nfunction sameSession(a: AuthSession | null, b: AuthSession | null): boolean {\n if (a === b) return true;\n if (!a || !b) return false;\n return (\n a.access_token === b.access_token &&\n a.refresh_token === b.refresh_token &&\n a.expires_at === b.expires_at &&\n a.user.id === b.user.id\n );\n}\n\nconst PROVIDER_NAMES: Record<string, string> = {\n google: 'Google',\n apple: 'Apple',\n github: 'GitHub',\n facebook: 'Facebook'\n};\n\n/** Inject loader-UI в about:blank popup. Same-origin как opener — мы можем\n * трогать popup.document напрямую. Используем createElement + textContent\n * (не innerHTML / document.write) чтобы не триггерить XSS-сканеры даже на\n * hard-coded строках. CSS-классы с pw-oauth-* префиксом — без коллизий со\n * стилями родительской страницы (popup всё равно изолирован, но на всякий).\n *\n * Defensive try/catch: если в каком-то edge-кейсе popup оказался не\n * same-origin (некоторые расширения это перехватывают) или document не\n * доступен — тихо забиваем, popup покажет дефолтный about:blank на 200-500мс\n * до redirect'а на provider'а. */\nfunction injectLoaderUI(popup: Window, provider: string): void {\n const name = PROVIDER_NAMES[provider] ?? provider;\n try {\n const doc = popup.document;\n doc.title = `Sign in with ${name}`;\n\n const style = doc.createElement('style');\n style.textContent =\n 'html,body{margin:0;padding:0;height:100%;font-family:-apple-system,system-ui,sans-serif;background:#fafafa;color:#475569}' +\n '.pw-oauth-wrap{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:16px}' +\n '.pw-oauth-spinner{width:36px;height:36px;border:3px solid #e2e8f0;border-top-color:#7c3aed;border-radius:50%;animation:pw-oauth-spin 800ms linear infinite}' +\n '.pw-oauth-label{font-size:14px;font-weight:500;letter-spacing:-0.01em}' +\n '@keyframes pw-oauth-spin{to{transform:rotate(360deg)}}';\n doc.head.appendChild(style);\n\n const wrap = doc.createElement('div');\n wrap.className = 'pw-oauth-wrap';\n const spinner = doc.createElement('div');\n spinner.className = 'pw-oauth-spinner';\n const label = doc.createElement('div');\n label.className = 'pw-oauth-label';\n label.textContent = `Connecting to ${name}…`;\n wrap.appendChild(spinner);\n wrap.appendChild(label);\n doc.body.appendChild(wrap);\n } catch {\n /* popup not same-origin or document not ready — fall back to blank */\n }\n}\n","// RemoteEventTracker — fire-and-forget proxy для аналитики. Все track()\n// call'ы из всех вкладок попадают в единственный EventTracker в offscreen'е,\n// который батчит и шлёт в /events. Победа — один батч на расширение,\n// один sendBeacon на unload, никаких дублирующихся `app_opened` событий.\n//\n// API специально минимальный — только track(name, props). Buffer / flush /\n// destroy логика живёт в offscreen'е, content её не контролирует.\n\nimport { TransportClient } from '../shared/transport-client';\n\nexport class RemoteEventTracker {\n constructor(private readonly transport: TransportClient) {}\n\n /** Отправить событие. Fire-and-forget — не возвращает Promise, не throw'ает.\n * Сетевые/транспортные ошибки логируются в console и не блокируют caller. */\n track(name: string, props?: Record<string, unknown>): void {\n if (typeof name !== 'string' || name.length === 0) return;\n this.transport.request('tracker.track', { name, props }).catch((e) => {\n console.warn('[paywall] track failed', e);\n });\n }\n}\n","// Client-side транспорт. Используется в content-script'е (поверх chrome.runtime\n// port'а к SW) и в любом другом surface'е, который ходит к offscreen через\n// тот же роутер (popup, extension page, side panel).\n//\n// Контракт:\n// - request<K>(kind, params, signal?) — запрос с типизированным результатом.\n// На disconnect канала pending request'ы reject'аются с reconnect-error;\n// next call воссоздаст канал через ChannelFactory и продолжит работу.\n// - on<K>(kind, handler) — подписка на broadcast от сервера. Переподписки\n// переживают reconnect автоматически — handler'ы хранятся локально, а\n// re-subscribe на сервере не требуется (сервер всегда broadcast'ит всем\n// подключённым каналам).\n//\n// Reconnect стратегия: lazy. Канал поднимается при первом request/on, мёртвый —\n// пересоздаётся в момент следующего запроса. Никаких exponential backoff'ов\n// в фоне — extension контекст это не любит (расход CPU + батарея).\n\nimport type {\n EventEnvelope,\n EventKind,\n EventPayload,\n RequestEnvelope,\n RequestKind,\n RequestParams,\n RequestResult,\n ResponseEnvelope,\n ResponseErr\n} from './protocol';\nimport { PROTOCOL_VERSION } from './protocol';\nimport { reconstructError } from './errors';\nimport type { ChannelFactory, MessageChannel } from './channel';\n\ninterface PendingRequest {\n resolve: (value: unknown) => void;\n reject: (reason: unknown) => void;\n abortListener?: () => void;\n signal?: AbortSignal;\n}\n\nexport class TransportClient {\n private channel: MessageChannel | null = null;\n private channelDisposers: Array<() => void> = [];\n private pending = new Map<string, PendingRequest>();\n private listeners = new Map<EventKind, Set<(payload: unknown) => void>>();\n private destroyed = false;\n private nextId = 0;\n /** Уникальный ID клиента — отправляется в handshake'е, server может логировать\n * для отладки connection-flap'а. */\n private readonly clientId = `c-${Math.random().toString(36).slice(2, 10)}`;\n\n constructor(private readonly factory: ChannelFactory) {}\n\n /** Гарантирует наличие живого канала. Lazy — поднимается при первом request.\n * Сразу после connect'а fire-and-forget шлёт handshake — на mismatch\n * логируем warning, но не блокируем дальнейшие запросы. */\n private ensureChannel(): MessageChannel {\n if (this.destroyed) throw new Error('TransportClient destroyed');\n if (this.channel) return this.channel;\n\n const channel = this.factory();\n this.channel = channel;\n\n const offMsg = channel.onMessage((env) => this.handleMessage(env));\n const offDisc = channel.onDisconnect(() => this.handleDisconnect());\n this.channelDisposers = [offMsg, offDisc];\n\n // Async, без await: основные запросы могут параллельно идти. На mismatch'е\n // ничего не ломаем — server может быть на другой минорной версии (например,\n // host обновил sdk-extension но не sdk).\n void this.request('handshake', {\n protocolVersion: PROTOCOL_VERSION,\n clientId: this.clientId\n })\n .then((res) => {\n if (res.protocolVersion !== PROTOCOL_VERSION) {\n console.warn(\n `[sdk-extension] protocol version mismatch: client=${PROTOCOL_VERSION}, ` +\n `offscreen=${res.protocolVersion}. Update host's @monetize.software/sdk-extension.`\n );\n }\n })\n .catch(() => {\n // Server без handshake-handler'а или умер — best-effort, не падаем.\n });\n\n return channel;\n }\n\n private handleMessage(envelope: unknown): void {\n if (!isEnvelope(envelope)) return;\n if (envelope.type === 'response') {\n const pending = this.pending.get(envelope.id);\n if (!pending) return;\n this.pending.delete(envelope.id);\n pending.signal?.removeEventListener('abort', pending.abortListener!);\n if (envelope.ok) {\n pending.resolve(envelope.result);\n } else {\n // Narrowing на discriminated union с generic'ом теряется в strict-режиме —\n // явный cast стабильнее, ok===false уже проверен.\n pending.reject(reconstructError((envelope as ResponseErr).error));\n }\n return;\n }\n if (envelope.type === 'event') {\n const set = this.listeners.get(envelope.kind);\n if (!set) return;\n // Snapshot, чтобы handler мог отписать сам себя без NaN-итерации.\n for (const handler of [...set]) {\n try {\n handler(envelope.payload);\n } catch (e) {\n console.error('[sdk-extension] event handler threw', e);\n }\n }\n }\n }\n\n private handleDisconnect(): void {\n for (const fn of this.channelDisposers) fn();\n this.channelDisposers = [];\n this.channel = null;\n // Reject все in-flight — они идут с reconnect-кодом, host может ретрайнуть.\n const pending = Array.from(this.pending.values());\n this.pending.clear();\n for (const p of pending) {\n p.signal?.removeEventListener('abort', p.abortListener!);\n p.reject(new TransportDisconnectedError());\n }\n }\n\n request<K extends RequestKind>(\n kind: K,\n params: RequestParams<K>,\n opts: { signal?: AbortSignal } = {}\n ): Promise<RequestResult<K>> {\n if (this.destroyed) {\n return Promise.reject(new Error('TransportClient destroyed'));\n }\n if (opts.signal?.aborted) {\n return Promise.reject(new DOMException('Aborted', 'AbortError'));\n }\n\n const channel = this.ensureChannel();\n const id = `r${++this.nextId}`;\n\n return new Promise<RequestResult<K>>((resolve, reject) => {\n const pending: PendingRequest = {\n resolve: resolve as (value: unknown) => void,\n reject,\n signal: opts.signal\n };\n\n if (opts.signal) {\n pending.abortListener = () => {\n if (this.pending.delete(id)) {\n reject(new DOMException('Aborted', 'AbortError'));\n // Послать cancel в offscreen чтобы там тоже abort'нуть underlying\n // fetch. Best-effort: канал мог отвалиться — тогда send бросит,\n // но pending уже удалён, юзер уже получил abort error.\n try {\n channel.send({ type: 'cancel', id });\n } catch {\n /* channel dead — server'у уже всё равно */\n }\n }\n };\n opts.signal.addEventListener('abort', pending.abortListener);\n }\n\n this.pending.set(id, pending);\n\n const envelope: RequestEnvelope<RequestParams<K>> = {\n type: 'request',\n id,\n kind,\n params\n };\n try {\n channel.send(envelope);\n } catch (e) {\n this.pending.delete(id);\n opts.signal?.removeEventListener('abort', pending.abortListener!);\n reject(e);\n }\n });\n }\n\n on<K extends EventKind>(\n kind: K,\n handler: (payload: EventPayload<K>) => void\n ): () => void {\n let set = this.listeners.get(kind);\n if (!set) {\n set = new Set();\n this.listeners.set(kind, set);\n }\n const wrapped = handler as (payload: unknown) => void;\n set.add(wrapped);\n\n // Lazy ensureChannel: подписка не требует немедленного канала, но первый\n // event может прилететь только если канал жив. Поднимаем заранее.\n this.ensureChannel();\n\n return () => {\n set!.delete(wrapped);\n };\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n for (const fn of this.channelDisposers) fn();\n this.channelDisposers = [];\n this.listeners.clear();\n const pending = Array.from(this.pending.values());\n this.pending.clear();\n for (const p of pending) {\n p.signal?.removeEventListener('abort', p.abortListener!);\n p.reject(new Error('TransportClient destroyed'));\n }\n this.channel?.close();\n this.channel = null;\n }\n}\n\nexport class TransportDisconnectedError extends Error {\n readonly code = 'transport_disconnected';\n constructor() {\n super('Transport channel disconnected mid-request');\n this.name = 'TransportDisconnectedError';\n }\n}\n\nfunction isEnvelope(value: unknown): value is RequestEnvelope | ResponseEnvelope | EventEnvelope {\n if (typeof value !== 'object' || value === null) return false;\n const t = (value as { type?: unknown }).type;\n return t === 'request' || t === 'response' || t === 'event';\n}\n","// Content-side singleton TransportClient. Один на content-script, переиспользуется\n// всеми инстансами PaywallUI на одной странице (на странице обычно один, но\n// несколько технически возможны — например один пейвол в overlay'е, другой\n// в popup'е host-расширения, оба внутри content-script'а одной страницы).\n\nimport { TransportClient } from '../shared/transport-client';\nimport { createRuntimeChannel } from '../shared/chrome-port';\nimport { PORT_NAME } from '../shared/port-name';\n\nlet cached: TransportClient | null = null;\n\nexport function getContentTransport(): TransportClient {\n if (cached) return cached;\n cached = new TransportClient(() => createRuntimeChannel(PORT_NAME));\n return cached;\n}\n\n/** Тестовая инжекция — для unit-тестов RemoteBillingClient'а с фейковым\n * каналом (без chrome.runtime). В проде не используется. */\nexport function _setContentTransportForTests(client: TransportClient | null): void {\n cached = client;\n}\n","// Drop-in `PaywallUI` для extension'а. Public API идентичен `@monetize.software/sdk`'у —\n// host пишет тот же код, опции те же. Под капотом:\n// - billing — RemoteBillingClient (proxy в offscreen)\n// - auth — RemoteAuthClient (когда `auth: true`)\n// - tracker — RemoteEventTracker (события forward'ятся в offscreen-EventTracker)\n//\n// EventTracker создаётся ОДИН на расширение, в offscreen'е. PaywallUI здесь\n// внутренний tracker отключает (`analytics: false` в base-конструкторе) и\n// сам подписывается на public-события, проксируя их через RemoteEventTracker.\n// Дубликат биндингов из base PaywallUI.initTracker — но это меньшее зло, чем\n// два EventTracker'а на одного юзера.\n\nimport { PaywallUI as BasePaywallUI, type PaywallUIOptions } from '@sdk/ui/PaywallUI';\nimport type { BillingClient } from '@sdk/core/BillingClient';\nimport type { AuthClient } from '@sdk/core/auth';\nimport { RemoteBillingClient } from './RemoteBillingClient';\nimport { RemoteAuthClient } from './RemoteAuthClient';\nimport { RemoteEventTracker } from './RemoteEventTracker';\nimport { getContentTransport } from './transport';\n\n/** Опции extension'овского PaywallUI. Убраны:\n * - `client` — RemoteBillingClient создаётся автоматически\n * - `storage` — storage живёт в offscreen'е, content его не видит\n * - `apiKey` — server-SDK key, не имеет смысла в content-script'е\n * - `fetch` — все сетевые запросы идут через offscreen\n *\n * `auth: true` подключит RemoteAuthClient. Передавать готовый AuthClient\n * из @monetize.software/sdk сюда не имеет смысла (мы хотим именно offscreen'овский). */\nexport interface ExtensionPaywallUIOptions\n extends Omit<PaywallUIOptions, 'client' | 'storage' | 'apiKey' | 'fetch'> {}\n\nexport class PaywallUI extends BasePaywallUI {\n /** RemoteEventTracker (proxy в offscreen-EventTracker). Не путать с\n * base-классовым `tracker` (там null — мы отключили внутренний). */\n private remoteTracker: RemoteEventTracker | null = null;\n private trackerUnsubs: Array<() => void> = [];\n\n constructor(opts: ExtensionPaywallUIOptions) {\n const transport = getContentTransport();\n\n const billing = new RemoteBillingClient(transport, {\n paywallId: opts.paywallId,\n apiOrigin: opts.apiOrigin\n });\n\n // Auth: если host попросил — конструируем RemoteAuthClient. Готовый\n // AuthClient из @monetize.software/sdk сюда не имеет смысла прокидывать (всё\n // равно нужен offscreen-instance). Поэтому accept только `true` или\n // ничего; явный AuthClient instance логирует warning и игнорится.\n let auth: RemoteAuthClient | undefined;\n if (opts.auth === true) {\n auth = new RemoteAuthClient(transport, {\n paywallId: opts.paywallId,\n apiOrigin: opts.apiOrigin\n });\n } else if (opts.auth) {\n console.warn(\n '[sdk-extension] passing AuthClient instance to PaywallUI.opts.auth ' +\n 'is not supported in extension mode — pass `auth: true` to use ' +\n 'offscreen-shared auth, or omit for hybrid identity-only mode.'\n );\n }\n\n // Прокидываем auth внутрь billing-клиента: PaywallRoot читает\n // `client.auth` для restore / preauth-flow / signin-detection. Настоящий\n // BillingClient выставляет это поле в конструкторе — у Remote-варианта\n // делаем явное присваивание перед super(), чтобы PaywallRoot увидел.\n if (auth) {\n (billing as { auth?: typeof auth }).auth = auth;\n }\n\n super({\n ...opts,\n // Cast'ы безопасны: PaywallUI'ев resolveAuth duck-type'ит auth (см.\n // sdk/src/ui/PaywallUI.ts isAuthClientLike), а billing-параметр идёт\n // через `opts.client ?? new BillingClient(...)` — RemoteBillingClient\n // там используется как есть, методы все сходятся.\n client: billing as unknown as BillingClient,\n auth: auth as unknown as AuthClient | undefined,\n // Внутренний EventTracker отключаем — единственный tracker живёт в\n // offscreen'е. Манчиально подписываемся ниже.\n analytics: false\n });\n\n if (opts.analytics !== false) {\n this.remoteTracker = new RemoteEventTracker(transport);\n this.bindAnalytics();\n }\n }\n\n /** Зеркало sdk/PaywallUI.initTracker'овских биндингов, но с RemoteEventTracker.\n * Когда @monetize.software/sdk экспоузнет публичный hook для inject'а tracker'а,\n * этот метод заменится на одну строку. */\n private bindAnalytics(): void {\n const t = this.remoteTracker;\n if (!t) return;\n\n this.trackerUnsubs.push(\n this.on('open', () => t.track('paywall_opened')),\n this.on('ready', (b) =>\n t.track('paywall_viewed', {\n is_test_mode: b.settings.is_test_mode,\n prices_count: b.prices.length,\n offers_count: b.offers.length\n })\n ),\n this.on('price_selected', (p) =>\n t.track('price_selected', { price_id: p.priceId })\n ),\n this.on('checkout_started', (p) =>\n t.track('checkout_started', { price_id: p.priceId, acquiring: p.acquiring })\n ),\n this.on('purchase_completed', (p) =>\n t.track('purchase_completed', { price_id: p.priceId, session_id: p.sessionId })\n ),\n this.on('purchase_failed', (p) => t.track('purchase_failed', { reason: p.reason })),\n this.on('close', () => t.track('paywall_closed')),\n this.on('trial_blocked', (s) =>\n t.track('trial_blocked', {\n mode: s.mode,\n ...(s.mode === 'time'\n ? { remaining_ms: s.remainingMs, total_ms: s.totalMs }\n : s.mode === 'opens'\n ? { remaining_actions: s.remainingActions, total_actions: s.totalActions }\n : {})\n })\n ),\n this.on('trial_expired', () => t.track('trial_expired')),\n this.on('visibility_blocked', (v) =>\n t.track('visibility_blocked', { reason: v.reason, country: v.country, tier: v.tier })\n ),\n this.on('error', (e) => t.track('error', { code: e.code, message: e.message }))\n );\n\n // auth_signin_success / auth_signout пока не фаерим: authChange эмитится\n // и на гидрации сессии (popup поднимает кеш из offscreen), и на token\n // refresh, и при параллельном content-script + popup — даёт ложные\n // signin'ы. Реальные login-события нужно ловить через прямые вызовы\n // signInWithEmail/signUp/signInWithOAuth/signOut, а не через authChange.\n }\n\n /** Прокси через RemoteEventTracker. Hosts могут вызывать paywall.track\n * для произвольных аналитических событий — летит в единственный\n * offscreen-tracker наряду с auto-emit'ами PaywallUI. */\n track(name: string, props?: Record<string, unknown>): void {\n this.remoteTracker?.track(name, props);\n }\n\n destroy(): void {\n for (const fn of this.trackerUnsubs) fn();\n this.trackerUnsubs = [];\n this.remoteTracker = null;\n super.destroy();\n }\n}\n"],"names":["twPropertiesRegistered","ensureTwPropertiesRegistered","rules","sheet","cssText","rule","r","mountShadow","Component","props","options","host","shadow","hostReset","style","mountPoint","currentProps","render","h","nextProps","FOCUSABLE","Modal","open","onClose","labelledBy","brandColor","testMode","allowClose","children","dialogRef","useRef","previouslyFocused","useEffect","dialog","onKey","e","focusables","el","first","last","active","prevOverflow","jsxs","jsx","PROVIDER_LABEL","AuthPanel","block","ctx","auth","session","allowSignup","allowReset","hideWhenAuthed","realSession","SignedIn","AuthForm","email","onSignOut","providers","mode","setMode","useState","setEmail","password","setPassword","otpCode","setOtpCode","busy","setBusy","error","setError","info","setInfo","onSubmit","msg","PaywallError","onOAuth","provider","p","ProviderIcon","Divider","Field","Fragment","submitLabel","switchMode","next","type","label","value","onInput","autocomplete","inputMode","required","AuthGate","bootstrap","authSession","onBack","showBack","AnonGate","onSuccess","heading","description","phase","setPhase","aliveRef","run","Spinner","SUBJECT_MIN","SUBJECT_MAX","CONTENT_MAX","MAX_FILES","MAX_FILE_SIZE","ACCEPTED_MIME","EMAIL_RE","SupportGate","client","origin","sessionEmail","lockedEmail","subject","setSubject","message","setMessage","files","setFiles","submitting","setSubmitting","submittedEmail","setSubmittedEmail","errors","setErrors","isValid","useMemo","s","m","validate","prev","finalEmail","err","resetForm","TextareaField","Dropzone","onChange","disabled","inputRef","dragOver","setDragOver","handleFiles","incoming","arr","valid","f","i","CtaButton","priceId","CurrentSession","signingOut","setSigningOut","onSupport","Dot","SupportLink","onClick","FeaturesList","item","BASE_FONT_PX","MIN_FONT_PX","MAX_LINES","fitHeading","lineHeight","maxHeight","size","Heading","level","Tag","className","ref","autoFit","cs","lh","formatPrice","price","display","intervalLabel","n","PriceGrid","filter","prices","horizontal","popularLabel","cols","selected","isPopular","Text","INTERVAL_MULTIPLIER","intervalNoun","interval","TokenizationGate","multiplier","q","rawCount","amount","blockRegistry","Renderer","layout","onAction","defaultPriceId","selectedPriceId","setSelectedPriceId","Cmp","computePaywallSnapshot","state","gate","purchased","sameSnapshot","a","b","PaywallRoot","onEvent","initialView","renew","onState","setState","setAuthSession","setGate","resumingRef","lastSnapshotRef","cancelled","data","runCheckout","result","popup","reopenCheckout","url","pending","handleAction","action","payload","brand","gateBlock","supportView","PurchaseSuccessView","AwaitingPaymentView","onReopen","onRetry","checking","setChecking","stillPending","setStillPending","stillPendingTimerRef","onContinue","restored","DEFAULT_TIMEOUT_MS","DEFAULT_VISIBLE_INTERVAL_MS","DEFAULT_HIDDEN_INTERVAL_MS","UserWatcher","opts","user","shouldRunUserWatcher","CLOSED_STATE","URL_MARKERS","PaywallUI$1","ownsAuth","resolveAuth","BillingClient","analytics","cfg","endpoint","EventTracker","v","name","handler","event","set","args","view","skipTrial","skipVisibility","cached","flags","trialCfg","store","status","updated","config","sameTrialConfig","factoryFn","createTrialStore","mountOpts","snapshot","sameStateSnapshot","cb","visibility","trial","redirect","hashMarkers","parseMarkers","searchMarkers","markers","notifyOpenerOfPurchase","stripMarkersFromUrl","AuthClient","isAuthClientLike","segment","params","clean","raw","prefix","out","RemoteTrialStore","transport","paywallId","RemoteBillingClient","key","balances","signal","identity","sameUser","sameBalances","RemoteAuthClient","input","tempName","injectLoaderUI","authorizeUrl","code","waitForOAuthCode","sameSession","PROVIDER_NAMES","doc","wrap","spinner","RemoteEventTracker","TransportClient","factory","channel","offMsg","env","offDisc","PROTOCOL_VERSION","res","envelope","isEnvelope","reconstructError","fn","TransportDisconnectedError","kind","id","resolve","reject","wrapped","t","getContentTransport","createRuntimeChannel","PORT_NAME","PaywallUI","BasePaywallUI","billing"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"content.js","sources":["../../sdk/src/ui/mount.ts","../../sdk/src/ui/Modal.tsx","../../sdk/src/ui/renderer/blocks/AuthPanel.tsx","../../sdk/src/ui/AuthGate.tsx","../../sdk/src/ui/AnonGate.tsx","../../sdk/src/ui/SupportGate.tsx","../../sdk/src/ui/renderer/blocks/CtaButton.tsx","../../sdk/src/ui/renderer/blocks/CurrentSession.tsx","../../sdk/src/ui/renderer/blocks/FeaturesList.tsx","../../sdk/src/ui/renderer/blocks/Heading.tsx","../../sdk/src/ui/renderer/blocks/PriceGrid.tsx","../../sdk/src/ui/renderer/blocks/Text.tsx","../../sdk/src/ui/renderer/blocks/TokenizationGate.tsx","../../sdk/src/ui/renderer/registry.ts","../../sdk/src/ui/renderer/Renderer.tsx","../../sdk/src/ui/PaywallRoot.tsx","../../sdk/src/ui/UserWatcher.ts","../../sdk/src/ui/PaywallUI.ts","../src/content/RemoteTrialStore.ts","../src/content/RemoteBillingClient.ts","../src/content/RemoteAuthClient.ts","../src/content/RemoteEventTracker.ts","../src/shared/transport-client.ts","../src/content/transport.ts","../src/content/PaywallUI.ts"],"sourcesContent":["import { h, render, type ComponentType } from 'preact';\nimport cssText from './styles.css?inline';\n\nexport interface MountHandle {\n update: (props: Record<string, unknown>) => void;\n unmount: () => void;\n shadowRoot: ShadowRoot;\n}\n\n// Tailwind v4 определяет утилиты вроде `.border` через `border-style: var(--tw-border-style)`,\n// где значение `solid` задаётся реестровой проперти `@property --tw-border-style { initial-value: solid }`.\n// В Chromium `@property`-объявления внутри shadow root не регистрируются document-wide, переменная\n// остаётся пустой → IACVT → border-style: none → used border-width: 0. Чтобы шорткаты работали в\n// shadow scope, регистрируем те же `@property` на уровне document один раз. `inherits: false`\n// держит изоляцию: имя проперти видно глобально, но значения на host-страницу не утекают.\nlet twPropertiesRegistered = false;\nfunction ensureTwPropertiesRegistered(): void {\n if (twPropertiesRegistered) return;\n twPropertiesRegistered = true;\n if (typeof CSS === 'undefined' || typeof CSS.registerProperty !== 'function') return;\n let rules: CSSRuleList;\n try {\n const sheet = new CSSStyleSheet();\n sheet.replaceSync(cssText);\n rules = sheet.cssRules;\n } catch {\n return;\n }\n for (const rule of rules) {\n if (rule.constructor.name !== 'CSSPropertyRule') continue;\n const r = rule as CSSRule & { name: string; syntax: string; inherits: boolean; initialValue: string | null };\n try {\n CSS.registerProperty({\n name: r.name,\n syntax: r.syntax,\n inherits: r.inherits,\n ...(r.initialValue != null ? { initialValue: r.initialValue } : {})\n });\n } catch {\n // Уже зарегистрирована другим инстансом SDK на той же странице — ок.\n }\n }\n}\n\nexport function mountShadow<P extends object>(\n Component: ComponentType<P>,\n props: P,\n options: { host?: HTMLElement; injectCss?: string; shadowMode?: 'open' | 'closed' } = {}\n): MountHandle {\n if (typeof document === 'undefined') {\n throw new Error('mountShadow called in non-DOM environment');\n }\n\n ensureTwPropertiesRegistered();\n\n const host = options.host ?? document.createElement('div');\n host.setAttribute('data-paywall-host', '');\n host.style.cssText = 'all: initial; position: fixed; inset: 0; z-index: 2147483647; pointer-events: none;';\n if (!host.isConnected) document.body.appendChild(host);\n\n // Дефолт `closed` — изоляция от хост-страницы. В e2e/demo тестах\n // включаем `open` через опцию, иначе Playwright не проходит через\n // shadow boundary accessibility-снапшотом и не кликает по внутренним кнопкам.\n const shadow = host.attachShadow({ mode: options.shadowMode ?? 'closed' });\n\n // Защита от протечки наследуемых свойств (color, font, letter-spacing, text-transform,\n // cursor, visibility) с host-страницы внутрь shadow через host-элемент. `!important`\n // в shadow перебивает внешний `!important` на host (спека CSS Scoping).\n // Рендер-фильтры (filter, transform, opacity) на ancestors защитить нельзя —\n // они применяются на уровне композитинга.\n const hostReset = `\n:host {\n all: initial !important;\n display: block !important;\n color: #111827 !important;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif !important;\n font-size: 16px !important;\n font-weight: 400 !important;\n font-style: normal !important;\n line-height: 1.5 !important;\n letter-spacing: normal !important;\n text-transform: none !important;\n text-decoration: none !important;\n text-align: left !important;\n direction: ltr !important;\n cursor: auto !important;\n visibility: visible !important;\n}\n`;\n\n const style = document.createElement('style');\n style.textContent = hostReset + cssText + (options.injectCss ?? '');\n shadow.appendChild(style);\n\n const mountPoint = document.createElement('div');\n mountPoint.style.pointerEvents = 'auto';\n shadow.appendChild(mountPoint);\n\n let currentProps = props;\n render(h(Component as ComponentType<object>, currentProps), mountPoint);\n\n return {\n shadowRoot: shadow,\n update(nextProps) {\n currentProps = { ...currentProps, ...nextProps } as P;\n render(h(Component as ComponentType<object>, currentProps), mountPoint);\n },\n unmount() {\n render(null, mountPoint);\n host.remove();\n }\n };\n}\n","import type { ComponentChildren } from 'preact';\nimport { useEffect, useRef } from 'preact/hooks';\n\nconst FOCUSABLE =\n 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n\nexport interface ModalProps {\n open: boolean;\n onClose: () => void;\n labelledBy?: string;\n brandColor?: string | null;\n /** Пейвол в тестовом режиме эквайринга — рисуем предупреждающую полоску сверху. */\n testMode?: boolean;\n /** Можно ли закрыть модалку: ESC, клик по overlay, крестик. По умолчанию true.\n * false — модалка остаётся открытой до явного host-close() / success-purchase. */\n allowClose?: boolean;\n children: ComponentChildren;\n}\n\nexport function Modal({\n open,\n onClose,\n labelledBy,\n brandColor,\n testMode,\n allowClose = true,\n children\n}: ModalProps) {\n const dialogRef = useRef<HTMLDivElement | null>(null);\n const previouslyFocused = useRef<HTMLElement | null>(null);\n\n useEffect(() => {\n if (!open) return;\n previouslyFocused.current = (document.activeElement as HTMLElement) ?? null;\n\n const dialog = dialogRef.current;\n if (dialog) {\n const first = dialog.querySelector<HTMLElement>(FOCUSABLE);\n (first ?? dialog).focus({ preventScroll: true });\n }\n\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n if (!allowClose) return;\n e.stopPropagation();\n onClose();\n return;\n }\n if (e.key !== 'Tab' || !dialogRef.current) return;\n const focusables = Array.from(\n dialogRef.current.querySelectorAll<HTMLElement>(FOCUSABLE)\n ).filter((el) => !el.hasAttribute('disabled') && el.tabIndex !== -1);\n if (focusables.length === 0) {\n e.preventDefault();\n return;\n }\n const first = focusables[0];\n const last = focusables[focusables.length - 1];\n const active = document.activeElement as HTMLElement | null;\n if (e.shiftKey && active === first) {\n e.preventDefault();\n last.focus();\n } else if (!e.shiftKey && active === last) {\n e.preventDefault();\n first.focus();\n }\n };\n\n document.addEventListener('keydown', onKey, true);\n const prevOverflow = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n\n return () => {\n document.removeEventListener('keydown', onKey, true);\n document.body.style.overflow = prevOverflow;\n previouslyFocused.current?.focus?.({ preventScroll: true });\n };\n }, [open, onClose, allowClose]);\n\n if (!open) return null;\n\n const onBackdrop = (e: MouseEvent) => {\n if (!allowClose) return;\n if (e.target === e.currentTarget) onClose();\n };\n\n const accent = brandColor ?? '#3b82f6';\n\n return (\n <div\n class=\"fixed inset-0 z-[2147483647] flex items-center justify-center bg-slate-950/50 p-2 sm:p-4 backdrop-blur-md animate-[pw-fade-in_180ms_ease-out]\"\n onClick={onBackdrop}\n data-pw-root\n >\n <div\n ref={dialogRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={labelledBy}\n tabIndex={-1}\n // max-h ограничивает высоту вьюпортом (использует dvh для мобильных\n // safe-area), flex-col + overflow на children даёт внутренний скролл\n // когда контент выше viewport'а — критично для extension popup'ов\n // (max 600px высоты) и узких контейнеров на сайтах.\n class=\"relative flex max-h-[calc(100dvh-1rem)] sm:max-h-[calc(100dvh-2rem)] w-full max-w-md flex-col overflow-hidden rounded-3xl bg-white outline-none ring-1 ring-black/5 animate-[pw-scale-in_220ms_cubic-bezier(0.16,1,0.3,1)]\"\n style={\n {\n '--pw-accent': accent,\n boxShadow:\n '0 1px 2px rgba(15,23,42,0.04), 0 12px 32px -8px rgba(15,23,42,0.18), 0 24px 64px -16px rgba(15,23,42,0.22)'\n } as unknown as Record<string, string>\n }\n >\n {testMode && (\n <div\n class=\"flex items-center justify-center gap-1.5 bg-gradient-to-r from-amber-300 to-amber-400 px-3 py-1.5 text-[11px] font-semibold uppercase tracking-wider text-amber-950\"\n role=\"status\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"M8 1L15 14H1L8 1Z\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linejoin=\"round\"\n />\n <path d=\"M8 6v3\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n <circle cx=\"8\" cy=\"11.5\" r=\"0.5\" fill=\"currentColor\" />\n </svg>\n Test mode — no real charge\n </div>\n )}\n <div class=\"flex-1 overflow-y-auto p-7\">\n {children}\n </div>\n {allowClose ? (\n <button\n type=\"button\"\n onClick={onClose}\n aria-label=\"Close\"\n // Absolute относительно dialog'а (не scrollable area) — кнопка\n // всегда в правом верхнем углу dialog'а, не двигается со скроллом\n // и не влияет на flow контента. top-10 при testMode чтобы не\n // залезть на жёлтый банер (~28px высотой).\n class={`absolute right-3 ${testMode ? 'top-10' : 'top-3'} z-10 flex h-8 w-8 items-center justify-center rounded-full bg-white/80 text-gray-400 backdrop-blur-sm transition-colors hover:bg-gray-100 hover:text-gray-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--pw-accent)]`}\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"M3 3l10 10M13 3L3 13\"\n stroke=\"currentColor\"\n stroke-width=\"1.75\"\n stroke-linecap=\"round\"\n />\n </svg>\n </button>\n ) : null}\n </div>\n\n <style>{`\n @keyframes pw-fade-in { from { opacity: 0 } to { opacity: 1 } }\n @keyframes pw-scale-in {\n from { opacity: 0; transform: translateY(12px) scale(0.96) }\n to { opacity: 1; transform: none }\n }\n `}</style>\n </div>\n );\n}\n","import { useState } from 'preact/hooks';\nimport type { OAuthProvider } from '../../../core/auth';\nimport type { LayoutBlock } from '../../../core/types';\nimport { PaywallError } from '../../../core/types';\nimport type { BlockProps } from '../types';\n\ntype AuthPanelBlock = Extract<LayoutBlock, { type: 'auth_panel' }>;\n\ntype Mode = 'signin' | 'signup' | 'forgot' | 'reset_sent' | 'reset_verify';\n\nconst PROVIDER_LABEL: Record<OAuthProvider, string> = {\n google: 'Continue with Google',\n apple: 'Continue with Apple',\n github: 'Continue with GitHub',\n facebook: 'Continue with Facebook'\n};\n\nexport function AuthPanel({ block, ctx }: BlockProps<AuthPanelBlock>) {\n const auth = ctx.auth;\n const session = ctx.authSession;\n const allowSignup = block.allow_signup !== false;\n const allowReset = block.allow_password_reset !== false;\n const hideWhenAuthed = block.hide_when_authenticated !== false;\n\n // Без AuthClient рендерим заметный fallback в dev, в проде ничего: блок\n // в layout не попадает по ошибке только если host забыл передать auth-опцию.\n if (!auth) {\n if (typeof console !== 'undefined') {\n console.warn('[paywall] auth_panel rendered without AuthClient — pass `auth: true` to PaywallUI');\n }\n return null;\n }\n\n // Анон-сессия в UI пейвола — это «нет авторизации»: анон нужен только для\n // api-gateway-токена, для покупки/restore юзеру всё равно надо реально\n // залогиниться. hide_when_authenticated анон тоже игнорирует (иначе блок\n // схлопнется, и юзер останется без формы).\n const realSession = session && !session.user.is_anonymous ? session : null;\n\n // Реально залогинен и block явно не просит показать summary — скрываемся целиком.\n if (realSession && hideWhenAuthed) return null;\n\n if (realSession) {\n return <SignedIn email={realSession.user.email ?? ''} onSignOut={() => auth.signOut().catch(() => {})} />;\n }\n\n return (\n <AuthForm\n block={block}\n allowSignup={allowSignup}\n allowReset={allowReset}\n ctx={ctx}\n />\n );\n}\n\nfunction SignedIn({ email, onSignOut }: { email: string; onSignOut: () => void }) {\n return (\n <div class=\"flex items-center justify-between gap-3 rounded-2xl border border-gray-200 bg-gray-50/60 px-4 py-3\">\n <div class=\"flex flex-col\">\n <span class=\"text-[10px] font-semibold uppercase tracking-wider text-gray-500\">Signed in</span>\n <span class=\"text-sm font-medium text-gray-900\">{email}</span>\n </div>\n <button\n type=\"button\"\n onClick={onSignOut}\n class=\"rounded-md px-1.5 py-0.5 text-xs font-medium text-gray-600 transition-colors hover:bg-white hover:text-gray-900 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--pw-accent)]\"\n >\n Sign out\n </button>\n </div>\n );\n}\n\ninterface FormProps {\n block: AuthPanelBlock;\n allowSignup: boolean;\n allowReset: boolean;\n ctx: BlockProps<AuthPanelBlock>['ctx'];\n}\n\nfunction AuthForm({ block, allowSignup, allowReset, ctx }: FormProps) {\n const auth = ctx.auth!;\n const providers = block.providers ?? [];\n\n const [mode, setMode] = useState<Mode>('signin');\n const [email, setEmail] = useState('');\n const [password, setPassword] = useState('');\n const [otpCode, setOtpCode] = useState('');\n const [busy, setBusy] = useState<null | OAuthProvider | 'email' | 'reset'>(null);\n const [error, setError] = useState<string | null>(null);\n const [info, setInfo] = useState<string | null>(null);\n\n // Успешный auth не эмитит через ctx.onAction — single source of truth для\n // signin/signout-аналитики = AuthClient.onAuthChange (PaywallUI на него\n // подписан и сам пишет в трекер). Failed-кейсы тоже не эмитим: показываем\n // юзеру inline-error, для funnel'а это пока не критично.\n const onSubmit = async (e: Event) => {\n e.preventDefault();\n if (busy) return;\n setBusy('email');\n setError(null);\n setInfo(null);\n try {\n if (mode === 'signin') {\n await auth.signInWithEmail({ email, password });\n } else if (mode === 'signup') {\n const res = await auth.signUp({ email, password });\n if (res.kind === 'confirmation_required') {\n // Сервер требует email confirm — переключаемся в OTP-verify, юзер\n // вводит 6-значный код из письма. После verify — мы сразу залогинены\n // (verifyOtp сам поставит session), AuthForm схлопнется.\n setMode('reset_verify');\n setInfo('Check your email for a confirmation code.');\n }\n } else if (mode === 'forgot') {\n await auth.requestPasswordReset({ email });\n setMode('reset_sent');\n setInfo('If that email exists, a reset code has been sent.');\n } else if (mode === 'reset_verify') {\n // Используется и для signup-confirm, и для recovery — оба flow одинаковые\n // (verifyOtp выдаёт session). Differentiator — какой type послали.\n await auth.verifyOtp({\n email,\n token: otpCode,\n type: password ? 'recovery' : 'email'\n });\n if (password) {\n // Recovery flow: после verify мы получили session, теперь меняем пароль.\n await auth.updatePassword({ password });\n }\n }\n } catch (e) {\n const msg = e instanceof PaywallError ? e.message : 'Something went wrong';\n setError(msg);\n } finally {\n setBusy(null);\n }\n };\n\n const onOAuth = async (provider: OAuthProvider) => {\n if (busy) return;\n setBusy(provider);\n setError(null);\n setInfo(null);\n try {\n // Лоадер снимаем сразу после открытия popup'а — дальше судьба флоу\n // в руках юзера. Юзер закрыл вкладку / отвлёкся / COOP-severance не дал\n // нам поймать closed → promise тихо дойдёт до oauth_timeout, но кнопка\n // не висит, юзер просто кликнет ещё раз.\n await auth.signInWithOAuth({\n provider,\n onPopupOpened: () => setBusy(null)\n });\n } catch (e) {\n // popup_blocked показываем (юзер должен включить popup'ы); cancelled/\n // timeout — нормальный ход событий, не считаем ошибкой.\n if (e instanceof PaywallError) {\n if (e.code === 'oauth_cancelled' || e.code === 'oauth_timeout') return;\n setError(e.message);\n } else {\n setError('Sign-in failed');\n }\n } finally {\n setBusy(null);\n }\n };\n\n return (\n <div class=\"flex flex-col gap-3\">\n {block.heading ? (\n <h2 class=\"text-lg font-semibold tracking-tight text-gray-900\">{block.heading}</h2>\n ) : null}\n\n {providers.length > 0 && (mode === 'signin' || mode === 'signup') ? (\n <div class=\"flex flex-col gap-2\">\n {providers.map((p) => (\n <button\n key={p}\n type=\"button\"\n onClick={() => onOAuth(p)}\n disabled={busy !== null}\n class=\"flex h-11 w-full items-center justify-center gap-2 rounded-xl border border-gray-200 bg-white px-4 text-sm font-medium text-gray-900 shadow-[0_1px_0_rgba(15,23,42,0.04)] transition-all hover:-translate-y-px hover:border-gray-300 hover:bg-gray-50 hover:shadow-sm disabled:cursor-not-allowed disabled:opacity-60 disabled:hover:translate-y-0 disabled:hover:shadow-[0_1px_0_rgba(15,23,42,0.04)] focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--pw-accent)]\"\n >\n {busy === p ? (\n <span class=\"inline-block h-4 w-4 animate-spin rounded-full border-2 border-gray-300 border-t-gray-700\" />\n ) : (\n <ProviderIcon provider={p} />\n )}\n <span>{PROVIDER_LABEL[p]}</span>\n </button>\n ))}\n <Divider />\n </div>\n ) : null}\n\n <form onSubmit={onSubmit} class=\"flex flex-col gap-2\">\n {(mode === 'signin' || mode === 'signup' || mode === 'forgot') && (\n <Field\n type=\"email\"\n label=\"Email\"\n value={email}\n onInput={setEmail}\n autocomplete=\"email\"\n required\n />\n )}\n\n {(mode === 'signin' || mode === 'signup') && (\n <Field\n type=\"password\"\n label=\"Password\"\n value={password}\n onInput={setPassword}\n autocomplete={mode === 'signin' ? 'current-password' : 'new-password'}\n required\n />\n )}\n\n {mode === 'reset_verify' && (\n <>\n <Field\n type=\"text\"\n label=\"Confirmation code\"\n value={otpCode}\n onInput={setOtpCode}\n autocomplete=\"one-time-code\"\n inputMode=\"numeric\"\n required\n />\n <Field\n type=\"password\"\n label=\"New password (optional — only for password reset)\"\n value={password}\n onInput={setPassword}\n autocomplete=\"new-password\"\n />\n </>\n )}\n\n {mode === 'reset_sent' && info && (\n <p class=\"rounded-lg bg-gray-50 px-3 py-2 text-xs text-gray-600\">{info}</p>\n )}\n\n {error && <p class=\"text-xs text-red-600\">{error}</p>}\n {info && mode !== 'reset_sent' && (\n <p class=\"text-xs text-gray-500\">{info}</p>\n )}\n\n {mode !== 'reset_sent' && (\n <button\n type=\"submit\"\n disabled={busy !== null}\n class=\"flex h-11 w-full items-center justify-center rounded-xl px-4 text-sm font-semibold tracking-tight text-white transition-all hover:-translate-y-px hover:brightness-105 disabled:cursor-not-allowed disabled:opacity-60 disabled:hover:translate-y-0 disabled:hover:brightness-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--pw-accent)]\"\n style={{\n background:\n 'linear-gradient(180deg, color-mix(in srgb, var(--pw-accent) 92%, white), var(--pw-accent))',\n boxShadow:\n '0 1px 2px rgba(15,23,42,0.08), 0 6px 14px -4px color-mix(in srgb, var(--pw-accent) 50%, transparent)'\n }}\n >\n {busy === 'email' ? (\n <span class=\"inline-block h-4 w-4 animate-spin rounded-full border-2 border-white/40 border-t-white\" />\n ) : (\n submitLabel(mode)\n )}\n </button>\n )}\n </form>\n\n <div class=\"flex flex-wrap items-center justify-between gap-x-3 gap-y-1 text-xs text-gray-500\">\n {mode === 'signin' && allowSignup && (\n <button type=\"button\" onClick={() => switchMode(setMode, setError, setInfo, 'signup')} class=\"font-medium text-gray-700 hover:underline\">\n Create account\n </button>\n )}\n {mode === 'signup' && (\n <button type=\"button\" onClick={() => switchMode(setMode, setError, setInfo, 'signin')} class=\"font-medium text-gray-700 hover:underline\">\n I already have an account\n </button>\n )}\n {mode === 'signin' && allowReset && (\n <button type=\"button\" onClick={() => switchMode(setMode, setError, setInfo, 'forgot')} class=\"hover:underline\">\n Forgot password?\n </button>\n )}\n {(mode === 'forgot' || mode === 'reset_sent' || mode === 'reset_verify') && (\n <button type=\"button\" onClick={() => switchMode(setMode, setError, setInfo, 'signin')} class=\"hover:underline\">\n Back to sign in\n </button>\n )}\n {mode === 'reset_sent' && (\n <button type=\"button\" onClick={() => switchMode(setMode, setError, setInfo, 'reset_verify')} class=\"font-medium text-gray-700 hover:underline\">\n I have a code\n </button>\n )}\n </div>\n </div>\n );\n}\n\nfunction submitLabel(mode: Mode): string {\n switch (mode) {\n case 'signin':\n return 'Sign in';\n case 'signup':\n return 'Create account';\n case 'forgot':\n return 'Send reset code';\n case 'reset_verify':\n return 'Verify';\n default:\n return 'Continue';\n }\n}\n\nfunction switchMode(\n setMode: (m: Mode) => void,\n setError: (s: string | null) => void,\n setInfo: (s: string | null) => void,\n next: Mode\n): void {\n setMode(next);\n setError(null);\n setInfo(null);\n}\n\ninterface FieldProps {\n type: 'email' | 'password' | 'text';\n label: string;\n value: string;\n onInput: (v: string) => void;\n autocomplete?: string;\n inputMode?: 'numeric' | 'text' | 'email';\n required?: boolean;\n}\n\nfunction Field({ type, label, value, onInput, autocomplete, inputMode, required }: FieldProps) {\n return (\n <label class=\"flex flex-col gap-1.5\">\n <span class=\"text-xs font-medium text-gray-700\">{label}</span>\n <input\n type={type}\n value={value}\n onInput={(e) => onInput((e.target as HTMLInputElement).value)}\n autocomplete={autocomplete}\n inputMode={inputMode}\n required={required}\n class=\"h-11 w-full rounded-xl border border-gray-200 bg-white px-3.5 text-sm text-gray-900 shadow-[0_1px_0_rgba(15,23,42,0.02)] outline-none transition-all placeholder:text-gray-400 hover:border-gray-300 focus:border-[var(--pw-accent)] focus:shadow-[0_0_0_3px_color-mix(in_srgb,var(--pw-accent)_18%,transparent)]\"\n />\n </label>\n );\n}\n\nfunction Divider() {\n return (\n <div class=\"flex items-center gap-2 py-1 text-[10px] uppercase tracking-[0.14em] text-gray-400\">\n <div class=\"h-px flex-1 bg-gradient-to-r from-gray-200 to-transparent\" />\n <span>or</span>\n <div class=\"h-px flex-1 bg-gradient-to-r from-transparent to-gray-200\" />\n </div>\n );\n}\n\n// Минималистичные SVG-иконки провайдеров. CWS требует bundled-ассеты, поэтому\n// никаких CDN-картинок. По цвету рисуем только Google (брендовый), остальные\n// чёрно-белые — это нейтрально и подходит под любой brand_color пейвола.\nfunction ProviderIcon({ provider }: { provider: OAuthProvider }) {\n if (provider === 'google') {\n return (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 18 18\" aria-hidden=\"true\">\n <path fill=\"#4285F4\" d=\"M17.64 9.2c0-.64-.06-1.25-.16-1.84H9v3.49h4.84a4.14 4.14 0 0 1-1.79 2.71v2.26h2.9c1.7-1.56 2.69-3.87 2.69-6.62Z\" />\n <path fill=\"#34A853\" d=\"M9 18c2.43 0 4.47-.8 5.96-2.18l-2.9-2.26c-.8.54-1.83.86-3.06.86-2.36 0-4.36-1.59-5.07-3.74H.92v2.33A9 9 0 0 0 9 18Z\" />\n <path fill=\"#FBBC05\" d=\"M3.93 10.68a5.4 5.4 0 0 1 0-3.36V4.99H.92a9 9 0 0 0 0 8.02l3-2.33Z\" />\n <path fill=\"#EA4335\" d=\"M9 3.58c1.32 0 2.5.45 3.44 1.34l2.58-2.58A9 9 0 0 0 .92 4.99l3.01 2.33C4.64 5.17 6.64 3.58 9 3.58Z\" />\n </svg>\n );\n }\n if (provider === 'apple') {\n return (\n <svg width=\"14\" height=\"16\" viewBox=\"0 0 14 16\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M11.4 8.5c0-2 1.6-3 1.7-3-.9-1.3-2.4-1.5-2.9-1.5-1.2-.1-2.4.7-3 .7-.6 0-1.6-.7-2.6-.7-1.3 0-2.6.8-3.3 2C-.4 8.4.7 12.5 2.2 14.7c.7 1.1 1.6 2.3 2.7 2.3 1.1 0 1.5-.7 2.8-.7 1.3 0 1.7.7 2.8.7 1.2 0 1.9-1.1 2.6-2.2.6-.9 1-1.8 1.1-2.7-1.4-.5-2.8-1.7-2.8-3.6Zm-2-6.5C10 1.3 10.4.4 10.3 0c-.7 0-1.6.5-2.1 1.1-.5.5-1 1.4-.9 2.2.7 0 1.5-.4 2.1-1.3Z\" />\n </svg>\n );\n }\n if (provider === 'github') {\n return (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M8 0C3.6 0 0 3.6 0 8a8 8 0 0 0 5.5 7.6c.4.1.5-.2.5-.4v-1.5c-2.2.5-2.7-1-2.7-1-.4-.9-.9-1.2-.9-1.2-.7-.5.1-.5.1-.5.8.1 1.2.8 1.2.8.7 1.2 1.9.9 2.4.7 0-.5.3-.9.5-1.1-1.8-.2-3.6-.9-3.6-4 0-.9.3-1.6.8-2.1-.1-.2-.4-1 .1-2.1 0 0 .7-.2 2.2.8a7.6 7.6 0 0 1 4 0c1.5-1 2.2-.8 2.2-.8.4 1.1.2 1.9.1 2.1.5.5.8 1.2.8 2.1 0 3.1-1.9 3.7-3.6 3.9.3.3.6.8.6 1.6V15c0 .2.1.5.6.4A8 8 0 0 0 16 8c0-4.4-3.6-8-8-8Z\" />\n </svg>\n );\n }\n return (\n <svg width=\"14\" height=\"16\" viewBox=\"0 0 14 16\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M14 2.7C14 1.2 12.8 0 11.3 0H2.7C1.2 0 0 1.2 0 2.7v10.6C0 14.8 1.2 16 2.7 16h4V9.8H4.7v-2H6.7V6.4c0-2 1.2-3.1 3-3.1.9 0 1.7.1 2 .2V5h-1.4c-.8 0-1 .4-1 1v1.5h2.4l-.3 2H9.3V16h2c1.5 0 2.7-1.2 2.7-2.7V2.7Z\" />\n </svg>\n );\n}\n","import type { AuthClient, AuthSession } from '../core/auth';\nimport type { LayoutBlock, PaywallBootstrap } from '../core/types';\nimport { AuthPanel } from './renderer/blocks/AuthPanel';\nimport type { BlockContext } from './renderer/types';\n\ntype AuthPanelBlock = Extract<LayoutBlock, { type: 'auth_panel' }>;\n\nexport interface AuthGateProps {\n block: AuthPanelBlock;\n bootstrap: PaywallBootstrap;\n auth: AuthClient;\n authSession: AuthSession | null;\n onBack: () => void;\n /** Показывать кнопку «← Back». Для preauth/restore-flow — true (юзер\n * пришёл сюда из layout, должен иметь возможность вернуться к тарифам).\n * Для standalone openAuth() — false: модалка открыта только ради signin'а,\n * ESC и X-кнопка модалки уже закрывают её, дублирующая Back путает. */\n showBack?: boolean;\n}\n\n// Полноэкранная обёртка над AuthPanel для preauth-gate flow. AuthPanel сам по\n// себе — layout-блок и не знает про \"вернуться к тарифам\"; gate добавляет\n// Back-кнопку + конструирует stub BlockContext (AuthPanel из контекста читает\n// только auth/authSession, остальные поля не использует).\nexport function AuthGate({\n block,\n bootstrap,\n auth,\n authSession,\n onBack,\n showBack = true\n}: AuthGateProps) {\n const ctx: BlockContext = {\n bootstrap,\n selectedPriceId: null,\n setSelectedPriceId: () => {},\n onAction: () => {},\n auth,\n authSession\n };\n\n return (\n <div class=\"flex flex-col gap-3\">\n {showBack ? (\n <button\n type=\"button\"\n onClick={onBack}\n class=\"-ml-1 self-start rounded-md px-1.5 py-0.5 text-xs font-medium text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--pw-accent)]\"\n >\n ← Back\n </button>\n ) : null}\n <AuthPanel block={block} ctx={ctx} />\n </div>\n );\n}\n","import { useEffect, useRef, useState } from 'preact/hooks';\nimport type { AuthClient, AuthSession } from '../core/auth';\n\n// Anonymous sign-in gate. После удаления Turnstile-iframe'а (captcha в Supabase\n// выключена, защита держится на Supabase rate-limit per real-IP + CF Bot Fight\n// Mode) экран превратился в индикатор прогресса:\n// 1. Mount → auth.signInAnonymously() — внутри AuthClient'а сначала проверит\n// idempotent (если уже анон — instant return) и resume (если есть\n// сохранённый refresh_token — silent restore), потом fresh signin.\n// 2. Success → onSuccess(session). Gate схлопывается через PaywallRoot.\n// 3. Error → отображаем сообщение + кнопку «Try again».\n//\n// `forceCaptcha` имя сохранили в API ради host backward-compat (на самом деле\n// — «принудительно создать нового anon-юзера, не resume'ить»).\n\nexport interface AnonGateProps {\n auth: AuthClient;\n /** Вызывается после успешного signin'а (любым путём — idempotent / resume / fresh). */\n onSuccess: (session: AuthSession) => void;\n /** Кнопка «← Back». Опциональна — когда AnonGate смонтирован как\n * standalone (paywallUI.openAnonGate()), `onBack` приведёт к закрытию\n * модалки целиком; для inline-варианта в layout — возврат к тарифам. */\n onBack?: () => void;\n heading?: string;\n description?: string;\n}\n\ntype Phase =\n | { kind: 'signing-in' }\n | { kind: 'error'; message: string };\n\nexport function AnonGate({\n auth,\n onSuccess,\n onBack,\n heading = 'Continue as guest',\n description = 'Setting up your guest session…'\n}: AnonGateProps) {\n const [phase, setPhase] = useState<Phase>({ kind: 'signing-in' });\n // Защита от race: если юзер закрыл модалку посреди signin'а, не зовём\n // onSuccess из устаревшего промиса.\n const aliveRef = useRef(true);\n useEffect(() => {\n return () => {\n aliveRef.current = false;\n };\n }, []);\n\n const run = (): void => {\n setPhase({ kind: 'signing-in' });\n void (async () => {\n try {\n const session = await auth.signInAnonymously();\n if (!aliveRef.current) return;\n onSuccess(session);\n } catch (e) {\n if (!aliveRef.current) return;\n setPhase({\n kind: 'error',\n message: e instanceof Error ? e.message : 'Anonymous sign-in failed'\n });\n }\n })();\n };\n\n // Один автозапуск на mount. Зависимости — пустые: повторное срабатывание\n // привело бы к лишним сетевым запросам на каждом ре-рендере родителя.\n useEffect(() => {\n run();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return (\n <div class=\"flex flex-col gap-3\">\n {onBack ? (\n <button\n type=\"button\"\n onClick={onBack}\n class=\"-ml-1 self-start rounded-md px-1.5 py-0.5 text-xs font-medium text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--pw-accent)]\"\n >\n ← Back\n </button>\n ) : null}\n\n <div class=\"flex flex-col gap-1\">\n <h2 class=\"text-xl font-semibold text-gray-900\">{heading}</h2>\n <p class=\"text-sm text-gray-500\">{description}</p>\n </div>\n\n {phase.kind === 'signing-in' ? (\n <div class=\"flex items-center justify-center py-6\">\n <Spinner />\n </div>\n ) : null}\n\n {phase.kind === 'error' ? (\n <div class=\"flex flex-col gap-3\">\n <div class=\"rounded-lg bg-red-50 px-3 py-2 text-sm text-red-700\">\n {phase.message}\n </div>\n <button\n type=\"button\"\n onClick={run}\n class=\"self-start rounded-md bg-[var(--pw-accent)] px-3 py-1.5 text-sm font-medium text-white hover:opacity-90 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--pw-accent)] focus-visible:ring-offset-2\"\n >\n Try again\n </button>\n </div>\n ) : null}\n </div>\n );\n}\n\nfunction Spinner() {\n return (\n <svg class=\"h-5 w-5 animate-spin text-[var(--pw-accent)]\" viewBox=\"0 0 24 24\" fill=\"none\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"3\" stroke-opacity=\"0.2\" />\n <path d=\"M22 12a10 10 0 0 0-10-10\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\" />\n </svg>\n );\n}\n","import { useMemo, useRef, useState } from 'preact/hooks';\nimport type { BillingClient } from '../core/BillingClient';\nimport type { AuthSession } from '../core/auth';\nimport { PaywallError } from '../core/types';\n\nexport interface SupportGateProps {\n client: BillingClient;\n authSession: AuthSession | null;\n // 'standalone' — модалка открыта только для саппорта (paywall.openSupport()),\n // Back/Done закрывают её. 'layout' — пришли из current_session-блока,\n // Back/Done возвращают в layout (и пейвол с тарифами остаётся открытым).\n origin: 'layout' | 'standalone';\n onBack: () => void;\n}\n\nconst SUBJECT_MIN = 3;\nconst SUBJECT_MAX = 200;\nconst CONTENT_MAX = 5000;\nconst MAX_FILES = 5;\nconst MAX_FILE_SIZE = 10 * 1024 * 1024;\nconst ACCEPTED_MIME = ['image/jpeg', 'image/png', 'image/webp'];\nconst EMAIL_RE = /.+@.+\\..+/;\n\nexport function SupportGate({ client, authSession, origin, onBack }: SupportGateProps) {\n const sessionEmail = authSession?.user.email ?? '';\n // Если есть сессия — email фиксируем из неё, форма его не редактирует.\n const lockedEmail = sessionEmail ? sessionEmail : null;\n const [email, setEmail] = useState<string>(sessionEmail);\n const [subject, setSubject] = useState('');\n const [message, setMessage] = useState('');\n const [files, setFiles] = useState<File[]>([]);\n const [submitting, setSubmitting] = useState(false);\n const [submittedEmail, setSubmittedEmail] = useState<string | null>(null);\n const [errors, setErrors] = useState<{\n subject?: string;\n email?: string;\n message?: string;\n files?: string;\n submit?: string;\n }>({});\n\n const isValid = useMemo(() => {\n const e = (lockedEmail ?? email).trim().toLowerCase();\n const s = subject.trim();\n const m = message.trim();\n return (\n EMAIL_RE.test(e) &&\n s.length >= SUBJECT_MIN &&\n s.length <= SUBJECT_MAX &&\n m.length >= 1 &&\n m.length <= CONTENT_MAX\n );\n }, [lockedEmail, email, subject, message]);\n\n const validate = (): boolean => {\n const next: typeof errors = {};\n const e = (lockedEmail ?? email).trim();\n const s = subject.trim();\n const m = message.trim();\n if (!e) next.email = 'Required';\n else if (!EMAIL_RE.test(e.toLowerCase())) next.email = 'Invalid email';\n if (s.length < SUBJECT_MIN || s.length > SUBJECT_MAX) {\n next.subject = `${SUBJECT_MIN}–${SUBJECT_MAX} characters`;\n }\n if (m.length < 1 || m.length > CONTENT_MAX) {\n next.message = `1–${CONTENT_MAX} characters`;\n }\n setErrors(next);\n return Object.keys(next).length === 0;\n };\n\n const onSubmit = async (e: Event): Promise<void> => {\n e.preventDefault();\n if (submitting) return;\n if (!validate()) return;\n setSubmitting(true);\n setErrors((prev) => ({ ...prev, submit: undefined }));\n try {\n const finalEmail = (lockedEmail ?? email).trim();\n await client.createSupportTicket({\n subject: subject.trim(),\n content: message.trim(),\n email: finalEmail || undefined,\n files: files.length > 0 ? files : undefined\n });\n setSubmittedEmail(finalEmail);\n } catch (err) {\n const msg =\n err instanceof PaywallError\n ? err.message || 'Failed to send. Please try again.'\n : 'Failed to send. Please try again.';\n setErrors((prev) => ({ ...prev, submit: msg }));\n } finally {\n setSubmitting(false);\n }\n };\n\n const resetForm = (): void => {\n setSubject('');\n setMessage('');\n setFiles([]);\n setErrors({});\n setSubmittedEmail(null);\n };\n\n if (submittedEmail) {\n return (\n <div class=\"flex flex-col items-center gap-4 py-2 text-center\">\n <div\n class=\"flex h-14 w-14 items-center justify-center rounded-full\"\n style={{\n background:\n 'linear-gradient(135deg, color-mix(in srgb, var(--pw-accent) 85%, white), var(--pw-accent))',\n color: '#fff',\n boxShadow:\n '0 0 0 8px color-mix(in srgb, var(--pw-accent) 12%, transparent), 0 8px 20px -6px color-mix(in srgb, var(--pw-accent) 45%, transparent)'\n }}\n aria-hidden=\"true\"\n >\n <svg viewBox=\"0 0 24 24\" class=\"h-7 w-7\">\n <path\n fill=\"currentColor\"\n d=\"M12 0a12 12 0 1 0 0 24 12 12 0 0 0 0-24Zm6.93 8.2-6.85 9.29a1.01 1.01 0 0 1-1.43.19L5.76 13.77a1 1 0 1 1 1.25-1.56l4.08 3.26 6.23-8.45a1 1 0 1 1 1.61 1.18Z\"\n />\n </svg>\n </div>\n <div class=\"text-lg font-semibold tracking-tight text-gray-900\">Request submitted</div>\n <div class=\"max-w-[320px] text-sm leading-relaxed text-gray-500\">\n We've received your message and will respond to{' '}\n <b class=\"text-gray-700\">{submittedEmail}</b>.\n </div>\n <div class=\"mt-2 flex items-center justify-center gap-3\">\n <button\n type=\"button\"\n onClick={onBack}\n class=\"rounded-xl px-3 py-2 text-sm font-medium text-gray-600 transition-colors hover:bg-gray-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--pw-accent)]\"\n >\n {origin === 'standalone' ? 'Done' : 'Back'}\n </button>\n <button\n type=\"button\"\n onClick={resetForm}\n class=\"flex h-10 items-center justify-center rounded-xl px-4 text-sm font-semibold text-white transition-all hover:-translate-y-px hover:brightness-105 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--pw-accent)]\"\n style={{\n background:\n 'linear-gradient(180deg, color-mix(in srgb, var(--pw-accent) 92%, white), var(--pw-accent))',\n boxShadow:\n '0 1px 2px rgba(15,23,42,0.08), 0 6px 14px -4px color-mix(in srgb, var(--pw-accent) 50%, transparent)'\n }}\n >\n Send another request\n </button>\n </div>\n </div>\n );\n }\n\n return (\n <div class=\"flex flex-col gap-3\">\n <div class=\"flex items-center justify-between\">\n <button\n type=\"button\"\n onClick={onBack}\n class=\"-ml-1 rounded-md px-1.5 py-0.5 text-xs font-medium text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--pw-accent)]\"\n >\n ← {origin === 'standalone' ? 'Close' : 'Back'}\n </button>\n </div>\n <h2 class=\"text-lg font-semibold tracking-tight text-gray-900\">Contact Support</h2>\n <p class=\"text-xs leading-relaxed text-gray-500\">\n Fill out the form below and we'll get back to you.\n </p>\n\n <form onSubmit={onSubmit} class=\"flex flex-col gap-3\">\n {!lockedEmail ? (\n <Field\n type=\"email\"\n label=\"Your email\"\n value={email}\n onInput={setEmail}\n error={errors.email}\n autocomplete=\"email\"\n required\n />\n ) : (\n <div class=\"rounded-xl border border-gray-200 bg-gray-50/60 px-3 py-2 text-xs text-gray-500\">\n Sending as <b class=\"font-medium text-gray-700\">{lockedEmail}</b>\n </div>\n )}\n <Field\n type=\"text\"\n label=\"Subject\"\n value={subject}\n onInput={setSubject}\n error={errors.subject}\n required\n />\n <TextareaField\n label=\"Message\"\n value={message}\n onInput={setMessage}\n error={errors.message}\n required\n />\n\n <Dropzone files={files} onChange={setFiles} disabled={submitting} />\n\n {errors.submit && <p class=\"text-xs text-red-600\">{errors.submit}</p>}\n\n <div class=\"mt-1 flex items-center justify-end gap-2\">\n <button\n type=\"button\"\n onClick={onBack}\n disabled={submitting}\n class=\"rounded-xl px-3 py-2 text-sm font-medium text-gray-600 transition-colors hover:bg-gray-100 disabled:cursor-not-allowed disabled:opacity-60 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--pw-accent)]\"\n >\n {origin === 'standalone' ? 'Close' : 'Back'}\n </button>\n <button\n type=\"submit\"\n disabled={!isValid || submitting}\n class=\"flex h-10 items-center justify-center rounded-xl px-4 text-sm font-semibold text-white transition-all hover:-translate-y-px hover:brightness-105 disabled:cursor-not-allowed disabled:opacity-60 disabled:hover:translate-y-0 disabled:hover:brightness-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--pw-accent)]\"\n style={{\n background:\n 'linear-gradient(180deg, color-mix(in srgb, var(--pw-accent) 92%, white), var(--pw-accent))',\n boxShadow:\n '0 1px 2px rgba(15,23,42,0.08), 0 6px 14px -4px color-mix(in srgb, var(--pw-accent) 50%, transparent)'\n }}\n >\n {submitting ? (\n <span class=\"inline-block h-4 w-4 animate-spin rounded-full border-2 border-white/40 border-t-white\" />\n ) : (\n 'Send'\n )}\n </button>\n </div>\n </form>\n </div>\n );\n}\n\ninterface FieldProps {\n type: 'email' | 'text';\n label: string;\n value: string;\n onInput: (v: string) => void;\n error?: string;\n autocomplete?: string;\n required?: boolean;\n}\n\nfunction Field({ type, label, value, onInput, error, autocomplete, required }: FieldProps) {\n return (\n <label class=\"flex flex-col gap-1.5\">\n <span class=\"text-xs font-medium text-gray-700\">{label}</span>\n <input\n type={type}\n value={value}\n onInput={(e) => onInput((e.target as HTMLInputElement).value)}\n autocomplete={autocomplete}\n required={required}\n class={`h-11 w-full rounded-xl border bg-white px-3.5 text-sm text-gray-900 shadow-[0_1px_0_rgba(15,23,42,0.02)] outline-none transition-all placeholder:text-gray-400 ${\n error\n ? 'border-red-400 focus:border-red-500 focus:shadow-[0_0_0_3px_rgba(239,68,68,0.18)]'\n : 'border-gray-200 hover:border-gray-300 focus:border-[var(--pw-accent)] focus:shadow-[0_0_0_3px_color-mix(in_srgb,var(--pw-accent)_18%,transparent)]'\n }`}\n />\n {error && <span class=\"text-xs text-red-600\">{error}</span>}\n </label>\n );\n}\n\ninterface TextareaFieldProps {\n label: string;\n value: string;\n onInput: (v: string) => void;\n error?: string;\n required?: boolean;\n}\n\nfunction TextareaField({ label, value, onInput, error, required }: TextareaFieldProps) {\n return (\n <label class=\"flex flex-col gap-1.5\">\n <span class=\"text-xs font-medium text-gray-700\">{label}</span>\n <textarea\n value={value}\n onInput={(e) => onInput((e.target as HTMLTextAreaElement).value)}\n required={required}\n rows={4}\n class={`min-h-[104px] w-full rounded-xl border bg-white px-3.5 py-2.5 text-sm leading-relaxed text-gray-900 shadow-[0_1px_0_rgba(15,23,42,0.02)] outline-none transition-all placeholder:text-gray-400 ${\n error\n ? 'border-red-400 focus:border-red-500 focus:shadow-[0_0_0_3px_rgba(239,68,68,0.18)]'\n : 'border-gray-200 hover:border-gray-300 focus:border-[var(--pw-accent)] focus:shadow-[0_0_0_3px_color-mix(in_srgb,var(--pw-accent)_18%,transparent)]'\n }`}\n />\n {error && <span class=\"text-xs text-red-600\">{error}</span>}\n </label>\n );\n}\n\ninterface DropzoneProps {\n files: File[];\n onChange: (next: File[]) => void;\n disabled?: boolean;\n}\n\nfunction Dropzone({ files, onChange, disabled }: DropzoneProps) {\n const inputRef = useRef<HTMLInputElement | null>(null);\n const [dragOver, setDragOver] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const handleFiles = (incoming: FileList | null): void => {\n if (!incoming || disabled) return;\n setError(null);\n const arr = Array.from(incoming);\n if (files.length + arr.length > MAX_FILES) {\n setError(`Up to ${MAX_FILES} files`);\n return;\n }\n const valid = arr.filter(\n (f) => ACCEPTED_MIME.includes(f.type) && f.size <= MAX_FILE_SIZE\n );\n if (valid.length !== arr.length) {\n setError('Only JPEG/PNG/WebP, ≤ 10MB each');\n return;\n }\n onChange([...files, ...valid]);\n };\n\n return (\n <div>\n <span class=\"text-xs font-medium text-gray-700\">Attachments (optional)</span>\n <div\n role=\"button\"\n tabIndex={0}\n aria-label=\"Attachments upload\"\n onClick={() => !disabled && inputRef.current?.click()}\n onDragOver={(e) => {\n e.preventDefault();\n if (!disabled) setDragOver(true);\n }}\n onDragLeave={() => setDragOver(false)}\n onDrop={(e) => {\n e.preventDefault();\n setDragOver(false);\n handleFiles(e.dataTransfer?.files ?? null);\n }}\n class={`mt-1.5 cursor-pointer rounded-2xl border border-dashed p-3.5 text-center transition-all ${\n dragOver\n ? 'border-[var(--pw-accent)] bg-[color-mix(in_srgb,var(--pw-accent)_6%,white)]'\n : 'border-gray-300 hover:border-gray-400 hover:bg-gray-50/60'\n } ${disabled ? 'cursor-not-allowed opacity-60' : ''}`}\n >\n <div class=\"text-xs text-gray-500\">Drop images here or click to select</div>\n <div class=\"mt-0.5 text-[11px] text-gray-400\">\n JPEG/PNG/WebP, up to {MAX_FILES} files, ≤ 10MB each\n </div>\n </div>\n <input\n ref={inputRef}\n type=\"file\"\n multiple\n accept={ACCEPTED_MIME.join(',')}\n class=\"hidden\"\n onChange={(e) => {\n handleFiles((e.target as HTMLInputElement).files);\n (e.currentTarget as HTMLInputElement).value = '';\n }}\n />\n {error && <p class=\"mt-1 text-xs text-red-600\">{error}</p>}\n {files.length > 0 && (\n <ul class=\"mt-2 flex flex-col gap-1\">\n {files.map((f, i) => (\n <li\n key={`${f.name}-${f.size}-${i}`}\n class=\"flex items-center justify-between gap-2 rounded bg-gray-50 px-2 py-1 text-xs\"\n >\n <span class=\"truncate text-gray-700\">{f.name}</span>\n <button\n type=\"button\"\n onClick={() => {\n const next = [...files];\n next.splice(i, 1);\n onChange(next);\n }}\n disabled={disabled}\n class=\"text-gray-500 hover:text-red-600 disabled:cursor-not-allowed disabled:opacity-60\"\n aria-label={`Remove ${f.name}`}\n >\n ✕\n </button>\n </li>\n ))}\n </ul>\n )}\n </div>\n );\n}\n","import { useState } from 'preact/hooks';\nimport type { LayoutBlock } from '../../../core/types';\nimport type { BlockProps } from '../types';\n\ntype CtaBlock = Extract<LayoutBlock, { type: 'cta_button' }>;\n\nexport function CtaButton({ block, ctx }: BlockProps<CtaBlock>) {\n const [busy, setBusy] = useState(false);\n const priceId = block.priceId ?? ctx.selectedPriceId;\n const disabled = busy || (block.action === 'checkout' && !priceId);\n\n const onClick = async () => {\n if (disabled) return;\n setBusy(true);\n try {\n await ctx.onAction(block.action, { priceId });\n } finally {\n setBusy(false);\n }\n };\n\n return (\n <button\n type=\"button\"\n disabled={disabled}\n onClick={onClick}\n class=\"relative flex h-12 w-full items-center justify-center overflow-hidden rounded-2xl px-4 text-sm font-semibold tracking-tight text-white transition-all duration-150 hover:-translate-y-px hover:brightness-105 active:translate-y-0 active:brightness-95 disabled:cursor-not-allowed disabled:opacity-60 disabled:hover:translate-y-0 disabled:hover:brightness-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--pw-accent)]\"\n style={{\n background:\n 'linear-gradient(180deg, color-mix(in srgb, var(--pw-accent) 92%, white), var(--pw-accent))',\n boxShadow:\n '0 1px 0 rgba(255,255,255,0.25) inset, 0 1px 2px rgba(15,23,42,0.08), 0 8px 20px -6px color-mix(in srgb, var(--pw-accent) 50%, transparent)'\n }}\n >\n {busy ? (\n <span class=\"inline-block h-4 w-4 animate-spin rounded-full border-2 border-white/40 border-t-white\" />\n ) : (\n block.label\n )}\n </button>\n );\n}\n","import { useState } from 'preact/hooks';\nimport type { LayoutBlock } from '../../../core/types';\nimport type { BlockProps } from '../types';\n\ntype CurrentSessionBlock = Extract<LayoutBlock, { type: 'current_session' }>;\n\n// Footer под cta_button. Зеркалит legacy v2 PaywallCurrentSession:\n// - залогинен → \"Signed in as <email>\" + Sign out (вызывает auth.signOut())\n// + Contact Support\n// - гость → \"Restore purchases\" + Contact Support\n// Без AuthClient в managed-режиме — рендерим только Restore + Support\n// (sign out нечему делать, restore без auth-клиента no-op'нет в handleAction).\n// Анон-сессия (is_anonymous=true) трактуется как «нет логина»: анон существует\n// только ради api-gateway-токена, у юзера нет email и UX-смысла «Signed in».\nexport function CurrentSession({ ctx }: BlockProps<CurrentSessionBlock>) {\n const session = ctx.authSession;\n const auth = ctx.auth;\n const [signingOut, setSigningOut] = useState(false);\n\n const onSupport = (): void => ctx.onAction('support');\n\n if (session && !session.user.is_anonymous) {\n const onSignOut = async (): Promise<void> => {\n if (!auth || signingOut) return;\n setSigningOut(true);\n try {\n await auth.signOut();\n } catch {\n /* signOut ошибки безшумные — onAuthChange всё равно отработает на refresh-fail */\n } finally {\n setSigningOut(false);\n }\n };\n\n return (\n <div class=\"mt-2 text-center text-xs text-gray-500\">\n <span>Signed in as </span>\n <b class=\"font-medium text-gray-700\">{session.user.email}</b>\n <div class=\"mt-1 flex items-center justify-center gap-3\">\n <button\n type=\"button\"\n onClick={onSignOut}\n disabled={!auth || signingOut}\n class=\"font-medium text-gray-600 underline-offset-2 hover:underline disabled:cursor-not-allowed disabled:opacity-60 focus:outline-none focus-visible:underline\"\n >\n {signingOut ? 'Signing out…' : 'Sign out'}\n </button>\n <Dot />\n <SupportLink onClick={onSupport} />\n </div>\n </div>\n );\n }\n\n return (\n <div class=\"mt-2 flex items-center justify-center gap-3 text-center text-xs text-gray-500\">\n <button\n type=\"button\"\n onClick={() => ctx.onAction('restore')}\n class=\"font-medium text-gray-600 underline-offset-2 hover:underline focus:outline-none focus-visible:underline\"\n >\n Restore purchases\n </button>\n <Dot />\n <SupportLink onClick={onSupport} />\n </div>\n );\n}\n\nfunction SupportLink({ onClick }: { onClick: () => void }) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n class=\"font-medium text-gray-600 underline-offset-2 hover:underline focus:outline-none focus-visible:underline\"\n >\n Contact Support\n </button>\n );\n}\n\nfunction Dot() {\n return <span class=\"h-1 w-1 rounded-full bg-gray-300\" aria-hidden=\"true\" />;\n}\n","import type { LayoutBlock } from '../../../core/types';\nimport type { BlockProps } from '../types';\n\ntype FeaturesListBlock = Extract<LayoutBlock, { type: 'features_list' }>;\n\nexport function FeaturesList({ block }: BlockProps<FeaturesListBlock>) {\n if (!block.items.length) return null;\n return (\n <ul class=\"flex flex-col gap-2.5\" role=\"list\">\n {block.items.map((item) => (\n <li key={item.id} class=\"flex items-start gap-3 text-sm text-gray-700\">\n <span\n class=\"mt-px flex h-5 w-5 flex-shrink-0 items-center justify-center rounded-full\"\n style={{\n background:\n 'color-mix(in srgb, var(--pw-accent) 12%, white)',\n color: 'var(--pw-accent)'\n }}\n aria-hidden=\"true\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 20 20\" fill=\"none\">\n <path\n d=\"M5 10l3 3 7-7\"\n stroke=\"currentColor\"\n stroke-width=\"2.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </span>\n <div class=\"flex flex-col gap-0.5\">\n <span class=\"font-medium leading-snug text-gray-900\">{item.name}</span>\n {item.desc ? (\n <span class=\"text-xs leading-relaxed text-gray-500\">{item.desc}</span>\n ) : null}\n </div>\n </li>\n ))}\n </ul>\n );\n}\n","import { useEffect, useRef } from 'preact/hooks';\nimport type { LayoutBlock } from '../../../core/types';\nimport type { BlockProps } from '../types';\n\ntype HeadingBlock = Extract<LayoutBlock, { type: 'heading' }>;\n\nconst BASE_FONT_PX = 26; // соответствует text-[1.625rem] у h1\nconst MIN_FONT_PX = 14;\nconst MAX_LINES = 2;\n\n// Авто-fit: если заголовок не вмещается в `MAX_LINES` строк при базовом размере,\n// уменьшаем font-size шагом 1px до тех пор, пока влезает или не упёрлись в\n// `MIN_FONT_PX`. Используется только для h1 — h2/h3 это подзаголовки, им\n// клиппинг не нужен. Считаем по реальной высоте элемента (scrollHeight) после\n// рендера — иначе пришлось бы держать canvas-измеритель.\nfunction fitHeading(el: HTMLElement, lineHeight: number): void {\n const maxHeight = lineHeight * MAX_LINES;\n let size = BASE_FONT_PX;\n el.style.fontSize = `${size}px`;\n while (el.scrollHeight > maxHeight && size > MIN_FONT_PX) {\n size -= 1;\n el.style.fontSize = `${size}px`;\n }\n}\n\nexport function Heading({ block, ctx }: BlockProps<HeadingBlock>) {\n const level = block.level ?? 1;\n const Tag = (`h${level}` as 'h1' | 'h2' | 'h3');\n const className =\n level === 1\n ? 'text-[1.625rem] font-semibold leading-tight text-gray-900 tracking-[-0.02em]'\n : level === 2\n ? 'text-xl font-semibold leading-snug text-gray-900 tracking-tight'\n : 'text-base font-medium text-gray-900';\n\n const ref = useRef<HTMLHeadingElement | null>(null);\n const autoFit = level === 1 && !!ctx.bootstrap.settings.title_auto_fit;\n\n useEffect(() => {\n if (!autoFit || !ref.current) return;\n // line-height у text-2xl = 1.5 (Tailwind дефолт). Считаем от текущего\n // computed line-height — устойчиво к будущим CSS-изменениям.\n const cs = getComputedStyle(ref.current);\n const lh = parseFloat(cs.lineHeight) || BASE_FONT_PX * 1.5;\n fitHeading(ref.current, lh);\n }, [autoFit, block.text]);\n\n return (\n <Tag ref={ref} class={className}>\n {block.text}\n </Tag>\n );\n}\n","import type { LayoutBlock, PaywallPrice } from '../../../core/types';\nimport type { BlockProps } from '../types';\n\ntype PriceGridBlock = Extract<LayoutBlock, { type: 'price_grid' }>;\n\nfunction formatPrice(price: PaywallPrice): string {\n const display = price.local ?? { currency: price.currency, amount: price.amount };\n try {\n return new Intl.NumberFormat(undefined, {\n style: 'currency',\n currency: display.currency,\n maximumFractionDigits: display.amount % 1 === 0 ? 0 : 2\n }).format(display.amount);\n } catch {\n return `${display.amount} ${display.currency}`;\n }\n}\n\nfunction intervalLabel(price: PaywallPrice): string {\n if (!price.interval || price.interval === 'lifetime') return 'one-time';\n const n = price.interval_count ?? 1;\n if (n === 1) return `per ${price.interval}`;\n return `every ${n} ${price.interval}s`;\n}\n\nexport function PriceGrid({ block, ctx }: BlockProps<PriceGridBlock>) {\n const filter = block.priceIds && block.priceIds.length > 0 ? new Set(block.priceIds) : null;\n const prices = ctx.bootstrap.prices.filter((p) => !filter || filter.has(p.id));\n\n if (prices.length === 0) {\n return <p class=\"text-sm text-gray-500\">No prices available.</p>;\n }\n\n const horizontal = block.view === 'horizontal';\n const popularLabel = block.popular_label ?? 'Most popular';\n\n // Horizontal раскладывает карточки в ряд через CSS grid. Кол-во колонок\n // = min(N,3) — задаём через inline-style, потому что Tailwind purge не\n // переживает runtime-конкатенацию `grid-cols-${N}`. Карточки в горизонтали\n // выкладывают цену снизу, не справа — иначе при N≥3 не хватает ширины.\n const cols = horizontal ? Math.min(prices.length, 3) : 1;\n\n return (\n <div\n class={horizontal ? 'grid gap-2.5' : 'flex flex-col gap-2.5'}\n style={horizontal ? { gridTemplateColumns: `repeat(${cols}, minmax(0, 1fr))` } : undefined}\n role=\"radiogroup\"\n aria-label=\"Plans\"\n >\n {prices.map((price) => {\n const selected = ctx.selectedPriceId === price.id;\n const isPopular = block.popular_price_id === price.id;\n return (\n <button\n key={price.id}\n type=\"button\"\n role=\"radio\"\n aria-checked={selected}\n onClick={() => {\n ctx.setSelectedPriceId(price.id);\n ctx.onAction('price_selected', { priceId: price.id, price });\n }}\n class={[\n 'group relative rounded-2xl border px-4 py-3.5 text-left transition-all duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--pw-accent)]',\n horizontal\n ? 'flex w-full flex-col items-start gap-1'\n : 'flex w-full items-center justify-between gap-3',\n selected\n ? 'border-[var(--pw-accent)] bg-[color-mix(in_srgb,var(--pw-accent)_6%,white)] shadow-[0_0_0_3px_color-mix(in_srgb,var(--pw-accent)_12%,transparent)]'\n : 'border-gray-200 bg-white hover:border-gray-300 hover:shadow-sm',\n isPopular ? 'mt-2.5' : ''\n ].join(' ')}\n >\n {isPopular ? (\n <span\n class=\"absolute -top-2.5 left-4 rounded-full px-2.5 py-0.5 text-[10px] font-semibold uppercase tracking-wider text-white shadow-sm\"\n style={{\n background:\n 'linear-gradient(135deg, var(--pw-accent), color-mix(in srgb, var(--pw-accent) 70%, black))'\n }}\n >\n {popularLabel}\n </span>\n ) : null}\n <div class={horizontal ? 'flex w-full items-start gap-2.5' : 'flex flex-1 items-start gap-2.5'}>\n <span\n class={[\n 'mt-0.5 flex h-4 w-4 flex-shrink-0 items-center justify-center rounded-full border transition-colors',\n selected\n ? 'border-[var(--pw-accent)] bg-[var(--pw-accent)]'\n : 'border-gray-300 bg-white group-hover:border-gray-400'\n ].join(' ')}\n aria-hidden=\"true\"\n >\n {selected ? <span class=\"h-1.5 w-1.5 rounded-full bg-white\" /> : null}\n </span>\n <div class=\"flex flex-col\">\n <span class=\"text-sm font-semibold text-gray-900\">{price.label ?? intervalLabel(price)}</span>\n {price.description ? (\n <span class=\"text-xs leading-relaxed text-gray-500\">{price.description}</span>\n ) : null}\n {price.trial_days ? (\n <span class=\"text-xs font-medium text-[var(--pw-accent)]\">\n {price.trial_days}-day free trial\n </span>\n ) : null}\n </div>\n </div>\n <div class={horizontal ? 'mt-1 flex flex-col items-start' : 'flex flex-col items-end'}>\n <span class=\"text-base font-semibold tracking-tight text-gray-900\">{formatPrice(price)}</span>\n <span class=\"text-[11px] text-gray-500\">{intervalLabel(price)}</span>\n </div>\n </button>\n );\n })}\n </div>\n );\n}\n","import type { LayoutBlock } from '../../../core/types';\nimport type { BlockProps } from '../types';\n\ntype TextBlock = Extract<LayoutBlock, { type: 'text' }>;\n\nexport function Text({ block }: BlockProps<TextBlock>) {\n return <p class=\"text-[0.9375rem] leading-relaxed text-gray-600\">{block.text}</p>;\n}\n","import type { LayoutBlock, PaywallPrice } from '../../../core/types';\nimport type { BlockProps } from '../types';\n\ntype TokenizationGateBlock = Extract<LayoutBlock, { type: 'tokenization_gate' }>;\n\nconst INTERVAL_MULTIPLIER: Record<string, number> = {\n week: 0.25,\n month: 1,\n year: 12\n};\n\nfunction intervalNoun(interval: PaywallPrice['interval']): string {\n if (!interval) return 'period';\n return interval;\n}\n\nexport function TokenizationGate({ block, ctx }: BlockProps<TokenizationGateBlock>) {\n if (!block.queries.length) return null;\n\n const selectedPrice = ctx.bootstrap.prices.find((p) => p.id === ctx.selectedPriceId);\n const interval = selectedPrice?.interval ?? null;\n const multiplier = interval ? INTERVAL_MULTIPLIER[interval] : undefined;\n\n return (\n <div class=\"flex flex-col gap-2\">\n <div class=\"text-sm font-semibold text-gray-800\">\n Included per <span>{intervalNoun(interval)}</span>:\n </div>\n <ul class=\"flex flex-col gap-2\" role=\"list\">\n {block.queries.map((q) => {\n const rawCount = Number.isFinite(q.count as number) ? (q.count as number) : 0;\n const amount =\n multiplier !== undefined ? Math.round(rawCount * multiplier) : rawCount;\n return (\n <li key={q.id} class={`flex gap-2 ${q.desc ? '' : 'items-center'}`}>\n <span\n class={`flex h-5 w-5 flex-shrink-0 items-center justify-center rounded-full ${q.desc ? 'mt-0.5' : ''}`}\n style={{\n background: 'color-mix(in srgb, var(--pw-accent) 12%, white)',\n color: 'var(--pw-accent)'\n }}\n aria-hidden=\"true\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 20 20\" fill=\"none\">\n <path\n d=\"M5 10l3 3 7-7\"\n stroke=\"currentColor\"\n stroke-width=\"2.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </span>\n <div>\n <span class=\"font-semibold text-gray-900 text-sm\">{amount}</span>{' '}\n <span class=\"text-sm text-gray-800\">{q.name}</span>\n {q.desc ? (\n <>\n <br />\n <span class=\"text-xs text-gray-500\">{q.desc}</span>\n </>\n ) : null}\n </div>\n </li>\n );\n })}\n </ul>\n </div>\n );\n}\n","import type { LayoutBlock } from '../../core/types';\nimport type { BlockComponent } from './types';\nimport { AuthPanel } from './blocks/AuthPanel';\nimport { CtaButton } from './blocks/CtaButton';\nimport { CurrentSession } from './blocks/CurrentSession';\nimport { FeaturesList } from './blocks/FeaturesList';\nimport { Heading } from './blocks/Heading';\nimport { PriceGrid } from './blocks/PriceGrid';\nimport { Text } from './blocks/Text';\nimport { TokenizationGate } from './blocks/TokenizationGate';\n\nexport const blockRegistry: Record<LayoutBlock['type'], BlockComponent<any>> = {\n heading: Heading,\n text: Text,\n price_grid: PriceGrid,\n cta_button: CtaButton,\n auth_panel: AuthPanel,\n current_session: CurrentSession,\n features_list: FeaturesList,\n tokenization_gate: TokenizationGate\n};\n","import { useMemo, useState } from 'preact/hooks';\nimport type { AuthClient, AuthSession } from '../../core/auth';\nimport type { Layout, PaywallBootstrap } from '../../core/types';\nimport { blockRegistry } from './registry';\nimport type { BlockContext } from './types';\n\nexport interface RendererProps {\n layout: Layout;\n bootstrap: PaywallBootstrap;\n onAction: (action: string, payload?: unknown) => void;\n auth?: AuthClient;\n authSession: AuthSession | null;\n}\n\nexport function Renderer({ layout, bootstrap, onAction, auth, authSession }: RendererProps) {\n const defaultPriceId = useMemo(() => bootstrap.prices[0]?.id ?? null, [bootstrap.prices]);\n const [selectedPriceId, setSelectedPriceId] = useState<string | null>(defaultPriceId);\n\n const ctx: BlockContext = {\n bootstrap,\n selectedPriceId,\n setSelectedPriceId,\n onAction,\n auth,\n authSession\n };\n\n return (\n <div class=\"flex flex-col gap-4\">\n {layout.blocks.map((block, i) => {\n const Cmp = blockRegistry[block.type];\n if (!Cmp) {\n if (typeof console !== 'undefined') {\n console.warn(`[paywall] unknown block type: ${block.type}`);\n }\n return null;\n }\n return <Cmp key={i} block={block as never} ctx={ctx} />;\n })}\n </div>\n );\n}\n","import { useEffect, useRef, useState } from 'preact/hooks';\nimport type { BillingClient } from '../core/BillingClient';\nimport type { AuthSession } from '../core/auth';\nimport type { LayoutBlock, PaywallBootstrap } from '../core/types';\nimport { PaywallError } from '../core/types';\nimport { Modal } from './Modal';\nimport { AuthGate } from './AuthGate';\nimport { AnonGate } from './AnonGate';\nimport { SupportGate } from './SupportGate';\nimport { Renderer } from './renderer/Renderer';\n\nexport type PaywallView = 'layout' | 'support' | 'auth' | 'anon';\n\n/**\n * Публичный snapshot состояния PaywallUI для host'а. Производится из internal\n * LoadState + GateState + open/purchased флагов. Каждое реальное изменение —\n * один call onState; повторы (`useSyncExternalStore`-friendly).\n */\nexport interface PaywallStateSnapshot {\n /** Модалка отрендерена и видна. False — closed (или ещё не открывалась). */\n open: boolean;\n /** Что показывается в модалке. null когда `open=false`. */\n view:\n | 'loading'\n | 'error'\n | 'layout'\n | 'auth'\n | 'anon'\n | 'support'\n | 'awaiting_payment'\n | 'popup_blocked'\n | 'purchased'\n | null;\n /** Заполнено только когда `view === 'error'`. */\n error: PaywallError | null;\n}\n\n// 'anon' — отдельная вью для signInAnonymously (silent resume / fresh signin).\n// Не сливается с 'auth', потому что UX разный: auth — формы email/oauth,\n// anon — спиннер без интеракции; и flow завершения тоже разный (анон не идёт\n// через has_active_subscription pre-check после signin'а).\n\nexport interface PaywallRootProps {\n client: BillingClient;\n open: boolean;\n onClose: () => void;\n onEvent: (event: string, payload?: unknown) => void;\n /** Какой view показать при open=true. 'support' стартует сразу с саппорт-формой,\n * Back/Done закрывают модалку (origin='standalone'). По умолчанию 'layout'. */\n initialView?: PaywallView;\n /** Server-confirmed покупка — показать success-вью с кнопкой Continue.\n * Управляется снаружи (PaywallUI выставляет true из watcher.onActive),\n * сбрасывается на open()/close(). Перебивает любые другие view. */\n purchased?: boolean;\n /** Renewal/upgrade flow. true — пропускаем все has_active_subscription\n * pre-check'и (bootstrap-time + post-auth), и при checkout передаём\n * `ignoreActivePurchase: true` на бэк, чтобы /start-checkout не вернул\n * 409 для уже подписанного юзера. См. OpenOptions.renew. */\n renew?: boolean;\n /** Публичный state-machine notify. PaywallUI прокидывает сюда колбек, который\n * кэширует snapshot и эмитит свой `onStateChange`. Если не передан —\n * state-tracking отключён (нет оверхеда для host'ов, которым не нужно). */\n onState?: (snapshot: PaywallStateSnapshot) => void;\n}\n\ntype LoadState =\n | { status: 'idle' }\n | { status: 'loading' }\n | { status: 'ready'; data: PaywallBootstrap }\n | { status: 'error'; error: PaywallError };\n\ntype GateState =\n | { kind: 'layout' }\n // pendingCheckout=undefined, origin='layout' — gate открыт через \"Restore purchases\"\n // (без последующего checkout-а), после signIn схлопываемся в layout. С\n // pendingCheckout — gate открыт по preauth-flow от cta_button и после signIn\n // auto-resume createCheckout. origin='standalone' — paywall.openAuth(): модалка\n // открыта только для логина, после signIn / Back закрываем модалку, layout\n // вообще не показываем.\n | {\n kind: 'auth_gate';\n pendingCheckout?: { priceId: string };\n origin?: 'layout' | 'standalone';\n }\n // origin='standalone' — paywall.openAnonGate(): модалка открыта только ради\n // анонимного логина, после signin'а закрываем модалку. origin='layout' — пока\n // не используется (анон-блока в layout нет), но оставляем для симметрии с\n // auth_gate на случай будущего inline-варианта.\n | {\n kind: 'anon_gate';\n origin: 'layout' | 'standalone';\n }\n // origin='layout' — пришли из current_session-блока, Back возвращает в layout.\n // origin='standalone' — модалка открыта только для саппорта (paywall.openSupport()),\n // Back закрывает модалку.\n | { kind: 'support'; origin: 'layout' | 'standalone' }\n // window.open отдал handle — checkout открылся в новой вкладке. Пейвол остаётся\n // как индикатор: «оплати в той вкладке». priceId храним, чтобы кнопка retry\n // могла пересоздать checkout (URL'ы у Stripe/Paddle expire'ятся). url храним,\n // чтобы fallback-ссылка «Didn't open? Click here» переоткрывала тот же URL без\n // повторного похода в createCheckout — нужно для случая когда window.open\n // отдал handle, но реально таб заблокирован (агрессивные мобильные блокеры).\n | { kind: 'awaiting_payment'; priceId: string; url: string }\n // window.open вернул null — попап заблокирован (бывает после async-резюма\n // post-auth, когда transient activation истёк). НЕ редиректим текущую вкладку:\n // пейвол должен остаться. URL уже выписан — кнопка «Open checkout» дёрнет\n // window.open под фреш-гестуром, без второго похода в createCheckout.\n | { kind: 'popup_blocked'; priceId: string; url: string }\n // Юзер уже залогинен и has_active_subscription — показываем success-view.\n // Срабатывает либо после auth-resume (поллим getUser сразу после signIn),\n // либо когда /start-checkout вернул 409 hasActivePurchase. restored=true\n // меняет текст PurchaseSuccessView на «Subscription restored».\n | { kind: 'purchase_success'; restored: boolean }\n // После signIn ждём getUser({force:true}), пока не узнаем есть ли уже\n // active subscription. Без этого промежуточного state'а юзер видит\n // несколько секунд auth_gate'овский «серый экран» с уже скрытой формой.\n | { kind: 'verifying' };\n\ntype AuthPanelBlock = Extract<LayoutBlock, { type: 'auth_panel' }>;\n\nfunction computePaywallSnapshot(\n open: boolean,\n state: LoadState,\n gate: GateState,\n purchased: boolean | undefined\n): PaywallStateSnapshot {\n if (!open) return { open: false, view: null, error: null };\n if (purchased) return { open: true, view: 'purchased', error: null };\n if (state.status === 'idle' || state.status === 'loading') {\n return { open: true, view: 'loading', error: null };\n }\n if (state.status === 'error') {\n return { open: true, view: 'error', error: state.error };\n }\n if (gate.kind === 'support') return { open: true, view: 'support', error: null };\n if (gate.kind === 'auth_gate') return { open: true, view: 'auth', error: null };\n if (gate.kind === 'anon_gate') return { open: true, view: 'anon', error: null };\n if (gate.kind === 'awaiting_payment') {\n return { open: true, view: 'awaiting_payment', error: null };\n }\n if (gate.kind === 'popup_blocked') {\n return { open: true, view: 'popup_blocked', error: null };\n }\n if (gate.kind === 'purchase_success') {\n return { open: true, view: 'purchased', error: null };\n }\n if (gate.kind === 'verifying') {\n return { open: true, view: 'loading', error: null };\n }\n return { open: true, view: 'layout', error: null };\n}\n\nfunction sameSnapshot(a: PaywallStateSnapshot, b: PaywallStateSnapshot): boolean {\n return a.open === b.open && a.view === b.view && a.error === b.error;\n}\n\nexport function PaywallRoot({\n client,\n open,\n onClose,\n onEvent,\n initialView,\n purchased,\n renew,\n onState\n}: PaywallRootProps) {\n const [state, setState] = useState<LoadState>({ status: 'idle' });\n // session держим в state, чтобы блоки (auth_panel) ре-рендерились на login/logout.\n // Без этого AuthPanel прочитал бы snapshot один раз и не схлопнулся бы после\n // успешного signin'а.\n const [authSession, setAuthSession] = useState<AuthSession | null>(\n () => client.auth?.getCachedSession() ?? null\n );\n const [gate, setGate] = useState<GateState>(() => {\n if (initialView === 'support') return { kind: 'support', origin: 'standalone' };\n if (initialView === 'auth') return { kind: 'auth_gate', origin: 'standalone' };\n if (initialView === 'anon') return { kind: 'anon_gate', origin: 'standalone' };\n return { kind: 'layout' };\n });\n // Защита от двойного auto-resume: useEffect ниже зависит от authSession,\n // и подписка onAuthChange может прислать одну и ту же сессию повторно\n // (refresh) — без флага мы дважды дёрнем createCheckout.\n const resumingRef = useRef(false);\n\n // State-machine bridge: эмитим snapshot когда меняется любое из\n // (open, state, gate, purchased). sameSnapshot гасит no-op'ы — например\n // переход loading→error меняет state.status, но если мы уже в error-вью\n // (иначе невозможно), эмит не повторится.\n const lastSnapshotRef = useRef<PaywallStateSnapshot | null>(null);\n useEffect(() => {\n if (!onState) return;\n const next = computePaywallSnapshot(open, state, gate, purchased);\n const prev = lastSnapshotRef.current;\n if (prev && sameSnapshot(prev, next)) return;\n lastSnapshotRef.current = next;\n onState(next);\n }, [open, state, gate, purchased, onState]);\n\n useEffect(() => {\n if (!client.auth) return;\n return client.auth.onAuthChange((s) => setAuthSession(s));\n }, [client.auth]);\n\n // Live-обновление bootstrap'а: BillingClient.setBootstrap (preview-mode в\n // редакторе админки) или cross-tab storage.watch эмитят onBootstrapChange.\n // Перерендериваем модалку, только если она уже в ready-фазе — иначе\n // bootstrap-effect ниже сам подхватит свежий cached на open().\n // Guard: тесты передают stub-клиента без onBootstrapChange — skip silently.\n useEffect(() => {\n if (typeof client.onBootstrapChange !== 'function') return;\n return client.onBootstrapChange((data) => {\n setState((prev) =>\n prev.status === 'ready' ? { status: 'ready', data } : prev\n );\n });\n }, [client]);\n\n useEffect(() => {\n if (!open) return;\n if (state.status === 'ready' || state.status === 'loading') return;\n\n let cancelled = false;\n setState({ status: 'loading' });\n client\n .bootstrap()\n .then((data) => {\n if (cancelled) return;\n setState({ status: 'ready', data });\n onEvent('ready', data);\n // Юзер уже подписан — host вызвал open() вслепую (без getAccess pre-check'а),\n // или просто из popup'а «Open paywall». Не показываем тарифы — переключаемся\n // в restored success-view. Эмитим purchase_completed чтобы host получил\n // согласованный сигнал, как из любых других путей (UserWatcher, 409 в\n // checkout, auth-resume). renew=true пропускает эту проверку — host явно\n // показывает «Renew»/«Upgrade», тарифы должны быть видны.\n if (data.user?.has_active_subscription && !renew) {\n onEvent('purchase_completed', {\n priceId: null,\n sessionId: null,\n restored: true\n });\n setGate({ kind: 'purchase_success', restored: true });\n }\n })\n .catch((error: unknown) => {\n if (cancelled) return;\n const err =\n error instanceof PaywallError\n ? error\n : new PaywallError('unknown', 'Failed to load paywall', { cause: error });\n setState({ status: 'error', error: err });\n onEvent('error', err);\n });\n return () => {\n cancelled = true;\n };\n }, [open, client]);\n\n // Закрытие/повторное открытие модалки сбрасывает gate. Standalone-flows\n // (openSupport / openAuth) PaywallUI вызывает на уже смонтированном компоненте\n // через handle.update({initialView: 'support'|'auth'}) — useState-initializer\n // отрабатывает только на первом mount'е, поэтому без этого useEffect'а gate\n // оставался бы 'layout' (с тарифами) при последующих standalone open'ах.\n useEffect(() => {\n if (!open) {\n setGate({ kind: 'layout' });\n resumingRef.current = false;\n return;\n }\n if (initialView === 'support') {\n setGate({ kind: 'support', origin: 'standalone' });\n } else if (initialView === 'auth') {\n setGate({ kind: 'auth_gate', origin: 'standalone' });\n } else if (initialView === 'anon') {\n setGate({ kind: 'anon_gate', origin: 'standalone' });\n }\n }, [open, initialView]);\n\n const runCheckout = async (priceId: string) => {\n try {\n const result = await client.createCheckout({\n priceId,\n ignoreActivePurchase: renew === true\n });\n onEvent('checkout_started', { priceId, url: result.url, acquiring: result.acquiring });\n if (typeof window === 'undefined' || !result.url) return;\n // Без `noopener,noreferrer` в фичах: эти флаги заставляют window.open\n // ВСЕГДА вернуть null (даже когда попап реально открылся), и мы не\n // могли отличить «успех» от «заблокирован». Severance делаем вручную\n // через popup.opener=null после успеха — на checkout-домене (Stripe/\n // Paddle) opener-доступ всё равно cross-origin-restricted, но явный\n // null безопаснее.\n const popup = window.open(result.url, '_blank');\n if (popup) {\n try {\n popup.opener = null;\n } catch {\n /* cross-origin already — ok */\n }\n setGate({ kind: 'awaiting_payment', priceId, url: result.url });\n } else {\n // Попап заблокирован — обычно из-за stale transient activation\n // (auto-resume после async signin). НЕ уносим юзера через\n // location.assign: пейвол должен остаться открытым. Показываем\n // inline retry; клик по кнопке — fresh gesture, попап откроется.\n setGate({ kind: 'popup_blocked', priceId, url: result.url });\n }\n } catch (error) {\n // 409 hasActivePurchase от бэка — это не ошибка чекаута, это «у юзера\n // уже есть активная подписка». Освежаем cache (host'овский userChange\n // должен увидеть has_active_subscription=true), эмитим purchase_completed\n // с restored=true и переключаемся в success-view. Не setGate в layout,\n // не onEvent('error') — иначе host увидит false-negative.\n if (error instanceof PaywallError && error.code === 'already_purchased') {\n try {\n await client.getUser({ force: true });\n } catch {\n /* offline / 401 — host'у getUser сам отрапортует, тут это не блокирует success-view */\n }\n onEvent('purchase_completed', { priceId, sessionId: null, restored: true });\n setGate({ kind: 'purchase_success', restored: true });\n return;\n }\n const err =\n error instanceof PaywallError\n ? error\n : new PaywallError('checkout_failed', 'Checkout failed', { cause: error });\n onEvent('error', err);\n // На ошибке возвращаем юзера в layout — иначе застрянем в auth_gate\n // (если пришли через preauth-flow) с уже залогиненной сессией.\n setGate({ kind: 'layout' });\n }\n };\n\n const reopenCheckout = (priceId: string, url: string) => {\n if (typeof window === 'undefined') return;\n const popup = window.open(url, '_blank');\n if (popup) {\n try {\n popup.opener = null;\n } catch {\n /* ignore */\n }\n setGate({ kind: 'awaiting_payment', priceId, url });\n }\n // Если и сейчас null — оставляем popup_blocked, юзер кликнет ещё раз.\n };\n\n // Auto-resume: появилась сессия в открытом gate → продолжаем флоу.\n // Pending preauth-checkout — НЕ схлопываем gate в layout до runCheckout:\n // иначе юзер видит мигание тарифов между submit'ом auth-формы и открытием\n // чекаут-вкладки. runCheckout сам переведёт gate в awaiting_payment /\n // popup_blocked / layout (на ошибке). Restore-flow без pendingCheckout —\n // просто возвращаемся в layout. resumingRef защищает от повторного запуска,\n // если authChange сработает несколько раз за один gate-цикл (refresh).\n useEffect(() => {\n if (gate.kind !== 'auth_gate') return;\n // Анон-сессия не считается логином: юзер пришёл в auth_gate реально\n // залогиниться. Иначе openAuth() при существующем анон-токене мгновенно\n // закрывал бы модалку через auto-resume, и формы он бы не увидел.\n if (!authSession || authSession.user.is_anonymous) return;\n if (resumingRef.current) return;\n resumingRef.current = true;\n const pending = gate.pendingCheckout;\n const origin = gate.origin;\n // Сразу переключаемся в verifying — иначе модалка висит в auth_gate с\n // уже залогиненным юзером (~3с пока getUser ходит к бэку), и юзер видит\n // «пустой серый экран» вместо progress'а. Loader честнее показывает что\n // SDK что-то делает.\n setGate({ kind: 'verifying' });\n void (async () => {\n // Прежде чем продолжать flow (runCheckout / возврат в layout / закрытие\n // модалки), проверяем — может, у юзера уже есть active subscription.\n // Сценарии: Restore-кнопка (он уже платил с другого аккаунта); preauth\n // signIn (юзер вспомнил, что подписка есть); standalone openAuth.\n // Без этой проверки юзер увидел бы тарифы и кликнул Buy → 409 от бэка\n // → fallback на already_purchased. Лучше не давать ему этот шаг.\n // renew=true пропускает проверку — host явно делает renewal-flow.\n if (!renew) {\n try {\n const user = await client.getUser({ force: true });\n if (user.has_active_subscription) {\n onEvent('purchase_completed', {\n priceId: pending?.priceId ?? null,\n sessionId: null,\n restored: true\n });\n setGate({ kind: 'purchase_success', restored: true });\n return;\n }\n } catch {\n /* getUser упал — продолжаем обычный flow, юзер увидит тарифы */\n }\n }\n if (!pending) {\n // openAuth standalone: после signIn закрываем модалку, layout не показываем.\n // Restore-flow (origin='layout' или undefined): возвращаемся в layout.\n if (origin === 'standalone') {\n onClose();\n } else {\n setGate({ kind: 'layout' });\n }\n return;\n }\n await runCheckout(pending.priceId);\n })().finally(() => {\n resumingRef.current = false;\n });\n }, [authSession, gate]);\n\n const handleAction = async (action: string, payload?: unknown) => {\n if (action === 'close') {\n onClose();\n return;\n }\n if (action === 'price_selected') {\n // Пробрасываем как есть — блок уже собрал { priceId, price }.\n onEvent('price_selected', payload);\n return;\n }\n if (action === 'restore') {\n // CurrentSession-блок: гость кликнул \"Restore purchases\". Открываем\n // gate без pendingCheckout — после signIn auto-resume просто схлопнётся.\n // Без AuthClient'а ничего не делаем (managed-auth не подключён).\n if (!client.auth) return;\n if (client.auth.getCachedSession()) return;\n setGate({ kind: 'auth_gate' });\n return;\n }\n if (action === 'support') {\n // CurrentSession-блок: открыть саппорт-форму. Видна и гостю, и залогиненному.\n // Из layout — Back возвращает к тарифам.\n setGate({ kind: 'support', origin: 'layout' });\n return;\n }\n if (action === 'checkout' && state.status === 'ready') {\n const priceId = (payload as { priceId?: string } | undefined)?.priceId;\n if (!priceId) {\n onEvent('error', new PaywallError('no_price', 'No price selected'));\n return;\n }\n const mode = state.data.settings.checkout_mode ?? 'guest';\n const needsAuth = mode === 'preauth' && !!client.auth && !client.auth.getCachedSession();\n if (needsAuth) {\n setGate({ kind: 'auth_gate', pendingCheckout: { priceId } });\n return;\n }\n await runCheckout(priceId);\n }\n };\n\n const brand = state.status === 'ready' ? state.data.settings.brand_color : null;\n const testMode = state.status === 'ready' ? !!state.data.settings.is_test_mode : false;\n // allow_close=undefined трактуем как true (default до bootstrap'а — пейвол\n // должен быть закрываемым во время loading/error, иначе юзера запрёт). После\n // ready settings.allow_close=false запретит ESC/overlay/крестик.\n const allowClose =\n state.status === 'ready' ? state.data.settings.allow_close !== false : true;\n\n const gateBlock: AuthPanelBlock = {\n type: 'auth_panel',\n heading: 'Sign in to continue',\n allow_signup: true,\n allow_password_reset: true,\n // Не скрываем при наличии сессии — auto-resume useEffect отрабатывает быстрее,\n // чем хотим показывать \"Signed in as ...\" промежуточным экраном.\n hide_when_authenticated: false,\n providers: state.status === 'ready' ? state.data.settings.auth_providers : undefined\n };\n\n // Support-view имеет приоритет над bootstrap-state: standalone-открытие\n // (paywall.openSupport()) должно работать даже если bootstrap ещё грузится\n // или упал — сама форма от settings/prices не зависит. Из layout-режима\n // Back возвращает к тарифам, из standalone — закрывает модалку.\n const supportView =\n gate.kind === 'support' ? (\n <SupportGate\n client={client}\n authSession={authSession}\n origin={gate.origin}\n onBack={() => {\n if (gate.origin === 'standalone') onClose();\n else setGate({ kind: 'layout' });\n }}\n />\n ) : null;\n\n return (\n <Modal\n open={open}\n onClose={onClose}\n brandColor={brand}\n testMode={testMode}\n allowClose={allowClose}\n labelledBy=\"pw-title\"\n >\n {purchased ? (\n <PurchaseSuccessView onContinue={onClose} />\n ) : gate.kind === 'purchase_success' ? (\n <PurchaseSuccessView restored={gate.restored} onContinue={onClose} />\n ) : supportView ? (\n supportView\n ) : state.status === 'loading' || state.status === 'idle' || gate.kind === 'verifying' ? (\n <div class=\"flex flex-col items-center justify-center gap-3 py-12\">\n <span class=\"inline-block h-7 w-7 animate-spin rounded-full border-[2.5px] border-gray-200 border-t-[var(--pw-accent)]\" />\n <span class=\"text-xs font-medium tracking-wide text-gray-500\">\n {gate.kind === 'verifying' ? 'Checking your subscription…' : 'Loading…'}\n </span>\n </div>\n ) : state.status === 'error' ? (\n <div class=\"flex flex-col items-center gap-2 py-8 text-center\">\n <div class=\"flex h-11 w-11 items-center justify-center rounded-full bg-red-50\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"M10 6v5M10 14h.01\"\n stroke=\"#dc2626\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n />\n <circle cx=\"10\" cy=\"10\" r=\"8\" stroke=\"#dc2626\" stroke-width=\"1.75\" />\n </svg>\n </div>\n <p class=\"text-sm font-semibold tracking-tight text-gray-900\">Something went wrong</p>\n <p class=\"text-xs leading-relaxed text-gray-500\">{state.error.message}</p>\n </div>\n ) : gate.kind === 'auth_gate' && client.auth ? (\n <AuthGate\n block={gateBlock}\n bootstrap={state.data}\n auth={client.auth}\n authSession={authSession}\n // standalone (paywall.openAuth()) — модалка открыта только ради\n // signin'а, Back-кнопка дублирует ESC/X. Скрываем. Для preauth/\n // restore-flow Back ведёт обратно в layout — оставляем.\n showBack={gate.origin !== 'standalone'}\n onBack={() => {\n if (gate.origin === 'standalone') onClose();\n else setGate({ kind: 'layout' });\n }}\n />\n ) : gate.kind === 'anon_gate' && client.auth ? (\n <AnonGate\n auth={client.auth}\n // standalone — pure anon-flow (paywall.openAnonGate()). Закрываем\n // модалку после signin'а: host подцепит свежую session через\n // onAuthChange/onUserChange. Для layout-варианта возвращаемся в\n // тарифы; pendingCheckout анону не выписываем (анон по дизайну\n // не покупает — он юзает api-gateway без email).\n onSuccess={() => {\n if (gate.origin === 'standalone') onClose();\n else setGate({ kind: 'layout' });\n }}\n onBack={\n gate.origin === 'standalone'\n ? undefined\n : () => setGate({ kind: 'layout' })\n }\n />\n ) : gate.kind === 'awaiting_payment' ? (\n <AwaitingPaymentView\n client={client}\n onBack={() => setGate({ kind: 'layout' })}\n onReopen={() => {\n if (typeof window === 'undefined') return;\n const popup = window.open(gate.url, '_blank');\n if (popup) {\n try {\n popup.opener = null;\n } catch {\n /* ignore */\n }\n }\n }}\n onRetry={() => runCheckout(gate.priceId)}\n />\n ) : gate.kind === 'popup_blocked' ? (\n <div class=\"flex flex-col items-center gap-3 py-8 text-center\">\n <div\n class=\"flex h-11 w-11 items-center justify-center rounded-full\"\n style={{ background: 'color-mix(in srgb, var(--pw-accent) 12%, white)', color: 'var(--pw-accent)' }}\n aria-hidden=\"true\"\n >\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\">\n <path\n d=\"M4 5h12v10H4z\"\n stroke=\"currentColor\"\n stroke-width=\"1.75\"\n stroke-linejoin=\"round\"\n />\n <path d=\"M7 9l3 3 4-5\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </div>\n <p class=\"text-sm font-semibold tracking-tight text-gray-900\">Allow popups to continue</p>\n <p class=\"max-w-[18rem] text-xs leading-relaxed text-gray-500\">\n Your browser blocked the checkout tab. Click below to open it.\n </p>\n <button\n type=\"button\"\n onClick={() => reopenCheckout(gate.priceId, gate.url)}\n class=\"mt-1 rounded-xl px-4 py-2 text-xs font-semibold text-white transition-all hover:-translate-y-px hover:brightness-105 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--pw-accent)]\"\n style={{\n background:\n 'linear-gradient(180deg, color-mix(in srgb, var(--pw-accent) 92%, white), var(--pw-accent))',\n boxShadow:\n '0 1px 2px rgba(15,23,42,0.08), 0 6px 14px -4px color-mix(in srgb, var(--pw-accent) 50%, transparent)'\n }}\n >\n Open checkout\n </button>\n </div>\n ) : (\n <Renderer\n layout={state.data.layout!}\n bootstrap={state.data}\n onAction={handleAction}\n auth={client.auth}\n authSession={authSession}\n />\n )}\n </Modal>\n );\n}\n\n// Экран ожидания после window.open(checkoutUrl). UserWatcher в PaywallUI уже\n// poll'ит user-state раз в 5s (visible вкладка) — этот экран только UI-обёртка.\n//\n// «I've paid» — для нетерпеливых: форсим getUser({force:true}), чтобы cache\n// обновился сразу, и постим внутрь окна 'paywall_purchase' message — этого\n// ждёт UserWatcher.handleMessage и сразу же тригерит свой check(). Если\n// подписка ещё не активна (webhook не дошёл), показываем inline-таймаут на 5s.\n//\n// «Open checkout again» — fallback для случая «window.open отдал handle, но таб\n// заблокирован» (агрессивные мобильные блокеры). Дёргает existing URL без\n// похода в createCheckout, не сбивая awaiting_payment state.\n//\n// «Tab closed? Try again» — крайний случай: URL у Stripe/Paddle/etc. может\n// expire'нуться, поэтому пересоздаём checkout. Менее prominent кнопка.\nfunction AwaitingPaymentView({\n client,\n onBack,\n onReopen,\n onRetry\n}: {\n client: BillingClient;\n onBack: () => void;\n onReopen: () => void;\n onRetry: () => void;\n}) {\n const [checking, setChecking] = useState(false);\n const [stillPending, setStillPending] = useState(false);\n const stillPendingTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n return () => {\n if (stillPendingTimerRef.current !== null) {\n clearTimeout(stillPendingTimerRef.current);\n }\n };\n }, []);\n\n const handleVerify = async () => {\n if (checking) return;\n setChecking(true);\n setStillPending(false);\n try {\n const user = await client.getUser({ force: true });\n if (user.has_active_subscription) {\n // Будит UserWatcher — он сразу же сделает check(), увидит fresh active\n // user из cache и эмитит purchase_completed (PaywallUI переведёт в\n // PurchaseSuccessView). Не эмитим purchase_completed напрямую отсюда —\n // single source of truth остаётся в watcher.onActive.\n if (typeof window !== 'undefined') {\n window.postMessage({ type: 'paywall_purchase' }, '*');\n }\n return;\n }\n // Webhook ещё не дошёл — показываем подсказку и через 5s сворачиваем,\n // чтобы юзер мог нажать ещё раз. setTimeout cancel'нется на unmount.\n setStillPending(true);\n if (stillPendingTimerRef.current !== null) {\n clearTimeout(stillPendingTimerRef.current);\n }\n stillPendingTimerRef.current = setTimeout(() => {\n setStillPending(false);\n stillPendingTimerRef.current = null;\n }, 5000);\n } catch {\n setStillPending(true);\n } finally {\n setChecking(false);\n }\n };\n\n return (\n <div class=\"flex flex-col gap-3\">\n <button\n type=\"button\"\n onClick={onBack}\n class=\"-ml-1 self-start rounded-md px-1.5 py-0.5 text-xs font-medium text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--pw-accent)]\"\n >\n ← Back\n </button>\n <div class=\"flex flex-col items-center gap-3 py-6 text-center\">\n <div class=\"relative flex h-12 w-12 items-center justify-center\">\n <span\n class=\"absolute inset-0 animate-ping rounded-full opacity-40\"\n style={{ background: 'color-mix(in srgb, var(--pw-accent) 30%, transparent)' }}\n aria-hidden=\"true\"\n />\n <span class=\"relative inline-block h-7 w-7 animate-spin rounded-full border-[2.5px] border-gray-200 border-t-[var(--pw-accent)]\" />\n </div>\n <p class=\"text-sm font-semibold tracking-tight text-gray-900\">Complete payment in the new tab</p>\n <p class=\"max-w-[20rem] text-xs leading-relaxed text-gray-500\">\n We'll detect your payment automatically — or click below once you're done.\n </p>\n <button\n type=\"button\"\n onClick={handleVerify}\n disabled={checking}\n class=\"mt-1 rounded-xl px-5 py-2.5 text-sm font-semibold text-white transition-all hover:-translate-y-px hover:brightness-105 disabled:cursor-not-allowed disabled:opacity-60 disabled:hover:translate-y-0 disabled:hover:brightness-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--pw-accent)]\"\n style={{\n background:\n 'linear-gradient(180deg, color-mix(in srgb, var(--pw-accent) 92%, white), var(--pw-accent))',\n boxShadow:\n '0 1px 2px rgba(15,23,42,0.08), 0 6px 14px -4px color-mix(in srgb, var(--pw-accent) 50%, transparent)'\n }}\n >\n {checking ? 'Checking…' : \"I've paid\"}\n </button>\n {stillPending ? (\n <p class=\"text-xs leading-relaxed text-gray-500\">\n Payment is still being processed. Please try again in a moment.\n </p>\n ) : null}\n </div>\n <div class=\"rounded-2xl border border-gray-200 bg-gray-50/60 p-3.5\">\n <p class=\"text-xs leading-relaxed text-gray-600\">\n Checkout window didn't open or got blocked? Click here to open it again.\n </p>\n <button\n type=\"button\"\n onClick={onReopen}\n class=\"mt-2.5 w-full rounded-xl border border-gray-200 bg-white px-3 py-2 text-xs font-semibold text-gray-700 transition-colors hover:border-gray-300 hover:bg-gray-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--pw-accent)]\"\n >\n Open checkout again\n </button>\n </div>\n <button\n type=\"button\"\n onClick={onRetry}\n class=\"self-center rounded-md px-2 py-1 text-xs text-gray-500 underline-offset-2 hover:text-gray-900 hover:underline focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--pw-accent)]\"\n >\n Tab closed? Try again\n </button>\n </div>\n );\n}\n\nfunction PurchaseSuccessView({\n onContinue,\n restored = false\n}: {\n onContinue: () => void;\n /** true — у юзера уже была активная подписка на момент попытки checkout\n * (или после signIn выяснилось, что подписка есть). Меняет heading на\n * «Subscription restored» — без этого юзер думает, что только что\n * оплатил. */\n restored?: boolean;\n}) {\n return (\n <div class=\"flex flex-col items-center gap-3 py-8 text-center\">\n <div\n class=\"flex h-14 w-14 items-center justify-center rounded-full ring-8\"\n style={{\n background: 'linear-gradient(135deg, #4ade80, #16a34a)',\n color: '#fff',\n // emerald ring with low alpha for a halo effect\n boxShadow: '0 0 0 8px rgba(74,222,128,0.12), 0 8px 20px -6px rgba(22,163,74,0.45)'\n }}\n aria-hidden=\"true\"\n >\n <svg width=\"28\" height=\"28\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path\n d=\"M5 13l4 4L19 7\"\n stroke=\"currentColor\"\n stroke-width=\"2.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </div>\n <p id=\"pw-title\" class=\"mt-1 text-lg font-semibold tracking-tight text-gray-900\">\n {restored ? 'Subscription restored' : 'Payment received'}\n </p>\n <p class=\"text-sm leading-relaxed text-gray-500\">\n {restored\n ? 'Welcome back — your subscription is already active.'\n : 'Your subscription is now active.'}\n </p>\n <button\n type=\"button\"\n onClick={onContinue}\n class=\"mt-3 rounded-xl px-5 py-2.5 text-sm font-semibold text-white transition-all hover:-translate-y-px hover:brightness-105 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--pw-accent)]\"\n style={{\n background:\n 'linear-gradient(180deg, color-mix(in srgb, var(--pw-accent) 92%, white), var(--pw-accent))',\n boxShadow:\n '0 1px 2px rgba(15,23,42,0.08), 0 8px 20px -6px color-mix(in srgb, var(--pw-accent) 50%, transparent)'\n }}\n >\n Continue\n </button>\n </div>\n );\n}\n","import type { BillingClient } from '../core/BillingClient';\nimport type { PaywallUser } from '../core/types';\n\n// Параметры по умолчанию подобраны под \"юзер платит ~60-90с после клика\n// Continue, иногда ходит за чашкой 5-10 минут\". См. обсуждение в TODO.md\n// (фаза \"Что это меняет в архитектуре\").\nexport interface UserWatcherOptions {\n client: BillingClient;\n /** Дёрнут, когда впервые увидели has_active_subscription === true. */\n onActive: (user: PaywallUser) => void;\n /** Полный таймаут наблюдения. По истечении — стоп без onActive. */\n onTimeout?: () => void;\n timeoutMs?: number;\n /** Интервал polling, когда вкладка видимая. */\n visibleIntervalMs?: number;\n /** Интервал polling, когда вкладка скрыта (браузер троттлит таймеры). */\n hiddenIntervalMs?: number;\n}\n\nconst DEFAULT_TIMEOUT_MS = 10 * 60_000;\nconst DEFAULT_VISIBLE_INTERVAL_MS = 5_000;\nconst DEFAULT_HIDDEN_INTERVAL_MS = 30_000;\n\n// Polling после checkout_started.\n//\n// Источники сигнала \"проверить сейчас\":\n// 1. visibility change → visible (юзер вернулся в исходную вкладку).\n// 2. window focus.\n// 3. postMessage вида { type: 'paywall_purchase' } от success-страницы\n// (acceleration: success_url на нашем origin делает window.opener.postMessage).\n// 4. Регулярный таймер с visibility-aware расписанием.\n//\n// Стоп: либо has_active_subscription === true, либо таймаут.\n//\n// Runtime detection: см. shouldRunUserWatcher() — extension popup отбрасывается,\n// он не доживает до возврата с checkout. Background service worker отсекается\n// проверкой `typeof document` в start().\nexport class UserWatcher {\n private opts: Required<Omit<UserWatcherOptions, 'client'>> & { client: BillingClient };\n private timer: ReturnType<typeof setTimeout> | null = null;\n private timeoutTimer: ReturnType<typeof setTimeout> | null = null;\n private visibilityHandler: (() => void) | null = null;\n private focusHandler: (() => void) | null = null;\n private messageHandler: ((e: MessageEvent) => void) | null = null;\n private stopped = false;\n private checking = false;\n\n constructor(opts: UserWatcherOptions) {\n this.opts = {\n client: opts.client,\n onActive: opts.onActive,\n onTimeout: opts.onTimeout ?? (() => {}),\n timeoutMs: opts.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n visibleIntervalMs: opts.visibleIntervalMs ?? DEFAULT_VISIBLE_INTERVAL_MS,\n hiddenIntervalMs: opts.hiddenIntervalMs ?? DEFAULT_HIDDEN_INTERVAL_MS\n };\n }\n\n start(): void {\n if (this.stopped) return;\n if (typeof document === 'undefined' || typeof window === 'undefined') return;\n\n void this.check();\n this.scheduleNext();\n\n this.visibilityHandler = () => this.handleVisibilityChange();\n document.addEventListener('visibilitychange', this.visibilityHandler);\n\n this.focusHandler = () => void this.check();\n window.addEventListener('focus', this.focusHandler);\n\n this.messageHandler = (e: MessageEvent) => this.handleMessage(e);\n window.addEventListener('message', this.messageHandler);\n\n this.timeoutTimer = setTimeout(() => {\n if (this.stopped) return;\n this.stop();\n this.opts.onTimeout();\n }, this.opts.timeoutMs);\n }\n\n stop(): void {\n this.stopped = true;\n if (this.timer !== null) clearTimeout(this.timer);\n this.timer = null;\n if (this.timeoutTimer !== null) clearTimeout(this.timeoutTimer);\n this.timeoutTimer = null;\n if (typeof document !== 'undefined' && this.visibilityHandler) {\n document.removeEventListener('visibilitychange', this.visibilityHandler);\n }\n if (typeof window !== 'undefined') {\n if (this.focusHandler) window.removeEventListener('focus', this.focusHandler);\n if (this.messageHandler) window.removeEventListener('message', this.messageHandler);\n }\n this.visibilityHandler = null;\n this.focusHandler = null;\n this.messageHandler = null;\n }\n\n private async check(): Promise<void> {\n if (this.stopped || this.checking) return;\n this.checking = true;\n try {\n const user = await this.opts.client.getUser({ force: true });\n if (this.stopped) return;\n if (user.has_active_subscription) {\n this.stop();\n this.opts.onActive(user);\n }\n } catch {\n /* транзиентные ошибки — пропустим один тик, поллер дёрнет ещё */\n } finally {\n this.checking = false;\n }\n }\n\n private scheduleNext(): void {\n if (this.stopped) return;\n const visible =\n typeof document !== 'undefined' && document.visibilityState === 'visible';\n const interval = visible\n ? this.opts.visibleIntervalMs\n : this.opts.hiddenIntervalMs;\n this.timer = setTimeout(async () => {\n await this.check();\n this.scheduleNext();\n }, interval);\n }\n\n private handleVisibilityChange(): void {\n if (typeof document === 'undefined') return;\n if (document.visibilityState === 'visible') void this.check();\n // Перепланируем таймер с интервалом нового состояния.\n if (this.timer !== null) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n this.scheduleNext();\n }\n\n private handleMessage(e: MessageEvent): void {\n const data = e.data as { type?: string } | null;\n if (!data || typeof data !== 'object') return;\n if (data.type !== 'paywall_purchase') return;\n void this.check();\n }\n}\n\n// Решаем, имеет ли смысл вообще запускать watcher в текущем рантайме.\n// false → код, который должен закрывать пейвол на оплату, полагается на\n// другой путь (bootstrap при следующем открытии для extension popup;\n// отсутствие document — для service worker).\nexport function shouldRunUserWatcher(): boolean {\n if (typeof document === 'undefined') return false;\n if (typeof window === 'undefined') return false;\n // Chrome extension popup живёт только пока открыт. window.open()\n // checkout-провайдера сразу ест фокус → popup закрывается → весь JS-context\n // (включая SDK и watcher) уничтожается. Polling тут бесполезен.\n if (typeof location !== 'undefined' && location.protocol === 'chrome-extension:') {\n return false;\n }\n return true;\n}\n","import { AuthClient, type AuthClientOptions, type AuthSession } from '../core/auth';\nimport { BillingClient, type BillingClientOptions } from '../core/BillingClient';\nimport { EventTracker } from '../core/EventTracker';\nimport { createTrialStore, type TrialStore } from '../core/trial';\nimport type {\n Acquiring,\n Identity,\n PaywallBootstrap,\n PaywallError,\n PaywallPrice,\n PaywallUser,\n TrialConfig,\n TrialStatus,\n UserLanguageInfo,\n VisibilityStatus\n} from '../core/types';\nimport { mountShadow, type MountHandle } from './mount';\nimport {\n PaywallRoot,\n type PaywallRootProps,\n type PaywallStateSnapshot,\n type PaywallView\n} from './PaywallRoot';\nimport { UserWatcher, shouldRunUserWatcher } from './UserWatcher';\n\ntype PaywallStateListener = (state: PaywallStateSnapshot) => void;\n\nconst CLOSED_STATE: PaywallStateSnapshot = { open: false, view: null, error: null };\n\n// Контракт событий SDK. Клиент подписывается через paywall.on(event, handler).\n// Каждый event строго типизирован — IDE даёт автокомплит на payload.\nexport interface PaywallEventPayloads {\n /** Модалка открыта (запрос на открытие — данные могут ещё грузиться). */\n open: void;\n /** Модалка закрыта. */\n close: void;\n /** Bootstrap загружен, модалка показывает контент. Подходит для impression-метрик. */\n ready: PaywallBootstrap;\n /** Любая ошибка SDK (bootstrap, checkout). */\n error: PaywallError;\n /** Юзер выбрал тариф (клик по плану), ещё не инициировал checkout. */\n price_selected: { priceId: string; price: PaywallPrice };\n /** Checkout URL получен с бэка и открыт в новой вкладке. `acquiring` —\n * имя платёжного процессора, на который ушёл checkout (для конверсии\n * по эквайрингам в host-аналитике). */\n checkout_started: { priceId: string; url: string; acquiring?: Acquiring };\n /** Юзер вернулся с успешной оплатой (через URL-маркеры или postMessage),\n * либо после signIn / попытки checkout-а выяснилось, что подписка уже\n * активна (`restored: true`). priceId = null когда payment-интент не\n * был привязан к конкретной цене (UserWatcher-tick, restore-flow). */\n purchase_completed: {\n priceId: string | null;\n sessionId: string | null;\n /** true — это не свежая оплата, а активная подписка, которую SDK обнаружил\n * и показал juзеру success/restored view. Hostу полезно различать (для\n * metrics — «restore» vs «new purchase»). */\n restored?: boolean;\n };\n /** Юзер вернулся с ошибкой/cancel от провайдера. */\n purchase_failed: { reason: string | null };\n /** User-state изменился (bootstrap snapshot, getUser refresh, watcher tick).\n * Дёргается также сразу с last-known user после первой подписки. */\n userChange: PaywallUser;\n /** Auth-session изменилась (signin / signup / refresh / signout / 401-revoke).\n * null = разлогинен. Дёргается сразу с last-known session после первой подписки. */\n authChange: AuthSession | null;\n /** Триал заблокировал показ модалки. payload содержит свежий статус (после\n * recordBlock). Для `mode: 'time'` — startedAt/expiresAt/remainingMs;\n * для `mode: 'opens'` — remainingActions/totalActions. Хост может\n * использовать payload для показа собственного UI («осталось 3 показа»). */\n trial_blocked: TrialStatus;\n /** Триал истёк, паывол показывается впервые после истечения. Эмитится\n * раз за жизнь PaywallUI-инстанса (не персистится между перезагрузками\n * страницы — на каждом page-load событие может стрельнуть один раз). */\n trial_expired: void;\n /** Targeting не сошёлся — паывол не открывается. payload содержит\n * server-computed snapshot из bootstrap (visible=false + reason + country +\n * tier). Хост может показать собственный fallback («сервис недоступен в\n * вашей стране») или просто залогировать impression для аналитики. */\n visibility_blocked: VisibilityStatus;\n}\n\nexport type PaywallEvent = keyof PaywallEventPayloads;\n\nexport type PaywallEventHandler<E extends PaywallEvent = PaywallEvent> = (\n payload: PaywallEventPayloads[E]\n) => void;\n\n// Вспомогательный тип: `void` payload эмитится без аргумента (`emit('open')`),\n// непустой — с аргументом (`emit('ready', bootstrap)`).\ntype EmitArgs<E extends PaywallEvent> = PaywallEventPayloads[E] extends void\n ? []\n : [PaywallEventPayloads[E]];\n\nexport interface AnalyticsOptions {\n enabled?: boolean;\n /** Полный URL до /events. По умолчанию — `${apiOrigin}/api/v1/paywall/${id}/events`. */\n endpoint?: string;\n flushIntervalMs?: number;\n maxBufferSize?: number;\n /** Тестовый override fetch'а (jsdom/Vitest). */\n fetch?: typeof fetch;\n /** Тестовый override sendBeacon'а. */\n sendBeacon?: (url: string, data: BodyInit) => boolean;\n}\n\n/**\n * Managed-auth конфиг. Передай `auth: true` — PaywallUI создаёт `AuthClient`\n * сам (с тем же `paywallId/apiOrigin/storage`, как BillingClient). Передай\n * объект — те же дефолты + override опций. Передай готовый `AuthClient` —\n * PaywallUI просто прокинет его в BillingClient (полезно, если хост хочет\n * иметь общий AuthClient на несколько пейволов / делать manual signIn/signOut\n * из своего UI до открытия модалки).\n *\n * Без `auth` опции SDK работает в hybrid-режиме: identity передаётся снаружи\n * через `opts.identity` или `paywall.open({identity})`.\n */\nexport type AuthOption = true | AuthClient | Partial<Omit<AuthClientOptions, 'paywallId'>>;\n\nexport interface PaywallUIOptions extends Omit<BillingClientOptions, 'auth'> {\n client?: BillingClient;\n host?: HTMLElement;\n /** Подключить managed-auth слой. См. {@link AuthOption}. */\n auth?: AuthOption;\n /**\n * Автоматически парсить URL при создании PaywallUI, чтобы поймать возврат\n * с checkout-провайдера (?paywall_status=paid|failed|cancelled). Дефолт: true.\n * Эмитит purchase_completed / purchase_failed через microtask — подпишись синхронно.\n */\n autoDetectReturn?: boolean;\n /**\n * Режим shadow DOM. По умолчанию `closed` — полная изоляция от хоста.\n * Для e2e тестов (Playwright) и live-preview в админке передавать `open`.\n */\n shadowMode?: 'open' | 'closed';\n /**\n * Аналитика SDK 3.0. По умолчанию включена. Передай `false` для полного\n * отключения (ничего не шлётся на бэк). Принимает объект с настройками\n * batch'а или endpoint-override.\n */\n analytics?: boolean | AnalyticsOptions;\n /**\n * Когда bootstrap не в кеше — модалку рендерить **сразу** со спиннером и\n * прогонять gates (visibility/trial) после получения данных, или **ждать**\n * bootstrap и монтировать только если gates прошли. Дефолт `true` —\n * snappy open, кнопка «открыть» отзывается мгновенно.\n *\n * Trade-off: при `true` и блокирующем gate'е модалка моргнёт (открылась\n * → закрылась через ~200-500мс). На extension'ах и сайтах с включённым\n * targeting-fallback'ом это редкий путь, поэтому дефолт оптимизирован\n * под основной 99%-кейс. Передай `false`, если для вашего use-case'а\n * флеш на blocked-странах/устройствах хуже воспринимаемой латентности.\n */\n mountThenLoad?: boolean;\n}\n\n/**\n * Результат `paywall.getAccess()` — отвечает на главный вопрос хоста: «нужно\n * ли блокировать фичу для этого юзера?». Без побочных эффектов: на trial-storage\n * `recordBlock` не вызывается (счётчики не двигаются), модалка не монтируется.\n *\n * Семантика `access`:\n * - `granted` — фичу НЕ блокировать. Один из сценариев:\n * - `has_subscription` — у юзера активная подписка/покупка;\n * - `visibility_blocked` — таргетинг (страна/девайс/visibility-флаг) не\n * сошёлся, юзер вне monetization-scope'а пейвола → монетизация неприменима;\n * - `trial_blocked` — пре-пейвольный триал ещё активен.\n * - `blocked` — фичу заблокировать и вызвать `paywall.open()`. Reason всегда\n * `no_subscription`.\n *\n * Discriminated union по `access`: type-narrowing на `result.access === 'blocked'`\n * сужает `reason` до `'no_subscription'`, на `'granted'` — до трёх granted-вариантов.\n */\nexport type PaywallAccessResult =\n | {\n access: 'granted';\n reason: 'has_subscription' | 'visibility_blocked' | 'trial_blocked';\n visibility: VisibilityStatus | null;\n trial: TrialStatus | null;\n user: PaywallUser | null;\n }\n | {\n access: 'blocked';\n reason: 'no_subscription';\n visibility: VisibilityStatus | null;\n trial: TrialStatus | null;\n user: PaywallUser | null;\n };\n\nexport interface GetAccessOptions {\n skipTrial?: boolean;\n skipVisibility?: boolean;\n signal?: AbortSignal;\n}\n\nexport interface OpenOptions {\n identity?: Identity;\n /** Принудительно открыть, минуя pre-paywall trial check. По умолчанию SDK\n * читает `bootstrap.settings.trial` и блокирует open(), пока триал активен.\n * Эскейп-хатч для случаев типа «host решил показать всё-таки» или дев-режим. */\n skipTrial?: boolean;\n /** Принудительно открыть, минуя targeting-gate. По умолчанию SDK читает\n * `bootstrap.settings.visibility` и эмитит `visibility_blocked` без\n * открытия модалки, если visible=false (страна/девайс/visibility-флаг\n * не сошлись). Эскейп-хатч для дев-отладки. */\n skipVisibility?: boolean;\n /** Renewal/upgrade flow. По умолчанию (false) SDK после bootstrap'а или\n * signIn проверяет `user.has_active_subscription` и переключается в\n * restored success-view, не показывая тарифы — open() для уже подписанного\n * юзера превращается в подтверждение «у вас уже есть подписка». С\n * `renew: true` все эти проверки пропускаются: тарифы показываются всегда,\n * и при checkout SDK передаёт `ignoreActivePurchase: true` на бэк, чтобы\n * /start-checkout не вернул 409. Использовать когда host-UI явно\n * показывает кнопку «Renew»/«Upgrade plan». */\n renew?: boolean;\n}\n\n// Маркеры в URL, по которым SDK определяет результат checkout.\n// Контракт общий с бэком — online добавляет их в success/cancel URLs.\nconst URL_MARKERS = {\n status: 'paywall_status',\n priceId: 'paywall_price_id',\n sessionId: 'paywall_session_id'\n} as const;\n\nexport class PaywallUI {\n readonly billing: BillingClient;\n /** AuthClient (managed-auth) или undefined в hybrid-режиме. Доступен публично:\n * host может вызывать `paywall.auth?.signOut()`, читать `getCachedSession()`,\n * подписываться на `onAuthChange` напрямую. */\n readonly auth: AuthClient | undefined;\n private ownsAuth: boolean;\n private host?: HTMLElement;\n private shadowMode: 'open' | 'closed';\n private handle: MountHandle | null = null;\n private isOpen = false;\n private listeners = new Map<PaywallEvent, Set<PaywallEventHandler>>();\n private userUnsub: (() => void) | null = null;\n private authUnsub: (() => void) | null = null;\n private watcher: UserWatcher | null = null;\n private tracker: EventTracker | null = null;\n private purchased = false;\n /** Lazy-инстанс TrialStore. Резолвится при первом open(), когда уже знаем\n * `bootstrap.settings.trial`. null — триал отключён в конфиге пейвола. */\n private trialStore: TrialStore | null = null;\n /** Конфиг, под который создан текущий trialStore — пересобираем, если он\n * поменялся между bootstrap-фетчами (например, владелец переключил режим\n * в админке между сессиями SDK). */\n private trialStoreConfig: TrialConfig | null = null;\n /** In-memory snapshot последнего check() — для синхронного getTrialStatus(). */\n private lastTrialStatus: TrialStatus | null = null;\n /** Флаг dedupe для `trial_expired` события в рамках жизни инстанса. */\n private trialExpiredFired = false;\n /** In-memory snapshot последнего bootstrap'а — для синхронного getVisibility(). */\n private lastVisibility: VisibilityStatus | null = null;\n /** Поведение open() при холодном bootstrap'е. См. PaywallUIOptions.mountThenLoad. */\n private mountThenLoad: boolean;\n /** Текущий snapshot UI state-machine. Обновляется PaywallRoot'ом через\n * `onState` prop; при close сбрасывается обратно в CLOSED_STATE. */\n private currentState: PaywallStateSnapshot = CLOSED_STATE;\n private stateListeners = new Set<PaywallStateListener>();\n\n constructor(opts: PaywallUIOptions) {\n // Резолвим AuthClient: готовый инстанс / managed-конфиг (true|object) /\n // undefined. ownsAuth=true → сами создавали и должны прибрать в destroy().\n const { auth, ownsAuth } = resolveAuth(opts);\n this.auth = auth;\n this.ownsAuth = ownsAuth;\n\n // Если auth есть — прокидываем в BillingClient (он сам подключит Bearer\n // и auto-sync identity через onAuthChange). client из opts побеждает —\n // считаем, что хост уже сконфигурировал его сам, не лезем перетирать auth.\n this.billing =\n opts.client ?? new BillingClient({ ...opts, auth: this.auth });\n this.host = opts.host;\n this.shadowMode = opts.shadowMode ?? 'closed';\n this.mountThenLoad = opts.mountThenLoad ?? true;\n\n // Форвардим user-change события из BillingClient на public-API PaywallUI.\n // Один источник правды (BillingClient cache) — два consumer'а (host через\n // paywall.onUserChange и сам watcher через billing.onUserChange).\n this.userUnsub = this.billing.onUserChange((user) => {\n this.emit('userChange', user);\n });\n\n if (this.auth) {\n this.authUnsub = this.auth.onAuthChange((session) => {\n this.emit('authChange', session);\n });\n }\n\n this.initTracker(opts.analytics);\n\n if (opts.autoDetectReturn !== false && typeof window !== 'undefined') {\n // Microtask — клиент успевает подписаться синхронно после конструктора,\n // до того как событие действительно стрельнёт.\n queueMicrotask(() => this.checkReturn());\n }\n }\n\n private initTracker(analytics: PaywallUIOptions['analytics']): void {\n if (analytics === false) return;\n const cfg: AnalyticsOptions =\n typeof analytics === 'object' && analytics !== null ? analytics : {};\n if (cfg.enabled === false) return;\n\n const endpoint =\n cfg.endpoint ?? `${this.billing.apiOrigin}/api/v1/paywall/${this.billing.paywallId}/events`;\n\n this.tracker = new EventTracker({\n endpoint,\n paywallId: this.billing.paywallId,\n capabilities: this.billing.capabilities,\n getVisitorId: () => this.billing.getVisitorId(),\n getCachedVisitorId: () => this.billing.getCachedVisitorId(),\n getUserId: () => this.billing.getIdentity()?.userId ?? null,\n flushIntervalMs: cfg.flushIntervalMs,\n maxBufferSize: cfg.maxBufferSize,\n fetch: cfg.fetch,\n sendBeacon: cfg.sendBeacon\n });\n\n // Биндим внутренние SDK-события на аналитический транспорт. Один эмиттер,\n // один потребитель (трекер) — никто кроме трекера не должен трогать\n // эти имена событий за пределами PaywallUI.\n this.on('open', () => this.tracker?.track('paywall_opened'));\n this.on('ready', (b) =>\n this.tracker?.track('paywall_viewed', {\n is_test_mode: b.settings.is_test_mode,\n prices_count: b.prices.length,\n offers_count: b.offers.length\n })\n );\n this.on('price_selected', (p) =>\n this.tracker?.track('price_selected', { price_id: p.priceId })\n );\n this.on('checkout_started', (p) =>\n this.tracker?.track('checkout_started', {\n price_id: p.priceId,\n acquiring: p.acquiring\n })\n );\n this.on('purchase_completed', (p) =>\n this.tracker?.track('purchase_completed', {\n price_id: p.priceId,\n session_id: p.sessionId\n })\n );\n this.on('purchase_failed', (p) =>\n this.tracker?.track('purchase_failed', { reason: p.reason })\n );\n this.on('close', () => this.tracker?.track('paywall_closed'));\n this.on('trial_blocked', (s) =>\n this.tracker?.track('trial_blocked', {\n mode: s.mode,\n ...(s.mode === 'time'\n ? { remaining_ms: s.remainingMs, total_ms: s.totalMs }\n : s.mode === 'opens'\n ? { remaining_actions: s.remainingActions, total_actions: s.totalActions }\n : {})\n })\n );\n this.on('trial_expired', () => this.tracker?.track('trial_expired'));\n this.on('visibility_blocked', (v) =>\n this.tracker?.track('visibility_blocked', {\n reason: v.reason,\n country: v.country,\n tier: v.tier\n })\n );\n this.on('error', (e) =>\n this.tracker?.track('error', { code: e.code, message: e.message })\n );\n // auth_signin_success / auth_signout пока не фаерим: authChange эмитится\n // и на гидрации сессии (UI поднимает кеш из storage), и на token refresh,\n // и при параллельных consumer'ах одного auth-state — даёт ложные signin'ы.\n // Реальные login-события нужно ловить через прямые вызовы\n // signInWithEmail/signUp/signInWithOAuth/signOut, а не через authChange.\n }\n\n /**\n * Отправить произвольное аналитическое событие. Имена из системного whitelist'а\n * (`app_opened`, `paywall_viewed`, ...) разрешены как есть. Кастомные —\n * с префиксом `host:` (например `host:user_clicked_upgrade`). Сервер\n * дропает события с неразрешёнными именами.\n *\n * Самый частый кейс — `track('app_opened')` от хоста сразу после загрузки\n * приложения, чтобы зафиксировать воронку до открытия пейвола.\n */\n track(name: string, props?: Record<string, unknown>): void {\n this.tracker?.track(name, props);\n }\n\n /**\n * Удобный шорткат вместо `paywall.on('userChange', cb)` — самый частый\n * паттерн в host-коде, поэтому отдельный named метод. Колбек получает\n * last-known user из кеша синхронно через microtask, если он есть.\n */\n onUserChange(handler: PaywallEventHandler<'userChange'>): () => void {\n return this.on('userChange', handler);\n }\n\n /**\n * Заменить cachedBootstrap живыми данными — для preview-режима в редакторе\n * админки. Если модалка открыта, PaywallRoot подписан на onBootstrapChange\n * и перерендерится мгновенно. До open() — затравка для bootstrap()-effect'а.\n *\n * См. {@link BillingClientOptions.preview} — обычно эту опцию ставят на\n * клиент, чтобы заодно отключить сетевой revalidate. setBootstrap технически\n * работает и в production-режиме, но конкуренция с revalidate'ом из сети\n * почти всегда нежелательна.\n */\n setBootstrap(partial: Partial<PaywallBootstrap>): void {\n this.billing.setBootstrap(partial);\n }\n\n on<E extends PaywallEvent>(event: E, handler: PaywallEventHandler<E>): () => 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(handler as PaywallEventHandler);\n return () => set!.delete(handler as PaywallEventHandler);\n }\n\n off<E extends PaywallEvent>(event: E, handler: PaywallEventHandler<E>): void {\n this.listeners.get(event)?.delete(handler as PaywallEventHandler);\n }\n\n private emit<E extends PaywallEvent>(event: E, ...args: EmitArgs<E>): void {\n const set = this.listeners.get(event);\n if (!set) return;\n const payload = args[0] as PaywallEventPayloads[E];\n for (const handler of set) {\n try {\n (handler as PaywallEventHandler<E>)(payload);\n } catch (error) {\n if (typeof console !== 'undefined') console.error('[paywall] listener error', error);\n }\n }\n }\n\n open(opts: OpenOptions = {}): void {\n this.openInternal('layout', opts);\n }\n\n /**\n * Прогревает bootstrap-кеш и balance-кеш заранее, без открытия модалки.\n * Полезно когда host знает, что юзер скоро откроет paywall (hover на CTA,\n * mount компонента) — первый `open()` рендерится мгновенно, без loading-flash.\n *\n * Не throw'ает: если сеть упала, тихо игнорирует (повторный open() сделает\n * fresh-bootstrap с error-state как обычно). `signal` для отмены — например,\n * если хост размонтирует компонент быстрее, чем bootstrap вернётся.\n *\n * Вызывать можно сколько угодно раз — последующие вызовы возвращают cached\n * Promise (BillingClient уже дедуплицирует).\n */\n async preload(opts: { signal?: AbortSignal } = {}): Promise<void> {\n try {\n await this.billing.bootstrap({ signal: opts.signal });\n // Балансы — best-effort: пейволы без `tokenization` отдают пустой\n // массив, и getBalances не делает сетевого запроса для unauth-юзера.\n if (this.billing.auth) {\n await this.billing.getBalances({ signal: opts.signal });\n }\n } catch {\n /* preload best-effort — open() сам покажет error-state */\n }\n }\n\n /**\n * Открывает модалку сразу с саппорт-формой (минуя layout с тарифами).\n * Полезно, когда host-приложение хочет дать юзеру кнопку «Help / Support»,\n * не связанную с пейволом-апгрейдом. Back/Done в саппорт-форме закрывают\n * модалку (не возвращают к тарифам), потому что юзер пришёл сюда напрямую.\n *\n * Из обычного `paywall.open()`-flow саппорт всё равно доступен через\n * Contact Support-ссылку в `current_session`-блоке (там Back возвращает\n * к layout).\n */\n openSupport(opts: OpenOptions = {}): void {\n this.openInternal('support', opts);\n }\n\n /**\n * Открывает модалку сразу с auth-gate (логин/регистрация), без layout с\n * тарифами. Сценарий: returning customer уже купил, ему просто нужно\n * залогиниться, чтобы SDK подцепил его purchases. После signIn модалка\n * закрывается; Back тоже закрывает (юзер пришёл только за логином).\n *\n * Без `auth` (managed-auth не подключён) метод — no-op: некому делать\n * signIn. Если юзер уже залогинен — модалка всё равно откроется и\n * закроется через auto-resume в auth_gate effect'е (мгновение).\n *\n * Триал не блокирует этот флоу — auth не connect'ится с trial-механикой.\n */\n openAuth(opts: OpenOptions = {}): void {\n if (!this.auth) return;\n this.openInternal('auth', { ...opts, skipTrial: true });\n }\n\n /**\n * Открывает модалку с anonymous-gate. AnonGate сразу зовёт\n * `auth.signInAnonymously()`:\n * - если в storage есть `anonRefreshToken` — silent resume через\n * /auth/refresh, модалка схлопывается мгновенно (юзер видит лёгкий\n * спиннер ~100мс);\n * - иначе — fresh POST /auth/anonymous/signin без captcha (защита от\n * abuse держится на Supabase per-real-IP rate-limit + CF Bot Fight Mode).\n *\n * После успешного signIn'а модалка закрывается; host подхватывает свежую\n * session через `auth.onAuthChange` или `paywall.onUserChange`. Для UX\n * \"просто залогиниться чтобы api-gateway работал, без покупок\".\n *\n * Без managed-auth (`auth` не подключён) метод — no-op. Триал и\n * visibility-таргетинг этот flow обходит: анон-логин не должен зависеть\n * от страны юзера или trial-стейджа.\n */\n openAnonGate(opts: OpenOptions = {}): void {\n if (!this.auth) return;\n this.openInternal('anon', { ...opts, skipTrial: true, skipVisibility: true });\n }\n\n private openInternal(view: PaywallView, opts: OpenOptions): void {\n if (opts.identity) this.billing.setIdentity(opts.identity);\n // Сбрасываем флаг success-вью — повторное открытие должно стартовать\n // с обычного layout, а не с прошлого \"Payment received\".\n this.purchased = false;\n\n // support и auth-standalone флоу обходят оба гейта (триал и таргетинг):\n // юзер пришёл за саппортом или за логином к уже купленной подписке —\n // блокировать его по trial-stage'у или таргетингу неуместно. openAuth\n // дополнительно передаёт skipTrial:true для совместимости с прежней\n // семантикой; здесь skip-флаги нормализуем единообразно.\n const skipTrial = opts.skipTrial === true || view === 'support';\n const skipVisibility =\n opts.skipVisibility === true ||\n view === 'support' ||\n view === 'auth' ||\n view === 'anon';\n const renew = opts.renew === true;\n\n if (skipTrial && skipVisibility) {\n this.mountAndShow(view, { renew });\n return;\n }\n\n // Cache hit — sync путь, gates до mount как раньше. Никаких компромиссов:\n // когда bootstrap уже в памяти, мы знаем за один tick можно открывать\n // или нет, без флеша.\n const cached = this.billing.getCachedBootstrap();\n if (cached) {\n this.runOpenGates(view, cached, { skipTrial, skipVisibility, renew });\n return;\n }\n\n // Cold bootstrap. Два режима:\n //\n // mountThenLoad=true (default): монтируем модалку немедленно — юзер видит\n // спиннер, кнопка отзывается мгновенно. Bootstrap идёт параллельно.\n // Когда придёт — гоняем gates, и если блокирует, закрываем модалку с\n // эмиссией *_blocked. Цена — флеш «открылась → закрылась» в редком\n // случае visibility/trial-блока. Для extension'ов и сайтов с включённым\n // targeting'ом большинство open()'ов проходят, флеш — edge case.\n //\n // mountThenLoad=false (legacy): ждём bootstrap до mount'а. Гарантия\n // отсутствия флеша на блоке, но кнопка кажется «мёртвой» 200-500мс\n // на холодном кеше.\n if (this.mountThenLoad) {\n this.mountAndShow(view, { renew });\n this.billing\n .bootstrap()\n .then((b) => this.runDelayedGates(b, { skipTrial, skipVisibility }))\n .catch(() => {\n // Bootstrap упал — модалка уже открыта, PaywallRoot сам в error-state.\n });\n return;\n }\n\n this.billing\n .bootstrap()\n .then((b) => this.runOpenGates(view, b, { skipTrial, skipVisibility, renew }))\n .catch(() => {\n // Bootstrap упал — открываем без gates; PaywallRoot покажет error.\n this.mountAndShow(view, { renew });\n });\n }\n\n /** Применить gates ПОСЛЕ того, как модалка уже смонтирована (mount-then-load\n * путь). Если gate блокирует — close() + emit. Если юзер уже сам закрыл\n * модалку до резолва bootstrap'а — no-op (isOpen=false). */\n private runDelayedGates(\n bootstrap: PaywallBootstrap,\n flags: { skipTrial: boolean; skipVisibility: boolean }\n ): void {\n if (!this.isOpen) return;\n\n if (!flags.skipVisibility) {\n const v = bootstrap.settings.visibility;\n if (v) {\n this.lastVisibility = v;\n if (!v.visible) {\n this.close();\n this.emit('visibility_blocked', v);\n return;\n }\n }\n }\n\n if (flags.skipTrial) return;\n\n const trialCfg = bootstrap.settings.trial;\n if (!trialCfg) return;\n const store = this.ensureTrialStore(trialCfg);\n void store\n .check()\n .then(async (status) => {\n if (!this.isOpen) return;\n this.lastTrialStatus = status;\n if (status.mode === 'none') return;\n if (status.blocked) {\n const updated = await store.recordBlock();\n this.lastTrialStatus = updated;\n if (!this.isOpen) return;\n this.close();\n this.emit('trial_blocked', updated);\n return;\n }\n if (!this.trialExpiredFired) {\n this.trialExpiredFired = true;\n this.emit('trial_expired');\n }\n })\n .catch((e) => {\n if (typeof console !== 'undefined') console.warn('[paywall] trial check failed', e);\n });\n }\n\n // Порядок гейтов: visibility → trial. Country-mismatch ≠ trial-block, и\n // вести trial-стейт «осталось N показов» под юзером, который вообще не\n // должен увидеть пейвол по таргетингу — бессмысленно: при возврате в\n // правильную страну он окажется со «слипшимся» триал-счётчиком.\n private runOpenGates(\n view: PaywallView,\n bootstrap: PaywallBootstrap,\n flags: { skipTrial: boolean; skipVisibility: boolean; renew: boolean }\n ): void {\n if (!flags.skipVisibility) {\n const v = bootstrap.settings.visibility;\n if (v) {\n this.lastVisibility = v;\n if (!v.visible) {\n this.emit('visibility_blocked', v);\n return;\n }\n }\n }\n\n if (flags.skipTrial) {\n this.mountAndShow(view, { renew: flags.renew });\n return;\n }\n this.gateThroughTrial(view, bootstrap, flags.renew);\n }\n\n private gateThroughTrial(view: PaywallView, bootstrap: PaywallBootstrap, renew: boolean): void {\n const trialCfg = bootstrap.settings.trial;\n if (!trialCfg) {\n this.mountAndShow(view, { renew });\n return;\n }\n const store = this.ensureTrialStore(trialCfg);\n void store\n .check()\n .then(async (status) => {\n this.lastTrialStatus = status;\n if (status.mode === 'none') {\n this.mountAndShow(view, { renew });\n return;\n }\n if (status.blocked) {\n // recordBlock делает запись (init firstOpen / inc skipTimes) и\n // возвращает обновлённый snapshot — его и эмитим, чтобы хост\n // получил актуальный счётчик.\n const updated = await store.recordBlock();\n this.lastTrialStatus = updated;\n this.emit('trial_blocked', updated);\n return;\n }\n // Триал в конфиге, но не блокирует → истёк. Эмитим один раз за\n // сессию, дальше открываем как обычно.\n if (!this.trialExpiredFired) {\n this.trialExpiredFired = true;\n this.emit('trial_expired');\n }\n this.mountAndShow(view, { renew });\n })\n .catch((e) => {\n // Storage недоступен (privacy mode, quota) — не блокируем юзера,\n // открываем модалку и не теряем продажу.\n if (typeof console !== 'undefined') console.warn('[paywall] trial check failed', e);\n this.mountAndShow(view, { renew });\n });\n }\n\n private ensureTrialStore(config: TrialConfig): TrialStore {\n if (this.trialStore && this.trialStoreConfig && sameTrialConfig(this.trialStoreConfig, config)) {\n return this.trialStore;\n }\n this.trialStoreConfig = config;\n // Duck-type: если billing-клиент предоставляет свой factory (extension'овский\n // RemoteBillingClient — атомарный TrialStore через offscreen + navigator.locks),\n // используем его. Иначе — обычный path через storage-adapter.\n const factoryFn = (this.billing as { createTrialStore?: (cfg: TrialConfig) => TrialStore })\n .createTrialStore;\n this.trialStore =\n typeof factoryFn === 'function'\n ? factoryFn.call(this.billing, config)\n : createTrialStore(this.billing.getStorage(), this.billing.paywallId, config);\n return this.trialStore;\n }\n\n private mountAndShow(view: PaywallView, mountOpts: { renew?: boolean } = {}): void {\n const renew = mountOpts.renew === true;\n if (this.handle) {\n this.isOpen = true;\n this.handle.update({ open: true, initialView: view, purchased: false, renew });\n this.emit('open');\n return;\n }\n\n this.isOpen = true;\n this.handle = mountShadow<PaywallRootProps>(\n PaywallRoot,\n {\n client: this.billing,\n open: true,\n initialView: view,\n purchased: false,\n renew,\n onClose: () => this.close(),\n onEvent: (event, payload) => {\n this.emit(event as PaywallEvent, payload as never);\n // Поднимаем watcher как только начался checkout — отсюда уже\n // полагаемся на server-confirmed flow, а не URL-маркеры.\n if (event === 'checkout_started') this.startUserWatcher();\n },\n onState: (snapshot) => this.applyState(snapshot)\n },\n { host: this.host, shadowMode: this.shadowMode }\n );\n this.emit('open');\n }\n\n private applyState(snapshot: PaywallStateSnapshot): void {\n if (sameStateSnapshot(this.currentState, snapshot)) return;\n this.currentState = snapshot;\n for (const cb of this.stateListeners) {\n try {\n cb(snapshot);\n } catch (e) {\n console.warn('[paywall] onStateChange listener threw', e);\n }\n }\n }\n\n /**\n * Sync-snapshot текущего состояния модалки. Подходит для `useSyncExternalStore`\n * в React (`useSyncExternalStore(paywall.onStateChange, paywall.getState)`)\n * и для одноразовых проверок («открыт ли пейвол сейчас?»).\n *\n * Snapshot стабилен — пока state не изменился, повторный getState() вернёт\n * `===`-равный объект (важно для useSyncExternalStore чтобы не ре-рендерить).\n */\n getState(): PaywallStateSnapshot {\n return this.currentState;\n }\n\n /**\n * Подписка на изменения state. Колбек вызывается при каждом реальном\n * изменении (closed → loading → ready → ...). По умолчанию initial snapshot\n * отдаётся через microtask после подписки; через `{immediate: 'sync'|'none'}`\n * можно сделать sync-доставку (для useSyncExternalStore — там она не нужна,\n * snapshot читается через getSnapshot отдельно) или вовсе пропустить\n * initial.\n *\n * Возвращает unsubscribe.\n */\n onStateChange(\n cb: PaywallStateListener,\n opts: { immediate?: 'microtask' | 'sync' | 'none' } = {}\n ): () => void {\n this.stateListeners.add(cb);\n const mode = opts.immediate ?? 'microtask';\n if (mode !== 'none') {\n const snapshot = this.currentState;\n if (mode === 'sync') {\n try {\n cb(snapshot);\n } catch (e) {\n console.warn('[paywall] onStateChange initial sync threw', e);\n }\n } else {\n queueMicrotask(() => {\n if (this.stateListeners.has(cb)) cb(snapshot);\n });\n }\n }\n return () => {\n this.stateListeners.delete(cb);\n };\n }\n\n /** Sync-доступ к последнему известному статусу триала. null — `paywall.open()`\n * ещё не вызывался либо триал отключён в конфиге пейвола. Удобно для\n * собственного UI хоста («осталось 3 показа», «триал истечёт через 2ч»). */\n getTrialStatus(): TrialStatus | null {\n return this.lastTrialStatus;\n }\n\n /** Sync-доступ к последнему server-computed visibility-статусу. null —\n * bootstrap ещё не загружен или сервер не отдаёт `settings.visibility`\n * (например, старая версия online без targeting-патча). Хост может\n * использовать для собственного fallback'а: «сервис недоступен в вашей\n * стране». Обновляется на каждом open(), который проходит через gate. */\n getVisibility(): VisibilityStatus | null {\n return this.lastVisibility;\n }\n\n /**\n * Цены пейвола — шорткат над `bootstrap()`. Локали уже применены, кэш и\n * stale-while-revalidate идентичны `billing.bootstrap()`. Подходит для\n * pricing-страниц/карточек на сайте, где host хочет показать те же цены,\n * что и в модалке, не вытаскивая bootstrap руками.\n */\n getPrices(opts: { force?: boolean; signal?: AbortSignal } = {}): Promise<PaywallPrice[]> {\n return this.billing.getPrices(opts);\n }\n\n /** Sync-снимок цен. null — bootstrap ещё не загружали. */\n getCachedPrices(): PaywallPrice[] | null {\n return this.billing.getCachedPrices();\n }\n\n /** Снимок текущего «языка юзера» — proxy над `billing.getUserLanguage()`.\n * Используй, чтобы синхронизировать i18n host'а с тем, что фактически\n * показывает пейвол. См. подробности в `BillingClient.getUserLanguage`. */\n getUserLanguage(): UserLanguageInfo {\n return this.billing.getUserLanguage();\n }\n\n /**\n * Решает, нужно ли блокировать фичу для текущего юзера. Без побочных эффектов\n * (на trial-storage `recordBlock` не вызывается, модалка не монтируется).\n *\n * Порядок проверок (первый сработавший — финальный):\n * 1. `has_active_subscription` — самый сильный сигнал, перебивает остальные.\n * Юзер с подпиской получает доступ независимо от visibility/trial.\n * 2. `visibility` (страна/девайс/disabled-флаг) — юзер вне monetization-scope'а\n * пейвола, гейтить нельзя.\n * 3. `trial` — пре-пейвольный бесплатный период активен.\n * 4. Иначе — `blocked`, host лочит фичу и зовёт `paywall.open()`.\n *\n * Bootstrap кешируется в BillingClient — `getAccess()` можно дёргать на\n * каждый рендер host-компонента, /bootstrap не дублируется. При упавшей сети\n * fallback на persistent-cached user из storage: юзер с прошлой подпиской\n * получает `granted` офлайн, иначе `blocked` (host покажет пейвол с\n * error-state, юзер сможет ретрайнуть). Side-эффект: обновляются\n * `lastVisibility` / `lastTrialStatus`, чтобы синхронные геттеры\n * `getVisibility()` / `getTrialStatus()` видели свежие данные после первого\n * `getAccess()`, а не только после первого `open()`.\n */\n async getAccess(opts: GetAccessOptions = {}): Promise<PaywallAccessResult> {\n let bootstrap = this.billing.getCachedBootstrap();\n if (!bootstrap) {\n try {\n bootstrap = await this.billing.bootstrap({ signal: opts.signal });\n } catch {\n // Сеть упала. Fallback на persistent-cached user (TTL 30 мин в storage).\n // Юзер с прошлой подпиской → granted (офлайн-friendly), иначе → blocked\n // (open() покажет пейвол с error-state, юзер ретрайнет).\n const cached = this.billing.getCachedUser();\n if (cached?.has_active_subscription) {\n return {\n access: 'granted',\n reason: 'has_subscription',\n visibility: null,\n trial: null,\n user: cached\n };\n }\n return {\n access: 'blocked',\n reason: 'no_subscription',\n visibility: null,\n trial: null,\n user: cached\n };\n }\n }\n\n const user = bootstrap.user ?? null;\n\n if (user?.has_active_subscription) {\n return {\n access: 'granted',\n reason: 'has_subscription',\n visibility: bootstrap.settings.visibility ?? null,\n trial: null,\n user\n };\n }\n\n let visibility: VisibilityStatus | null = null;\n if (!opts.skipVisibility) {\n const v = bootstrap.settings.visibility;\n if (v) {\n visibility = v;\n this.lastVisibility = v;\n if (!v.visible) {\n return { access: 'granted', reason: 'visibility_blocked', visibility, trial: null, user };\n }\n }\n }\n\n let trial: TrialStatus | null = null;\n if (!opts.skipTrial) {\n const trialCfg = bootstrap.settings.trial;\n if (trialCfg) {\n try {\n const store = this.ensureTrialStore(trialCfg);\n trial = await store.check();\n this.lastTrialStatus = trial;\n if (trial.blocked) {\n return { access: 'granted', reason: 'trial_blocked', visibility, trial, user };\n }\n } catch (e) {\n if (typeof console !== 'undefined') console.warn('[paywall] getAccess: trial check failed', e);\n }\n }\n }\n\n return { access: 'blocked', reason: 'no_subscription', visibility, trial, user };\n }\n\n /** Сбросить состояние триала в storage. Полезно для дев-режима / админ-кнопки\n * «прогнать сценарий заново». В проде хост обычно не дёргает. */\n async resetTrial(): Promise<void> {\n if (!this.trialStore) return;\n await this.trialStore.reset();\n this.lastTrialStatus = null;\n this.trialExpiredFired = false;\n }\n\n // Запускает polling user-state до has_active_subscription=true либо до\n // таймаута. Идемпотентен: повторный вызов на уже работающем watcher'е —\n // no-op (юзер мог нажать Continue повторно после возврата).\n //\n // В extension popup runtime — no-op (popup не доживёт). Там полагаемся на\n // bootstrap при следующем открытии.\n private startUserWatcher(): void {\n if (this.watcher) return;\n if (!shouldRunUserWatcher()) return;\n\n this.watcher = new UserWatcher({\n client: this.billing,\n onActive: (user) => {\n this.watcher = null;\n // Серверная правда — эмитим финальный purchase_completed\n // (server-confirmed), чтобы host получил согласованный сигнал\n // независимо от того, был ли URL-маркер. userChange эмитит сам\n // billing-listener.\n this.emit('purchase_completed', { priceId: null, sessionId: null });\n // success_redirect_url из settings — host явно попросил отправить\n // юзера в свой apps-flow после оплаты. Редирект имеет приоритет\n // над PurchaseSuccessView: рисовать success ради 200мс перед\n // переходом — мерцание. Берём snapshot из cached bootstrap\n // (он гарантированно загружен — иначе watcher не запустился бы).\n const redirect = this.billing\n .getCachedBootstrap()\n ?.settings.success_redirect_url;\n if (redirect && typeof window !== 'undefined') {\n try {\n window.location.assign(redirect);\n return;\n } catch {\n /* navigation заблокирована — fallback на success-view */\n }\n }\n // Если пейвол открыт — переключаем во вью «Payment received» с\n // кнопкой Continue. Молчаливое закрытие сбивало юзера с толку:\n // окно просто исчезало, без подтверждения, что оплата прошла.\n // Если пейвол закрыт — событие уже эмитнуто, host решит сам.\n if (this.isOpen && this.handle) {\n this.purchased = true;\n this.handle.update({ purchased: true });\n }\n void user; // shape доступен через paywall.billing.getCachedUser()\n },\n onTimeout: () => {\n this.watcher = null;\n }\n });\n this.watcher.start();\n }\n\n close(): void {\n if (!this.isOpen || !this.handle) return;\n this.isOpen = false;\n this.purchased = false;\n this.handle.update({ open: false, purchased: false });\n // PaywallRoot эмитит onState с open=false при handle.update, но из-за\n // microtask'ов хост может прочитать getState() до того, как PaywallRoot\n // useEffect отстреляет. Применяем закрытое состояние сразу.\n this.applyState(CLOSED_STATE);\n this.emit('close');\n }\n\n /**\n * Сканирует текущий URL на маркеры возврата с checkout и эмитит\n * purchase_completed / purchase_failed. Маркеры удаляются из URL\n * через history.replaceState. Ищет и в hash, и в search (hash приоритетнее —\n * защита от клиентских SPA-роутеров, перехватывающих query).\n */\n checkReturn(): void {\n if (typeof window === 'undefined') return;\n const url = new URL(window.location.href);\n\n const hashMarkers = parseMarkers(url.hash.replace(/^#/, ''));\n const searchMarkers = parseMarkers(url.search.replace(/^\\?/, ''));\n const markers = hashMarkers ?? searchMarkers;\n if (!markers) return;\n\n if (markers.status === 'paid') {\n this.emit('purchase_completed', {\n priceId: markers.priceId,\n sessionId: markers.sessionId\n });\n // Acceleration: если страница загружена в новой вкладке от исходного\n // приложения (typical Stripe success_url flow), шлём opener'у postMessage.\n // Watcher в исходной вкладке среагирует мгновенно, не дожидаясь focus\n // event. Если opener'а нет (юзер закрыл/не было) — fallback на polling.\n notifyOpenerOfPurchase(markers);\n } else if (markers.status === 'failed' || markers.status === 'cancelled') {\n this.emit('purchase_failed', { reason: markers.status });\n }\n\n stripMarkersFromUrl(url);\n }\n\n destroy(): void {\n this.tracker?.destroy();\n this.tracker = null;\n this.listeners.clear();\n this.stateListeners.clear();\n this.watcher?.stop();\n this.watcher = null;\n this.userUnsub?.();\n this.userUnsub = null;\n this.authUnsub?.();\n this.authUnsub = null;\n // Если AuthClient был передан хостом — его жизненный цикл не наш,\n // ничего не дёргаем. Если создавали мы — отписываемся через BillingClient\n // (он сам держит listener на onAuthChange) и оставляем session в storage,\n // чтобы следующее открытие подхватило её через hydrate.\n if (this.ownsAuth && this.auth) {\n // Если AuthClient создавали мы — destroy сами, чтобы snapshot listener\n // отписался и не висел дальше. Externally-supplied auth не трогаем.\n this.auth.destroy?.();\n }\n this.ownsAuth = false;\n this.billing.destroy?.();\n this.handle?.unmount();\n this.handle = null;\n this.isOpen = false;\n this.currentState = CLOSED_STATE;\n }\n}\n\nfunction resolveAuth(opts: PaywallUIOptions): {\n auth: AuthClient | undefined;\n ownsAuth: boolean;\n} {\n if (!opts.auth) return { auth: undefined, ownsAuth: false };\n // Duck-typing: AuthClient ИЛИ структурный совместимец (RemoteAuthClient из\n // @monetize/sdk-extension). Проверяем по public-методам, которые\n // PaywallUI использует — если все на месте, доверяем. Это позволяет host'у\n // подставить proxy-реализацию (offscreen-architecture) без изменений в\n // PaywallUI. instanceof не подходит — runtime в content-script'е и в\n // sdk-extension'е разные, классы не nominally равны.\n if (opts.auth instanceof AuthClient || isAuthClientLike(opts.auth)) {\n return { auth: opts.auth as AuthClient, ownsAuth: false };\n }\n // true | partial-options → создаём свой AuthClient. apiOrigin/storage/fetch\n // подхватываем из общих опций PaywallUI, чтобы конфиг был \"одно поле — вся\n // система\". Юзер может перебить точечно через opts.auth = { apiOrigin: ... }.\n const cfg = opts.auth === true ? {} : opts.auth;\n return {\n auth: new AuthClient({\n paywallId: opts.paywallId,\n apiOrigin: cfg.apiOrigin ?? opts.apiOrigin,\n storage: cfg.storage ?? opts.storage,\n fetch: cfg.fetch ?? opts.fetch,\n openPopup: cfg.openPopup\n }),\n ownsAuth: true\n };\n}\n\n// Проверяет «AuthClient-подобность» переданного объекта по public-методам,\n// которые PaywallUI трогает (`onAuthChange`, `getCachedSession`, `signOut`).\n// Partial<AuthClientOptions> этих методов не имеет — пересечения с этим\n// объединением нет, ложноположительных не будет.\nfunction isAuthClientLike(value: unknown): value is AuthClient {\n if (typeof value !== 'object' || value === null) return false;\n const v = value as Record<string, unknown>;\n return (\n typeof v.onAuthChange === 'function' &&\n typeof v.getCachedSession === 'function' &&\n typeof v.signOut === 'function'\n );\n}\n\nfunction sameStateSnapshot(\n a: PaywallStateSnapshot,\n b: PaywallStateSnapshot\n): boolean {\n return a.open === b.open && a.view === b.view && a.error === b.error;\n}\n\nfunction sameTrialConfig(a: TrialConfig, b: TrialConfig): boolean {\n return a.mode === b.mode && a.payload === b.payload && a.storage === b.storage;\n}\n\nfunction parseMarkers(\n segment: string\n): { status: string; priceId: string | null; sessionId: string | null } | null {\n if (!segment) return null;\n const params = new URLSearchParams(segment);\n const status = params.get(URL_MARKERS.status);\n if (!status) return null;\n return {\n status,\n priceId: params.get(URL_MARKERS.priceId),\n sessionId: params.get(URL_MARKERS.sessionId)\n };\n}\n\n// Контракт сообщения должен совпадать с UserWatcher.handleMessage:\n// `{ type: 'paywall_purchase' }`. opener — исходная вкладка хоста, на ней живёт\n// PaywallUI с активным watcher'ом, ждущим этого сигнала.\nfunction notifyOpenerOfPurchase(markers: {\n status: string;\n priceId: string | null;\n sessionId: string | null;\n}): void {\n if (typeof window === 'undefined' || !window.opener) return;\n try {\n window.opener.postMessage(\n {\n type: 'paywall_purchase',\n status: markers.status,\n priceId: markers.priceId,\n sessionId: markers.sessionId\n },\n '*'\n );\n } catch {\n /* opener из другого origin или закрыт — watcher через focus подхватит */\n }\n}\n\nfunction stripMarkersFromUrl(url: URL): void {\n const clean = (raw: string, prefix: '?' | '#'): string => {\n if (!raw) return '';\n const p = new URLSearchParams(raw.replace(/^[?#]/, ''));\n p.delete(URL_MARKERS.status);\n p.delete(URL_MARKERS.priceId);\n p.delete(URL_MARKERS.sessionId);\n const out = p.toString();\n return out ? prefix + out : '';\n };\n const next = url.pathname + clean(url.search, '?') + clean(url.hash, '#');\n window.history.replaceState(null, '', next);\n}\n","// RemoteTrialStore — TrialStore-совместимый proxy. check / recordBlock /\n// reset идут через transport в offscreen, где реальный TrialStore работает\n// под navigator.locks — два таба не могут одновременно read-modify-write\n// один и тот же counter, drift'а нет.\n\nimport type { TrialStore } from '@sdk/core/trial';\nimport type { TrialConfig, TrialStatus } from '@sdk/core/types';\nimport type { TransportClient } from '../shared/transport-client';\n\nexport class RemoteTrialStore implements TrialStore {\n constructor(\n private readonly transport: TransportClient,\n private readonly paywallId: string,\n private readonly config: TrialConfig\n ) {}\n\n async check(): Promise<TrialStatus> {\n return this.transport.request('trial.check', {\n paywallId: this.paywallId,\n config: this.config\n });\n }\n\n async recordBlock(): Promise<TrialStatus> {\n return this.transport.request('trial.recordBlock', {\n paywallId: this.paywallId,\n config: this.config\n });\n }\n\n async reset(): Promise<void> {\n await this.transport.request('trial.reset', {\n paywallId: this.paywallId,\n config: this.config\n });\n }\n}\n","// RemoteBillingClient — структурный совместимец BillingClient, который\n// проксирует все методы в offscreen через TransportClient. Public API\n// идентичен (host пишет тот же код, что для @monetize.software/sdk), реализация\n// другая. Sync-getCached* остаются sync — ходят в локальный mirror, который\n// обновляется (a) ответами на async-методы и (b) broadcast-событиями\n// userChange/balancesChange.\n\nimport type {\n Balance,\n CheckoutResult,\n Identity,\n PaywallBootstrap,\n PaywallPrice,\n PaywallPurchaseDetailed,\n PaywallUser,\n TrialConfig\n} from '@sdk/core/types';\nimport type { StorageAdapter } from '@sdk/core/storage';\nimport type { TrialStore } from '@sdk/core/trial';\nimport { TransportClient } from '../shared/transport-client';\nimport { RemoteTrialStore } from './RemoteTrialStore';\n\nexport type UserListener = (user: PaywallUser) => void;\nexport type BalanceListener = (balances: Balance[]) => void;\n\nexport interface RemoteBillingClientOptions {\n paywallId: string;\n apiOrigin?: string;\n}\n\nexport class RemoteBillingClient {\n readonly paywallId: string;\n readonly apiOrigin: string | undefined;\n\n // Локальные mirror'ы. Источник правды — offscreen; mirror нужен только\n // чтобы getCached* оставались sync. Updated после каждого async-ответа +\n // на каждом broadcast-событии.\n private cachedBootstrap: PaywallBootstrap | null = null;\n private cachedUser: PaywallUser | null = null;\n private cachedBalances: Balance[] | null = null;\n private identity: Identity | null = null;\n /** Storage proxy через transport: get/set/remove идут в offscreen'овский\n * StorageAdapter (single source of truth для всех вкладок). Trial-state\n * PaywallUI пишет сюда — все табы видят один и тот же counter, не\n * drift'ит между вкладками.\n *\n * Race-окно read-modify-write всё ещё существует (две вкладки одновременно\n * читают N → пишут N-1, drift 1). Для exact atomicity нужен Phase 9:\n * TrialStore целиком переехать в offscreen и делать recordBlock как\n * single-handler с одной atomic-операцией. Это редкий edge case\n * (одновременное открытие пейвола в нескольких вкладках в рамках мс). */\n private remoteStorageAdapter: StorageAdapter;\n\n private userListeners = new Set<UserListener>();\n private balanceListeners = new Set<BalanceListener>();\n private unsubUserBroadcast: (() => void) | null = null;\n private unsubBalancesBroadcast: (() => void) | null = null;\n\n constructor(\n private readonly transport: TransportClient,\n opts: RemoteBillingClientOptions\n ) {\n this.paywallId = opts.paywallId;\n this.apiOrigin = opts.apiOrigin;\n\n this.remoteStorageAdapter = {\n getItem: (key) => this.transport.request('storage.get', { key }),\n setItem: async (key, value) => {\n await this.transport.request('storage.set', { key, value });\n },\n removeItem: async (key) => {\n await this.transport.request('storage.remove', { key });\n }\n // watch не реализуем — для cross-context уведомлений consumer'ы (AuthClient,\n // TrialStore) подписываются на broadcast-events напрямую через transport.\n // Если когда-то понадобится — добавим storage.watch broadcast.\n };\n\n this.unsubUserBroadcast = this.transport.on('userChange', (user) => {\n this.applyUser(user);\n });\n\n this.unsubBalancesBroadcast = this.transport.on('balancesChange', (balances) => {\n this.applyBalances([...balances]);\n });\n }\n\n // === Bootstrap ===\n\n async bootstrap(opts: { force?: boolean; signal?: AbortSignal } = {}): Promise<PaywallBootstrap> {\n const result = await this.transport.request(\n 'billing.bootstrap',\n { force: opts.force },\n { signal: opts.signal }\n );\n this.cachedBootstrap = result;\n if (result.user) this.applyUser(result.user);\n return result;\n }\n\n getCachedBootstrap(): PaywallBootstrap | null {\n return this.cachedBootstrap;\n }\n\n /** Шорткат над `bootstrap()` — возвращает цены пейвола (locale-оверрайды\n * уже применены в offscreen'е). Те же кэш-семантики, что у `bootstrap()`. */\n async getPrices(opts: { force?: boolean; signal?: AbortSignal } = {}): Promise<PaywallPrice[]> {\n const b = await this.bootstrap(opts);\n return b.prices;\n }\n\n /** Sync-снимок цен из локального mirror'а bootstrap'а. null = ещё не грузили. */\n getCachedPrices(): PaywallPrice[] | null {\n return this.cachedBootstrap?.prices ?? null;\n }\n\n // === Visitor ===\n\n async getVisitorId(): Promise<string> {\n return this.transport.request('billing.getVisitorId', undefined);\n }\n\n // === User ===\n\n async getUser(opts: { force?: boolean; signal?: AbortSignal } = {}): Promise<PaywallUser> {\n const result = await this.transport.request(\n 'billing.getUser',\n { force: opts.force },\n { signal: opts.signal }\n );\n this.applyUser(result);\n return result;\n }\n\n getCachedUser(): PaywallUser | null {\n return this.cachedUser;\n }\n\n /** Подписка на user-state. Mirror'имся на broadcast'ы offscreen'а; initial\n * snapshot отдаётся через microtask из локального cache (если есть) —\n * ровно как в BillingClient.onUserChange. Возвращает функцию отписки. */\n onUserChange(\n cb: UserListener,\n opts: { immediate?: 'microtask' | 'sync' | 'none' } = {}\n ): () => void {\n this.userListeners.add(cb);\n const mode = opts.immediate ?? 'microtask';\n if (this.cachedUser && mode !== 'none') {\n const snapshot = this.cachedUser;\n if (mode === 'sync') {\n try {\n cb(snapshot);\n } catch (e) {\n console.warn('[paywall] onUserChange initial sync threw', e);\n }\n } else {\n queueMicrotask(() => {\n if (this.userListeners.has(cb)) cb(snapshot);\n });\n }\n }\n return () => {\n this.userListeners.delete(cb);\n };\n }\n\n // === Balances ===\n\n async getBalances(opts: { force?: boolean; signal?: AbortSignal } = {}): Promise<Balance[]> {\n const result = await this.transport.request(\n 'billing.getBalances',\n { force: opts.force },\n { signal: opts.signal }\n );\n const arr = [...result];\n this.applyBalances(arr);\n return arr;\n }\n\n getCachedBalances(): Balance[] | null {\n return this.cachedBalances;\n }\n\n onBalanceChange(\n cb: BalanceListener,\n opts: { immediate?: 'microtask' | 'sync' | 'none' } = {}\n ): () => void {\n this.balanceListeners.add(cb);\n const mode = opts.immediate ?? 'microtask';\n if (this.cachedBalances && mode !== 'none') {\n const snapshot = this.cachedBalances;\n if (mode === 'sync') {\n try {\n cb(snapshot);\n } catch (e) {\n console.warn('[paywall] onBalanceChange initial sync threw', e);\n }\n } else {\n queueMicrotask(() => {\n if (this.balanceListeners.has(cb)) cb(snapshot);\n });\n }\n }\n return () => {\n this.balanceListeners.delete(cb);\n };\n }\n\n // === Checkout ===\n\n async createCheckout(params: {\n priceId: string;\n successUrl?: string;\n errorUrl?: string;\n shopUrl?: string;\n trialDays?: number;\n idempotencyKey?: string;\n ignoreActivePurchase?: boolean;\n signal?: AbortSignal;\n }): Promise<CheckoutResult> {\n const { signal, ...payload } = params;\n return this.transport.request('billing.createCheckout', payload, { signal });\n }\n\n // === Customer portal: list/cancel purchases ===\n\n /** Rich-shape список покупок юзера (с ценой, валютой, interval, discount,\n * cancel-метаданными). Через offscreen — там настоящий BillingClient\n * ходит на `/api/v1/paywall/[id]/user` с Bearer'ом. Полезно для\n * customer-portal UI: cards + Cancel/Renew кнопки. */\n async listPurchases(opts: { signal?: AbortSignal } = {}): Promise<PaywallPurchaseDetailed[]> {\n const result = await this.transport.request('billing.listPurchases', undefined, {\n signal: opts.signal\n });\n return [...result];\n }\n\n /** Отменить подписку через бэк. По умолчанию cancel в конце текущего\n * периода (юзер сохраняет access до renewal date'ы). reason обязательна\n * (валидируется бэком) — собирается через select причин в host-UI. */\n async cancelSubscription(params: {\n subscriptionId: string;\n reason: string;\n signal?: AbortSignal;\n }): Promise<{\n subscription: {\n status: string | null;\n canceled_at: string | null;\n cancel_at: string | null;\n cancel_at_period_end: boolean | null;\n };\n }> {\n const { signal, ...payload } = params;\n return this.transport.request('billing.cancelSubscription', payload, { signal });\n }\n\n // === Storage ===\n\n /** PaywallUI просит storage у billing-клиента для TrialStore и других\n * consumer'ов. Возвращает proxy: get/set/remove идут через transport\n * в offscreen'овский storage = single source of truth для всех вкладок. */\n getStorage(): StorageAdapter {\n return this.remoteStorageAdapter;\n }\n\n /** Factory-метод для PaywallUI: вместо локального createTrialStore'а на\n * storage-proxy, возвращаем RemoteTrialStore — он шлёт каждую операцию\n * одним атомарным RPC в offscreen, где navigator.locks сериализуют\n * read-modify-write. PaywallUI duck-types этот метод и предпочитает его\n * локальной фабрике, если он есть. */\n createTrialStore(config: TrialConfig): TrialStore {\n return new RemoteTrialStore(this.transport, this.paywallId, config);\n }\n\n // === Identity ===\n\n getIdentity(): Identity | null {\n return this.identity;\n }\n\n async setIdentity(identity: Identity | null): Promise<void> {\n this.identity = identity;\n await this.transport.request('billing.setIdentity', { identity });\n }\n\n /** Подгрузить identity с offscreen'а. Используется при первом подключении\n * content-script'а — если другая вкладка уже залогинила юзера, текущая\n * тут же подхватит identity без ожидания authChange. */\n async syncIdentity(): Promise<Identity | null> {\n const result = await this.transport.request('billing.getIdentity', undefined);\n this.identity = result;\n return result;\n }\n\n destroy(): void {\n this.unsubUserBroadcast?.();\n this.unsubBalancesBroadcast?.();\n this.unsubUserBroadcast = null;\n this.unsubBalancesBroadcast = null;\n this.userListeners.clear();\n this.balanceListeners.clear();\n this.cachedBootstrap = null;\n this.cachedUser = null;\n this.cachedBalances = null;\n this.identity = null;\n }\n\n /** Обновить mirror user'а и эмитнуть listener'ам если он реально изменился.\n * Используется и для self-инициированных RPC (bootstrap/getUser), и для\n * broadcast'ов от offscreen — чтобы host'овский onUserChange handler\n * получил signal независимо от того, кто триггернул обновление. */\n private applyUser(user: PaywallUser): void {\n if (sameUser(this.cachedUser, user)) return;\n this.cachedUser = user;\n this.fireUserListeners(user);\n }\n\n private applyBalances(balances: Balance[]): void {\n if (sameBalances(this.cachedBalances, balances)) return;\n this.cachedBalances = balances;\n this.fireBalanceListeners(balances);\n }\n\n private fireUserListeners(user: PaywallUser): void {\n for (const cb of [...this.userListeners]) {\n try {\n cb(user);\n } catch (e) {\n console.warn('[paywall] onUserChange listener threw', e);\n }\n }\n }\n\n private fireBalanceListeners(balances: Balance[]): void {\n for (const cb of [...this.balanceListeners]) {\n try {\n cb(balances);\n } catch (e) {\n console.warn('[paywall] onBalanceChange listener threw', e);\n }\n }\n }\n}\n\nfunction sameUser(a: PaywallUser | null, b: PaywallUser | null): boolean {\n if (a === b) return true;\n if (!a || !b) return false;\n return (\n a.has_active_subscription === b.has_active_subscription &&\n (a.purchases?.length ?? 0) === (b.purchases?.length ?? 0)\n );\n}\n\nfunction sameBalances(a: Balance[] | null, b: Balance[] | null): boolean {\n if (a === b) return true;\n if (!a || !b) return false;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i].type !== b[i].type || a[i].count !== b[i].count) return false;\n }\n return true;\n}\n","// RemoteAuthClient — структурный совместимец AuthClient. Public методы\n// идентичны, под капотом — async-прокси через TransportClient в offscreen,\n// где живёт реальная сессия и storage.\n//\n// Sync-getCachedSession поддерживается через локальный mirror, обновляемый\n// (a) на каждом ответе async-метода, (b) на broadcast'е authChange.\n//\n// OAuth (signInWithOAuth) пока бросает not-implemented — требует publicного\n// split-API в @sdk/core/auth (Phase 4.5). Для email/password/refresh/signOut\n// и прочей сетевой части — всё работает.\n\nimport type { AuthSession, AuthUser, OAuthProvider, OtpVerifyType, SignUpResult } from '@sdk/core/auth';\nimport { waitForOAuthCode } from '@sdk/core/auth';\nimport { PaywallError } from '@sdk/core/types';\nimport { TransportClient } from '../shared/transport-client';\n\nexport type AuthChangeListener = (session: AuthSession | null) => void;\n\nexport interface RemoteAuthClientOptions {\n paywallId: string;\n apiOrigin?: string;\n}\n\nexport class RemoteAuthClient {\n readonly paywallId: string;\n readonly apiOrigin: string | undefined;\n\n private session: AuthSession | null = null;\n private listeners = new Set<AuthChangeListener>();\n private unsubBroadcast: (() => void) | null = null;\n private hydrated: Promise<void>;\n\n constructor(\n private readonly transport: TransportClient,\n opts: RemoteAuthClientOptions\n ) {\n this.paywallId = opts.paywallId;\n this.apiOrigin = opts.apiOrigin;\n\n this.unsubBroadcast = this.transport.on('authChange', (session) => {\n this.applySession(session);\n });\n\n // Initial sync с offscreen'а — чтобы getCachedSession() мог отдать что-то\n // не-null уже на первом тике после ready().\n this.hydrated = this.transport\n .request('auth.getCachedSession', undefined)\n .then((session) => {\n if (this.session === null && session !== null) {\n this.applySession(session);\n }\n })\n .catch(() => {\n /* offscreen не готов или транспорт упал — getCachedSession отдаст null */\n });\n }\n\n /** Promise, который резолвится после первичной синхронизации session с\n * offscreen'а. Аналог AuthClient.ready(). */\n ready(): Promise<void> {\n return this.hydrated;\n }\n\n getCachedSession(): AuthSession | null {\n return this.session;\n }\n\n getCachedUser(): AuthUser | null {\n return this.session?.user ?? null;\n }\n\n onAuthChange(cb: AuthChangeListener): () => void {\n this.listeners.add(cb);\n // Initial snapshot через microtask — match AuthClient.onAuthChange UX.\n if (this.session) {\n const snapshot = this.session;\n queueMicrotask(() => {\n if (this.listeners.has(cb)) cb(snapshot);\n });\n }\n return () => {\n this.listeners.delete(cb);\n };\n }\n\n // === Email/password ===\n\n async signInWithEmail(input: { email: string; password: string }): Promise<AuthSession> {\n const session = await this.transport.request('auth.signInWithEmail', input);\n this.applySession(session);\n return session;\n }\n\n async signUp(input: {\n email: string;\n password: string;\n userMeta?: Record<string, string>;\n }): Promise<SignUpResult> {\n const result = await this.transport.request('auth.signUp', input);\n if (result.kind === 'signed_in') this.applySession(result.session);\n return result;\n }\n\n async signOut(): Promise<void> {\n await this.transport.request('auth.signOut', undefined);\n // Broadcast authChange придёт от offscreen'а с session=null, applySession\n // там уже отработает. Тут ничего не делаем, чтобы не дёрнуть listener'ы\n // дважды.\n }\n\n async refresh(): Promise<AuthSession | null> {\n const session = await this.transport.request('auth.refresh', undefined);\n this.applySession(session);\n return session;\n }\n\n // === OTP / password reset / confirmation ===\n\n async sendOtp(input: {\n email: string;\n createUser?: boolean;\n userMeta?: Record<string, unknown>;\n }): Promise<void> {\n await this.transport.request('auth.sendOtp', input);\n }\n\n async verifyOtp(input: {\n email: string;\n token: string;\n type: OtpVerifyType;\n }): Promise<AuthSession> {\n const session = await this.transport.request('auth.verifyOtp', input);\n this.applySession(session);\n return session;\n }\n\n async resendConfirmation(input: { email: string }): Promise<void> {\n await this.transport.request('auth.resendConfirmation', input);\n }\n\n async requestPasswordReset(input: { email: string }): Promise<void> {\n await this.transport.request('auth.requestPasswordReset', input);\n }\n\n async updatePassword(input: { password: string }): Promise<void> {\n await this.transport.request('auth.updatePassword', input);\n }\n\n async revokeAllSessions(): Promise<void> {\n await this.transport.request('auth.revokeAllSessions', undefined);\n }\n\n // === Anonymous sign-in ===\n\n /** Анонимный sign-in (Supabase user без email). Логика (idempotent-check +\n * resume через сохранённый refresh_token + fresh signin) живёт в\n * offscreen-AuthClient'е — content только проксирует. captchaToken и\n * forceCaptcha — pass-through для forward-compat / switch-account flow. */\n async signInAnonymously(input: {\n captchaToken?: string;\n userMeta?: Record<string, string>;\n forceCaptcha?: boolean;\n } = {}): Promise<AuthSession> {\n const session = await this.transport.request('auth.signInAnonymously', {\n captchaToken: input.captchaToken,\n userMeta: input.userMeta,\n forceCaptcha: input.forceCaptcha\n });\n this.applySession(session);\n return session;\n }\n\n /** Текущий access token (lazy-refreshable в offscreen'е). content/popup\n * использует для Bearer'а в внешние fetch'и — ApiGatewayClient в\n * content-script'е, прямые запросы из demo-UI. null если разлогинен или\n * offscreen'овский AuthClient не смог рефрешнуть. */\n async getAccessToken(): Promise<string | null> {\n return this.transport.request('auth.getAccessToken', undefined);\n }\n\n // === OAuth (web-flow через split-API) ===\n\n /** OAuth через web-вариант: window.open в content-script'е, provider\n * redirect, callback page постит code в opener. Под капотом split на\n * два request'а в offscreen — startOAuthFlow (дёргаем /init, получаем\n * authorize_url) → открываем popup → waitForOAuthCode → exchange.\n *\n * PKCE verifier живёт ТОЛЬКО в offscreen'е (внутри AuthClient'а), через\n * runtime-границу не идёт. Content получает только authorize_url и state.\n *\n * Popup-gesture: `window.open(authorize_url, ...)` идёт в том же synchronous\n * flow'е, что startOAuthFlow ответ; user-gesture сохраняется потому что\n * content-script unloaded не за этот tick (gesture сохраняется через все\n * microtask'и одного call stack'а). Если в каком-то браузере gesture\n * всё-таки теряется — host получит `popup_blocked` (тот же что в @monetize.software/sdk).\n */\n async signInWithOAuth(input: {\n provider: OAuthProvider;\n scopes?: string;\n userMeta?: Record<string, string>;\n onPopupOpened?: () => void;\n }): Promise<AuthSession> {\n if (typeof window === 'undefined') {\n throw new PaywallError('oauth_unavailable', 'window is required for OAuth');\n }\n\n // Открываем popup СИНХРОННО — user-gesture сохраняется только в том же\n // synchronous frame, что click-handler. Async `await` на transport.request\n // до window.open съедает gesture, и Chrome открывает popup с пустым URL'ом\n // / блокирует совсем.\n //\n // about:blank вместо data:text/html (которым раньше показывали inline-loader):\n // data:-URL'ы триггерят static-сканеры CWS и EDR'ы как подозрительные. Вместо\n // этого открываем about:blank (наследует origin opener'а) и инжектим loader-DOM\n // через document.createElement + textContent — ровно то же UX, без data:-URL'а.\n const tempName = `pw-oauth-pending-${Math.random().toString(36).slice(2, 10)}`;\n const popup = window.open('about:blank', tempName, 'width=480,height=640,popup=yes');\n if (!popup) {\n throw new PaywallError(\n 'popup_blocked',\n 'browser blocked auth popup — call from a user gesture'\n );\n }\n injectLoaderUI(popup, input.provider);\n\n try {\n // Async-часть: дёргаем offscreen за authorize_url и state. Popup пока\n // показывает about:blank.\n const { authorizeUrl, state } = await this.transport.request('auth.oauthStart', {\n provider: input.provider,\n scopes: input.scopes,\n userMeta: input.userMeta\n });\n\n // Перед navigate'ом меняем имя popup'а на формат, который ожидает\n // callback page (pw-oauth-<state>) — name переживает cross-origin\n // редиректы (Google → Supabase → наш callback). callback page\n // читает window.name → извлекает state → posts back.\n popup.name = `pw-oauth-${state}`;\n popup.location.replace(authorizeUrl);\n\n input.onPopupOpened?.();\n\n const code = await waitForOAuthCode(popup, state);\n const session = await this.transport.request('auth.oauthExchange', { state, code });\n this.applySession(session);\n return session;\n } catch (e) {\n try {\n popup.close();\n } catch {\n /* ignore */\n }\n throw e;\n }\n }\n\n destroy(): void {\n this.unsubBroadcast?.();\n this.unsubBroadcast = null;\n this.listeners.clear();\n this.session = null;\n }\n\n private applySession(next: AuthSession | null): void {\n if (sameSession(this.session, next)) return;\n this.session = next;\n for (const cb of [...this.listeners]) {\n try {\n cb(next);\n } catch (e) {\n console.warn('[paywall] onAuthChange listener threw', e);\n }\n }\n }\n}\n\nfunction sameSession(a: AuthSession | null, b: AuthSession | null): boolean {\n if (a === b) return true;\n if (!a || !b) return false;\n return (\n a.access_token === b.access_token &&\n a.refresh_token === b.refresh_token &&\n a.expires_at === b.expires_at &&\n a.user.id === b.user.id\n );\n}\n\nconst PROVIDER_NAMES: Record<string, string> = {\n google: 'Google',\n apple: 'Apple',\n github: 'GitHub',\n facebook: 'Facebook'\n};\n\n/** Inject loader-UI в about:blank popup. Same-origin как opener — мы можем\n * трогать popup.document напрямую. Используем createElement + textContent\n * (не innerHTML / document.write) чтобы не триггерить XSS-сканеры даже на\n * hard-coded строках. CSS-классы с pw-oauth-* префиксом — без коллизий со\n * стилями родительской страницы (popup всё равно изолирован, но на всякий).\n *\n * Defensive try/catch: если в каком-то edge-кейсе popup оказался не\n * same-origin (некоторые расширения это перехватывают) или document не\n * доступен — тихо забиваем, popup покажет дефолтный about:blank на 200-500мс\n * до redirect'а на provider'а. */\nfunction injectLoaderUI(popup: Window, provider: string): void {\n const name = PROVIDER_NAMES[provider] ?? provider;\n try {\n const doc = popup.document;\n doc.title = `Sign in with ${name}`;\n\n const style = doc.createElement('style');\n style.textContent =\n 'html,body{margin:0;padding:0;height:100%;font-family:-apple-system,system-ui,sans-serif;background:#fafafa;color:#475569}' +\n '.pw-oauth-wrap{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:16px}' +\n '.pw-oauth-spinner{width:36px;height:36px;border:3px solid #e2e8f0;border-top-color:#7c3aed;border-radius:50%;animation:pw-oauth-spin 800ms linear infinite}' +\n '.pw-oauth-label{font-size:14px;font-weight:500;letter-spacing:-0.01em}' +\n '@keyframes pw-oauth-spin{to{transform:rotate(360deg)}}';\n doc.head.appendChild(style);\n\n const wrap = doc.createElement('div');\n wrap.className = 'pw-oauth-wrap';\n const spinner = doc.createElement('div');\n spinner.className = 'pw-oauth-spinner';\n const label = doc.createElement('div');\n label.className = 'pw-oauth-label';\n label.textContent = `Connecting to ${name}…`;\n wrap.appendChild(spinner);\n wrap.appendChild(label);\n doc.body.appendChild(wrap);\n } catch {\n /* popup not same-origin or document not ready — fall back to blank */\n }\n}\n","// RemoteEventTracker — fire-and-forget proxy для аналитики. Все track()\n// call'ы из всех вкладок попадают в единственный EventTracker в offscreen'е,\n// который батчит и шлёт в /events. Победа — один батч на расширение,\n// один sendBeacon на unload, никаких дублирующихся `app_opened` событий.\n//\n// API специально минимальный — только track(name, props). Buffer / flush /\n// destroy логика живёт в offscreen'е, content её не контролирует.\n\nimport { TransportClient } from '../shared/transport-client';\n\nexport class RemoteEventTracker {\n constructor(private readonly transport: TransportClient) {}\n\n /** Отправить событие. Fire-and-forget — не возвращает Promise, не throw'ает.\n * Сетевые/транспортные ошибки логируются в console и не блокируют caller. */\n track(name: string, props?: Record<string, unknown>): void {\n if (typeof name !== 'string' || name.length === 0) return;\n this.transport.request('tracker.track', { name, props }).catch((e) => {\n console.warn('[paywall] track failed', e);\n });\n }\n}\n","// Client-side транспорт. Используется в content-script'е (поверх chrome.runtime\n// port'а к SW) и в любом другом surface'е, который ходит к offscreen через\n// тот же роутер (popup, extension page, side panel).\n//\n// Контракт:\n// - request<K>(kind, params, signal?) — запрос с типизированным результатом.\n// На disconnect канала pending request'ы reject'аются с reconnect-error;\n// next call воссоздаст канал через ChannelFactory и продолжит работу.\n// - on<K>(kind, handler) — подписка на broadcast от сервера. Переподписки\n// переживают reconnect автоматически — handler'ы хранятся локально, а\n// re-subscribe на сервере не требуется (сервер всегда broadcast'ит всем\n// подключённым каналам).\n//\n// Reconnect стратегия: lazy. Канал поднимается при первом request/on, мёртвый —\n// пересоздаётся в момент следующего запроса. Никаких exponential backoff'ов\n// в фоне — extension контекст это не любит (расход CPU + батарея).\n\nimport type {\n EventEnvelope,\n EventKind,\n EventPayload,\n RequestEnvelope,\n RequestKind,\n RequestParams,\n RequestResult,\n ResponseEnvelope,\n ResponseErr\n} from './protocol';\nimport { PROTOCOL_VERSION } from './protocol';\nimport { reconstructError } from './errors';\nimport type { ChannelFactory, MessageChannel } from './channel';\n\ninterface PendingRequest {\n resolve: (value: unknown) => void;\n reject: (reason: unknown) => void;\n abortListener?: () => void;\n signal?: AbortSignal;\n}\n\nexport class TransportClient {\n private channel: MessageChannel | null = null;\n private channelDisposers: Array<() => void> = [];\n private pending = new Map<string, PendingRequest>();\n private listeners = new Map<EventKind, Set<(payload: unknown) => void>>();\n private destroyed = false;\n private nextId = 0;\n /** Уникальный ID клиента — отправляется в handshake'е, server может логировать\n * для отладки connection-flap'а. */\n private readonly clientId = `c-${Math.random().toString(36).slice(2, 10)}`;\n\n constructor(private readonly factory: ChannelFactory) {}\n\n /** Гарантирует наличие живого канала. Lazy — поднимается при первом request.\n * Сразу после connect'а fire-and-forget шлёт handshake — на mismatch\n * логируем warning, но не блокируем дальнейшие запросы. */\n private ensureChannel(): MessageChannel {\n if (this.destroyed) throw new Error('TransportClient destroyed');\n if (this.channel) return this.channel;\n\n const channel = this.factory();\n this.channel = channel;\n\n const offMsg = channel.onMessage((env) => this.handleMessage(env));\n const offDisc = channel.onDisconnect(() => this.handleDisconnect());\n this.channelDisposers = [offMsg, offDisc];\n\n // Async, без await: основные запросы могут параллельно идти. На mismatch'е\n // ничего не ломаем — server может быть на другой минорной версии (например,\n // host обновил sdk-extension но не sdk).\n void this.request('handshake', {\n protocolVersion: PROTOCOL_VERSION,\n clientId: this.clientId\n })\n .then((res) => {\n if (res.protocolVersion !== PROTOCOL_VERSION) {\n console.warn(\n `[sdk-extension] protocol version mismatch: client=${PROTOCOL_VERSION}, ` +\n `offscreen=${res.protocolVersion}. Update host's @monetize.software/sdk-extension.`\n );\n }\n })\n .catch(() => {\n // Server без handshake-handler'а или умер — best-effort, не падаем.\n });\n\n return channel;\n }\n\n private handleMessage(envelope: unknown): void {\n if (!isEnvelope(envelope)) return;\n if (envelope.type === 'response') {\n const pending = this.pending.get(envelope.id);\n if (!pending) return;\n this.pending.delete(envelope.id);\n pending.signal?.removeEventListener('abort', pending.abortListener!);\n if (envelope.ok) {\n pending.resolve(envelope.result);\n } else {\n // Narrowing на discriminated union с generic'ом теряется в strict-режиме —\n // явный cast стабильнее, ok===false уже проверен.\n pending.reject(reconstructError((envelope as ResponseErr).error));\n }\n return;\n }\n if (envelope.type === 'event') {\n const set = this.listeners.get(envelope.kind);\n if (!set) return;\n // Snapshot, чтобы handler мог отписать сам себя без NaN-итерации.\n for (const handler of [...set]) {\n try {\n handler(envelope.payload);\n } catch (e) {\n console.error('[sdk-extension] event handler threw', e);\n }\n }\n }\n }\n\n private handleDisconnect(): void {\n for (const fn of this.channelDisposers) fn();\n this.channelDisposers = [];\n this.channel = null;\n // Reject все in-flight — они идут с reconnect-кодом, host может ретрайнуть.\n const pending = Array.from(this.pending.values());\n this.pending.clear();\n for (const p of pending) {\n p.signal?.removeEventListener('abort', p.abortListener!);\n p.reject(new TransportDisconnectedError());\n }\n }\n\n request<K extends RequestKind>(\n kind: K,\n params: RequestParams<K>,\n opts: { signal?: AbortSignal } = {}\n ): Promise<RequestResult<K>> {\n if (this.destroyed) {\n return Promise.reject(new Error('TransportClient destroyed'));\n }\n if (opts.signal?.aborted) {\n return Promise.reject(new DOMException('Aborted', 'AbortError'));\n }\n\n const channel = this.ensureChannel();\n const id = `r${++this.nextId}`;\n\n return new Promise<RequestResult<K>>((resolve, reject) => {\n const pending: PendingRequest = {\n resolve: resolve as (value: unknown) => void,\n reject,\n signal: opts.signal\n };\n\n if (opts.signal) {\n pending.abortListener = () => {\n if (this.pending.delete(id)) {\n reject(new DOMException('Aborted', 'AbortError'));\n // Послать cancel в offscreen чтобы там тоже abort'нуть underlying\n // fetch. Best-effort: канал мог отвалиться — тогда send бросит,\n // но pending уже удалён, юзер уже получил abort error.\n try {\n channel.send({ type: 'cancel', id });\n } catch {\n /* channel dead — server'у уже всё равно */\n }\n }\n };\n opts.signal.addEventListener('abort', pending.abortListener);\n }\n\n this.pending.set(id, pending);\n\n const envelope: RequestEnvelope<RequestParams<K>> = {\n type: 'request',\n id,\n kind,\n params\n };\n try {\n channel.send(envelope);\n } catch (e) {\n this.pending.delete(id);\n opts.signal?.removeEventListener('abort', pending.abortListener!);\n reject(e);\n }\n });\n }\n\n on<K extends EventKind>(\n kind: K,\n handler: (payload: EventPayload<K>) => void\n ): () => void {\n let set = this.listeners.get(kind);\n if (!set) {\n set = new Set();\n this.listeners.set(kind, set);\n }\n const wrapped = handler as (payload: unknown) => void;\n set.add(wrapped);\n\n // Lazy ensureChannel: подписка не требует немедленного канала, но первый\n // event может прилететь только если канал жив. Поднимаем заранее.\n this.ensureChannel();\n\n return () => {\n set!.delete(wrapped);\n };\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n for (const fn of this.channelDisposers) fn();\n this.channelDisposers = [];\n this.listeners.clear();\n const pending = Array.from(this.pending.values());\n this.pending.clear();\n for (const p of pending) {\n p.signal?.removeEventListener('abort', p.abortListener!);\n p.reject(new Error('TransportClient destroyed'));\n }\n this.channel?.close();\n this.channel = null;\n }\n}\n\nexport class TransportDisconnectedError extends Error {\n readonly code = 'transport_disconnected';\n constructor() {\n super('Transport channel disconnected mid-request');\n this.name = 'TransportDisconnectedError';\n }\n}\n\nfunction isEnvelope(value: unknown): value is RequestEnvelope | ResponseEnvelope | EventEnvelope {\n if (typeof value !== 'object' || value === null) return false;\n const t = (value as { type?: unknown }).type;\n return t === 'request' || t === 'response' || t === 'event';\n}\n","// Content-side singleton TransportClient. Один на content-script, переиспользуется\n// всеми инстансами PaywallUI на одной странице (на странице обычно один, но\n// несколько технически возможны — например один пейвол в overlay'е, другой\n// в popup'е host-расширения, оба внутри content-script'а одной страницы).\n\nimport { TransportClient } from '../shared/transport-client';\nimport { createRuntimeChannel } from '../shared/chrome-port';\nimport { PORT_NAME } from '../shared/port-name';\n\nlet cached: TransportClient | null = null;\n\nexport function getContentTransport(): TransportClient {\n if (cached) return cached;\n cached = new TransportClient(() => createRuntimeChannel(PORT_NAME));\n return cached;\n}\n\n/** Тестовая инжекция — для unit-тестов RemoteBillingClient'а с фейковым\n * каналом (без chrome.runtime). В проде не используется. */\nexport function _setContentTransportForTests(client: TransportClient | null): void {\n cached = client;\n}\n","// Drop-in `PaywallUI` для extension'а. Public API идентичен `@monetize.software/sdk`'у —\n// host пишет тот же код, опции те же. Под капотом:\n// - billing — RemoteBillingClient (proxy в offscreen)\n// - auth — RemoteAuthClient (когда `auth: true`)\n// - tracker — RemoteEventTracker (события forward'ятся в offscreen-EventTracker)\n//\n// EventTracker создаётся ОДИН на расширение, в offscreen'е. PaywallUI здесь\n// внутренний tracker отключает (`analytics: false` в base-конструкторе) и\n// сам подписывается на public-события, проксируя их через RemoteEventTracker.\n// Дубликат биндингов из base PaywallUI.initTracker — но это меньшее зло, чем\n// два EventTracker'а на одного юзера.\n\nimport { PaywallUI as BasePaywallUI, type PaywallUIOptions } from '@sdk/ui/PaywallUI';\nimport type { BillingClient } from '@sdk/core/BillingClient';\nimport type { AuthClient } from '@sdk/core/auth';\nimport { RemoteBillingClient } from './RemoteBillingClient';\nimport { RemoteAuthClient } from './RemoteAuthClient';\nimport { RemoteEventTracker } from './RemoteEventTracker';\nimport { getContentTransport } from './transport';\n\n/** Опции extension'овского PaywallUI. Убраны:\n * - `client` — RemoteBillingClient создаётся автоматически\n * - `storage` — storage живёт в offscreen'е, content его не видит\n * - `apiKey` — server-SDK key, не имеет смысла в content-script'е\n * - `fetch` — все сетевые запросы идут через offscreen\n *\n * `auth: true` подключит RemoteAuthClient. Передавать готовый AuthClient\n * из @monetize.software/sdk сюда не имеет смысла (мы хотим именно offscreen'овский). */\nexport interface ExtensionPaywallUIOptions\n extends Omit<PaywallUIOptions, 'client' | 'storage' | 'apiKey' | 'fetch'> {}\n\nexport class PaywallUI extends BasePaywallUI {\n /** RemoteEventTracker (proxy в offscreen-EventTracker). Не путать с\n * base-классовым `tracker` (там null — мы отключили внутренний). */\n private remoteTracker: RemoteEventTracker | null = null;\n private trackerUnsubs: Array<() => void> = [];\n\n constructor(opts: ExtensionPaywallUIOptions) {\n const transport = getContentTransport();\n\n const billing = new RemoteBillingClient(transport, {\n paywallId: opts.paywallId,\n apiOrigin: opts.apiOrigin\n });\n\n // Auth: если host попросил — конструируем RemoteAuthClient. Готовый\n // AuthClient из @monetize.software/sdk сюда не имеет смысла прокидывать (всё\n // равно нужен offscreen-instance). Поэтому accept только `true` или\n // ничего; явный AuthClient instance логирует warning и игнорится.\n let auth: RemoteAuthClient | undefined;\n if (opts.auth === true) {\n auth = new RemoteAuthClient(transport, {\n paywallId: opts.paywallId,\n apiOrigin: opts.apiOrigin\n });\n } else if (opts.auth) {\n console.warn(\n '[sdk-extension] passing AuthClient instance to PaywallUI.opts.auth ' +\n 'is not supported in extension mode — pass `auth: true` to use ' +\n 'offscreen-shared auth, or omit for hybrid identity-only mode.'\n );\n }\n\n // Прокидываем auth внутрь billing-клиента: PaywallRoot читает\n // `client.auth` для restore / preauth-flow / signin-detection. Настоящий\n // BillingClient выставляет это поле в конструкторе — у Remote-варианта\n // делаем явное присваивание перед super(), чтобы PaywallRoot увидел.\n if (auth) {\n (billing as { auth?: typeof auth }).auth = auth;\n }\n\n super({\n ...opts,\n // Cast'ы безопасны: PaywallUI'ев resolveAuth duck-type'ит auth (см.\n // sdk/src/ui/PaywallUI.ts isAuthClientLike), а billing-параметр идёт\n // через `opts.client ?? new BillingClient(...)` — RemoteBillingClient\n // там используется как есть, методы все сходятся.\n client: billing as unknown as BillingClient,\n auth: auth as unknown as AuthClient | undefined,\n // Внутренний EventTracker отключаем — единственный tracker живёт в\n // offscreen'е. Манчиально подписываемся ниже.\n analytics: false\n });\n\n if (opts.analytics !== false) {\n this.remoteTracker = new RemoteEventTracker(transport);\n this.bindAnalytics();\n }\n }\n\n /** Зеркало sdk/PaywallUI.initTracker'овских биндингов, но с RemoteEventTracker.\n * Когда @monetize.software/sdk экспоузнет публичный hook для inject'а tracker'а,\n * этот метод заменится на одну строку. */\n private bindAnalytics(): void {\n const t = this.remoteTracker;\n if (!t) return;\n\n this.trackerUnsubs.push(\n this.on('open', () => t.track('paywall_opened')),\n this.on('ready', (b) =>\n t.track('paywall_viewed', {\n is_test_mode: b.settings.is_test_mode,\n prices_count: b.prices.length,\n offers_count: b.offers.length\n })\n ),\n this.on('price_selected', (p) =>\n t.track('price_selected', { price_id: p.priceId })\n ),\n this.on('checkout_started', (p) =>\n t.track('checkout_started', { price_id: p.priceId, acquiring: p.acquiring })\n ),\n this.on('purchase_completed', (p) =>\n t.track('purchase_completed', { price_id: p.priceId, session_id: p.sessionId })\n ),\n this.on('purchase_failed', (p) => t.track('purchase_failed', { reason: p.reason })),\n this.on('close', () => t.track('paywall_closed')),\n this.on('trial_blocked', (s) =>\n t.track('trial_blocked', {\n mode: s.mode,\n ...(s.mode === 'time'\n ? { remaining_ms: s.remainingMs, total_ms: s.totalMs }\n : s.mode === 'opens'\n ? { remaining_actions: s.remainingActions, total_actions: s.totalActions }\n : {})\n })\n ),\n this.on('trial_expired', () => t.track('trial_expired')),\n this.on('visibility_blocked', (v) =>\n t.track('visibility_blocked', { reason: v.reason, country: v.country, tier: v.tier })\n ),\n this.on('error', (e) => t.track('error', { code: e.code, message: e.message }))\n );\n\n // auth_signin_success / auth_signout пока не фаерим: authChange эмитится\n // и на гидрации сессии (popup поднимает кеш из offscreen), и на token\n // refresh, и при параллельном content-script + popup — даёт ложные\n // signin'ы. Реальные login-события нужно ловить через прямые вызовы\n // signInWithEmail/signUp/signInWithOAuth/signOut, а не через authChange.\n }\n\n /** Прокси через RemoteEventTracker. Hosts могут вызывать paywall.track\n * для произвольных аналитических событий — летит в единственный\n * offscreen-tracker наряду с auto-emit'ами PaywallUI. */\n track(name: string, props?: Record<string, unknown>): void {\n this.remoteTracker?.track(name, props);\n }\n\n destroy(): void {\n for (const fn of this.trackerUnsubs) fn();\n this.trackerUnsubs = [];\n this.remoteTracker = null;\n super.destroy();\n }\n}\n"],"names":["twPropertiesRegistered","ensureTwPropertiesRegistered","rules","sheet","cssText","rule","r","mountShadow","Component","props","options","host","shadow","hostReset","style","mountPoint","currentProps","render","h","nextProps","FOCUSABLE","Modal","open","onClose","labelledBy","brandColor","testMode","allowClose","children","dialogRef","useRef","previouslyFocused","useEffect","dialog","onKey","e","focusables","el","first","last","active","prevOverflow","jsxs","jsx","PROVIDER_LABEL","AuthPanel","block","ctx","auth","session","allowSignup","allowReset","hideWhenAuthed","realSession","SignedIn","AuthForm","email","onSignOut","providers","mode","setMode","useState","setEmail","password","setPassword","otpCode","setOtpCode","busy","setBusy","error","setError","info","setInfo","onSubmit","msg","PaywallError","onOAuth","provider","p","ProviderIcon","Divider","Field","Fragment","submitLabel","switchMode","next","type","label","value","onInput","autocomplete","inputMode","required","AuthGate","bootstrap","authSession","onBack","showBack","AnonGate","onSuccess","heading","description","phase","setPhase","aliveRef","run","Spinner","SUBJECT_MIN","SUBJECT_MAX","CONTENT_MAX","MAX_FILES","MAX_FILE_SIZE","ACCEPTED_MIME","EMAIL_RE","SupportGate","client","origin","sessionEmail","lockedEmail","subject","setSubject","message","setMessage","files","setFiles","submitting","setSubmitting","submittedEmail","setSubmittedEmail","errors","setErrors","isValid","useMemo","s","m","validate","prev","finalEmail","err","resetForm","TextareaField","Dropzone","onChange","disabled","inputRef","dragOver","setDragOver","handleFiles","incoming","arr","valid","f","i","CtaButton","priceId","CurrentSession","signingOut","setSigningOut","onSupport","Dot","SupportLink","onClick","FeaturesList","item","BASE_FONT_PX","MIN_FONT_PX","MAX_LINES","fitHeading","lineHeight","maxHeight","size","Heading","level","Tag","className","ref","autoFit","cs","lh","formatPrice","price","display","intervalLabel","n","PriceGrid","filter","prices","horizontal","popularLabel","cols","selected","isPopular","Text","INTERVAL_MULTIPLIER","intervalNoun","interval","TokenizationGate","multiplier","q","rawCount","amount","blockRegistry","Renderer","layout","onAction","defaultPriceId","selectedPriceId","setSelectedPriceId","Cmp","computePaywallSnapshot","state","gate","purchased","sameSnapshot","a","b","PaywallRoot","onEvent","initialView","renew","onState","setState","setAuthSession","setGate","resumingRef","lastSnapshotRef","data","cancelled","runCheckout","result","popup","reopenCheckout","url","pending","handleAction","action","payload","brand","gateBlock","supportView","PurchaseSuccessView","AwaitingPaymentView","onReopen","onRetry","checking","setChecking","stillPending","setStillPending","stillPendingTimerRef","onContinue","restored","DEFAULT_TIMEOUT_MS","DEFAULT_VISIBLE_INTERVAL_MS","DEFAULT_HIDDEN_INTERVAL_MS","UserWatcher","opts","user","shouldRunUserWatcher","CLOSED_STATE","URL_MARKERS","PaywallUI$1","ownsAuth","resolveAuth","BillingClient","analytics","cfg","endpoint","EventTracker","v","name","handler","partial","event","set","args","view","skipTrial","skipVisibility","cached","flags","trialCfg","store","status","updated","config","sameTrialConfig","factoryFn","createTrialStore","mountOpts","snapshot","sameStateSnapshot","cb","visibility","trial","redirect","hashMarkers","parseMarkers","searchMarkers","markers","notifyOpenerOfPurchase","stripMarkersFromUrl","AuthClient","isAuthClientLike","segment","params","clean","raw","prefix","out","RemoteTrialStore","transport","paywallId","RemoteBillingClient","key","balances","signal","identity","sameUser","sameBalances","RemoteAuthClient","input","tempName","injectLoaderUI","authorizeUrl","code","waitForOAuthCode","sameSession","PROVIDER_NAMES","doc","wrap","spinner","RemoteEventTracker","TransportClient","factory","channel","offMsg","env","offDisc","PROTOCOL_VERSION","res","envelope","isEnvelope","reconstructError","fn","TransportDisconnectedError","kind","id","resolve","reject","wrapped","t","getContentTransport","createRuntimeChannel","PORT_NAME","PaywallUI","BasePaywallUI","billing"],"mappings":"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"}
|