@orangecheck/auth-client 2.34.0 → 2.35.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/provider.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport {\n buildAddAccountUrl,\n buildSignInUrl,\n DEFAULT_CONFIG,\n DISPLAY_IDENTITY_KINDS,\n resolveConfig,\n type DisplayIdentity,\n type DisplayIdentityKind,\n type OcAccount,\n type OcAccountSummary,\n type OcAuthConfig,\n type OcSessionState,\n type OcSignOutScope,\n} from './types';\nimport { keepFragmentOnReturnLinks, restoreReturnFragment } from './return-to';\nimport {\n clearTabSession,\n consumeTabAccountHint,\n consumeTabAdoptMarker,\n installTabFetchInterceptor,\n installTabLinkDecorator,\n readTabSession,\n tabSessionHeader,\n writeTabSession,\n} from './tab-session';\n\nconst SessionContext = React.createContext<OcSessionState | null>(null);\n\ntype RawSigningMethod = 'fedimint_threshold' | 'fedimint_client' | 'bip322';\n\ninterface RawRosterEntry {\n did_oc?: string;\n didOc?: string;\n display_name?: string | null;\n displayName?: string | null;\n primary_btc?: string | null;\n primaryBtc?: string | null;\n display_identity?: { kind?: string; value?: string } | null;\n displayIdentity?: { kind?: string; value?: string } | null;\n last_seen_at?: string | null;\n lastSeenAt?: string | null;\n}\n\ninterface MeResponse {\n account?: {\n id?: string;\n account_id?: string;\n accountId?: string;\n did_oc?: string;\n didOc?: string;\n primary_btc?: string | null;\n primaryBtc?: string | null;\n has_email?: boolean;\n hasEmail?: boolean;\n display_name?: string | null;\n displayName?: string | null;\n nostr_npub?: string | null;\n nostrNpub?: string | null;\n home_federation_slug?: string | null;\n homeFederation?: string | null;\n signing_method?: RawSigningMethod | null;\n signingMethod?: RawSigningMethod | null;\n is_owner?: boolean;\n isOwner?: boolean;\n display_identity?: { kind?: string; value?: string } | null;\n displayIdentity?: { kind?: string; value?: string } | null;\n };\n /** Multi-account roster · other accounts in this browser. Absent\n * on hosts that haven't deployed the multi-account migration yet\n * — treated as `[]` (single-account fallback). */\n roster?: RawRosterEntry[];\n}\n\n/**\n * Resolve the badge identity from a `/api/auth/me` account payload.\n * Total: returns the carried `{kind,value}` when well-formed, else\n * `{kind:'did',value:didOc}` — so accounts that never promoted, and\n * sessions minted before the field shipped, degrade cleanly.\n */\nfunction normalizeDisplayIdentity(\n raw: NonNullable<MeResponse['account']>,\n didOc: string\n): DisplayIdentity {\n const di = raw.display_identity ?? raw.displayIdentity;\n if (\n di &&\n typeof di === 'object' &&\n typeof di.value === 'string' &&\n di.value.length > 0 &&\n typeof di.kind === 'string' &&\n (DISPLAY_IDENTITY_KINDS as readonly string[]).includes(di.kind)\n ) {\n return { kind: di.kind as DisplayIdentityKind, value: di.value };\n }\n return { kind: 'did', value: didOc };\n}\n\n/**\n * Multi-account · normalize one roster entry from /api/auth/me. Drops\n * entries with no did_oc (defensive — the host shouldn't emit those).\n */\nfunction normalizeRosterEntry(raw: RawRosterEntry): OcAccountSummary | null {\n const didOc = raw.did_oc ?? raw.didOc;\n if (!didOc) return null;\n const di = raw.display_identity ?? raw.displayIdentity;\n const displayIdentity: DisplayIdentity =\n di &&\n typeof di === 'object' &&\n typeof di.value === 'string' &&\n di.value.length > 0 &&\n typeof di.kind === 'string' &&\n (DISPLAY_IDENTITY_KINDS as readonly string[]).includes(di.kind)\n ? { kind: di.kind as DisplayIdentityKind, value: di.value }\n : { kind: 'did', value: didOc };\n return {\n didOc,\n displayName: raw.display_name ?? raw.displayName ?? null,\n primaryBtc: raw.primary_btc ?? raw.primaryBtc ?? null,\n displayIdentity,\n lastSeenAt: raw.last_seen_at ?? raw.lastSeenAt ?? null,\n };\n}\n\n/**\n * Multi-account · fetch the roster from the AUTH HOST cross-origin.\n *\n * Consumer subdomains expose a LOCAL `/api/auth/me` that verifies the JWT\n * but has no database, so it never carries the `roster` (which is host DB\n * state). When the local /me comes back authenticated with an empty roster,\n * we source it from the host directly — the same credentialed cross-origin\n * pattern this provider already uses for logout + account PATCH, and the\n * host already serves `.ochk.io` consumers via CORS.\n *\n * Best-effort by construction: any failure resolves to `[]`, so a roster\n * fetch can never change the authenticated state derived from the local /me.\n */\n/** How long a session check may take before the page stops waiting on it. */\nexport const ME_TIMEOUT_MS = 10_000;\n\n/** `AbortSignal.timeout` where the browser has it, a timer where it does not. */\nfunction timeoutSignal(ms: number): AbortSignal {\n if (typeof AbortSignal.timeout === 'function') return AbortSignal.timeout(ms);\n const controller = new AbortController();\n setTimeout(() => controller.abort(), ms);\n return controller.signal;\n}\n\nfunction isTimeout(err: unknown): boolean {\n const name = (err as { name?: unknown } | null)?.name;\n return name === 'TimeoutError' || name === 'AbortError';\n}\n\nasync function fetchHostRoster(cfg: Required<OcAuthConfig>): Promise<OcAccountSummary[]> {\n try {\n // Per-tab · carry the pin so the host computes \"peers\" relative\n // to THIS tab's effective account, not the cookie default.\n const res = await fetch(`${cfg.authOrigin}${cfg.mePath}`, {\n method: 'GET',\n credentials: 'include',\n headers: { Accept: 'application/json', ...tabSessionHeader() },\n signal: timeoutSignal(ME_TIMEOUT_MS),\n });\n if (!res.ok) return [];\n const body = (await res.json()) as MeResponse;\n return Array.isArray(body.roster)\n ? body.roster\n .map(normalizeRosterEntry)\n .filter((r): r is OcAccountSummary => r !== null)\n : [];\n } catch {\n return [];\n }\n}\n\nfunction normalizeAccount(raw: MeResponse['account']): OcAccount | null {\n if (!raw) return null;\n const didOc = raw.did_oc ?? raw.didOc;\n const accountId = raw.id ?? raw.account_id ?? raw.accountId;\n if (!didOc || !accountId) return null;\n return {\n accountId,\n didOc,\n primaryBtc: raw.primary_btc ?? raw.primaryBtc ?? null,\n hasEmail: raw.has_email ?? raw.hasEmail ?? false,\n displayName: raw.display_name ?? raw.displayName ?? null,\n nostrNpub: raw.nostr_npub ?? raw.nostrNpub ?? null,\n homeFederation: raw.home_federation_slug ?? raw.homeFederation ?? null,\n signingMethod: raw.signing_method ?? raw.signingMethod ?? null,\n isOwner: Boolean(raw.is_owner ?? raw.isOwner ?? false),\n displayIdentity: normalizeDisplayIdentity(raw, didOc),\n };\n}\n\nexport interface OcSessionProviderProps {\n children: React.ReactNode;\n config?: OcAuthConfig;\n /**\n * Optional return URL passed to the sign-in page. Defaults to the\n * current page. Its fragment is never sent: it is kept in this tab and\n * restored on return (./return-to).\n */\n defaultReturnTo?: string;\n}\n\n/**\n * Top-level provider that exposes the cross-subdomain oc_session to every\n * component below it. Mount once, near the root of your tree.\n */\nexport function OcSessionProvider({\n children,\n config,\n defaultReturnTo,\n}: OcSessionProviderProps): React.ReactElement {\n const cfg = React.useMemo(() => resolveConfig(config), [config]);\n // Back from a sign-in or a check: put back the fragment its return\n // address left in this tab, before any page below reads the address.\n // The adoption marker the auth host appends goes first, so the fragment\n // takes its place. Children's effects run before this provider's, so\n // this happens during the first render, once; both calls are idempotent.\n React.useState(() => {\n if (typeof window !== 'undefined') {\n consumeTabAdoptMarker();\n restoreReturnFragment();\n }\n return null;\n });\n const [account, setAccount] = React.useState<OcAccount | null>(null);\n const [roster, setRoster] = React.useState<OcAccountSummary[]>([]);\n const [status, setStatus] = React.useState<OcSessionState['status']>('loading');\n const [error, setError] = React.useState<Error | null>(null);\n const [tabPinned, setTabPinned] = React.useState(false);\n\n // Per-tab · mint a pin for this tab via the host. Called once per\n // tab on first authenticated resolve, so a later switch in ANOTHER\n // tab can't yank this one — it keeps presenting its own token.\n // Best-effort: a host that predates `/api/auth/tab` (or a network\n // failure) leaves the tab unpinned, which is exactly the legacy\n // cookie-following behavior.\n const pinInFlightRef = React.useRef(false);\n const pinThisTab = React.useCallback(\n async (didOc: string) => {\n if (!cfg.multiAccount || pinInFlightRef.current || readTabSession()) return;\n pinInFlightRef.current = true;\n try {\n const res = await fetch(`${cfg.authOrigin}/api/auth/tab`, {\n method: 'POST',\n credentials: 'include',\n headers: { Accept: 'application/json' },\n });\n if (!res.ok) return;\n const body = (await res.json()) as {\n ok?: boolean;\n token?: string;\n account?: { did_oc?: string };\n };\n // Only pin when the host minted for the account this tab\n // just resolved — a cookie flip racing between our /me and\n // /tab calls must not pin the WRONG account.\n if (\n body.ok &&\n typeof body.token === 'string' &&\n body.account?.did_oc === didOc\n ) {\n writeTabSession({ token: body.token, didOc });\n setTabPinned(true);\n }\n } catch {\n // best-effort by construction\n } finally {\n pinInFlightRef.current = false;\n }\n },\n [cfg.authOrigin, cfg.multiAccount]\n );\n\n const refresh = React.useCallback(async () => {\n if (typeof window === 'undefined') return;\n try {\n let pin = readTabSession();\n // A check that never answers would leave the page loading for good;\n // past the timeout it is an error, which every site already shows.\n let res = await fetch(cfg.mePath, {\n method: 'GET',\n credentials: 'include',\n headers: { Accept: 'application/json', ...tabSessionHeader() },\n signal: timeoutSignal(ME_TIMEOUT_MS),\n });\n // Per-tab · 401 with a pin present means the pinned token is\n // dead (expired, or revoked and rejected by the host). Drop\n // the pin and retry once as the browser-default account.\n if (res.status === 401 && pin) {\n clearTabSession();\n pin = null;\n setTabPinned(false);\n res = await fetch(cfg.mePath, {\n method: 'GET',\n credentials: 'include',\n headers: { Accept: 'application/json' },\n signal: timeoutSignal(ME_TIMEOUT_MS),\n });\n }\n if (res.status === 401) {\n setAccount(null);\n setRoster([]);\n setStatus('anonymous');\n setError(null);\n return;\n }\n if (!res.ok) {\n setStatus('error');\n setError(new Error(`me endpoint returned ${res.status}`));\n return;\n }\n const body = (await res.json()) as MeResponse;\n const acct = normalizeAccount(body.account);\n // Per-tab reconciliation · the server answered as a different\n // account than the pin → it doesn't honor `x-oc-tab-session`\n // yet (pre-migration deploy). Drop the pin: the UI must never\n // display an account the server isn't acting as.\n if (acct && pin && acct.didOc !== pin.didOc) {\n clearTabSession();\n pin = null;\n }\n setTabPinned(pin !== null);\n const rosterEntries = Array.isArray(body.roster)\n ? body.roster\n .map(normalizeRosterEntry)\n .filter((r): r is OcAccountSummary => r !== null)\n : [];\n setAccount(acct);\n setRoster(rosterEntries);\n setStatus(acct ? 'authenticated' : 'anonymous');\n setError(null);\n\n // Per-tab · authenticated but unpinned → pin this tab to the\n // account it just resolved (fire-and-forget).\n if (acct && !pin) void pinThisTab(acct.didOc);\n\n // Multi-account · the local /me carried no roster (the standard\n // case on consumer subdomains, whose local /me has no DB). When\n // we're authenticated and NOT already on the auth host, source\n // the roster from the host so the §accounts switcher lights up.\n // Best-effort: never touches account/status on failure.\n const currentOrigin =\n typeof window !== 'undefined' ? window.location.origin : null;\n if (\n cfg.multiAccount &&\n acct &&\n rosterEntries.length === 0 &&\n currentOrigin !== null &&\n !cfg.authOrigin.startsWith(currentOrigin)\n ) {\n const peers = await fetchHostRoster(cfg);\n if (peers.length > 0) setRoster(peers);\n }\n } catch (err) {\n setStatus('error');\n setError(\n isTimeout(err)\n ? new Error(`me endpoint did not answer within ${ME_TIMEOUT_MS / 1000}s`)\n : err instanceof Error\n ? err\n : new Error(String(err))\n );\n }\n }, [cfg, pinThisTab]);\n\n React.useEffect(() => {\n let cancelled = false;\n // Post-ceremony adoption · the auth host appends `#oc-adopt` when\n // returning from a sign-in/add ceremony: the user expects THIS tab\n // to become the account they just authenticated, so a stale pin\n // must not survive the round trip.\n consumeTabAdoptMarker();\n void (async () => {\n // New-tab inheritance · if the opener stamped `#oc-as=<did>`,\n // adopt that account BEFORE the first /me fetch so this tab\n // resolves as the opener's account, not the cookie default.\n if (cfg.multiAccount) await consumeTabAccountHint(cfg.authOrigin);\n if (!cancelled) void refresh();\n })();\n return () => {\n cancelled = true;\n };\n }, [refresh, cfg.authOrigin, cfg.multiAccount]);\n\n // A link that brings the browser back here keeps this page's fragment\n // in the tab, since its return_to cannot carry it.\n React.useEffect(() => keepFragmentOnReturnLinks(), []);\n\n // Per-tab · attach the pin to every same-site fetch so app-level data\n // calls execute as the account this tab displays. Scoped + reversible;\n // see installTabFetchInterceptor for the conservative rules.\n React.useEffect(\n () => (cfg.multiAccount ? installTabFetchInterceptor(cfg.authOrigin) : undefined),\n [cfg.authOrigin, cfg.multiAccount]\n );\n\n // Per-tab · stamp this tab's effective account onto outgoing\n // family-origin links so a new/cross-subdomain tab inherits it\n // instead of the shared cookie default. See installTabLinkDecorator.\n React.useEffect(\n () => (cfg.multiAccount ? installTabLinkDecorator(cfg.authOrigin) : undefined),\n [cfg.authOrigin, cfg.multiAccount]\n );\n\n const signOut = React.useCallback(\n async (opts?: { scope?: OcSignOutScope }) => {\n const scope: OcSignOutScope = opts?.scope ?? 'all';\n try {\n const url = new URL(`${cfg.authOrigin}${cfg.logoutPath}`);\n if (scope === 'current') url.searchParams.set('scope', 'current');\n const res = await fetch(url.toString(), {\n method: 'POST',\n credentials: 'include',\n // Per-tab · carry the pin so scope='current' signs out\n // the account THIS tab is operating as, not whichever\n // account the shared cookie happens to point at.\n headers: { ...tabSessionHeader() },\n // `keepalive` lets the logout round-trip complete even if\n // the caller hard-navigates away in the same tick (e.g.\n // `<OcAccountMenu>` redirects home immediately on sign-out).\n // Without it the in-flight request is cancelled on unload\n // and the `.ochk.io` cookie may never get cleared.\n keepalive: true,\n });\n // The signed-out account's token is dead either way — the\n // pin must not outlive it.\n clearTabSession();\n setTabPinned(false);\n // Multi-account: with scope='current', the server may have\n // switched the active session to a roster peer instead of\n // clearing the cookie. Re-fetch /api/auth/me so this\n // provider reflects the new active account; if the body\n // says `switched_to !== null`, refresh resolves to the\n // peer, otherwise it resolves to anonymous and the\n // setState below is redundant-but-harmless.\n if (scope === 'current' && res.ok) {\n await refresh();\n return;\n }\n } catch {\n // fall through — we still clear local state so the UI reflects\n // the user's intent even if the server round-trip fails.\n clearTabSession();\n setTabPinned(false);\n }\n setAccount(null);\n setRoster([]);\n setStatus('anonymous');\n },\n [cfg.authOrigin, cfg.logoutPath, refresh]\n );\n\n const switchAccount = React.useCallback(\n async (didOc: string) => {\n if (typeof window === 'undefined') return;\n const res = await fetch(`${cfg.authOrigin}/api/auth/switch`, {\n method: 'POST',\n credentials: 'include',\n headers: { 'Content-Type': 'application/json', ...tabSessionHeader() },\n body: JSON.stringify({ did_oc: didOc }),\n });\n if (!res.ok) {\n let reason = `http_${res.status}`;\n try {\n const body = (await res.json()) as { reason?: string };\n if (body.reason) reason = body.reason;\n } catch {\n // keep http_ fallback\n }\n throw new Error(`[@orangecheck/auth-client] switchAccount failed: ${reason}`);\n }\n // Per-tab · pin THIS tab to the switch target using the fresh\n // JWT the host echoes in the body. The host has also flipped\n // the shared cookie — the default for FUTURE tabs — but other\n // open tabs keep their own pins and are not yanked. Pin to the\n // did the SERVER minted for (merge-graph collapse may differ\n // from the requested did).\n try {\n const body = (await res.json()) as {\n token?: string;\n account?: { did_oc?: string };\n };\n if (typeof body.token === 'string' && body.account?.did_oc) {\n writeTabSession({ token: body.token, didOc: body.account.did_oc });\n setTabPinned(true);\n }\n } catch {\n // body unreadable — cookie still flipped; refresh resolves it\n }\n // Re-fetch /me to surface the new active account + freshly\n // computed roster (the previous active account is now a peer,\n // swap is symmetric).\n await refresh();\n },\n [cfg.authOrigin, refresh]\n );\n\n const addAccountUrl = React.useCallback(\n (returnTo?: string) => {\n const rt =\n returnTo ?? (typeof window !== 'undefined' ? window.location.href : undefined);\n return buildAddAccountUrl(cfg, rt);\n },\n [cfg]\n );\n\n const setDisplayIdentity = React.useCallback(\n async (kind: DisplayIdentityKind) => {\n if (typeof window === 'undefined') return;\n // PATCH the auth host directly (family-CORS). It writes the\n // pref, re-mints the `.ochk.io` session cookie with the new\n // `display_identity` claim, and Set-Cookies it back — so the\n // choice propagates to every subdomain. Then refresh so this\n // session reflects it immediately.\n const res = await fetch(`${cfg.authOrigin}/api/auth/account`, {\n method: 'PATCH',\n credentials: 'include',\n headers: { 'Content-Type': 'application/json', ...tabSessionHeader() },\n body: JSON.stringify({ display_identity: kind }),\n });\n if (!res.ok) {\n let reason = `http_${res.status}`;\n try {\n const body = (await res.json()) as { reason?: string };\n if (body.reason) reason = body.reason;\n } catch {\n // keep the http_ fallback\n }\n throw new Error(`[@orangecheck/auth-client] setDisplayIdentity failed: ${reason}`);\n }\n // Per-tab · the PATCH re-mints; when this tab is pinned the\n // host echoes the fresh token in the body (it only rewrites\n // the shared cookie when the cookie account was the actor).\n // Re-pin so the tab carries the updated claims.\n try {\n const body = (await res.json()) as {\n token?: string;\n account?: { did_oc?: string };\n };\n const pin = readTabSession();\n if (\n pin &&\n typeof body.token === 'string' &&\n body.account?.did_oc === pin.didOc\n ) {\n writeTabSession({ token: body.token, didOc: pin.didOc });\n }\n } catch {\n // body unreadable — refresh below still reconciles state\n }\n await refresh();\n },\n [cfg.authOrigin, refresh]\n );\n\n const value = React.useMemo<OcSessionState>(() => {\n const returnTo =\n defaultReturnTo ?? (typeof window !== 'undefined' ? window.location.href : undefined);\n return {\n status,\n account,\n roster,\n tabPinned,\n error,\n refresh,\n signOut,\n switchAccount,\n addAccountUrl,\n setDisplayIdentity,\n signInUrl: buildSignInUrl(cfg, returnTo),\n };\n }, [\n status,\n account,\n roster,\n tabPinned,\n error,\n refresh,\n signOut,\n switchAccount,\n addAccountUrl,\n setDisplayIdentity,\n cfg,\n defaultReturnTo,\n ]);\n\n return <SessionContext.Provider value={value}>{children}</SessionContext.Provider>;\n}\n\n/**\n * Access the current cross-subdomain oc_session. Must be called inside\n * an `<OcSessionProvider>`.\n */\nexport function useOcSession(): OcSessionState {\n const ctx = React.useContext(SessionContext);\n if (!ctx) {\n throw new Error(\n '[@orangecheck/auth-client] useOcSession() must be called inside <OcSessionProvider>'\n );\n }\n return ctx;\n}\n\n/**\n * Non-throwing variant — returns `null` if called outside a provider.\n * Useful for libraries that want to read the session *if it exists* but\n * shouldn't crash on apps that haven't opted in.\n */\nexport function useOptionalOcSession(): OcSessionState | null {\n return React.useContext(SessionContext);\n}\n\nexport { DEFAULT_CONFIG };\n"],"mappings":"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","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  useOcSession
3
- } from "./chunk-7ST4Q5KR.mjs";
3
+ } from "./chunk-E2XQ7YED.mjs";
4
4
 
5
5
  // src/components.tsx
6
6
  import * as React from "react";
@@ -649,4 +649,4 @@ export {
649
649
  useOcAddressSuggestion,
650
650
  OcAddressInput
651
651
  };
652
- //# sourceMappingURL=chunk-7UYPMTAV.mjs.map
652
+ //# sourceMappingURL=chunk-EZKAMTG7.mjs.map
@@ -11,7 +11,7 @@ import {
11
11
  inputStyle,
12
12
  panelStyle,
13
13
  primaryBtnStyle
14
- } from "./chunk-D3HXJ64H.mjs";
14
+ } from "./chunk-UFRU3I3D.mjs";
15
15
  import {
16
16
  SIGNIN_FONT
17
17
  } from "./chunk-H2TXOCXH.mjs";
@@ -487,4 +487,4 @@ export {
487
487
  fetchOcBond,
488
488
  OcBond
489
489
  };
490
- //# sourceMappingURL=chunk-FDWEXEZL.mjs.map
490
+ //# sourceMappingURL=chunk-HZ4FJMF3.mjs.map
@@ -847,7 +847,6 @@ var inputStyle = {
847
847
  fontFamily: SIGNIN_FONT,
848
848
  // 16px — never lower · suppresses iOS Safari focus auto-zoom.
849
849
  fontSize: 16,
850
- outline: "none",
851
850
  boxSizing: "border-box"
852
851
  };
853
852
  function btnBase(disabled) {
@@ -911,4 +910,4 @@ export {
911
910
  ghostBtnStyle,
912
911
  dangerBtnStyle
913
912
  };
914
- //# sourceMappingURL=chunk-D3HXJ64H.mjs.map
913
+ //# sourceMappingURL=chunk-UFRU3I3D.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/linked-identities.tsx"],"sourcesContent":["'use client';\n\n/**\n * @orangecheck/auth-client · OcLinkedIdentities\n *\n * The shared linked-identities management surface for every `X.ochk.io`\n * consumer. A signed-in user sees their linked email + Bitcoin address,\n * can reveal/hide each raw value, unlink a non-primary identity, and\n * link a new one. Linking a Bitcoin address that's already on another\n * OC account offers a dual-proof transfer; the same is now true for\n * email (auth host F4). Consolidated (`merged_from`) accounts render in\n * a tombstone panel.\n *\n * This generalizes me.ochk.io's bespoke `LinkedIdentitiesPanel` so\n * linking is no longer a single-product, one-tab feature — any family\n * site can drop in `<OcLinkedIdentities />`. See AUTH-PLAN.md §5.\n *\n * All endpoints live on the auth host (`ochk.io`); calls are\n * cross-origin with `credentials: 'include'` and rely on the auth\n * host's family-CORS — the same posture `OcSignIn` uses. No local\n * proxy route is needed (AUTH-AUDIT.md F6: direct-to-host is the\n * intended design).\n *\n * Style: inline, CSS-variable-themed (same approach as `OcSignIn`), so\n * the component carries zero CSS dependency and themes to each site.\n * Every block carries a `data-oc-li-*` attribute so a site may\n * override via its own stylesheet.\n */\n\nimport * as React from 'react';\nimport {\n handleSudoRequired,\n isSudoAccountMismatch,\n SUDO_ACCOUNT_MISMATCH_MESSAGE,\n} from './sudo';\n\nimport {\n SIGNIN_BUTTON_RADIUS,\n SIGNIN_CASE,\n SIGNIN_FONT,\n signinRadius,\n signinSize,\n signinTracking,\n} from './appearance';\nimport { tabSessionHeader } from './tab-session';\n\nconst DEFAULT_AUTH_ORIGIN = 'https://ochk.io';\n\n/* --- types --- */\n\nexport interface OcLinkedIdentity {\n id: string;\n kind: 'email' | 'btc';\n /** Raw value · null when the identity is pending recovery. */\n value: string | null;\n verified_at: string | null;\n verification_method: 'email-otp' | 'bip322' | null;\n /** The signup-time identity for its kind · cannot be unlinked here. */\n is_primary: boolean;\n}\n\ninterface MergedFromEntry {\n did_oc: string;\n created_at: string;\n last_signed_in_at: string | null;\n}\n\ninterface IdentitiesResp {\n ok: boolean;\n account_id?: string;\n did_oc?: string | null;\n linked?: OcLinkedIdentity[];\n merged_from?: MergedFromEntry[];\n reason?: string;\n}\n\n/**\n * Fetch the signed-in user's linked identities from the auth host —\n * the same data `<OcLinkedIdentities>` renders, exposed as a plain\n * function for surfaces that need the list without the full\n * management UI (notably `<OcAccountMenu>`'s badge-identity promote\n * list). Returns `[]` when anonymous; throws on network / server\n * error so the caller can show a deferred state.\n */\nexport async function fetchOcLinkedIdentities(opts?: {\n authOrigin?: string;\n}): Promise<OcLinkedIdentity[]> {\n const authOrigin = opts?.authOrigin ?? DEFAULT_AUTH_ORIGIN;\n // Per-tab pinning: forward the tab's session JWT so the host resolves\n // the SAME account this tab displays (its chip reads /api/auth/me\n // tab-aware). Without this, a pinned tab's show-as list is the shared\n // cookie account's identities — a menu that disagrees with itself.\n // Unpinned tabs send no header → unchanged cookie-default behavior.\n const r = await fetch(`${authOrigin}/api/auth/identities`, {\n credentials: 'include',\n headers: { ...tabSessionHeader() },\n });\n if (r.status === 401) return [];\n if (!r.ok) throw new Error(`identities fetch failed: http_${r.status}`);\n const body = (await r.json()) as IdentitiesResp;\n return body.linked ?? [];\n}\n\nexport interface OcLinkedIdentitiesProps {\n /** Auth host origin. Defaults to `https://ochk.io`. */\n authOrigin?: string;\n /** Outer container className. */\n className?: string;\n /** Called after every successful link / unlink / transfer. */\n onChange?: () => void;\n /**\n * Show the \"portable attestation\" card that links out to attest.ochk.io.\n * Defaults to `true`. Pass `false` on a page that already hosts its own\n * attestation builder (ochk.io's dashboard), or the same action is offered\n * twice, one panel apart.\n */\n attestationLink?: boolean;\n}\n\n/* --- main --- */\n\nexport function OcLinkedIdentities({\n authOrigin = DEFAULT_AUTH_ORIGIN,\n className,\n onChange,\n attestationLink = true,\n}: OcLinkedIdentitiesProps): React.ReactElement {\n const [resp, setResp] = React.useState<IdentitiesResp | null>(null);\n const [error, setError] = React.useState<string | null>(null);\n const [showEmailLink, setShowEmailLink] = React.useState(false);\n const [showBtcLink, setShowBtcLink] = React.useState(false);\n\n const refresh = React.useCallback(async () => {\n setError(null);\n try {\n const r = await fetch(`${authOrigin}/api/auth/identities`, {\n credentials: 'include',\n headers: { ...tabSessionHeader() },\n });\n if (!r.ok) {\n if (r.status === 401) {\n setError('not signed in');\n return;\n }\n throw new Error(`http_${r.status}`);\n }\n setResp((await r.json()) as IdentitiesResp);\n } catch (e) {\n setError(e instanceof Error ? e.message : 'load failed');\n }\n }, [authOrigin]);\n\n React.useEffect(() => {\n void refresh();\n }, [refresh]);\n\n const afterChange = React.useCallback(() => {\n setShowEmailLink(false);\n setShowBtcLink(false);\n void refresh();\n onChange?.();\n }, [refresh, onChange]);\n\n if (error) {\n return (\n <div className={className} data-oc-li=\"\" style={panelStyle('warn')} role=\"alert\">\n <SectionLabel tone=\"warn\">§ identities · load deferred</SectionLabel>\n <p style={bodyStyle}>\n Couldn&apos;t reach the auth host ({error}). Linking is temporarily\n unavailable; your signed-in identity still works as normal.\n </p>\n <button type=\"button\" onClick={() => void refresh()} style={ghostBtnStyle(false)}>\n retry\n </button>\n </div>\n );\n }\n\n if (!resp) {\n return (\n <div className={className} data-oc-li=\"\" style={panelStyle()}>\n <span style={hintStyle}>loading…</span>\n </div>\n );\n }\n\n const linked = resp.linked ?? [];\n const emailRows = linked.filter((i) => i.kind === 'email');\n const btcRows = linked.filter((i) => i.kind === 'btc');\n const hasEmail = emailRows.some((i) => i.value);\n const hasBtc = btcRows.some((i) => i.value);\n const mergedFrom = resp.merged_from ?? [];\n\n return (\n <div className={className} data-oc-li=\"\" style={{ display: 'grid', gap: 16 }}>\n <div style={panelStyle()}>\n <SectionLabel>§ what this is</SectionLabel>\n <p style={bodyStyle}>\n These are the ways you can sign in to this account. An app you sign in to\n sees one only if you grant it. Linking a second one is also your way back\n in: lose one, and the other still signs you in.\n </p>\n </div>\n\n {mergedFrom.length > 0 && <ConsolidationPanel entries={mergedFrom} />}\n\n <div style={panelStyle('plain')} data-oc-li-rows=\"\">\n <IdentityGroup\n kind=\"email\"\n title=\"email\"\n rows={emailRows}\n authOrigin={authOrigin}\n onChange={afterChange}\n addCta={hasEmail ? null : 'link an email'}\n onAdd={() => {\n setShowEmailLink((v) => !v);\n setShowBtcLink(false);\n }}\n />\n <div style={{ borderTop: `1px solid ${V.border}` }} />\n <IdentityGroup\n kind=\"btc\"\n title=\"bitcoin address\"\n rows={btcRows}\n authOrigin={authOrigin}\n onChange={afterChange}\n addCta={hasBtc ? null : 'link a Bitcoin address'}\n onAdd={() => {\n setShowBtcLink((v) => !v);\n setShowEmailLink(false);\n }}\n />\n </div>\n\n {showEmailLink && (\n <EmailLinkForm\n authOrigin={authOrigin}\n onDone={afterChange}\n onCancel={() => setShowEmailLink(false)}\n />\n )}\n {showBtcLink && (\n <BtcLinkForm\n authOrigin={authOrigin}\n didOc={resp.did_oc ?? null}\n onDone={afterChange}\n onCancel={() => setShowBtcLink(false)}\n />\n )}\n\n {hasBtc && attestationLink && <AttestationLink />}\n </div>\n );\n}\n\n/* --- identity group (rows for one kind, or an empty-state add CTA) --- */\n\nfunction IdentityGroup({\n kind,\n title,\n rows,\n authOrigin,\n onChange,\n addCta,\n onAdd,\n}: {\n kind: 'email' | 'btc';\n title: string;\n rows: OcLinkedIdentity[];\n authOrigin: string;\n onChange: () => void;\n addCta: string | null;\n onAdd: () => void;\n}): React.ReactElement {\n if (rows.length === 0) {\n return (\n <div style={rowStyle}>\n <div>\n <div style={rowLabelStyle}>§ {title}</div>\n <div style={{ ...bodyStyle, marginTop: 4 }}>none linked</div>\n </div>\n {addCta && (\n <button type=\"button\" onClick={onAdd} style={primaryChipStyle}>\n + {addCta}\n </button>\n )}\n </div>\n );\n }\n return (\n <>\n {rows.map((r) => (\n <IdentityRow\n key={r.id}\n row={r}\n kind={kind}\n authOrigin={authOrigin}\n onChange={onChange}\n />\n ))}\n </>\n );\n}\n\n/* --- single identity row --- */\n\nfunction IdentityRow({\n row,\n kind,\n authOrigin,\n onChange,\n}: {\n row: OcLinkedIdentity;\n kind: 'email' | 'btc';\n authOrigin: string;\n onChange: () => void;\n}): React.ReactElement {\n const [shown, setShown] = React.useState(false);\n const [confirming, setConfirming] = React.useState(false);\n const [busy, setBusy] = React.useState(false);\n const [err, setErr] = React.useState<string | null>(null);\n\n const masked = row.value ? mask(row.value, kind) : '— pending recovery —';\n const display = shown && row.value ? row.value : masked;\n\n async function unlink(): Promise<void> {\n if (row.is_primary) return;\n setBusy(true);\n setErr(null);\n try {\n const r = await fetch(\n `${authOrigin}/api/auth/identities?id=${encodeURIComponent(row.id)}`,\n { method: 'DELETE', credentials: 'include' }\n );\n if (!r.ok) {\n const j = (await r.json().catch(() => ({}))) as { reason?: string };\n throw new Error(j.reason ?? `http_${r.status}`);\n }\n onChange();\n } catch (e) {\n setErr(e instanceof Error ? e.message : 'unlink failed');\n setBusy(false);\n setConfirming(false);\n }\n }\n\n return (\n <div style={rowStyle} data-oc-li-row={kind}>\n <div style={{ minWidth: 0, flex: 1 }}>\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: '4px 12px' }}>\n <span style={rowLabelStyle}>\n § {kind === 'email' ? 'email' : 'bitcoin address'}\n </span>\n {row.is_primary && (\n <span style={{ ...rowLabelStyle, color: V.primary }}>\n primary · signup-time\n </span>\n )}\n </div>\n <div\n style={{\n marginTop: 4,\n color: V.foreground,\n fontFamily: SIGNIN_FONT,\n fontSize: signinSize(12),\n wordBreak: 'break-all',\n }}\n >\n {display}\n </div>\n {row.verified_at && (\n <div style={{ ...hintStyle, marginTop: 4 }}>\n verified · {new Date(row.verified_at).toUTCString()}\n </div>\n )}\n {err && <ErrorLine>{err}</ErrorLine>}\n </div>\n <div style={{ display: 'flex', flexShrink: 0, gap: 8, flexWrap: 'wrap' }}>\n {row.value && (\n <button\n type=\"button\"\n onClick={() => setShown((s) => !s)}\n style={ghostBtnStyle(false)}\n >\n {shown ? 'hide' : 'show'}\n </button>\n )}\n {!row.is_primary && !confirming && (\n <button\n type=\"button\"\n onClick={() => setConfirming(true)}\n style={dangerBtnStyle(false)}\n >\n unlink\n </button>\n )}\n {!row.is_primary && confirming && (\n <>\n <button\n type=\"button\"\n onClick={() => void unlink()}\n disabled={busy}\n style={dangerBtnStyle(busy)}\n >\n {busy ? 'unlinking…' : 'confirm unlink'}\n </button>\n <button\n type=\"button\"\n onClick={() => setConfirming(false)}\n disabled={busy}\n style={ghostBtnStyle(busy)}\n >\n cancel\n </button>\n </>\n )}\n </div>\n </div>\n );\n}\n\n/* --- consolidated-accounts tombstone panel --- */\n\nfunction ConsolidationPanel({ entries }: { entries: MergedFromEntry[] }): React.ReactElement {\n return (\n <div style={panelStyle('success')} data-oc-li-consolidated=\"\">\n <SectionLabel tone=\"success\">\n § consolidated accounts · {entries.length}\n </SectionLabel>\n <p style={bodyStyle}>\n {entries.length === 1 ? 'A previous OC account' : 'Previous OC accounts'} you\n signed up with, folded into this one under dual proof of control. Historical\n activity is unioned into this account automatically;{' '}\n {entries.length === 1 ? 'it' : 'they'} can no longer sign in.\n </p>\n <ul style={{ listStyle: 'none', margin: '12px 0 0', padding: 0 }}>\n {entries.map((e) => (\n <li\n key={e.did_oc}\n style={{\n display: 'flex',\n flexWrap: 'wrap',\n gap: 8,\n padding: '8px 0',\n borderTop: `1px solid ${V.border}`,\n fontFamily: SIGNIN_FONT,\n fontSize: signinSize(11),\n }}\n >\n <code style={{ color: V.foreground, wordBreak: 'break-all' }}>\n {e.did_oc}\n </code>\n <span style={{ ...hintStyle, marginLeft: 'auto' }}>\n created {new Date(e.created_at).toLocaleDateString()}\n </span>\n </li>\n ))}\n </ul>\n </div>\n );\n}\n\n/* --- portable-attestation pointer --- */\n\n/**\n * A thin pointer to the OC Attest builder. The portable, self-sovereign\n * form of identity linking is a standard OrangeCheck attestation — one\n * BIP-322 signature by the Bitcoin address binding it to its handles,\n * published to Nostr. That ceremony already lives at\n * `attest.ochk.io/create` (which pre-fills the address from the signed-in\n * session); this panel routes the user there rather than re-implementing\n * it. Shown once the account has a linked Bitcoin address to attest with.\n */\nfunction AttestationLink(): React.ReactElement {\n return (\n <div style={panelStyle()} data-oc-li-attest=\"\">\n <SectionLabel>§ portable attestation</SectionLabel>\n <p style={bodyStyle}>\n Make your identity links portable: publish a standard OrangeCheck\n attestation — one Bitcoin-wallet signature (BIP-322) binding your address\n to your handles (Nostr, GitHub, …). It is published to Nostr and verifies\n with zero trust in OrangeCheck, so it outlives any single service.\n </p>\n <a\n href=\"https://attest.ochk.io/create\"\n target=\"_blank\"\n rel=\"noreferrer\"\n style={{ ...primaryChipStyle, textDecoration: 'none', display: 'inline-block' }}\n data-oc-li-attest-link=\"\"\n >\n open the attestation builder ↗\n </a>\n </div>\n );\n}\n\n/* --- post-sign-in link prompt (used by OcSignIn) --- */\n\n/** Where the ways to sign in are managed: ochk.app, which replaces me.ochk.io's pages. */\nconst ACCOUNT_WAYS_URL = 'https://ochk.app/me/account#ways';\n\n/**\n * Shown by `<OcSignIn>` after a successful sign-in *when the user ticked\n * the \"also link my other identity\" checkbox on the sign-in form*. It runs\n * the complementary identity's link ceremony inline — BIP-322 for a\n * Bitcoin address, OTP for an email.\n *\n * Skipping is first-class: even though the user opted in at the form, they\n * may change their mind here — an explicit \"skip, I'll do this later\"\n * control exits cleanly and tells them where to link later. `onResolved`\n * (linked, or skipped) hands control back to OcSignIn's post-sign-in\n * navigation.\n */\nexport function LinkPromptStep({\n method,\n didOc,\n authOrigin,\n onResolved,\n}: {\n /** The complementary identity to link — the opposite of the sign-in method. */\n method: 'btc' | 'email';\n didOc: string;\n authOrigin: string;\n /** Called once the user has linked the identity, or skipped. */\n onResolved: () => void;\n}): React.ReactElement {\n const what = method === 'btc' ? 'a Bitcoin address' : 'an email';\n return (\n <div data-oc-linkprompt={method}>\n <div style={{ ...panelStyle('success'), marginBottom: 16 }}>\n <SectionLabel tone=\"success\">§ signed in</SectionLabel>\n <p style={{ ...bodyStyle, margin: 0 }}>\n You chose to also link {what}. Complete it below — or skip for now; it is\n entirely optional and changes nothing about the sign-in you just completed.\n </p>\n </div>\n {method === 'btc' ? (\n <BtcLinkForm\n authOrigin={authOrigin}\n didOc={didOc}\n onDone={onResolved}\n onCancel={onResolved}\n />\n ) : (\n <EmailLinkForm authOrigin={authOrigin} onDone={onResolved} onCancel={onResolved} />\n )}\n <div\n data-oc-linkprompt-skip=\"\"\n style={{ marginTop: 14, paddingTop: 14, borderTop: `1px solid ${V.border}` }}\n >\n <button type=\"button\" onClick={onResolved} style={ghostBtnStyle(false)}>\n skip — I&apos;ll do this later\n </button>\n <p style={{ ...hintStyle, marginTop: 8 }}>\n Changed your mind? Skipping continues you straight on — your sign-in is\n unaffected. You can link {what} anytime later from your account page,{' '}\n <a\n href={ACCOUNT_WAYS_URL}\n target=\"_blank\"\n rel=\"noreferrer\"\n style={{ color: 'inherit' }}\n data-oc-linkprompt-account=\"\"\n >\n ochk.app/me/account\n </a>\n .\n </p>\n </div>\n </div>\n );\n}\n\n/* --- email link form --- */\n\nfunction EmailLinkForm({\n authOrigin,\n onDone,\n onCancel,\n}: {\n authOrigin: string;\n onDone: () => void;\n onCancel: () => void;\n}): React.ReactElement {\n const [step, setStep] = React.useState<'email' | 'code'>('email');\n const [email, setEmail] = React.useState('');\n const [code, setCode] = React.useState('');\n const [token, setToken] = React.useState('');\n const [busy, setBusy] = React.useState(false);\n const [err, setErr] = React.useState<string | null>(null);\n // Set when the auth host reports the email is linked elsewhere — the\n // user can confirm a dual-proof transfer (session + verified OTP).\n const [transfer, setTransfer] = React.useState(false);\n\n async function start(): Promise<void> {\n setBusy(true);\n setErr(null);\n try {\n const r = await fetch(`${authOrigin}/api/auth/email-otp/start`, {\n method: 'POST',\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ email: email.trim().toLowerCase() }),\n });\n const j = (await r.json()) as { ok?: boolean; token?: string; reason?: string };\n if (!r.ok || !j.token) throw new Error(j.reason ?? `http_${r.status}`);\n setToken(j.token);\n setStep('code');\n } catch (e) {\n setErr(e instanceof Error ? e.message : 'could not send code');\n } finally {\n setBusy(false);\n }\n }\n\n async function verify(confirmTransfer: boolean): Promise<void> {\n setBusy(true);\n setErr(null);\n try {\n const r = await fetch(`${authOrigin}/api/auth/link/email`, {\n method: 'POST',\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n email: email.trim().toLowerCase(),\n code: code.trim(),\n token,\n ...(confirmTransfer ? { confirm_transfer: true } : {}),\n }),\n });\n const j = (await r.json()) as { ok?: boolean; reason?: string };\n if (r.status === 409 && j.reason === 'email_linked_elsewhere') {\n setTransfer(true);\n setBusy(false);\n return;\n }\n if (handleSudoRequired(j, { purpose: 'link an email' })) return;\n if (isSudoAccountMismatch(j)) {\n setErr(SUDO_ACCOUNT_MISMATCH_MESSAGE);\n setBusy(false);\n return;\n }\n if (!r.ok || !j.ok) throw new Error(j.reason ?? `http_${r.status}`);\n onDone();\n } catch (e) {\n setErr(e instanceof Error ? e.message : 'link failed');\n setBusy(false);\n }\n }\n\n return (\n <div style={panelStyle('accent')} data-oc-li-form=\"email\">\n <SectionLabel>§ link an email</SectionLabel>\n {step === 'email' ? (\n <>\n <input\n type=\"email\"\n autoComplete=\"email\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n placeholder=\"you@example.com\"\n style={inputStyle}\n />\n {err && <ErrorLine>{err}</ErrorLine>}\n <FormButtons>\n <button\n type=\"button\"\n onClick={() => void start()}\n disabled={busy || email.trim().length < 5}\n style={primaryBtnStyle(busy || email.trim().length < 5)}\n >\n {busy ? 'sending…' : 'send code'}\n </button>\n <button type=\"button\" onClick={onCancel} style={ghostBtnStyle(busy)}>\n cancel\n </button>\n </FormButtons>\n </>\n ) : transfer ? (\n <TransferConfirm\n what=\"email\"\n busy={busy}\n onConfirm={() => void verify(true)}\n onCancel={onCancel}\n err={err}\n />\n ) : (\n <>\n <p style={bodyStyle}>Enter the 6-digit code sent to {email}.</p>\n <input\n type=\"text\"\n inputMode=\"numeric\"\n autoComplete=\"one-time-code\"\n maxLength={6}\n value={code}\n onChange={(e) => setCode(e.target.value.replace(/\\D/g, '').slice(0, 6))}\n placeholder=\"123456\"\n style={{ ...inputStyle, letterSpacing: '0.3em' }}\n />\n {err && <ErrorLine>{err}</ErrorLine>}\n <FormButtons>\n <button\n type=\"button\"\n onClick={() => void verify(false)}\n disabled={busy || code.length !== 6}\n style={primaryBtnStyle(busy || code.length !== 6)}\n >\n {busy ? 'linking…' : 'verify + link'}\n </button>\n <button\n type=\"button\"\n onClick={() => setStep('email')}\n disabled={busy}\n style={ghostBtnStyle(busy)}\n >\n back\n </button>\n </FormButtons>\n </>\n )}\n </div>\n );\n}\n\n/* --- btc link form --- */\n\nfunction BtcLinkForm({\n authOrigin,\n didOc,\n onDone,\n onCancel,\n}: {\n authOrigin: string;\n didOc: string | null;\n onDone: () => void;\n onCancel: () => void;\n}): React.ReactElement {\n const [step, setStep] = React.useState<'request' | 'sign'>('request');\n const [addr, setAddr] = React.useState('');\n const [challenge, setChallenge] = React.useState('');\n const [busy, setBusy] = React.useState(false);\n const [err, setErr] = React.useState<string | null>(null);\n // Holds the signature awaiting transfer confirmation when the\n // address is already linked to another account.\n const [pendingSig, setPendingSig] = React.useState<string | null>(null);\n\n async function getChallenge(): Promise<void> {\n if (!didOc) {\n setErr('session not loaded yet · refresh and retry');\n return;\n }\n setBusy(true);\n setErr(null);\n try {\n // /api/challenge is a public primitive — no session needed.\n // audience MUST be the caller's did_oc: the link endpoint\n // verifies expectedAudience=session.did_oc, so a signature\n // obtained here can't be replayed against another account.\n const r = await fetch(\n `${authOrigin}/api/challenge?addr=${encodeURIComponent(addr)}` +\n `&purpose=link&audience=${encodeURIComponent(didOc)}`\n );\n const j = (await r.json()) as { message?: string; reason?: string };\n if (!r.ok || !j.message) throw new Error(j.reason ?? `http_${r.status}`);\n setChallenge(j.message);\n setStep('sign');\n } catch (e) {\n setErr(e instanceof Error ? e.message : 'challenge failed');\n } finally {\n setBusy(false);\n }\n }\n\n async function postLink(sig: string, confirmTransfer: boolean): Promise<void> {\n setBusy(true);\n setErr(null);\n try {\n const r = await fetch(`${authOrigin}/api/auth/link/btc`, {\n method: 'POST',\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n message: challenge,\n signature: sig,\n ...(confirmTransfer ? { confirm_transfer: true } : {}),\n }),\n });\n const j = (await r.json()) as { ok?: boolean; reason?: string };\n if (r.status === 409 && j.reason === 'address_linked_elsewhere') {\n setPendingSig(sig);\n setBusy(false);\n return;\n }\n if (handleSudoRequired(j, { purpose: 'link a Bitcoin address' })) return;\n if (isSudoAccountMismatch(j)) {\n setErr(SUDO_ACCOUNT_MISMATCH_MESSAGE);\n setBusy(false);\n return;\n }\n if (!r.ok || !j.ok) throw new Error(j.reason ?? `http_${r.status}`);\n onDone();\n } catch (e) {\n setErr(e instanceof Error ? e.message : 'link failed');\n setBusy(false);\n }\n }\n\n return (\n <div style={panelStyle('accent')} data-oc-li-form=\"btc\">\n <SectionLabel>§ link a Bitcoin address</SectionLabel>\n {step === 'request' ? (\n <>\n <p style={bodyStyle}>\n Enter a Bitcoin address you control. We generate a challenge and your\n wallet signs it (BIP-322).\n </p>\n <input\n type=\"text\"\n value={addr}\n onChange={(e) => setAddr(e.target.value.trim())}\n placeholder=\"bc1q…\"\n autoCapitalize=\"none\"\n spellCheck={false}\n style={{ ...inputStyle, fontFamily: SIGNIN_FONT }}\n />\n {err && <ErrorLine>{err}</ErrorLine>}\n <FormButtons>\n <button\n type=\"button\"\n onClick={() => void getChallenge()}\n disabled={busy || addr.length < 14}\n style={primaryBtnStyle(busy || addr.length < 14)}\n >\n {busy ? 'requesting…' : 'get challenge'}\n </button>\n <button type=\"button\" onClick={onCancel} style={ghostBtnStyle(busy)}>\n cancel\n </button>\n </FormButtons>\n </>\n ) : pendingSig ? (\n <TransferConfirm\n what=\"bitcoin address\"\n busy={busy}\n onConfirm={() => void postLink(pendingSig, true)}\n onCancel={onCancel}\n err={err}\n />\n ) : (\n <>\n <p style={bodyStyle}>\n Sign the challenge with the wallet that controls{' '}\n <span style={{ fontFamily: SIGNIN_FONT, wordBreak: 'break-all' }}>{addr}</span>.\n </p>\n <LazyWalletButton\n address={addr}\n message={challenge}\n showManual\n layout=\"list\"\n heading={null}\n onSigned={(sig: string) => void postLink(sig, false)}\n onError={(e: { message?: string }) =>\n setErr(e?.message ?? 'wallet rejected')\n }\n />\n {err && <ErrorLine>{err}</ErrorLine>}\n <FormButtons>\n <button\n type=\"button\"\n onClick={() => {\n setStep('request');\n setErr(null);\n }}\n disabled={busy}\n style={ghostBtnStyle(busy)}\n >\n back\n </button>\n </FormButtons>\n </>\n )}\n </div>\n );\n}\n\n/* --- transfer-under-dual-proof confirm --- */\n\nfunction TransferConfirm({\n what,\n busy,\n onConfirm,\n onCancel,\n err,\n}: {\n what: string;\n busy: boolean;\n onConfirm: () => void;\n onCancel: () => void;\n err: string | null;\n}): React.ReactElement {\n return (\n <>\n <p style={bodyStyle}>\n This {what} is already linked to another OrangeCheck account. You&apos;ve just\n proven control of it — you can transfer the link to this account under dual proof\n of control. The other account loses this sign-in path; its history stays\n verifiable. Continue?\n </p>\n {err && <ErrorLine>{err}</ErrorLine>}\n <FormButtons>\n <button\n type=\"button\"\n onClick={onConfirm}\n disabled={busy}\n style={dangerBtnStyle(busy)}\n >\n {busy ? 'transferring…' : 'transfer link here'}\n </button>\n <button type=\"button\" onClick={onCancel} disabled={busy} style={ghostBtnStyle(busy)}>\n cancel\n </button>\n </FormButtons>\n </>\n );\n}\n\n/* --- lazy wallet button · wallet-adapter loads on demand, not with the page --- */\n\ninterface WalletButtonProps {\n address: string;\n message: string;\n showManual?: boolean;\n layout?: string;\n heading?: React.ReactNode;\n onSigned: (sig: string) => void;\n onError: (err: { message?: string }, walletId?: string) => void;\n}\n\nexport function LazyWalletButton(props: WalletButtonProps): React.ReactElement {\n const [Comp, setComp] = React.useState<React.ComponentType<WalletButtonProps> | null>(null);\n const [failed, setFailed] = React.useState(false);\n\n React.useEffect(() => {\n let alive = true;\n // A bundler resolves this specifier at build time, so wallet-adapter\n // is a real dependency; only the chunk is deferred.\n import('@orangecheck/wallet-adapter/react')\n .then((m) => {\n if (alive) {\n setComp(\n () => (m as { OcWalletButton: React.ComponentType<WalletButtonProps> })\n .OcWalletButton\n );\n }\n })\n .catch(() => {\n if (alive) setFailed(true);\n });\n return () => {\n alive = false;\n };\n }, []);\n\n if (failed) {\n return (\n <ErrorLine>\n could not load wallet support · refresh to try again\n </ErrorLine>\n );\n }\n if (!Comp) return <span style={hintStyle}>loading wallet options…</span>;\n return <Comp {...props} />;\n}\n\n/* --- helpers --- */\n\nfunction mask(value: string, kind: 'email' | 'btc'): string {\n if (kind === 'email') {\n const [user, host] = value.split('@');\n if (!user || !host) return '••••';\n const m =\n user.length <= 2\n ? '•'.repeat(user.length)\n : user[0] + '•'.repeat(user.length - 2) + user.slice(-1);\n return `${m}@${host}`;\n }\n if (value.length <= 12) return '•'.repeat(value.length);\n return `${value.slice(0, 6)}…${value.slice(-4)}`;\n}\n\n/* --- presentational atoms · also used by bond.tsx; not re-exported from index --- */\n\nexport function SectionLabel({\n children,\n tone = 'primary',\n}: {\n children: React.ReactNode;\n tone?: 'primary' | 'success' | 'warn';\n}): React.ReactElement {\n const color = tone === 'success' ? V.success : tone === 'warn' ? V.warn : V.primary;\n return (\n <div\n style={{\n color,\n fontFamily: SIGNIN_FONT,\n fontSize: signinSize(10),\n letterSpacing: signinTracking('0.18em'),\n textTransform: SIGNIN_CASE,\n marginBottom: 8,\n }}\n >\n {children}\n </div>\n );\n}\n\nexport function ErrorLine({ children }: { children: React.ReactNode }): React.ReactElement {\n return (\n <p\n role=\"alert\"\n style={{\n margin: '8px 0 0',\n color: V.destructive,\n fontFamily: SIGNIN_FONT,\n fontSize: signinSize(11),\n }}\n >\n {children}\n </p>\n );\n}\n\nexport function FormButtons({ children }: { children: React.ReactNode }): React.ReactElement {\n return (\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8, marginTop: 12 }}>{children}</div>\n );\n}\n\n/* --- style tokens (CSS-variable-themed, same approach as OcSignIn) --- */\n\n\nexport const V = {\n primary: 'var(--primary, #f97316)',\n foreground: 'var(--foreground, #fafafa)',\n muted: 'var(--muted-foreground, #a1a1aa)',\n background: 'var(--background, #0a0a0a)',\n card: 'var(--card, #111113)',\n border: 'var(--border, #27272a)',\n destructive: 'var(--destructive, #ef4444)',\n success: 'var(--success, #22c55e)',\n warn: 'var(--warning, #eab308)',\n} as const;\n\nexport function panelStyle(tone: 'plain' | 'accent' | 'success' | 'warn' | 'default' = 'default'): React.CSSProperties {\n const base: React.CSSProperties = {\n background: V.card,\n border: `1px solid ${V.border}`,\n padding: '1.25rem',\n };\n if (tone === 'plain') return { ...base, padding: 0 };\n if (tone === 'accent') return { ...base, borderColor: V.primary, background: 'var(--card, #111113)' };\n if (tone === 'success') return { ...base, borderColor: V.success };\n if (tone === 'warn') return { ...base, borderColor: V.warn };\n return base;\n}\n\nexport const bodyStyle: React.CSSProperties = {\n color: V.muted,\n fontSize: 13,\n lineHeight: 1.55,\n margin: '0 0 12px',\n};\n\nexport const hintStyle: React.CSSProperties = {\n color: V.muted,\n fontFamily: SIGNIN_FONT,\n fontSize: signinSize(10.5),\n opacity: 0.8,\n};\n\nconst rowStyle: React.CSSProperties = {\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: 12,\n padding: '1rem',\n};\n\nconst rowLabelStyle: React.CSSProperties = {\n color: V.foreground,\n fontFamily: SIGNIN_FONT,\n fontSize: signinSize(10.5),\n letterSpacing: signinTracking('0.12em'),\n textTransform: SIGNIN_CASE,\n opacity: 0.8,\n};\n\nexport const inputStyle: React.CSSProperties = {\n width: '100%',\n padding: '0.55rem 0.75rem',\n background: V.background,\n color: V.foreground,\n border: `1px solid ${V.border}`,\n borderRadius: signinRadius(6),\n fontFamily: SIGNIN_FONT,\n // 16px — never lower · suppresses iOS Safari focus auto-zoom.\n fontSize: 16,\n outline: 'none',\n boxSizing: 'border-box',\n};\n\nfunction btnBase(disabled: boolean): React.CSSProperties {\n return {\n padding: '0.5rem 0.875rem',\n borderRadius: SIGNIN_BUTTON_RADIUS,\n fontFamily: SIGNIN_FONT,\n fontSize: signinSize(10.5),\n letterSpacing: signinTracking('0.12em'),\n textTransform: SIGNIN_CASE,\n cursor: disabled ? 'not-allowed' : 'pointer',\n opacity: disabled ? 0.5 : 1,\n };\n}\n\nexport function primaryBtnStyle(disabled: boolean): React.CSSProperties {\n return {\n ...btnBase(disabled),\n background: V.primary,\n color: 'var(--primary-foreground, #0a0a0a)',\n border: `1px solid ${V.primary}`,\n fontWeight: 600,\n };\n}\n\nexport function ghostBtnStyle(disabled: boolean): React.CSSProperties {\n return {\n ...btnBase(disabled),\n background: V.background,\n color: V.foreground,\n border: `1px solid ${V.border}`,\n };\n}\n\nexport function dangerBtnStyle(disabled: boolean): React.CSSProperties {\n return {\n ...btnBase(disabled),\n background: 'transparent',\n color: V.destructive,\n border: `1px solid ${V.destructive}`,\n };\n}\n\nconst primaryChipStyle: React.CSSProperties = {\n ...btnBase(false),\n background: 'transparent',\n color: V.primary,\n border: `1px solid ${V.primary}`,\n};\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/linked-identities.tsx"],"sourcesContent":["'use client';\n\n/**\n * @orangecheck/auth-client · OcLinkedIdentities\n *\n * The shared linked-identities management surface for every `X.ochk.io`\n * consumer. A signed-in user sees their linked email + Bitcoin address,\n * can reveal/hide each raw value, unlink a non-primary identity, and\n * link a new one. Linking a Bitcoin address that's already on another\n * OC account offers a dual-proof transfer; the same is now true for\n * email (auth host F4). Consolidated (`merged_from`) accounts render in\n * a tombstone panel.\n *\n * This generalizes me.ochk.io's bespoke `LinkedIdentitiesPanel` so\n * linking is no longer a single-product, one-tab feature — any family\n * site can drop in `<OcLinkedIdentities />`. See AUTH-PLAN.md §5.\n *\n * All endpoints live on the auth host (`ochk.io`); calls are\n * cross-origin with `credentials: 'include'` and rely on the auth\n * host's family-CORS — the same posture `OcSignIn` uses. No local\n * proxy route is needed (AUTH-AUDIT.md F6: direct-to-host is the\n * intended design).\n *\n * Style: inline, CSS-variable-themed (same approach as `OcSignIn`), so\n * the component carries zero CSS dependency and themes to each site.\n * Every block carries a `data-oc-li-*` attribute so a site may\n * override via its own stylesheet.\n */\n\nimport * as React from 'react';\nimport {\n handleSudoRequired,\n isSudoAccountMismatch,\n SUDO_ACCOUNT_MISMATCH_MESSAGE,\n} from './sudo';\n\nimport {\n SIGNIN_BUTTON_RADIUS,\n SIGNIN_CASE,\n SIGNIN_FONT,\n signinRadius,\n signinSize,\n signinTracking,\n} from './appearance';\nimport { tabSessionHeader } from './tab-session';\n\nconst DEFAULT_AUTH_ORIGIN = 'https://ochk.io';\n\n/* --- types --- */\n\nexport interface OcLinkedIdentity {\n id: string;\n kind: 'email' | 'btc';\n /** Raw value · null when the identity is pending recovery. */\n value: string | null;\n verified_at: string | null;\n verification_method: 'email-otp' | 'bip322' | null;\n /** The signup-time identity for its kind · cannot be unlinked here. */\n is_primary: boolean;\n}\n\ninterface MergedFromEntry {\n did_oc: string;\n created_at: string;\n last_signed_in_at: string | null;\n}\n\ninterface IdentitiesResp {\n ok: boolean;\n account_id?: string;\n did_oc?: string | null;\n linked?: OcLinkedIdentity[];\n merged_from?: MergedFromEntry[];\n reason?: string;\n}\n\n/**\n * Fetch the signed-in user's linked identities from the auth host —\n * the same data `<OcLinkedIdentities>` renders, exposed as a plain\n * function for surfaces that need the list without the full\n * management UI (notably `<OcAccountMenu>`'s badge-identity promote\n * list). Returns `[]` when anonymous; throws on network / server\n * error so the caller can show a deferred state.\n */\nexport async function fetchOcLinkedIdentities(opts?: {\n authOrigin?: string;\n}): Promise<OcLinkedIdentity[]> {\n const authOrigin = opts?.authOrigin ?? DEFAULT_AUTH_ORIGIN;\n // Per-tab pinning: forward the tab's session JWT so the host resolves\n // the SAME account this tab displays (its chip reads /api/auth/me\n // tab-aware). Without this, a pinned tab's show-as list is the shared\n // cookie account's identities — a menu that disagrees with itself.\n // Unpinned tabs send no header → unchanged cookie-default behavior.\n const r = await fetch(`${authOrigin}/api/auth/identities`, {\n credentials: 'include',\n headers: { ...tabSessionHeader() },\n });\n if (r.status === 401) return [];\n if (!r.ok) throw new Error(`identities fetch failed: http_${r.status}`);\n const body = (await r.json()) as IdentitiesResp;\n return body.linked ?? [];\n}\n\nexport interface OcLinkedIdentitiesProps {\n /** Auth host origin. Defaults to `https://ochk.io`. */\n authOrigin?: string;\n /** Outer container className. */\n className?: string;\n /** Called after every successful link / unlink / transfer. */\n onChange?: () => void;\n /**\n * Show the \"portable attestation\" card that links out to attest.ochk.io.\n * Defaults to `true`. Pass `false` on a page that already hosts its own\n * attestation builder (ochk.io's dashboard), or the same action is offered\n * twice, one panel apart.\n */\n attestationLink?: boolean;\n}\n\n/* --- main --- */\n\nexport function OcLinkedIdentities({\n authOrigin = DEFAULT_AUTH_ORIGIN,\n className,\n onChange,\n attestationLink = true,\n}: OcLinkedIdentitiesProps): React.ReactElement {\n const [resp, setResp] = React.useState<IdentitiesResp | null>(null);\n const [error, setError] = React.useState<string | null>(null);\n const [showEmailLink, setShowEmailLink] = React.useState(false);\n const [showBtcLink, setShowBtcLink] = React.useState(false);\n\n const refresh = React.useCallback(async () => {\n setError(null);\n try {\n const r = await fetch(`${authOrigin}/api/auth/identities`, {\n credentials: 'include',\n headers: { ...tabSessionHeader() },\n });\n if (!r.ok) {\n if (r.status === 401) {\n setError('not signed in');\n return;\n }\n throw new Error(`http_${r.status}`);\n }\n setResp((await r.json()) as IdentitiesResp);\n } catch (e) {\n setError(e instanceof Error ? e.message : 'load failed');\n }\n }, [authOrigin]);\n\n React.useEffect(() => {\n void refresh();\n }, [refresh]);\n\n const afterChange = React.useCallback(() => {\n setShowEmailLink(false);\n setShowBtcLink(false);\n void refresh();\n onChange?.();\n }, [refresh, onChange]);\n\n if (error) {\n return (\n <div className={className} data-oc-li=\"\" style={panelStyle('warn')} role=\"alert\">\n <SectionLabel tone=\"warn\">§ identities · load deferred</SectionLabel>\n <p style={bodyStyle}>\n Couldn&apos;t reach the auth host ({error}). Linking is temporarily\n unavailable; your signed-in identity still works as normal.\n </p>\n <button type=\"button\" onClick={() => void refresh()} style={ghostBtnStyle(false)}>\n retry\n </button>\n </div>\n );\n }\n\n if (!resp) {\n return (\n <div className={className} data-oc-li=\"\" style={panelStyle()}>\n <span style={hintStyle}>loading…</span>\n </div>\n );\n }\n\n const linked = resp.linked ?? [];\n const emailRows = linked.filter((i) => i.kind === 'email');\n const btcRows = linked.filter((i) => i.kind === 'btc');\n const hasEmail = emailRows.some((i) => i.value);\n const hasBtc = btcRows.some((i) => i.value);\n const mergedFrom = resp.merged_from ?? [];\n\n return (\n <div className={className} data-oc-li=\"\" style={{ display: 'grid', gap: 16 }}>\n <div style={panelStyle()}>\n <SectionLabel>§ what this is</SectionLabel>\n <p style={bodyStyle}>\n These are the ways you can sign in to this account. An app you sign in to\n sees one only if you grant it. Linking a second one is also your way back\n in: lose one, and the other still signs you in.\n </p>\n </div>\n\n {mergedFrom.length > 0 && <ConsolidationPanel entries={mergedFrom} />}\n\n <div style={panelStyle('plain')} data-oc-li-rows=\"\">\n <IdentityGroup\n kind=\"email\"\n title=\"email\"\n rows={emailRows}\n authOrigin={authOrigin}\n onChange={afterChange}\n addCta={hasEmail ? null : 'link an email'}\n onAdd={() => {\n setShowEmailLink((v) => !v);\n setShowBtcLink(false);\n }}\n />\n <div style={{ borderTop: `1px solid ${V.border}` }} />\n <IdentityGroup\n kind=\"btc\"\n title=\"bitcoin address\"\n rows={btcRows}\n authOrigin={authOrigin}\n onChange={afterChange}\n addCta={hasBtc ? null : 'link a Bitcoin address'}\n onAdd={() => {\n setShowBtcLink((v) => !v);\n setShowEmailLink(false);\n }}\n />\n </div>\n\n {showEmailLink && (\n <EmailLinkForm\n authOrigin={authOrigin}\n onDone={afterChange}\n onCancel={() => setShowEmailLink(false)}\n />\n )}\n {showBtcLink && (\n <BtcLinkForm\n authOrigin={authOrigin}\n didOc={resp.did_oc ?? null}\n onDone={afterChange}\n onCancel={() => setShowBtcLink(false)}\n />\n )}\n\n {hasBtc && attestationLink && <AttestationLink />}\n </div>\n );\n}\n\n/* --- identity group (rows for one kind, or an empty-state add CTA) --- */\n\nfunction IdentityGroup({\n kind,\n title,\n rows,\n authOrigin,\n onChange,\n addCta,\n onAdd,\n}: {\n kind: 'email' | 'btc';\n title: string;\n rows: OcLinkedIdentity[];\n authOrigin: string;\n onChange: () => void;\n addCta: string | null;\n onAdd: () => void;\n}): React.ReactElement {\n if (rows.length === 0) {\n return (\n <div style={rowStyle}>\n <div>\n <div style={rowLabelStyle}>§ {title}</div>\n <div style={{ ...bodyStyle, marginTop: 4 }}>none linked</div>\n </div>\n {addCta && (\n <button type=\"button\" onClick={onAdd} style={primaryChipStyle}>\n + {addCta}\n </button>\n )}\n </div>\n );\n }\n return (\n <>\n {rows.map((r) => (\n <IdentityRow\n key={r.id}\n row={r}\n kind={kind}\n authOrigin={authOrigin}\n onChange={onChange}\n />\n ))}\n </>\n );\n}\n\n/* --- single identity row --- */\n\nfunction IdentityRow({\n row,\n kind,\n authOrigin,\n onChange,\n}: {\n row: OcLinkedIdentity;\n kind: 'email' | 'btc';\n authOrigin: string;\n onChange: () => void;\n}): React.ReactElement {\n const [shown, setShown] = React.useState(false);\n const [confirming, setConfirming] = React.useState(false);\n const [busy, setBusy] = React.useState(false);\n const [err, setErr] = React.useState<string | null>(null);\n\n const masked = row.value ? mask(row.value, kind) : '— pending recovery —';\n const display = shown && row.value ? row.value : masked;\n\n async function unlink(): Promise<void> {\n if (row.is_primary) return;\n setBusy(true);\n setErr(null);\n try {\n const r = await fetch(\n `${authOrigin}/api/auth/identities?id=${encodeURIComponent(row.id)}`,\n { method: 'DELETE', credentials: 'include' }\n );\n if (!r.ok) {\n const j = (await r.json().catch(() => ({}))) as { reason?: string };\n throw new Error(j.reason ?? `http_${r.status}`);\n }\n onChange();\n } catch (e) {\n setErr(e instanceof Error ? e.message : 'unlink failed');\n setBusy(false);\n setConfirming(false);\n }\n }\n\n return (\n <div style={rowStyle} data-oc-li-row={kind}>\n <div style={{ minWidth: 0, flex: 1 }}>\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: '4px 12px' }}>\n <span style={rowLabelStyle}>\n § {kind === 'email' ? 'email' : 'bitcoin address'}\n </span>\n {row.is_primary && (\n <span style={{ ...rowLabelStyle, color: V.primary }}>\n primary · signup-time\n </span>\n )}\n </div>\n <div\n style={{\n marginTop: 4,\n color: V.foreground,\n fontFamily: SIGNIN_FONT,\n fontSize: signinSize(12),\n wordBreak: 'break-all',\n }}\n >\n {display}\n </div>\n {row.verified_at && (\n <div style={{ ...hintStyle, marginTop: 4 }}>\n verified · {new Date(row.verified_at).toUTCString()}\n </div>\n )}\n {err && <ErrorLine>{err}</ErrorLine>}\n </div>\n <div style={{ display: 'flex', flexShrink: 0, gap: 8, flexWrap: 'wrap' }}>\n {row.value && (\n <button\n type=\"button\"\n onClick={() => setShown((s) => !s)}\n style={ghostBtnStyle(false)}\n >\n {shown ? 'hide' : 'show'}\n </button>\n )}\n {!row.is_primary && !confirming && (\n <button\n type=\"button\"\n onClick={() => setConfirming(true)}\n style={dangerBtnStyle(false)}\n >\n unlink\n </button>\n )}\n {!row.is_primary && confirming && (\n <>\n <button\n type=\"button\"\n onClick={() => void unlink()}\n disabled={busy}\n style={dangerBtnStyle(busy)}\n >\n {busy ? 'unlinking…' : 'confirm unlink'}\n </button>\n <button\n type=\"button\"\n onClick={() => setConfirming(false)}\n disabled={busy}\n style={ghostBtnStyle(busy)}\n >\n cancel\n </button>\n </>\n )}\n </div>\n </div>\n );\n}\n\n/* --- consolidated-accounts tombstone panel --- */\n\nfunction ConsolidationPanel({ entries }: { entries: MergedFromEntry[] }): React.ReactElement {\n return (\n <div style={panelStyle('success')} data-oc-li-consolidated=\"\">\n <SectionLabel tone=\"success\">\n § consolidated accounts · {entries.length}\n </SectionLabel>\n <p style={bodyStyle}>\n {entries.length === 1 ? 'A previous OC account' : 'Previous OC accounts'} you\n signed up with, folded into this one under dual proof of control. Historical\n activity is unioned into this account automatically;{' '}\n {entries.length === 1 ? 'it' : 'they'} can no longer sign in.\n </p>\n <ul style={{ listStyle: 'none', margin: '12px 0 0', padding: 0 }}>\n {entries.map((e) => (\n <li\n key={e.did_oc}\n style={{\n display: 'flex',\n flexWrap: 'wrap',\n gap: 8,\n padding: '8px 0',\n borderTop: `1px solid ${V.border}`,\n fontFamily: SIGNIN_FONT,\n fontSize: signinSize(11),\n }}\n >\n <code style={{ color: V.foreground, wordBreak: 'break-all' }}>\n {e.did_oc}\n </code>\n <span style={{ ...hintStyle, marginLeft: 'auto' }}>\n created {new Date(e.created_at).toLocaleDateString()}\n </span>\n </li>\n ))}\n </ul>\n </div>\n );\n}\n\n/* --- portable-attestation pointer --- */\n\n/**\n * A thin pointer to the OC Attest builder. The portable, self-sovereign\n * form of identity linking is a standard OrangeCheck attestation — one\n * BIP-322 signature by the Bitcoin address binding it to its handles,\n * published to Nostr. That ceremony already lives at\n * `attest.ochk.io/create` (which pre-fills the address from the signed-in\n * session); this panel routes the user there rather than re-implementing\n * it. Shown once the account has a linked Bitcoin address to attest with.\n */\nfunction AttestationLink(): React.ReactElement {\n return (\n <div style={panelStyle()} data-oc-li-attest=\"\">\n <SectionLabel>§ portable attestation</SectionLabel>\n <p style={bodyStyle}>\n Make your identity links portable: publish a standard OrangeCheck\n attestation — one Bitcoin-wallet signature (BIP-322) binding your address\n to your handles (Nostr, GitHub, …). It is published to Nostr and verifies\n with zero trust in OrangeCheck, so it outlives any single service.\n </p>\n <a\n href=\"https://attest.ochk.io/create\"\n target=\"_blank\"\n rel=\"noreferrer\"\n style={{ ...primaryChipStyle, textDecoration: 'none', display: 'inline-block' }}\n data-oc-li-attest-link=\"\"\n >\n open the attestation builder ↗\n </a>\n </div>\n );\n}\n\n/* --- post-sign-in link prompt (used by OcSignIn) --- */\n\n/** Where the ways to sign in are managed: ochk.app, which replaces me.ochk.io's pages. */\nconst ACCOUNT_WAYS_URL = 'https://ochk.app/me/account#ways';\n\n/**\n * Shown by `<OcSignIn>` after a successful sign-in *when the user ticked\n * the \"also link my other identity\" checkbox on the sign-in form*. It runs\n * the complementary identity's link ceremony inline — BIP-322 for a\n * Bitcoin address, OTP for an email.\n *\n * Skipping is first-class: even though the user opted in at the form, they\n * may change their mind here — an explicit \"skip, I'll do this later\"\n * control exits cleanly and tells them where to link later. `onResolved`\n * (linked, or skipped) hands control back to OcSignIn's post-sign-in\n * navigation.\n */\nexport function LinkPromptStep({\n method,\n didOc,\n authOrigin,\n onResolved,\n}: {\n /** The complementary identity to link — the opposite of the sign-in method. */\n method: 'btc' | 'email';\n didOc: string;\n authOrigin: string;\n /** Called once the user has linked the identity, or skipped. */\n onResolved: () => void;\n}): React.ReactElement {\n const what = method === 'btc' ? 'a Bitcoin address' : 'an email';\n return (\n <div data-oc-linkprompt={method}>\n <div style={{ ...panelStyle('success'), marginBottom: 16 }}>\n <SectionLabel tone=\"success\">§ signed in</SectionLabel>\n <p style={{ ...bodyStyle, margin: 0 }}>\n You chose to also link {what}. Complete it below — or skip for now; it is\n entirely optional and changes nothing about the sign-in you just completed.\n </p>\n </div>\n {method === 'btc' ? (\n <BtcLinkForm\n authOrigin={authOrigin}\n didOc={didOc}\n onDone={onResolved}\n onCancel={onResolved}\n />\n ) : (\n <EmailLinkForm authOrigin={authOrigin} onDone={onResolved} onCancel={onResolved} />\n )}\n <div\n data-oc-linkprompt-skip=\"\"\n style={{ marginTop: 14, paddingTop: 14, borderTop: `1px solid ${V.border}` }}\n >\n <button type=\"button\" onClick={onResolved} style={ghostBtnStyle(false)}>\n skip — I&apos;ll do this later\n </button>\n <p style={{ ...hintStyle, marginTop: 8 }}>\n Changed your mind? Skipping continues you straight on — your sign-in is\n unaffected. You can link {what} anytime later from your account page,{' '}\n <a\n href={ACCOUNT_WAYS_URL}\n target=\"_blank\"\n rel=\"noreferrer\"\n style={{ color: 'inherit' }}\n data-oc-linkprompt-account=\"\"\n >\n ochk.app/me/account\n </a>\n .\n </p>\n </div>\n </div>\n );\n}\n\n/* --- email link form --- */\n\nfunction EmailLinkForm({\n authOrigin,\n onDone,\n onCancel,\n}: {\n authOrigin: string;\n onDone: () => void;\n onCancel: () => void;\n}): React.ReactElement {\n const [step, setStep] = React.useState<'email' | 'code'>('email');\n const [email, setEmail] = React.useState('');\n const [code, setCode] = React.useState('');\n const [token, setToken] = React.useState('');\n const [busy, setBusy] = React.useState(false);\n const [err, setErr] = React.useState<string | null>(null);\n // Set when the auth host reports the email is linked elsewhere — the\n // user can confirm a dual-proof transfer (session + verified OTP).\n const [transfer, setTransfer] = React.useState(false);\n\n async function start(): Promise<void> {\n setBusy(true);\n setErr(null);\n try {\n const r = await fetch(`${authOrigin}/api/auth/email-otp/start`, {\n method: 'POST',\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ email: email.trim().toLowerCase() }),\n });\n const j = (await r.json()) as { ok?: boolean; token?: string; reason?: string };\n if (!r.ok || !j.token) throw new Error(j.reason ?? `http_${r.status}`);\n setToken(j.token);\n setStep('code');\n } catch (e) {\n setErr(e instanceof Error ? e.message : 'could not send code');\n } finally {\n setBusy(false);\n }\n }\n\n async function verify(confirmTransfer: boolean): Promise<void> {\n setBusy(true);\n setErr(null);\n try {\n const r = await fetch(`${authOrigin}/api/auth/link/email`, {\n method: 'POST',\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n email: email.trim().toLowerCase(),\n code: code.trim(),\n token,\n ...(confirmTransfer ? { confirm_transfer: true } : {}),\n }),\n });\n const j = (await r.json()) as { ok?: boolean; reason?: string };\n if (r.status === 409 && j.reason === 'email_linked_elsewhere') {\n setTransfer(true);\n setBusy(false);\n return;\n }\n if (handleSudoRequired(j, { purpose: 'link an email' })) return;\n if (isSudoAccountMismatch(j)) {\n setErr(SUDO_ACCOUNT_MISMATCH_MESSAGE);\n setBusy(false);\n return;\n }\n if (!r.ok || !j.ok) throw new Error(j.reason ?? `http_${r.status}`);\n onDone();\n } catch (e) {\n setErr(e instanceof Error ? e.message : 'link failed');\n setBusy(false);\n }\n }\n\n return (\n <div style={panelStyle('accent')} data-oc-li-form=\"email\">\n <SectionLabel>§ link an email</SectionLabel>\n {step === 'email' ? (\n <>\n <input\n type=\"email\"\n autoComplete=\"email\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n placeholder=\"you@example.com\"\n style={inputStyle}\n />\n {err && <ErrorLine>{err}</ErrorLine>}\n <FormButtons>\n <button\n type=\"button\"\n onClick={() => void start()}\n disabled={busy || email.trim().length < 5}\n style={primaryBtnStyle(busy || email.trim().length < 5)}\n >\n {busy ? 'sending…' : 'send code'}\n </button>\n <button type=\"button\" onClick={onCancel} style={ghostBtnStyle(busy)}>\n cancel\n </button>\n </FormButtons>\n </>\n ) : transfer ? (\n <TransferConfirm\n what=\"email\"\n busy={busy}\n onConfirm={() => void verify(true)}\n onCancel={onCancel}\n err={err}\n />\n ) : (\n <>\n <p style={bodyStyle}>Enter the 6-digit code sent to {email}.</p>\n <input\n type=\"text\"\n inputMode=\"numeric\"\n autoComplete=\"one-time-code\"\n maxLength={6}\n value={code}\n onChange={(e) => setCode(e.target.value.replace(/\\D/g, '').slice(0, 6))}\n placeholder=\"123456\"\n style={{ ...inputStyle, letterSpacing: '0.3em' }}\n />\n {err && <ErrorLine>{err}</ErrorLine>}\n <FormButtons>\n <button\n type=\"button\"\n onClick={() => void verify(false)}\n disabled={busy || code.length !== 6}\n style={primaryBtnStyle(busy || code.length !== 6)}\n >\n {busy ? 'linking…' : 'verify + link'}\n </button>\n <button\n type=\"button\"\n onClick={() => setStep('email')}\n disabled={busy}\n style={ghostBtnStyle(busy)}\n >\n back\n </button>\n </FormButtons>\n </>\n )}\n </div>\n );\n}\n\n/* --- btc link form --- */\n\nfunction BtcLinkForm({\n authOrigin,\n didOc,\n onDone,\n onCancel,\n}: {\n authOrigin: string;\n didOc: string | null;\n onDone: () => void;\n onCancel: () => void;\n}): React.ReactElement {\n const [step, setStep] = React.useState<'request' | 'sign'>('request');\n const [addr, setAddr] = React.useState('');\n const [challenge, setChallenge] = React.useState('');\n const [busy, setBusy] = React.useState(false);\n const [err, setErr] = React.useState<string | null>(null);\n // Holds the signature awaiting transfer confirmation when the\n // address is already linked to another account.\n const [pendingSig, setPendingSig] = React.useState<string | null>(null);\n\n async function getChallenge(): Promise<void> {\n if (!didOc) {\n setErr('session not loaded yet · refresh and retry');\n return;\n }\n setBusy(true);\n setErr(null);\n try {\n // /api/challenge is a public primitive — no session needed.\n // audience MUST be the caller's did_oc: the link endpoint\n // verifies expectedAudience=session.did_oc, so a signature\n // obtained here can't be replayed against another account.\n const r = await fetch(\n `${authOrigin}/api/challenge?addr=${encodeURIComponent(addr)}` +\n `&purpose=link&audience=${encodeURIComponent(didOc)}`\n );\n const j = (await r.json()) as { message?: string; reason?: string };\n if (!r.ok || !j.message) throw new Error(j.reason ?? `http_${r.status}`);\n setChallenge(j.message);\n setStep('sign');\n } catch (e) {\n setErr(e instanceof Error ? e.message : 'challenge failed');\n } finally {\n setBusy(false);\n }\n }\n\n async function postLink(sig: string, confirmTransfer: boolean): Promise<void> {\n setBusy(true);\n setErr(null);\n try {\n const r = await fetch(`${authOrigin}/api/auth/link/btc`, {\n method: 'POST',\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n message: challenge,\n signature: sig,\n ...(confirmTransfer ? { confirm_transfer: true } : {}),\n }),\n });\n const j = (await r.json()) as { ok?: boolean; reason?: string };\n if (r.status === 409 && j.reason === 'address_linked_elsewhere') {\n setPendingSig(sig);\n setBusy(false);\n return;\n }\n if (handleSudoRequired(j, { purpose: 'link a Bitcoin address' })) return;\n if (isSudoAccountMismatch(j)) {\n setErr(SUDO_ACCOUNT_MISMATCH_MESSAGE);\n setBusy(false);\n return;\n }\n if (!r.ok || !j.ok) throw new Error(j.reason ?? `http_${r.status}`);\n onDone();\n } catch (e) {\n setErr(e instanceof Error ? e.message : 'link failed');\n setBusy(false);\n }\n }\n\n return (\n <div style={panelStyle('accent')} data-oc-li-form=\"btc\">\n <SectionLabel>§ link a Bitcoin address</SectionLabel>\n {step === 'request' ? (\n <>\n <p style={bodyStyle}>\n Enter a Bitcoin address you control. We generate a challenge and your\n wallet signs it (BIP-322).\n </p>\n <input\n type=\"text\"\n value={addr}\n onChange={(e) => setAddr(e.target.value.trim())}\n placeholder=\"bc1q…\"\n autoCapitalize=\"none\"\n spellCheck={false}\n style={{ ...inputStyle, fontFamily: SIGNIN_FONT }}\n />\n {err && <ErrorLine>{err}</ErrorLine>}\n <FormButtons>\n <button\n type=\"button\"\n onClick={() => void getChallenge()}\n disabled={busy || addr.length < 14}\n style={primaryBtnStyle(busy || addr.length < 14)}\n >\n {busy ? 'requesting…' : 'get challenge'}\n </button>\n <button type=\"button\" onClick={onCancel} style={ghostBtnStyle(busy)}>\n cancel\n </button>\n </FormButtons>\n </>\n ) : pendingSig ? (\n <TransferConfirm\n what=\"bitcoin address\"\n busy={busy}\n onConfirm={() => void postLink(pendingSig, true)}\n onCancel={onCancel}\n err={err}\n />\n ) : (\n <>\n <p style={bodyStyle}>\n Sign the challenge with the wallet that controls{' '}\n <span style={{ fontFamily: SIGNIN_FONT, wordBreak: 'break-all' }}>{addr}</span>.\n </p>\n <LazyWalletButton\n address={addr}\n message={challenge}\n showManual\n layout=\"list\"\n heading={null}\n onSigned={(sig: string) => void postLink(sig, false)}\n onError={(e: { message?: string }) =>\n setErr(e?.message ?? 'wallet rejected')\n }\n />\n {err && <ErrorLine>{err}</ErrorLine>}\n <FormButtons>\n <button\n type=\"button\"\n onClick={() => {\n setStep('request');\n setErr(null);\n }}\n disabled={busy}\n style={ghostBtnStyle(busy)}\n >\n back\n </button>\n </FormButtons>\n </>\n )}\n </div>\n );\n}\n\n/* --- transfer-under-dual-proof confirm --- */\n\nfunction TransferConfirm({\n what,\n busy,\n onConfirm,\n onCancel,\n err,\n}: {\n what: string;\n busy: boolean;\n onConfirm: () => void;\n onCancel: () => void;\n err: string | null;\n}): React.ReactElement {\n return (\n <>\n <p style={bodyStyle}>\n This {what} is already linked to another OrangeCheck account. You&apos;ve just\n proven control of it — you can transfer the link to this account under dual proof\n of control. The other account loses this sign-in path; its history stays\n verifiable. Continue?\n </p>\n {err && <ErrorLine>{err}</ErrorLine>}\n <FormButtons>\n <button\n type=\"button\"\n onClick={onConfirm}\n disabled={busy}\n style={dangerBtnStyle(busy)}\n >\n {busy ? 'transferring…' : 'transfer link here'}\n </button>\n <button type=\"button\" onClick={onCancel} disabled={busy} style={ghostBtnStyle(busy)}>\n cancel\n </button>\n </FormButtons>\n </>\n );\n}\n\n/* --- lazy wallet button · wallet-adapter loads on demand, not with the page --- */\n\ninterface WalletButtonProps {\n address: string;\n message: string;\n showManual?: boolean;\n layout?: string;\n heading?: React.ReactNode;\n onSigned: (sig: string) => void;\n onError: (err: { message?: string }, walletId?: string) => void;\n}\n\nexport function LazyWalletButton(props: WalletButtonProps): React.ReactElement {\n const [Comp, setComp] = React.useState<React.ComponentType<WalletButtonProps> | null>(null);\n const [failed, setFailed] = React.useState(false);\n\n React.useEffect(() => {\n let alive = true;\n // A bundler resolves this specifier at build time, so wallet-adapter\n // is a real dependency; only the chunk is deferred.\n import('@orangecheck/wallet-adapter/react')\n .then((m) => {\n if (alive) {\n setComp(\n () => (m as { OcWalletButton: React.ComponentType<WalletButtonProps> })\n .OcWalletButton\n );\n }\n })\n .catch(() => {\n if (alive) setFailed(true);\n });\n return () => {\n alive = false;\n };\n }, []);\n\n if (failed) {\n return (\n <ErrorLine>\n could not load wallet support · refresh to try again\n </ErrorLine>\n );\n }\n if (!Comp) return <span style={hintStyle}>loading wallet options…</span>;\n return <Comp {...props} />;\n}\n\n/* --- helpers --- */\n\nfunction mask(value: string, kind: 'email' | 'btc'): string {\n if (kind === 'email') {\n const [user, host] = value.split('@');\n if (!user || !host) return '••••';\n const m =\n user.length <= 2\n ? '•'.repeat(user.length)\n : user[0] + '•'.repeat(user.length - 2) + user.slice(-1);\n return `${m}@${host}`;\n }\n if (value.length <= 12) return '•'.repeat(value.length);\n return `${value.slice(0, 6)}…${value.slice(-4)}`;\n}\n\n/* --- presentational atoms · also used by bond.tsx; not re-exported from index --- */\n\nexport function SectionLabel({\n children,\n tone = 'primary',\n}: {\n children: React.ReactNode;\n tone?: 'primary' | 'success' | 'warn';\n}): React.ReactElement {\n const color = tone === 'success' ? V.success : tone === 'warn' ? V.warn : V.primary;\n return (\n <div\n style={{\n color,\n fontFamily: SIGNIN_FONT,\n fontSize: signinSize(10),\n letterSpacing: signinTracking('0.18em'),\n textTransform: SIGNIN_CASE,\n marginBottom: 8,\n }}\n >\n {children}\n </div>\n );\n}\n\nexport function ErrorLine({ children }: { children: React.ReactNode }): React.ReactElement {\n return (\n <p\n role=\"alert\"\n style={{\n margin: '8px 0 0',\n color: V.destructive,\n fontFamily: SIGNIN_FONT,\n fontSize: signinSize(11),\n }}\n >\n {children}\n </p>\n );\n}\n\nexport function FormButtons({ children }: { children: React.ReactNode }): React.ReactElement {\n return (\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8, marginTop: 12 }}>{children}</div>\n );\n}\n\n/* --- style tokens (CSS-variable-themed, same approach as OcSignIn) --- */\n\n\nexport const V = {\n primary: 'var(--primary, #f97316)',\n foreground: 'var(--foreground, #fafafa)',\n muted: 'var(--muted-foreground, #a1a1aa)',\n background: 'var(--background, #0a0a0a)',\n card: 'var(--card, #111113)',\n border: 'var(--border, #27272a)',\n destructive: 'var(--destructive, #ef4444)',\n success: 'var(--success, #22c55e)',\n warn: 'var(--warning, #eab308)',\n} as const;\n\nexport function panelStyle(tone: 'plain' | 'accent' | 'success' | 'warn' | 'default' = 'default'): React.CSSProperties {\n const base: React.CSSProperties = {\n background: V.card,\n border: `1px solid ${V.border}`,\n padding: '1.25rem',\n };\n if (tone === 'plain') return { ...base, padding: 0 };\n if (tone === 'accent') return { ...base, borderColor: V.primary, background: 'var(--card, #111113)' };\n if (tone === 'success') return { ...base, borderColor: V.success };\n if (tone === 'warn') return { ...base, borderColor: V.warn };\n return base;\n}\n\nexport const bodyStyle: React.CSSProperties = {\n color: V.muted,\n fontSize: 13,\n lineHeight: 1.55,\n margin: '0 0 12px',\n};\n\nexport const hintStyle: React.CSSProperties = {\n color: V.muted,\n fontFamily: SIGNIN_FONT,\n fontSize: signinSize(10.5),\n opacity: 0.8,\n};\n\nconst rowStyle: React.CSSProperties = {\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: 12,\n padding: '1rem',\n};\n\nconst rowLabelStyle: React.CSSProperties = {\n color: V.foreground,\n fontFamily: SIGNIN_FONT,\n fontSize: signinSize(10.5),\n letterSpacing: signinTracking('0.12em'),\n textTransform: SIGNIN_CASE,\n opacity: 0.8,\n};\n\nexport const inputStyle: React.CSSProperties = {\n width: '100%',\n padding: '0.55rem 0.75rem',\n background: V.background,\n color: V.foreground,\n border: `1px solid ${V.border}`,\n borderRadius: signinRadius(6),\n fontFamily: SIGNIN_FONT,\n // 16px — never lower · suppresses iOS Safari focus auto-zoom.\n fontSize: 16,\n boxSizing: 'border-box',\n};\n\nfunction btnBase(disabled: boolean): React.CSSProperties {\n return {\n padding: '0.5rem 0.875rem',\n borderRadius: SIGNIN_BUTTON_RADIUS,\n fontFamily: SIGNIN_FONT,\n fontSize: signinSize(10.5),\n letterSpacing: signinTracking('0.12em'),\n textTransform: SIGNIN_CASE,\n cursor: disabled ? 'not-allowed' : 'pointer',\n opacity: disabled ? 0.5 : 1,\n };\n}\n\nexport function primaryBtnStyle(disabled: boolean): React.CSSProperties {\n return {\n ...btnBase(disabled),\n background: V.primary,\n color: 'var(--primary-foreground, #0a0a0a)',\n border: `1px solid ${V.primary}`,\n fontWeight: 600,\n };\n}\n\nexport function ghostBtnStyle(disabled: boolean): React.CSSProperties {\n return {\n ...btnBase(disabled),\n background: V.background,\n color: V.foreground,\n border: `1px solid ${V.border}`,\n };\n}\n\nexport function dangerBtnStyle(disabled: boolean): React.CSSProperties {\n return {\n ...btnBase(disabled),\n background: 'transparent',\n color: V.destructive,\n border: `1px solid ${V.destructive}`,\n };\n}\n\nconst primaryChipStyle: React.CSSProperties = {\n ...btnBase(false),\n background: 'transparent',\n color: V.primary,\n border: `1px solid ${V.primary}`,\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AA6BA,YAAY,WAAW;AAyIP,SA4HR,UA5HQ,KACA,YADA;AAxHhB,IAAM,sBAAsB;AAsC5B,eAAsB,wBAAwB,MAEd;AAC5B,QAAM,aAAa,MAAM,cAAc;AAMvC,QAAM,IAAI,MAAM,MAAM,GAAG,UAAU,wBAAwB;AAAA,IACvD,aAAa;AAAA,IACb,SAAS,EAAE,GAAG,iBAAiB,EAAE;AAAA,EACrC,CAAC;AACD,MAAI,EAAE,WAAW,IAAK,QAAO,CAAC;AAC9B,MAAI,CAAC,EAAE,GAAI,OAAM,IAAI,MAAM,iCAAiC,EAAE,MAAM,EAAE;AACtE,QAAM,OAAQ,MAAM,EAAE,KAAK;AAC3B,SAAO,KAAK,UAAU,CAAC;AAC3B;AAoBO,SAAS,mBAAmB;AAAA,EAC/B,aAAa;AAAA,EACb;AAAA,EACA;AAAA,EACA,kBAAkB;AACtB,GAAgD;AAC5C,QAAM,CAAC,MAAM,OAAO,IAAU,eAAgC,IAAI;AAClE,QAAM,CAAC,OAAO,QAAQ,IAAU,eAAwB,IAAI;AAC5D,QAAM,CAAC,eAAe,gBAAgB,IAAU,eAAS,KAAK;AAC9D,QAAM,CAAC,aAAa,cAAc,IAAU,eAAS,KAAK;AAE1D,QAAM,UAAgB,kBAAY,YAAY;AAC1C,aAAS,IAAI;AACb,QAAI;AACA,YAAM,IAAI,MAAM,MAAM,GAAG,UAAU,wBAAwB;AAAA,QACvD,aAAa;AAAA,QACb,SAAS,EAAE,GAAG,iBAAiB,EAAE;AAAA,MACrC,CAAC;AACD,UAAI,CAAC,EAAE,IAAI;AACP,YAAI,EAAE,WAAW,KAAK;AAClB,mBAAS,eAAe;AACxB;AAAA,QACJ;AACA,cAAM,IAAI,MAAM,QAAQ,EAAE,MAAM,EAAE;AAAA,MACtC;AACA,cAAS,MAAM,EAAE,KAAK,CAAoB;AAAA,IAC9C,SAAS,GAAG;AACR,eAAS,aAAa,QAAQ,EAAE,UAAU,aAAa;AAAA,IAC3D;AAAA,EACJ,GAAG,CAAC,UAAU,CAAC;AAEf,EAAM,gBAAU,MAAM;AAClB,SAAK,QAAQ;AAAA,EACjB,GAAG,CAAC,OAAO,CAAC;AAEZ,QAAM,cAAoB,kBAAY,MAAM;AACxC,qBAAiB,KAAK;AACtB,mBAAe,KAAK;AACpB,SAAK,QAAQ;AACb,eAAW;AAAA,EACf,GAAG,CAAC,SAAS,QAAQ,CAAC;AAEtB,MAAI,OAAO;AACP,WACI,qBAAC,SAAI,WAAsB,cAAW,IAAG,OAAO,WAAW,MAAM,GAAG,MAAK,SACrE;AAAA,0BAAC,gBAAa,MAAK,QAAO,gDAA4B;AAAA,MACtD,qBAAC,OAAE,OAAO,WAAW;AAAA;AAAA,QACmB;AAAA,QAAM;AAAA,SAE9C;AAAA,MACA,oBAAC,YAAO,MAAK,UAAS,SAAS,MAAM,KAAK,QAAQ,GAAG,OAAO,cAAc,KAAK,GAAG,mBAElF;AAAA,OACJ;AAAA,EAER;AAEA,MAAI,CAAC,MAAM;AACP,WACI,oBAAC,SAAI,WAAsB,cAAW,IAAG,OAAO,WAAW,GACvD,8BAAC,UAAK,OAAO,WAAW,2BAAQ,GACpC;AAAA,EAER;AAEA,QAAM,SAAS,KAAK,UAAU,CAAC;AAC/B,QAAM,YAAY,OAAO,OAAO,CAAC,MAAM,EAAE,SAAS,OAAO;AACzD,QAAM,UAAU,OAAO,OAAO,CAAC,MAAM,EAAE,SAAS,KAAK;AACrD,QAAM,WAAW,UAAU,KAAK,CAAC,MAAM,EAAE,KAAK;AAC9C,QAAM,SAAS,QAAQ,KAAK,CAAC,MAAM,EAAE,KAAK;AAC1C,QAAM,aAAa,KAAK,eAAe,CAAC;AAExC,SACI,qBAAC,SAAI,WAAsB,cAAW,IAAG,OAAO,EAAE,SAAS,QAAQ,KAAK,GAAG,GACvE;AAAA,yBAAC,SAAI,OAAO,WAAW,GACnB;AAAA,0BAAC,gBAAa,+BAAc;AAAA,MAC5B,oBAAC,OAAE,OAAO,WAAW,iNAIrB;AAAA,OACJ;AAAA,IAEC,WAAW,SAAS,KAAK,oBAAC,sBAAmB,SAAS,YAAY;AAAA,IAEnE,qBAAC,SAAI,OAAO,WAAW,OAAO,GAAG,mBAAgB,IAC7C;AAAA;AAAA,QAAC;AAAA;AAAA,UACG,MAAK;AAAA,UACL,OAAM;AAAA,UACN,MAAM;AAAA,UACN;AAAA,UACA,UAAU;AAAA,UACV,QAAQ,WAAW,OAAO;AAAA,UAC1B,OAAO,MAAM;AACT,6BAAiB,CAAC,MAAM,CAAC,CAAC;AAC1B,2BAAe,KAAK;AAAA,UACxB;AAAA;AAAA,MACJ;AAAA,MACA,oBAAC,SAAI,OAAO,EAAE,WAAW,aAAa,EAAE,MAAM,GAAG,GAAG;AAAA,MACpD;AAAA,QAAC;AAAA;AAAA,UACG,MAAK;AAAA,UACL,OAAM;AAAA,UACN,MAAM;AAAA,UACN;AAAA,UACA,UAAU;AAAA,UACV,QAAQ,SAAS,OAAO;AAAA,UACxB,OAAO,MAAM;AACT,2BAAe,CAAC,MAAM,CAAC,CAAC;AACxB,6BAAiB,KAAK;AAAA,UAC1B;AAAA;AAAA,MACJ;AAAA,OACJ;AAAA,IAEC,iBACG;AAAA,MAAC;AAAA;AAAA,QACG;AAAA,QACA,QAAQ;AAAA,QACR,UAAU,MAAM,iBAAiB,KAAK;AAAA;AAAA,IAC1C;AAAA,IAEH,eACG;AAAA,MAAC;AAAA;AAAA,QACG;AAAA,QACA,OAAO,KAAK,UAAU;AAAA,QACtB,QAAQ;AAAA,QACR,UAAU,MAAM,eAAe,KAAK;AAAA;AAAA,IACxC;AAAA,IAGH,UAAU,mBAAmB,oBAAC,mBAAgB;AAAA,KACnD;AAER;AAIA,SAAS,cAAc;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ,GAQuB;AACnB,MAAI,KAAK,WAAW,GAAG;AACnB,WACI,qBAAC,SAAI,OAAO,UACR;AAAA,2BAAC,SACG;AAAA,6BAAC,SAAI,OAAO,eAAe;AAAA;AAAA,UAAG;AAAA,WAAM;AAAA,QACpC,oBAAC,SAAI,OAAO,EAAE,GAAG,WAAW,WAAW,EAAE,GAAG,yBAAW;AAAA,SAC3D;AAAA,MACC,UACG,qBAAC,YAAO,MAAK,UAAS,SAAS,OAAO,OAAO,kBAAkB;AAAA;AAAA,QACxD;AAAA,SACP;AAAA,OAER;AAAA,EAER;AACA,SACI,gCACK,eAAK,IAAI,CAAC,MACP;AAAA,IAAC;AAAA;AAAA,MAEG,KAAK;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA;AAAA,IAJK,EAAE;AAAA,EAKX,CACH,GACL;AAER;AAIA,SAAS,YAAY;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ,GAKuB;AACnB,QAAM,CAAC,OAAO,QAAQ,IAAU,eAAS,KAAK;AAC9C,QAAM,CAAC,YAAY,aAAa,IAAU,eAAS,KAAK;AACxD,QAAM,CAAC,MAAM,OAAO,IAAU,eAAS,KAAK;AAC5C,QAAM,CAAC,KAAK,MAAM,IAAU,eAAwB,IAAI;AAExD,QAAM,SAAS,IAAI,QAAQ,KAAK,IAAI,OAAO,IAAI,IAAI;AACnD,QAAM,UAAU,SAAS,IAAI,QAAQ,IAAI,QAAQ;AAEjD,iBAAe,SAAwB;AACnC,QAAI,IAAI,WAAY;AACpB,YAAQ,IAAI;AACZ,WAAO,IAAI;AACX,QAAI;AACA,YAAM,IAAI,MAAM;AAAA,QACZ,GAAG,UAAU,2BAA2B,mBAAmB,IAAI,EAAE,CAAC;AAAA,QAClE,EAAE,QAAQ,UAAU,aAAa,UAAU;AAAA,MAC/C;AACA,UAAI,CAAC,EAAE,IAAI;AACP,cAAM,IAAK,MAAM,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC1C,cAAM,IAAI,MAAM,EAAE,UAAU,QAAQ,EAAE,MAAM,EAAE;AAAA,MAClD;AACA,eAAS;AAAA,IACb,SAAS,GAAG;AACR,aAAO,aAAa,QAAQ,EAAE,UAAU,eAAe;AACvD,cAAQ,KAAK;AACb,oBAAc,KAAK;AAAA,IACvB;AAAA,EACJ;AAEA,SACI,qBAAC,SAAI,OAAO,UAAU,kBAAgB,MAClC;AAAA,yBAAC,SAAI,OAAO,EAAE,UAAU,GAAG,MAAM,EAAE,GAC/B;AAAA,2BAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,UAAU,QAAQ,KAAK,WAAW,GAC7D;AAAA,6BAAC,UAAK,OAAO,eAAe;AAAA;AAAA,UACrB,SAAS,UAAU,UAAU;AAAA,WACpC;AAAA,QACC,IAAI,cACD,oBAAC,UAAK,OAAO,EAAE,GAAG,eAAe,OAAO,EAAE,QAAQ,GAAG,sCAErD;AAAA,SAER;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACG,OAAO;AAAA,YACH,WAAW;AAAA,YACX,OAAO,EAAE;AAAA,YACT,YAAY;AAAA,YACZ,UAAU,WAAW,EAAE;AAAA,YACvB,WAAW;AAAA,UACf;AAAA,UAEC;AAAA;AAAA,MACL;AAAA,MACC,IAAI,eACD,qBAAC,SAAI,OAAO,EAAE,GAAG,WAAW,WAAW,EAAE,GAAG;AAAA;AAAA,QAC5B,IAAI,KAAK,IAAI,WAAW,EAAE,YAAY;AAAA,SACtD;AAAA,MAEH,OAAO,oBAAC,aAAW,eAAI;AAAA,OAC5B;AAAA,IACA,qBAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,YAAY,GAAG,KAAK,GAAG,UAAU,OAAO,GAClE;AAAA,UAAI,SACD;AAAA,QAAC;AAAA;AAAA,UACG,MAAK;AAAA,UACL,SAAS,MAAM,SAAS,CAAC,MAAM,CAAC,CAAC;AAAA,UACjC,OAAO,cAAc,KAAK;AAAA,UAEzB,kBAAQ,SAAS;AAAA;AAAA,MACtB;AAAA,MAEH,CAAC,IAAI,cAAc,CAAC,cACjB;AAAA,QAAC;AAAA;AAAA,UACG,MAAK;AAAA,UACL,SAAS,MAAM,cAAc,IAAI;AAAA,UACjC,OAAO,eAAe,KAAK;AAAA,UAC9B;AAAA;AAAA,MAED;AAAA,MAEH,CAAC,IAAI,cAAc,cAChB,iCACI;AAAA;AAAA,UAAC;AAAA;AAAA,YACG,MAAK;AAAA,YACL,SAAS,MAAM,KAAK,OAAO;AAAA,YAC3B,UAAU;AAAA,YACV,OAAO,eAAe,IAAI;AAAA,YAEzB,iBAAO,oBAAe;AAAA;AAAA,QAC3B;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACG,MAAK;AAAA,YACL,SAAS,MAAM,cAAc,KAAK;AAAA,YAClC,UAAU;AAAA,YACV,OAAO,cAAc,IAAI;AAAA,YAC5B;AAAA;AAAA,QAED;AAAA,SACJ;AAAA,OAER;AAAA,KACJ;AAER;AAIA,SAAS,mBAAmB,EAAE,QAAQ,GAAuD;AACzF,SACI,qBAAC,SAAI,OAAO,WAAW,SAAS,GAAG,2BAAwB,IACvD;AAAA,yBAAC,gBAAa,MAAK,WAAU;AAAA;AAAA,MACE,QAAQ;AAAA,OACvC;AAAA,IACA,qBAAC,OAAE,OAAO,WACL;AAAA,cAAQ,WAAW,IAAI,0BAA0B;AAAA,MAAuB;AAAA,MAEpB;AAAA,MACpD,QAAQ,WAAW,IAAI,OAAO;AAAA,MAAO;AAAA,OAC1C;AAAA,IACA,oBAAC,QAAG,OAAO,EAAE,WAAW,QAAQ,QAAQ,YAAY,SAAS,EAAE,GAC1D,kBAAQ,IAAI,CAAC,MACV;AAAA,MAAC;AAAA;AAAA,QAEG,OAAO;AAAA,UACH,SAAS;AAAA,UACT,UAAU;AAAA,UACV,KAAK;AAAA,UACL,SAAS;AAAA,UACT,WAAW,aAAa,EAAE,MAAM;AAAA,UAChC,YAAY;AAAA,UACZ,UAAU,WAAW,EAAE;AAAA,QAC3B;AAAA,QAEA;AAAA,8BAAC,UAAK,OAAO,EAAE,OAAO,EAAE,YAAY,WAAW,YAAY,GACtD,YAAE,QACP;AAAA,UACA,qBAAC,UAAK,OAAO,EAAE,GAAG,WAAW,YAAY,OAAO,GAAG;AAAA;AAAA,YACtC,IAAI,KAAK,EAAE,UAAU,EAAE,mBAAmB;AAAA,aACvD;AAAA;AAAA;AAAA,MAhBK,EAAE;AAAA,IAiBX,CACH,GACL;AAAA,KACJ;AAER;AAaA,SAAS,kBAAsC;AAC3C,SACI,qBAAC,SAAI,OAAO,WAAW,GAAG,qBAAkB,IACxC;AAAA,wBAAC,gBAAa,uCAAsB;AAAA,IACpC,oBAAC,OAAE,OAAO,WAAW,gTAKrB;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACG,MAAK;AAAA,QACL,QAAO;AAAA,QACP,KAAI;AAAA,QACJ,OAAO,EAAE,GAAG,kBAAkB,gBAAgB,QAAQ,SAAS,eAAe;AAAA,QAC9E,0BAAuB;AAAA,QAC1B;AAAA;AAAA,IAED;AAAA,KACJ;AAER;AAKA,IAAM,mBAAmB;AAclB,SAAS,eAAe;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ,GAOuB;AACnB,QAAM,OAAO,WAAW,QAAQ,sBAAsB;AACtD,SACI,qBAAC,SAAI,sBAAoB,QACrB;AAAA,yBAAC,SAAI,OAAO,EAAE,GAAG,WAAW,SAAS,GAAG,cAAc,GAAG,GACrD;AAAA,0BAAC,gBAAa,MAAK,WAAU,4BAAW;AAAA,MACxC,qBAAC,OAAE,OAAO,EAAE,GAAG,WAAW,QAAQ,EAAE,GAAG;AAAA;AAAA,QACX;AAAA,QAAK;AAAA,SAEjC;AAAA,OACJ;AAAA,IACC,WAAW,QACR;AAAA,MAAC;AAAA;AAAA,QACG;AAAA,QACA;AAAA,QACA,QAAQ;AAAA,QACR,UAAU;AAAA;AAAA,IACd,IAEA,oBAAC,iBAAc,YAAwB,QAAQ,YAAY,UAAU,YAAY;AAAA,IAErF;AAAA,MAAC;AAAA;AAAA,QACG,2BAAwB;AAAA,QACxB,OAAO,EAAE,WAAW,IAAI,YAAY,IAAI,WAAW,aAAa,EAAE,MAAM,GAAG;AAAA,QAE3E;AAAA,8BAAC,YAAO,MAAK,UAAS,SAAS,YAAY,OAAO,cAAc,KAAK,GAAG,4CAExE;AAAA,UACA,qBAAC,OAAE,OAAO,EAAE,GAAG,WAAW,WAAW,EAAE,GAAG;AAAA;AAAA,YAEZ;AAAA,YAAK;AAAA,YAAuC;AAAA,YACtE;AAAA,cAAC;AAAA;AAAA,gBACG,MAAM;AAAA,gBACN,QAAO;AAAA,gBACP,KAAI;AAAA,gBACJ,OAAO,EAAE,OAAO,UAAU;AAAA,gBAC1B,8BAA2B;AAAA,gBAC9B;AAAA;AAAA,YAED;AAAA,YAAI;AAAA,aAER;AAAA;AAAA;AAAA,IACJ;AAAA,KACJ;AAER;AAIA,SAAS,cAAc;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AACJ,GAIuB;AACnB,QAAM,CAAC,MAAM,OAAO,IAAU,eAA2B,OAAO;AAChE,QAAM,CAAC,OAAO,QAAQ,IAAU,eAAS,EAAE;AAC3C,QAAM,CAAC,MAAM,OAAO,IAAU,eAAS,EAAE;AACzC,QAAM,CAAC,OAAO,QAAQ,IAAU,eAAS,EAAE;AAC3C,QAAM,CAAC,MAAM,OAAO,IAAU,eAAS,KAAK;AAC5C,QAAM,CAAC,KAAK,MAAM,IAAU,eAAwB,IAAI;AAGxD,QAAM,CAAC,UAAU,WAAW,IAAU,eAAS,KAAK;AAEpD,iBAAe,QAAuB;AAClC,YAAQ,IAAI;AACZ,WAAO,IAAI;AACX,QAAI;AACA,YAAM,IAAI,MAAM,MAAM,GAAG,UAAU,6BAA6B;AAAA,QAC5D,QAAQ;AAAA,QACR,aAAa;AAAA,QACb,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,OAAO,MAAM,KAAK,EAAE,YAAY,EAAE,CAAC;AAAA,MAC9D,CAAC;AACD,YAAM,IAAK,MAAM,EAAE,KAAK;AACxB,UAAI,CAAC,EAAE,MAAM,CAAC,EAAE,MAAO,OAAM,IAAI,MAAM,EAAE,UAAU,QAAQ,EAAE,MAAM,EAAE;AACrE,eAAS,EAAE,KAAK;AAChB,cAAQ,MAAM;AAAA,IAClB,SAAS,GAAG;AACR,aAAO,aAAa,QAAQ,EAAE,UAAU,qBAAqB;AAAA,IACjE,UAAE;AACE,cAAQ,KAAK;AAAA,IACjB;AAAA,EACJ;AAEA,iBAAe,OAAO,iBAAyC;AAC3D,YAAQ,IAAI;AACZ,WAAO,IAAI;AACX,QAAI;AACA,YAAM,IAAI,MAAM,MAAM,GAAG,UAAU,wBAAwB;AAAA,QACvD,QAAQ;AAAA,QACR,aAAa;AAAA,QACb,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU;AAAA,UACjB,OAAO,MAAM,KAAK,EAAE,YAAY;AAAA,UAChC,MAAM,KAAK,KAAK;AAAA,UAChB;AAAA,UACA,GAAI,kBAAkB,EAAE,kBAAkB,KAAK,IAAI,CAAC;AAAA,QACxD,CAAC;AAAA,MACL,CAAC;AACD,YAAM,IAAK,MAAM,EAAE,KAAK;AACxB,UAAI,EAAE,WAAW,OAAO,EAAE,WAAW,0BAA0B;AAC3D,oBAAY,IAAI;AAChB,gBAAQ,KAAK;AACb;AAAA,MACJ;AACA,UAAI,mBAAmB,GAAG,EAAE,SAAS,gBAAgB,CAAC,EAAG;AACzD,UAAI,sBAAsB,CAAC,GAAG;AAC1B,eAAO,6BAA6B;AACpC,gBAAQ,KAAK;AACb;AAAA,MACJ;AACA,UAAI,CAAC,EAAE,MAAM,CAAC,EAAE,GAAI,OAAM,IAAI,MAAM,EAAE,UAAU,QAAQ,EAAE,MAAM,EAAE;AAClE,aAAO;AAAA,IACX,SAAS,GAAG;AACR,aAAO,aAAa,QAAQ,EAAE,UAAU,aAAa;AACrD,cAAQ,KAAK;AAAA,IACjB;AAAA,EACJ;AAEA,SACI,qBAAC,SAAI,OAAO,WAAW,QAAQ,GAAG,mBAAgB,SAC9C;AAAA,wBAAC,gBAAa,gCAAe;AAAA,IAC5B,SAAS,UACN,iCACI;AAAA;AAAA,QAAC;AAAA;AAAA,UACG,MAAK;AAAA,UACL,cAAa;AAAA,UACb,OAAO;AAAA,UACP,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,UACxC,aAAY;AAAA,UACZ,OAAO;AAAA;AAAA,MACX;AAAA,MACC,OAAO,oBAAC,aAAW,eAAI;AAAA,MACxB,qBAAC,eACG;AAAA;AAAA,UAAC;AAAA;AAAA,YACG,MAAK;AAAA,YACL,SAAS,MAAM,KAAK,MAAM;AAAA,YAC1B,UAAU,QAAQ,MAAM,KAAK,EAAE,SAAS;AAAA,YACxC,OAAO,gBAAgB,QAAQ,MAAM,KAAK,EAAE,SAAS,CAAC;AAAA,YAErD,iBAAO,kBAAa;AAAA;AAAA,QACzB;AAAA,QACA,oBAAC,YAAO,MAAK,UAAS,SAAS,UAAU,OAAO,cAAc,IAAI,GAAG,oBAErE;AAAA,SACJ;AAAA,OACJ,IACA,WACA;AAAA,MAAC;AAAA;AAAA,QACG,MAAK;AAAA,QACL;AAAA,QACA,WAAW,MAAM,KAAK,OAAO,IAAI;AAAA,QACjC;AAAA,QACA;AAAA;AAAA,IACJ,IAEA,iCACI;AAAA,2BAAC,OAAE,OAAO,WAAW;AAAA;AAAA,QAAgC;AAAA,QAAM;AAAA,SAAC;AAAA,MAC5D;AAAA,QAAC;AAAA;AAAA,UACG,MAAK;AAAA,UACL,WAAU;AAAA,UACV,cAAa;AAAA,UACb,WAAW;AAAA,UACX,OAAO;AAAA,UACP,UAAU,CAAC,MAAM,QAAQ,EAAE,OAAO,MAAM,QAAQ,OAAO,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAAA,UACtE,aAAY;AAAA,UACZ,OAAO,EAAE,GAAG,YAAY,eAAe,QAAQ;AAAA;AAAA,MACnD;AAAA,MACC,OAAO,oBAAC,aAAW,eAAI;AAAA,MACxB,qBAAC,eACG;AAAA;AAAA,UAAC;AAAA;AAAA,YACG,MAAK;AAAA,YACL,SAAS,MAAM,KAAK,OAAO,KAAK;AAAA,YAChC,UAAU,QAAQ,KAAK,WAAW;AAAA,YAClC,OAAO,gBAAgB,QAAQ,KAAK,WAAW,CAAC;AAAA,YAE/C,iBAAO,kBAAa;AAAA;AAAA,QACzB;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACG,MAAK;AAAA,YACL,SAAS,MAAM,QAAQ,OAAO;AAAA,YAC9B,UAAU;AAAA,YACV,OAAO,cAAc,IAAI;AAAA,YAC5B;AAAA;AAAA,QAED;AAAA,SACJ;AAAA,OACJ;AAAA,KAER;AAER;AAIA,SAAS,YAAY;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ,GAKuB;AACnB,QAAM,CAAC,MAAM,OAAO,IAAU,eAA6B,SAAS;AACpE,QAAM,CAAC,MAAM,OAAO,IAAU,eAAS,EAAE;AACzC,QAAM,CAAC,WAAW,YAAY,IAAU,eAAS,EAAE;AACnD,QAAM,CAAC,MAAM,OAAO,IAAU,eAAS,KAAK;AAC5C,QAAM,CAAC,KAAK,MAAM,IAAU,eAAwB,IAAI;AAGxD,QAAM,CAAC,YAAY,aAAa,IAAU,eAAwB,IAAI;AAEtE,iBAAe,eAA8B;AACzC,QAAI,CAAC,OAAO;AACR,aAAO,+CAA4C;AACnD;AAAA,IACJ;AACA,YAAQ,IAAI;AACZ,WAAO,IAAI;AACX,QAAI;AAKA,YAAM,IAAI,MAAM;AAAA,QACZ,GAAG,UAAU,uBAAuB,mBAAmB,IAAI,CAAC,0BAC9B,mBAAmB,KAAK,CAAC;AAAA,MAC3D;AACA,YAAM,IAAK,MAAM,EAAE,KAAK;AACxB,UAAI,CAAC,EAAE,MAAM,CAAC,EAAE,QAAS,OAAM,IAAI,MAAM,EAAE,UAAU,QAAQ,EAAE,MAAM,EAAE;AACvE,mBAAa,EAAE,OAAO;AACtB,cAAQ,MAAM;AAAA,IAClB,SAAS,GAAG;AACR,aAAO,aAAa,QAAQ,EAAE,UAAU,kBAAkB;AAAA,IAC9D,UAAE;AACE,cAAQ,KAAK;AAAA,IACjB;AAAA,EACJ;AAEA,iBAAe,SAAS,KAAa,iBAAyC;AAC1E,YAAQ,IAAI;AACZ,WAAO,IAAI;AACX,QAAI;AACA,YAAM,IAAI,MAAM,MAAM,GAAG,UAAU,sBAAsB;AAAA,QACrD,QAAQ;AAAA,QACR,aAAa;AAAA,QACb,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU;AAAA,UACjB,SAAS;AAAA,UACT,WAAW;AAAA,UACX,GAAI,kBAAkB,EAAE,kBAAkB,KAAK,IAAI,CAAC;AAAA,QACxD,CAAC;AAAA,MACL,CAAC;AACD,YAAM,IAAK,MAAM,EAAE,KAAK;AACxB,UAAI,EAAE,WAAW,OAAO,EAAE,WAAW,4BAA4B;AAC7D,sBAAc,GAAG;AACjB,gBAAQ,KAAK;AACb;AAAA,MACJ;AACA,UAAI,mBAAmB,GAAG,EAAE,SAAS,yBAAyB,CAAC,EAAG;AAClE,UAAI,sBAAsB,CAAC,GAAG;AAC1B,eAAO,6BAA6B;AACpC,gBAAQ,KAAK;AACb;AAAA,MACJ;AACA,UAAI,CAAC,EAAE,MAAM,CAAC,EAAE,GAAI,OAAM,IAAI,MAAM,EAAE,UAAU,QAAQ,EAAE,MAAM,EAAE;AAClE,aAAO;AAAA,IACX,SAAS,GAAG;AACR,aAAO,aAAa,QAAQ,EAAE,UAAU,aAAa;AACrD,cAAQ,KAAK;AAAA,IACjB;AAAA,EACJ;AAEA,SACI,qBAAC,SAAI,OAAO,WAAW,QAAQ,GAAG,mBAAgB,OAC9C;AAAA,wBAAC,gBAAa,yCAAwB;AAAA,IACrC,SAAS,YACN,iCACI;AAAA,0BAAC,OAAE,OAAO,WAAW,8GAGrB;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACG,MAAK;AAAA,UACL,OAAO;AAAA,UACP,UAAU,CAAC,MAAM,QAAQ,EAAE,OAAO,MAAM,KAAK,CAAC;AAAA,UAC9C,aAAY;AAAA,UACZ,gBAAe;AAAA,UACf,YAAY;AAAA,UACZ,OAAO,EAAE,GAAG,YAAY,YAAY,YAAY;AAAA;AAAA,MACpD;AAAA,MACC,OAAO,oBAAC,aAAW,eAAI;AAAA,MACxB,qBAAC,eACG;AAAA;AAAA,UAAC;AAAA;AAAA,YACG,MAAK;AAAA,YACL,SAAS,MAAM,KAAK,aAAa;AAAA,YACjC,UAAU,QAAQ,KAAK,SAAS;AAAA,YAChC,OAAO,gBAAgB,QAAQ,KAAK,SAAS,EAAE;AAAA,YAE9C,iBAAO,qBAAgB;AAAA;AAAA,QAC5B;AAAA,QACA,oBAAC,YAAO,MAAK,UAAS,SAAS,UAAU,OAAO,cAAc,IAAI,GAAG,oBAErE;AAAA,SACJ;AAAA,OACJ,IACA,aACA;AAAA,MAAC;AAAA;AAAA,QACG,MAAK;AAAA,QACL;AAAA,QACA,WAAW,MAAM,KAAK,SAAS,YAAY,IAAI;AAAA,QAC/C;AAAA,QACA;AAAA;AAAA,IACJ,IAEA,iCACI;AAAA,2BAAC,OAAE,OAAO,WAAW;AAAA;AAAA,QACgC;AAAA,QACjD,oBAAC,UAAK,OAAO,EAAE,YAAY,aAAa,WAAW,YAAY,GAAI,gBAAK;AAAA,QAAO;AAAA,SACnF;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACG,SAAS;AAAA,UACT,SAAS;AAAA,UACT,YAAU;AAAA,UACV,QAAO;AAAA,UACP,SAAS;AAAA,UACT,UAAU,CAAC,QAAgB,KAAK,SAAS,KAAK,KAAK;AAAA,UACnD,SAAS,CAAC,MACN,OAAO,GAAG,WAAW,iBAAiB;AAAA;AAAA,MAE9C;AAAA,MACC,OAAO,oBAAC,aAAW,eAAI;AAAA,MACxB,oBAAC,eACG;AAAA,QAAC;AAAA;AAAA,UACG,MAAK;AAAA,UACL,SAAS,MAAM;AACX,oBAAQ,SAAS;AACjB,mBAAO,IAAI;AAAA,UACf;AAAA,UACA,UAAU;AAAA,UACV,OAAO,cAAc,IAAI;AAAA,UAC5B;AAAA;AAAA,MAED,GACJ;AAAA,OACJ;AAAA,KAER;AAER;AAIA,SAAS,gBAAgB;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ,GAMuB;AACnB,SACI,iCACI;AAAA,yBAAC,OAAE,OAAO,WAAW;AAAA;AAAA,MACX;AAAA,MAAK;AAAA,OAIf;AAAA,IACC,OAAO,oBAAC,aAAW,eAAI;AAAA,IACxB,qBAAC,eACG;AAAA;AAAA,QAAC;AAAA;AAAA,UACG,MAAK;AAAA,UACL,SAAS;AAAA,UACT,UAAU;AAAA,UACV,OAAO,eAAe,IAAI;AAAA,UAEzB,iBAAO,uBAAkB;AAAA;AAAA,MAC9B;AAAA,MACA,oBAAC,YAAO,MAAK,UAAS,SAAS,UAAU,UAAU,MAAM,OAAO,cAAc,IAAI,GAAG,oBAErF;AAAA,OACJ;AAAA,KACJ;AAER;AAcO,SAAS,iBAAiB,OAA8C;AAC3E,QAAM,CAAC,MAAM,OAAO,IAAU,eAAwD,IAAI;AAC1F,QAAM,CAAC,QAAQ,SAAS,IAAU,eAAS,KAAK;AAEhD,EAAM,gBAAU,MAAM;AAClB,QAAI,QAAQ;AAGZ,WAAO,mCAAmC,EACrC,KAAK,CAAC,MAAM;AACT,UAAI,OAAO;AACP;AAAA,UACI,MAAO,EACF;AAAA,QACT;AAAA,MACJ;AAAA,IACJ,CAAC,EACA,MAAM,MAAM;AACT,UAAI,MAAO,WAAU,IAAI;AAAA,IAC7B,CAAC;AACL,WAAO,MAAM;AACT,cAAQ;AAAA,IACZ;AAAA,EACJ,GAAG,CAAC,CAAC;AAEL,MAAI,QAAQ;AACR,WACI,oBAAC,aAAU,qEAEX;AAAA,EAER;AACA,MAAI,CAAC,KAAM,QAAO,oBAAC,UAAK,OAAO,WAAW,0CAAuB;AACjE,SAAO,oBAAC,QAAM,GAAG,OAAO;AAC5B;AAIA,SAAS,KAAK,OAAe,MAA+B;AACxD,MAAI,SAAS,SAAS;AAClB,UAAM,CAAC,MAAM,IAAI,IAAI,MAAM,MAAM,GAAG;AACpC,QAAI,CAAC,QAAQ,CAAC,KAAM,QAAO;AAC3B,UAAM,IACF,KAAK,UAAU,IACT,SAAI,OAAO,KAAK,MAAM,IACtB,KAAK,CAAC,IAAI,SAAI,OAAO,KAAK,SAAS,CAAC,IAAI,KAAK,MAAM,EAAE;AAC/D,WAAO,GAAG,CAAC,IAAI,IAAI;AAAA,EACvB;AACA,MAAI,MAAM,UAAU,GAAI,QAAO,SAAI,OAAO,MAAM,MAAM;AACtD,SAAO,GAAG,MAAM,MAAM,GAAG,CAAC,CAAC,SAAI,MAAM,MAAM,EAAE,CAAC;AAClD;AAIO,SAAS,aAAa;AAAA,EACzB;AAAA,EACA,OAAO;AACX,GAGuB;AACnB,QAAM,QAAQ,SAAS,YAAY,EAAE,UAAU,SAAS,SAAS,EAAE,OAAO,EAAE;AAC5E,SACI;AAAA,IAAC;AAAA;AAAA,MACG,OAAO;AAAA,QACH;AAAA,QACA,YAAY;AAAA,QACZ,UAAU,WAAW,EAAE;AAAA,QACvB,eAAe,eAAe,QAAQ;AAAA,QACtC,eAAe;AAAA,QACf,cAAc;AAAA,MAClB;AAAA,MAEC;AAAA;AAAA,EACL;AAER;AAEO,SAAS,UAAU,EAAE,SAAS,GAAsD;AACvF,SACI;AAAA,IAAC;AAAA;AAAA,MACG,MAAK;AAAA,MACL,OAAO;AAAA,QACH,QAAQ;AAAA,QACR,OAAO,EAAE;AAAA,QACT,YAAY;AAAA,QACZ,UAAU,WAAW,EAAE;AAAA,MAC3B;AAAA,MAEC;AAAA;AAAA,EACL;AAER;AAEO,SAAS,YAAY,EAAE,SAAS,GAAsD;AACzF,SACI,oBAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,UAAU,QAAQ,KAAK,GAAG,WAAW,GAAG,GAAI,UAAS;AAE5F;AAKO,IAAM,IAAI;AAAA,EACb,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,SAAS;AAAA,EACT,MAAM;AACV;AAEO,SAAS,WAAW,OAA4D,WAAgC;AACnH,QAAM,OAA4B;AAAA,IAC9B,YAAY,EAAE;AAAA,IACd,QAAQ,aAAa,EAAE,MAAM;AAAA,IAC7B,SAAS;AAAA,EACb;AACA,MAAI,SAAS,QAAS,QAAO,EAAE,GAAG,MAAM,SAAS,EAAE;AACnD,MAAI,SAAS,SAAU,QAAO,EAAE,GAAG,MAAM,aAAa,EAAE,SAAS,YAAY,uBAAuB;AACpG,MAAI,SAAS,UAAW,QAAO,EAAE,GAAG,MAAM,aAAa,EAAE,QAAQ;AACjE,MAAI,SAAS,OAAQ,QAAO,EAAE,GAAG,MAAM,aAAa,EAAE,KAAK;AAC3D,SAAO;AACX;AAEO,IAAM,YAAiC;AAAA,EAC1C,OAAO,EAAE;AAAA,EACT,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,QAAQ;AACZ;AAEO,IAAM,YAAiC;AAAA,EAC1C,OAAO,EAAE;AAAA,EACT,YAAY;AAAA,EACZ,UAAU,WAAW,IAAI;AAAA,EACzB,SAAS;AACb;AAEA,IAAM,WAAgC;AAAA,EAClC,SAAS;AAAA,EACT,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,KAAK;AAAA,EACL,SAAS;AACb;AAEA,IAAM,gBAAqC;AAAA,EACvC,OAAO,EAAE;AAAA,EACT,YAAY;AAAA,EACZ,UAAU,WAAW,IAAI;AAAA,EACzB,eAAe,eAAe,QAAQ;AAAA,EACtC,eAAe;AAAA,EACf,SAAS;AACb;AAEO,IAAM,aAAkC;AAAA,EAC3C,OAAO;AAAA,EACP,SAAS;AAAA,EACT,YAAY,EAAE;AAAA,EACd,OAAO,EAAE;AAAA,EACT,QAAQ,aAAa,EAAE,MAAM;AAAA,EAC7B,cAAc,aAAa,CAAC;AAAA,EAC5B,YAAY;AAAA;AAAA,EAEZ,UAAU;AAAA,EACV,WAAW;AACf;AAEA,SAAS,QAAQ,UAAwC;AACrD,SAAO;AAAA,IACH,SAAS;AAAA,IACT,cAAc;AAAA,IACd,YAAY;AAAA,IACZ,UAAU,WAAW,IAAI;AAAA,IACzB,eAAe,eAAe,QAAQ;AAAA,IACtC,eAAe;AAAA,IACf,QAAQ,WAAW,gBAAgB;AAAA,IACnC,SAAS,WAAW,MAAM;AAAA,EAC9B;AACJ;AAEO,SAAS,gBAAgB,UAAwC;AACpE,SAAO;AAAA,IACH,GAAG,QAAQ,QAAQ;AAAA,IACnB,YAAY,EAAE;AAAA,IACd,OAAO;AAAA,IACP,QAAQ,aAAa,EAAE,OAAO;AAAA,IAC9B,YAAY;AAAA,EAChB;AACJ;AAEO,SAAS,cAAc,UAAwC;AAClE,SAAO;AAAA,IACH,GAAG,QAAQ,QAAQ;AAAA,IACnB,YAAY,EAAE;AAAA,IACd,OAAO,EAAE;AAAA,IACT,QAAQ,aAAa,EAAE,MAAM;AAAA,EACjC;AACJ;AAEO,SAAS,eAAe,UAAwC;AACnE,SAAO;AAAA,IACH,GAAG,QAAQ,QAAQ;AAAA,IACnB,YAAY;AAAA,IACZ,OAAO,EAAE;AAAA,IACT,QAAQ,aAAa,EAAE,WAAW;AAAA,EACtC;AACJ;AAEA,IAAM,mBAAwC;AAAA,EAC1C,GAAG,QAAQ,KAAK;AAAA,EAChB,YAAY;AAAA,EACZ,OAAO,EAAE;AAAA,EACT,QAAQ,aAAa,EAAE,OAAO;AAClC;","names":[]}
@@ -4,8 +4,8 @@ import {
4
4
  OcAddressInput,
5
5
  OcSignInButton,
6
6
  useOcAddressSuggestion
7
- } from "./chunk-7UYPMTAV.mjs";
8
- import "./chunk-7ST4Q5KR.mjs";
7
+ } from "./chunk-EZKAMTG7.mjs";
8
+ import "./chunk-E2XQ7YED.mjs";
9
9
  import "./chunk-VRLFZAGK.mjs";
10
10
  import "./chunk-CYR7J4UY.mjs";
11
11
  import "./chunk-TJJNTITX.mjs";