@synap-core/sdk 1.5.1 → 1.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/connect/guard.ts","../src/connect/errors.ts","../src/connect/http.ts","../src/connect/discovery.ts","../src/connect/seal.ts","../src/connect/cloud.ts","../src/connect/client.ts","../src/connect/flow.ts","../src/connect/store.ts","../src/connect/auth.ts","../src/connect/index.ts"],"sourcesContent":["/**\n * `@synap-core/sdk/connect` holds pod tokens and the app secret: it must never\n * run in a browser. Two guards:\n *\n * - BUILD: the package's `exports[\"./connect\"]` maps the `browser` condition\n * to `connect.browser` (src/connect/browser.ts), which exports nothing and\n * throws on load, so a client bundle never contains this code and a strict\n * ESM bundler fails on the missing named import. Server and edge\n * conditions (`edge-light`, `workerd`, `import`, `require`) get the real\n * module;\n * - RUNTIME: importing the real module where `window` and `document` exist\n * throws (a test DOM, a misconfigured bundler).\n */\nexport const BROWSER_REFUSAL =\n \"@synap-core/sdk/connect is server-only: it holds the pod's tokens and the app secret. \" +\n \"Import it from route handlers, proxy.ts or server code, never from a client component.\";\n\nexport function isBrowserRuntime(): boolean {\n const g = globalThis as { window?: unknown; document?: unknown };\n return typeof g.window !== \"undefined\" && typeof g.document !== \"undefined\";\n}\n\nexport function assertServerRuntime(): void {\n if (isBrowserRuntime()) throw new Error(BROWSER_REFUSAL);\n}\n","/**\n * The one error type of `@synap-core/sdk/connect`. `code` is the contract a UI\n * branches on; `message` and `detail` are for the developer's logs, never for\n * the person (a sign-in failure page says one sentence per code, never why).\n */\n\nexport type ConnectErrorCode =\n /** The typed pod address is not a usable URL. */\n | \"invalid_pod_address\"\n /** Something answered, but it is not a Synap pod (no AS metadata). */\n | \"not_a_pod\"\n /** The pod did not answer (network, timeout, 5xx). */\n | \"pod_unreachable\"\n /** A Synap pod, but too old for app sign-in (no CIMD, no `iss`, no S256). */\n | \"pod_unsupported\"\n /** Connecting through Synap Cloud is not configured for this app (no Control Plane URL). */\n | \"cp_unavailable\"\n /** The person said no on the consent screen. */\n | \"denied\"\n /** No sign-in in progress here, or it is older than 10 minutes. */\n | \"expired_transaction\"\n /** The answer does not belong to the sign-in this browser started (`state` or `iss`). */\n | \"mismatch\"\n /** The app's own setup is wrong (client metadata, scopes, secret, redirect). */\n | \"misconfigured\"\n /** The pod asked to slow down; see `retryAfterSec`. */\n | \"rate_limited\"\n /** The connection lapsed (refresh token idle past its life, or none was issued). */\n | \"expired\"\n /** The pod refused the connection's refresh token while it should still live. */\n | \"revoked\"\n /**\n * The pod answered a redirect where it must answer itself (a moved pod\n * answers 308 from its old name for a while): connect again with its new\n * address. The new address is never taken from the redirect.\n */\n | \"pod_moved\"\n /** The pod answered something the SDK cannot use. */\n | \"server_error\";\n\nexport class ConnectError extends Error {\n readonly code: ConnectErrorCode;\n /** Seconds to wait, from `Retry-After`, when `code` is `rate_limited`. */\n readonly retryAfterSec?: number;\n /** The OAuth error code the pod sent, when there was one (logs only). */\n readonly oauthError?: string;\n\n constructor(\n code: ConnectErrorCode,\n message: string,\n extra: { retryAfterSec?: number; oauthError?: string } = {},\n ) {\n super(message);\n this.name = \"ConnectError\";\n this.code = code;\n if (extra.retryAfterSec !== undefined) this.retryAfterSec = extra.retryAfterSec;\n if (extra.oauthError !== undefined) this.oauthError = extra.oauthError;\n }\n}\n\nexport function isConnectError(err: unknown): err is ConnectError {\n return err instanceof ConnectError;\n}\n\nexport const CONNECT_ERROR_CODES: readonly ConnectErrorCode[] = [\n \"invalid_pod_address\",\n \"not_a_pod\",\n \"pod_unreachable\",\n \"pod_unsupported\",\n \"cp_unavailable\",\n \"denied\",\n \"expired_transaction\",\n \"mismatch\",\n \"misconfigured\",\n \"rate_limited\",\n \"expired\",\n \"revoked\",\n \"pod_moved\",\n \"server_error\",\n];\n\n/** Parse an untrusted `?error=` value back into a code (null when unknown). */\nexport function parseConnectErrorCode(value: string | null | undefined): ConnectErrorCode | null {\n return (CONNECT_ERROR_CODES as readonly string[]).includes(value ?? \"\") ? (value as ConnectErrorCode) : null;\n}\n\n/**\n * RFC 6749 §4.1.2.1 / §5.2 error codes → ours. `invalid_grant` at the token\n * endpoint during a callback means the code was used, expired or does not\n * match: for the person that is \"the link expired\".\n */\nexport function fromOAuthError(oauthError: string, during: \"authorize\" | \"token\" | \"refresh\"): ConnectErrorCode {\n switch (oauthError) {\n case \"access_denied\":\n return \"denied\";\n case \"temporarily_unavailable\":\n case \"server_error\":\n return \"pod_unreachable\";\n case \"invalid_grant\":\n return during === \"refresh\" ? \"revoked\" : \"expired_transaction\";\n case \"invalid_request\":\n case \"invalid_client\":\n case \"invalid_scope\":\n case \"invalid_target\":\n case \"unauthorized_client\":\n case \"unsupported_grant_type\":\n case \"unsupported_response_type\":\n return \"misconfigured\";\n default:\n return \"server_error\";\n }\n}\n\n/** `Retry-After` as whole seconds (delta-seconds or an HTTP date); undefined when unusable. */\nexport function retryAfterSeconds(header: string | null, now: number): number | undefined {\n if (!header) return undefined;\n const trimmed = header.trim();\n if (/^\\d{1,9}$/.test(trimmed)) return Number(trimmed);\n const at = Date.parse(trimmed);\n if (Number.isNaN(at)) return undefined;\n return Math.max(0, Math.ceil((at - now) / 1000));\n}\n","/**\n * Reading answers from hosts the SDK does not control: a pod a visitor typed,\n * its token endpoint, its Hub API. Bounded in time (`timeoutSignal`) and in\n * bytes (`readCapped`): the bytes are counted on the stream, so an answer past\n * the cap is cancelled there, never buffered whole first.\n */\n\n/** An abort signal after `ms`, where the runtime has `AbortSignal.timeout` (workers and edge runtimes may not). */\nexport function timeoutSignal(ms: number): AbortSignal | undefined {\n return typeof AbortSignal.timeout === \"function\" ? AbortSignal.timeout(ms) : undefined;\n}\n\nexport type CappedRead = { ok: true; text: string } | { ok: false; reason: \"too_large\" | \"unreadable\" };\n\n/** The body as UTF-8 text, at most `maxBytes` bytes. */\nexport async function readCapped(res: Response, maxBytes: number): Promise<CappedRead> {\n const declared = Number(res.headers.get(\"content-length\"));\n if (Number.isFinite(declared) && declared > maxBytes) {\n await res.body?.cancel().catch(() => {});\n return { ok: false, reason: \"too_large\" };\n }\n if (!res.body) return { ok: true, text: \"\" };\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let total = 0;\n let text = \"\";\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n total += value.byteLength;\n if (total > maxBytes) {\n await reader.cancel().catch(() => {});\n return { ok: false, reason: \"too_large\" };\n }\n text += decoder.decode(value, { stream: true });\n }\n } catch {\n return { ok: false, reason: \"unreadable\" };\n }\n return { ok: true, text: text + decoder.decode() };\n}\n","/**\n * Pod discovery: a typed address or a launch `iss` → the pod's authorization\n * server metadata (RFC 8414), validated before any redirect.\n *\n * - the metadata's `issuer` must equal the address it was fetched for, byte\n * for byte (RFC 8414 §3.3): a document naming another issuer is not used;\n * - it must advertise CIMD clients, `iss` on every authorization response\n * (RFC 9207) and PKCE S256, else the pod is too old for app sign-in;\n * - every endpoint must live on the issuer's own origin.\n *\n * The fetch runs on the app's server with an address a visitor typed, so it is\n * fenced: https only (http only on loopback when `allowInsecureLoopback`), no\n * IP literal outside public space, no single-label or `.local`/`.internal`\n * host, no redirect followed, 5 s, 64 KB counted on the stream. At most three\n * addresses are tried per call (`discoverPod`). Residual: a public DNS name\n * that resolves to a private address cannot be refused without a resolver; a\n * deployment that cares adds egress rules, and an app rate-limits the route\n * that calls this (it fetches an address a visitor chose).\n *\n * Connecting through Synap Cloud (the Control Plane's pod chooser) is ./cloud.ts.\n */\nimport { ConnectError, retryAfterSeconds } from \"./errors\";\nimport { readCapped, timeoutSignal } from \"./http\";\nimport { CLOUD_STATE_RE, LOOPBACK_HOSTS, SPACE_HINT_RE } from \"./rules\";\nimport type { PodIssuer } from \"./types\";\n\nconst METADATA_PATH = \"/.well-known/oauth-authorization-server\";\nconst TIMEOUT_MS = 5_000;\nconst MAX_METADATA_BYTES = 64 * 1024;\n\nexport interface DiscoverOptions {\n fetch?: typeof fetch;\n /** Allow `http://localhost` (and 127.0.0.1, [::1]) pods: development only. */\n allowInsecureLoopback?: boolean;\n now?: () => number;\n}\n\nfunction isPrivateIpv4(host: string): boolean {\n const m = /^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/.exec(host);\n if (!m) return false;\n const [a, b] = [Number(m[1]), Number(m[2])];\n return (\n a === 0 ||\n a === 10 ||\n a === 127 ||\n (a === 100 && b >= 64 && b <= 127) ||\n (a === 169 && b === 254) ||\n (a === 172 && b >= 16 && b <= 31) ||\n (a === 192 && b === 168) ||\n a >= 224\n );\n}\n\n/**\n * The canonical issuer for a typed address, or a ConnectError. Adds `https://`\n * when no scheme is given; drops a trailing slash.\n */\nexport function canonicalPodUrl(input: string, opts: { allowInsecureLoopback?: boolean } = {}): string {\n const raw = input.trim();\n if (!raw || raw.length > 512) throw new ConnectError(\"invalid_pod_address\", \"Empty or overlong pod address\");\n let url: URL;\n try {\n url = new URL(/^[a-z][a-z0-9+.-]*:\\/\\//i.test(raw) ? raw : `https://${raw}`);\n } catch {\n throw new ConnectError(\"invalid_pod_address\", \"Not a URL\");\n }\n const host = url.hostname.toLowerCase();\n const loopback = LOOPBACK_HOSTS.has(host); // WHATWG keeps the brackets: \"[::1]\"\n if (url.protocol === \"http:\" && !(loopback && opts.allowInsecureLoopback)) {\n throw new ConnectError(\"invalid_pod_address\", \"A pod address must use https\");\n }\n if (url.protocol !== \"https:\" && url.protocol !== \"http:\") {\n throw new ConnectError(\"invalid_pod_address\", \"A pod address must use https\");\n }\n if (url.username || url.password || url.search || url.hash) {\n throw new ConnectError(\"invalid_pod_address\", \"A pod address has no credentials, query or fragment\");\n }\n if (!(loopback && opts.allowInsecureLoopback)) {\n if (loopback || host.startsWith(\"[\") || isPrivateIpv4(host) || /^\\d+$/.test(host)) {\n throw new ConnectError(\"invalid_pod_address\", \"A pod address must be a public host name\");\n }\n if (!host.includes(\".\") || /\\.(local|internal|localhost|lan|home\\.arpa)$/.test(host)) {\n throw new ConnectError(\"invalid_pod_address\", \"A pod address must be a public host name\");\n }\n }\n const path = url.pathname.replace(/\\/+$/, \"\");\n return `${url.origin}${path}`;\n}\n\nfunction metadataUrl(issuer: string): string {\n const url = new URL(issuer);\n const path = url.pathname === \"/\" ? \"\" : url.pathname;\n return `${url.origin}${METADATA_PATH}${path}`;\n}\n\nfunction sameOriginEndpoint(value: unknown, issuer: string): string | null {\n if (typeof value !== \"string\") return null;\n try {\n const url = new URL(value);\n return url.origin === new URL(issuer).origin && !url.username && !url.hash ? url.href : null;\n } catch {\n return null;\n }\n}\n\nasync function readJsonCapped(res: Response): Promise<unknown> {\n const read = await readCapped(res, MAX_METADATA_BYTES);\n if (!read.ok) {\n throw read.reason === \"too_large\"\n ? new ConnectError(\"not_a_pod\", \"Metadata document too large\")\n : new ConnectError(\"pod_unreachable\", \"The metadata answer broke off\");\n }\n try {\n return JSON.parse(read.text);\n } catch {\n throw new ConnectError(\"not_a_pod\", \"Metadata is not JSON\");\n }\n}\n\n/** Fetch and validate one candidate issuer's metadata. */\nasync function discoverIssuer(issuer: string, opts: DiscoverOptions): Promise<PodIssuer> {\n const doFetch = opts.fetch ?? fetch;\n let res: Response;\n try {\n res = await doFetch(metadataUrl(issuer), {\n method: \"GET\",\n headers: { Accept: \"application/json\" },\n redirect: \"manual\",\n signal: timeoutSignal(TIMEOUT_MS),\n });\n } catch {\n throw new ConnectError(\"pod_unreachable\", \"The pod did not answer\");\n }\n if (res.status >= 300 && res.status < 400) {\n await res.body?.cancel().catch(() => {});\n throw new ConnectError(\"pod_moved\", `The address answered ${res.status}: the pod moved (its new address is not followed)`);\n }\n if (res.status === 429) {\n throw new ConnectError(\"rate_limited\", \"The pod asked to slow down\", {\n retryAfterSec: retryAfterSeconds(res.headers.get(\"retry-after\"), (opts.now ?? Date.now)()),\n });\n }\n if (res.status >= 500) throw new ConnectError(\"pod_unreachable\", `The pod answered ${res.status}`);\n if (res.status !== 200) {\n await res.body?.cancel().catch(() => {});\n throw new ConnectError(\"not_a_pod\", `No authorization server metadata (${res.status})`);\n }\n const doc = await readJsonCapped(res);\n if (!doc || typeof doc !== \"object\" || Array.isArray(doc)) throw new ConnectError(\"not_a_pod\", \"Metadata is not an object\");\n const m = doc as Record<string, unknown>;\n\n // RFC 8414 §3.3 — the mix-up defence at discovery time.\n if (m.issuer !== issuer) {\n throw new ConnectError(\"not_a_pod\", \"The metadata names a different issuer than the address it was fetched from\");\n }\n const list = (v: unknown) => (Array.isArray(v) ? v.filter((x): x is string => typeof x === \"string\") : null);\n const pkce = list(m.code_challenge_methods_supported);\n const responseTypes = list(m.response_types_supported);\n if (\n m.authorization_response_iss_parameter_supported !== true ||\n m.client_id_metadata_document_supported !== true ||\n !pkce?.includes(\"S256\") ||\n (responseTypes !== null && !responseTypes.includes(\"code\"))\n ) {\n throw new ConnectError(\"pod_unsupported\", \"This pod does not support app sign-in (CIMD, iss, S256)\");\n }\n const authorizationEndpoint = sameOriginEndpoint(m.authorization_endpoint, issuer);\n const tokenEndpoint = sameOriginEndpoint(m.token_endpoint, issuer);\n if (!authorizationEndpoint || !tokenEndpoint) {\n throw new ConnectError(\"pod_unsupported\", \"The pod's endpoints are missing or not on its own origin\");\n }\n return {\n issuer,\n authorizationEndpoint,\n tokenEndpoint,\n revocationEndpoint: sameOriginEndpoint(m.revocation_endpoint, issuer),\n refreshSupported: list(m.grant_types_supported)?.includes(\"refresh_token\") ?? false,\n };\n}\n\n/** Synap Cloud's pod domain: pods are `<name>.pod.synap.live`; `<name>.synap.live` and `pod.<name>.synap.live` are older names. */\nconst CLOUD_DOMAIN = \"synap.live\";\nconst CLOUD_POD_ZONE = `pod.${CLOUD_DOMAIN}`;\nconst POD_NAME_RE = /^[a-z0-9]([a-z0-9-]{0,30}[a-z0-9])?$/;\n\n/** The addresses to try after the typed one fails, in order (at most two). */\nfunction fallbackIssuers(first: string): string[] {\n const url = new URL(first);\n const host = url.hostname;\n if (url.pathname !== \"/\" || !host.includes(\".\") || host.endsWith(`.${CLOUD_POD_ZONE}`)) return [];\n const label = host.endsWith(`.${CLOUD_DOMAIN}`) ? host.slice(0, -CLOUD_DOMAIN.length - 1) : null;\n if (label !== null && POD_NAME_RE.test(label)) {\n // A Synap Cloud name typed the old way: the pod's current name, then the old nested one.\n return [`${url.protocol}//${label}.${CLOUD_POD_ZONE}`, `${url.protocol}//pod.${url.host}`];\n }\n return host.startsWith(\"pod.\") ? [] : [`${url.protocol}//pod.${url.host}`];\n}\n\n/**\n * A typed pod address (or a launch `iss`) → its validated authorization server.\n * Tries the address, then the pod names it may stand for: on Synap Cloud,\n * \"you.synap.live\" finds \"you.pod.synap.live\" (and the older\n * \"pod.you.synap.live\"); elsewhere, \"example.com\" finds \"pod.example.com\".\n * A redirect is never followed: an old name that answers 308 counts as a\n * miss (`pod_moved` when nothing else answers).\n */\nexport async function discoverPod(input: string, opts: DiscoverOptions = {}): Promise<PodIssuer> {\n const first = canonicalPodUrl(input, opts);\n let firstError: unknown;\n try {\n return await discoverIssuer(first, opts);\n } catch (err) {\n firstError = err;\n }\n const retryable = (err: unknown) =>\n err instanceof ConnectError && (err.code === \"not_a_pod\" || err.code === \"pod_unreachable\" || err.code === \"pod_moved\");\n if (!retryable(firstError)) throw firstError;\n let tooOld: ConnectError | null = null;\n for (const candidate of fallbackIssuers(first)) {\n try {\n return await discoverIssuer(candidate, opts);\n } catch (err) {\n // A pod that is there but too old says more than \"nothing answered\".\n if (err instanceof ConnectError && err.code === \"pod_unsupported\") tooOld ??= err;\n }\n }\n throw tooOld ?? firstError;\n}\n\n/**\n * A same-origin path to return to after connecting, or null. Accepts a\n * relative path (never `//host` or `/\\host`) or an absolute URL on the app's\n * own origin; anything else is dropped (no open redirect).\n */\nexport function safeNextPath(value: string | null | undefined, appOrigin: string): string | null {\n if (!value || value.length > 2048) return null;\n if (value.startsWith(\"/\") && !value.startsWith(\"//\") && !value.startsWith(\"/\\\\\")) {\n try {\n const url = new URL(value, appOrigin);\n return url.origin === appOrigin ? `${url.pathname}${url.search}${url.hash}` : null;\n } catch {\n return null;\n }\n }\n try {\n const url = new URL(value);\n return url.origin === appOrigin ? `${url.pathname}${url.search}${url.hash}` : null;\n } catch {\n return null;\n }\n}\n\nexport interface LaunchHint {\n /** The pod issuer the launcher named (`iss`), to pre-fill and CONFIRM, never to auto-connect. */\n pod: string | null;\n /** Where to land after connecting (`target_link_uri`), same origin only. */\n next: string | null;\n /** A space hint for the pod's consent (`synap_space_hint`); a pod may ignore it. */\n spaceHint: string | null;\n /**\n * The `state` of a Synap Cloud return (the CP pod chooser answers\n * `initiate_login_uri?iss=…&state=…`, or `?state=…&error=access_denied` when\n * the person cancelled), shape-checked only. NOT verified here:\n * send the request's query to the cloud handler (GET `paths.cloud`), which\n * checks it against this browser's sealed cookie. Never act on `pod` because\n * a state is present.\n */\n cloudState: string | null;\n}\n\n/**\n * Read a third-party-initiated login (OIDC Core §4): the browser\n * opens the app's `initiate_login_uri?iss=…&target_link_uri=…`. One parser for\n * the browser launch and the Synap Cloud chooser's return (`cloudState`).\n */\nexport function acceptLaunch(url: string | URL, appOrigin: string, opts: { allowInsecureLoopback?: boolean } = {}): LaunchHint {\n const params = new URL(url).searchParams;\n let pod: string | null = null;\n const iss = params.get(\"iss\");\n if (iss) {\n try {\n pod = canonicalPodUrl(iss, opts);\n } catch {\n pod = null;\n }\n }\n const hint = params.get(\"synap_space_hint\");\n const state = params.get(\"state\");\n return {\n pod,\n next: safeNextPath(params.get(\"target_link_uri\"), appOrigin),\n spaceHint: hint && SPACE_HINT_RE.test(hint) ? hint : null,\n // A browser launch never carries a state: any well-formed one is a Synap Cloud return\n // (a pick: iss + state; a cancel: state + error=access_denied, no iss).\n cloudState: state && CLOUD_STATE_RE.test(state) ? state : null,\n };\n}\n","/**\n * Sealed values: AES-256-GCM under a key derived (HKDF-SHA-256) from the app\n * secret and a PURPOSE. The purpose is both the HKDF `info` and the GCM\n * additional data, so a value sealed as a sign-in transaction never opens as a\n * session, and the reverse. Any change to the sealed text, a wrong secret or a\n * wrong purpose fails GCM authentication: the value reads as absent.\n *\n * v1.<base64url(iv[12] ‖ ciphertext ‖ tag[16])>\n *\n * The plaintext carries its own expiry (`exp`, ms), checked after decryption,\n * so a cookie kept past its Max-Age by a hostile client still expires.\n */\nimport { ConnectError } from \"./errors\";\nimport { base64url, fromBase64url, fromUtf8, randomBytes, utf8 } from \"./encoding\";\n\nexport type SealPurpose = \"synap-connect/transaction\" | \"synap-connect/session\" | \"synap-connect/cloud\";\n\n/** The app secret: one value, or several to rotate (the first seals, all open). */\nexport type AppSecret = string | readonly string[];\n\nconst VERSION = \"v1\";\nconst IV_BYTES = 12;\nconst TAG_BYTES = 16;\nexport const MIN_SECRET_LENGTH = 32;\nconst SALT = utf8(\"synap-connect/v1\");\n\nconst keyCache = new Map<string, Promise<CryptoKey>>();\n\nfunction secretsOf(secret: AppSecret): readonly string[] {\n const list = typeof secret === \"string\" ? [secret] : secret;\n if (list.length === 0 || list.some((s) => typeof s !== \"string\" || s.length < MIN_SECRET_LENGTH)) {\n throw new ConnectError(\n \"misconfigured\",\n `The app session secret must be at least ${MIN_SECRET_LENGTH} characters (set SYNAP_APP_SESSION_SECRET on the server).`,\n );\n }\n return list;\n}\n\n/** Fail fast at setup when the secret is unusable. */\nexport function assertAppSecret(secret: AppSecret): void {\n secretsOf(secret);\n}\n\nfunction keyFor(secret: string, purpose: SealPurpose): Promise<CryptoKey> {\n const cacheKey = `${purpose}\\u0000${secret}`;\n let key = keyCache.get(cacheKey);\n if (!key) {\n key = crypto.subtle\n .importKey(\"raw\", utf8(secret), \"HKDF\", false, [\"deriveKey\"])\n .then((base) =>\n crypto.subtle.deriveKey(\n { name: \"HKDF\", hash: \"SHA-256\", salt: SALT, info: utf8(purpose) },\n base,\n { name: \"AES-GCM\", length: 256 },\n false,\n [\"encrypt\", \"decrypt\"],\n ),\n );\n keyCache.set(cacheKey, key);\n }\n return key;\n}\n\nexport async function seal(value: unknown, opts: { secret: AppSecret; purpose: SealPurpose; expiresAt: number }): Promise<string> {\n const [current] = secretsOf(opts.secret);\n const iv = randomBytes(IV_BYTES);\n const plaintext = utf8(JSON.stringify({ exp: opts.expiresAt, data: value }));\n const ciphertext = await crypto.subtle.encrypt(\n { name: \"AES-GCM\", iv, additionalData: utf8(opts.purpose) },\n await keyFor(current, opts.purpose),\n plaintext,\n );\n const out = new Uint8Array(IV_BYTES + ciphertext.byteLength);\n out.set(iv, 0);\n out.set(new Uint8Array(ciphertext), IV_BYTES);\n return `${VERSION}.${base64url(out)}`;\n}\n\nexport type Opened<T> = { ok: true; value: T; expiresAt: number } | { ok: false; reason: \"invalid\" | \"expired\" };\n\n/**\n * `invalid` = not ours (malformed, tampered, other secret, other purpose);\n * `expired` = ours, but past its `exp`.\n */\nexport async function unseal<T = unknown>(\n sealed: string,\n opts: { secret: AppSecret; purpose: SealPurpose; now: number },\n): Promise<Opened<T>> {\n const secrets = secretsOf(opts.secret);\n const dot = sealed.indexOf(\".\");\n if (dot < 0 || sealed.slice(0, dot) !== VERSION) return { ok: false, reason: \"invalid\" };\n const bytes = fromBase64url(sealed.slice(dot + 1));\n if (!bytes || bytes.length < IV_BYTES + TAG_BYTES) return { ok: false, reason: \"invalid\" };\n const iv = bytes.slice(0, IV_BYTES);\n const ciphertext = bytes.slice(IV_BYTES);\n for (const secret of secrets) {\n let plaintext: ArrayBuffer;\n try {\n plaintext = await crypto.subtle.decrypt(\n { name: \"AES-GCM\", iv, additionalData: utf8(opts.purpose) },\n await keyFor(secret, opts.purpose),\n ciphertext,\n );\n } catch {\n continue; // not this secret (or tampered): try the next, then refuse\n }\n let parsed: { exp?: unknown; data?: unknown };\n try {\n parsed = JSON.parse(fromUtf8(new Uint8Array(plaintext))) as { exp?: unknown; data?: unknown };\n } catch {\n return { ok: false, reason: \"invalid\" };\n }\n if (typeof parsed.exp !== \"number\") return { ok: false, reason: \"invalid\" };\n if (parsed.exp <= opts.now) return { ok: false, reason: \"expired\" };\n return { ok: true, value: parsed.data as T, expiresAt: parsed.exp };\n }\n return { ok: false, reason: \"invalid\" };\n}\n","/**\n * Connecting through Synap Cloud: the person picks their pod on the Control\n * Plane instead of typing its address.\n *\n * app POST <cloud handler> → 303 <cp>/connect/pod?client_id=<CIMD url>&state=<nonce>\n * CP the person signs in, picks a pod → 302 <initiate_login_uri>?iss=<pod issuer>&state=<nonce>\n * app GET <cloud handler>?iss&state → state checked against this browser's sealed\n * cookie → the SAME sign-in as a typed address\n * (discovery, fresh PKCE + state, `iss` on the answer)\n *\n * The CP never sees a code or a token, and takes the return URL from the\n * app's client metadata, never from the query. `state` is what makes the\n * return trustworthy: it is drawn here, kept sealed in an HttpOnly cookie of\n * this browser, and compared in constant time, so an `iss` link that another\n * site sends a visitor to (login CSRF into an attacker's pod) is refused. A\n * return without a matching `state` never starts a sign-in.\n *\n * The CP's answer only names a pod. The pod itself is then validated exactly\n * like a typed address (`discoverPod`), and its answer like any other.\n */\nimport { canonicalPodUrl } from \"./discovery\";\nimport { constantTimeEqual, randomToken } from \"./encoding\";\nimport { ConnectError, fromOAuthError } from \"./errors\";\nimport { CLOUD_STATE_RE, LOOPBACK_HOSTS } from \"./rules\";\nimport { seal, unseal, type AppSecret } from \"./seal\";\n\n/** How long a Synap Cloud sign-in may take, from the click to the pod chooser's return. */\nexport const CLOUD_TRANSACTION_TTL_MS = 10 * 60 * 1000;\n\n\n/** A sealed `CloudTransaction`: opaque, safe to put in an HttpOnly cookie. */\nexport type SealedCloudTransaction = string & { readonly __sealedCloudTransaction: true };\n\n/** What a started Synap Cloud sign-in remembers (sealed; never sent to the CP). */\nexport interface CloudTransaction {\n v: 1;\n state: string;\n clientId: string;\n /** The CP base it was sent to. */\n cloudUrl: string;\n next: string | null;\n spaceHint: string | null;\n prompt: \"consent\" | null;\n createdAt: number;\n}\n\nexport interface CloudConnectConfig {\n /** The Control Plane's base URL, e.g. https://api.synap.live. Unset = no Synap Cloud option. */\n cloudUrl?: string | null;\n /** Accept a CP on http://localhost (development). */\n allowInsecureLoopback?: boolean;\n}\n\n/** The CP base (no trailing slash), or a ConnectError (`misconfigured`). */\nexport function cloudBaseUrl(cloudUrl: string, opts: { allowInsecureLoopback?: boolean } = {}): string {\n let url: URL;\n try {\n url = new URL(cloudUrl.trim());\n } catch {\n throw new ConnectError(\"misconfigured\", \"cloudUrl is not a URL\");\n }\n const loopback = LOOPBACK_HOSTS.has(url.hostname);\n if (url.protocol !== \"https:\" && !(url.protocol === \"http:\" && loopback && opts.allowInsecureLoopback)) {\n throw new ConnectError(\"misconfigured\", \"cloudUrl must be https (http only on localhost in development)\");\n }\n if (url.username || url.password || url.search || url.hash) {\n throw new ConnectError(\"misconfigured\", \"cloudUrl has no credentials, query or fragment\");\n }\n return `${url.origin}${url.pathname.replace(/\\/+$/, \"\")}`;\n}\n\n/** Whether \"Continue with Synap Cloud\" can be offered: a valid Control Plane URL is configured. */\nexport function cloudConnectAvailable(config?: CloudConnectConfig | null): boolean {\n if (!config?.cloudUrl) return false;\n try {\n cloudBaseUrl(config.cloudUrl, config);\n return true;\n } catch {\n return false;\n }\n}\n\nexport interface StartCloudConnectOptions extends CloudConnectConfig {\n /** The app's client_id (its CIMD URL): the CP reads `initiate_login_uri` from it. */\n clientId: string;\n /**\n * SERVER-ONLY app secret that seals the transaction. Required: without it the call throws `misconfigured`.\n * Optional in the type only for 1.x semver; required in 2.0.\n */\n secret?: AppSecret;\n /** A same-origin path to land on afterwards (validate it first: `safeNextPath`). */\n next?: string | null;\n /** A space hint for the pod's consent (`synap_space_hint`); a pod may ignore it. */\n spaceHint?: string | null;\n /** Show the pod's consent again even if this browser is already connected to the pod the person picks. */\n prompt?: \"consent\" | null;\n /** @deprecated Ignored: the SDK always draws its own 32-byte state. Removed in 2.0. */\n state?: string;\n now?: () => number;\n}\n\nexport interface StartedCloudConnect {\n /** Redirect the browser here (303): the CP's pod chooser. */\n url: string;\n /** Store in an HttpOnly cookie until the CP returns. */\n transaction: SealedCloudTransaction;\n /** ms since epoch. */\n expiresAt: number;\n}\n\n/**\n * Start a Synap Cloud sign-in. Throws `cp_unavailable` at once when no CP URL\n * is configured, `misconfigured` without a secret: both SYNCHRONOUSLY, before\n * a Promise exists, so wrap the call in try/catch rather than relying on\n * `.catch` (kept from 1.3 for compatibility; 2.0 makes it a plain async\n * function with a required secret).\n */\nexport function startCloudConnect(opts: StartCloudConnectOptions): Promise<StartedCloudConnect> {\n if (!cloudConnectAvailable(opts)) {\n throw new ConnectError(\"cp_unavailable\", \"cp: not configured — pass cloudUrl (the Control Plane URL) to offer Synap Cloud\");\n }\n if (!opts.secret) throw new ConnectError(\"misconfigured\", \"startCloudConnect needs the app secret to seal its state\");\n const base = cloudBaseUrl(opts.cloudUrl!, opts);\n const secret = opts.secret;\n return (async () => {\n const now = (opts.now ?? Date.now)();\n const state = randomToken();\n const url = new URL(`${base}/connect/pod`);\n url.searchParams.set(\"client_id\", opts.clientId);\n url.searchParams.set(\"state\", state);\n const tx: CloudTransaction = {\n v: 1,\n state,\n clientId: opts.clientId,\n cloudUrl: base,\n next: opts.next ?? null,\n spaceHint: opts.spaceHint ?? null,\n prompt: opts.prompt === \"consent\" ? \"consent\" : null,\n createdAt: now,\n };\n const expiresAt = now + CLOUD_TRANSACTION_TTL_MS;\n const transaction = (await seal(tx, { secret, purpose: \"synap-connect/cloud\", expiresAt })) as SealedCloudTransaction;\n return { url: url.href, transaction, expiresAt };\n })();\n}\n\nexport interface AcceptCloudReturnOptions {\n /** The URL the CP sent the browser back to (`?iss=…&state=…`). */\n url: string | URL;\n transaction: string | null | undefined;\n clientId: string;\n secret: AppSecret;\n allowInsecureLoopback?: boolean;\n now?: () => number;\n}\n\nexport interface CloudReturn {\n /** The pod the person picked (canonical issuer), still to be discovered and validated. */\n pod: string;\n next: string | null;\n spaceHint: string | null;\n prompt: \"consent\" | null;\n}\n\n/**\n * Check the CP's return against the sealed transaction of THIS browser.\n * Throws `expired_transaction` (none, foreign, tampered or older than 10\n * minutes), `mismatch` (the state is not this browser's, or no pod is named),\n * `denied` (the person cancelled on the chooser: `error=access_denied`, no\n * `iss`; nothing is started), `invalid_pod_address`.\n * `state` is compared BEFORE anything else in the query is used.\n */\nexport async function acceptCloudReturn(opts: AcceptCloudReturnOptions): Promise<CloudReturn> {\n if (!opts.transaction) throw new ConnectError(\"expired_transaction\", \"No Synap Cloud sign-in in progress in this browser\");\n const opened = await unseal<CloudTransaction>(opts.transaction, {\n secret: opts.secret,\n purpose: \"synap-connect/cloud\",\n now: (opts.now ?? Date.now)(),\n });\n if (!opened.ok) {\n throw new ConnectError(\n \"expired_transaction\",\n opened.reason === \"expired\" ? \"The Synap Cloud sign-in is older than 10 minutes\" : \"The Synap Cloud cookie is not one this app sealed\",\n );\n }\n const tx = opened.value;\n if (!tx || tx.v !== 1 || typeof tx.state !== \"string\") {\n throw new ConnectError(\"expired_transaction\", \"The Synap Cloud cookie is not one this app sealed\");\n }\n if (tx.clientId !== opts.clientId) {\n throw new ConnectError(\"misconfigured\", \"The Synap Cloud sign-in was started for another client_id\");\n }\n const params = new URL(opts.url).searchParams;\n const state = params.get(\"state\");\n if (state === null || !CLOUD_STATE_RE.test(state) || !constantTimeEqual(state, tx.state)) {\n throw new ConnectError(\"mismatch\", \"The Synap Cloud answer does not belong to the sign-in this browser started\");\n }\n const error = params.get(\"error\");\n if (error !== null) {\n throw new ConnectError(fromOAuthError(error, \"authorize\"), `Synap Cloud answered ${error}`, { oauthError: error });\n }\n const iss = params.get(\"iss\");\n if (!iss) throw new ConnectError(\"mismatch\", \"The Synap Cloud answer names no pod\");\n return {\n pod: canonicalPodUrl(iss, opts),\n next: tx.next,\n spaceHint: tx.spaceHint,\n prompt: tx.prompt === \"consent\" ? \"consent\" : null,\n };\n}\n","/**\n * A server-side Hub REST client for one PodConnection: `<pod>/api/hub/*` with\n * the connection's Bearer token. Results, not throws: `{ ok: true, … }` or\n * `{ ok: false, error }` with a typed `code`, a malformed path included.\n *\n * - `expired`: the access token is past its expiry here; nothing was sent\n * (refresh in `proxy.ts` / a route handler, or connect again);\n * - `unauthorized`: the pod refused the token. With a `renew` (a client that\n * can persist cookies) the connection is renewed once and the request\n * retried once first; `unauthorized` then means the pod refused the\n * connection itself. Without one, `refreshable` says a renewal may help;\n * - `forbidden`: not granted; `door_unsupported`: a door a space-scoped app\n * token can never use (403 GRANT_DOOR_UNSUPPORTED);\n * - `rate_limited` with `retryAfterSec`; `pod_unreachable` (network, a\n * timeout, 502-504); `pod_moved` (a redirect: connect again);\n * - `invalid`: the pod refused the input (400/422), or the SDK did (a path\n * that could leave the Hub API: encode each segment, `encodeURIComponent`).\n *\n * Writes are governed: `proposed` is SUCCESS (queued for the person's review,\n * never retry it), with `reviewUrl` on the pod. That is the rule of an AGENT\n * connection (`actsAs: \"agent\"`, every app but an official Synap one): its\n * writes are proposals. A PERSON connection (`actsAs: \"person\"`, an official\n * Synap app) writes as the person, directly: its writes apply and come back\n * `applied`, with no review step and no proposal. The result\n * still reports what the pod did, so a `proposed` answer to a person (a space\n * that reviews even its members' changes) is still success, never an error.\n *\n * Typed methods (1.5) for the doors a pod app reads: `listEntities`,\n * `getEntity`, `getEntityConnections`, `getDocument`, `listFocusSessions`,\n * `getFileUrl`, `listPendingProposals`, `approveProposal`. Same results, same\n * codes; a refusal the SDK can decide alone (no grant, an agent approving, a\n * bad id) answers with `status: null` and a `reason`, and sends nothing.\n *\n * The token goes to the pod's own origin only: paths are pod-relative and\n * checked again after URL resolution, no redirect is followed. Each request\n * is bounded: 10 s and 8 MiB by default (`timeoutMs`, `maxResponseBytes`).\n */\nimport { isConnectError, retryAfterSeconds, type ConnectError } from \"./errors\";\nimport { readCapped, timeoutSignal } from \"./http\";\nimport type {\n ApproveProposalOptions,\n GetDocumentOptions,\n GetEntityConnectionsOptions,\n ListEntitiesOptions,\n ListFocusSessionsOptions,\n PodApproval,\n PodDocument,\n PodEntity,\n PodEntityConnection,\n PodEntityConnections,\n PodFileUrl,\n PodFocusSession,\n PodSessionSlot,\n} from \"./hub-types\";\nimport type { PodActsAs, PodConnection, PodGrant, PodSpace } from \"./types\";\n\nexport type PodErrorCode =\n | \"expired\"\n | \"unauthorized\"\n | \"forbidden\"\n | \"door_unsupported\"\n | \"not_found\"\n | \"invalid\"\n | \"conflict\"\n | \"rate_limited\"\n | \"pod_unreachable\"\n /** The pod answered a redirect (a moved pod answers 308 from its old name): connect again at its new address. */\n | \"pod_moved\"\n | \"server\";\n\n/**\n * Why the SDK refused before sending anything (`status: null`):\n * - `agent_cannot_approve`: an agent connection asked to approve (the pod\n * never lets an agent credential approve; `code: \"forbidden\"`);\n * - `not_granted`: the connection holds no permission for it (`forbidden`);\n * - `invalid_input`: an id, a path or an option the pod would refuse (`invalid`).\n */\nexport type PodRefusalReason = \"agent_cannot_approve\" | \"not_granted\" | \"invalid_input\";\n\nexport interface PodError {\n code: PodErrorCode;\n /** The HTTP status, or null when nothing was answered. */\n status: number | null;\n retryAfterSec?: number;\n /** The pod's own error text, for logs only. */\n detail?: string;\n /** Set when the SDK refused locally (1.5). */\n reason?: PodRefusalReason;\n /**\n * On `unauthorized` (1.5): the pod refused the access token, the connection\n * still holds a refresh token, and this client could not renew it (no\n * `renew`). A forced refresh may restore it (a narrow, or a pod-side \"Revoke\n * key\", ends the key, not the connection): run `refreshIfNeeded(request,\n * { force: true })` where cookies can be set. Absent = the pod refused the\n * connection itself: connect again.\n */\n refreshable?: true;\n}\n\nexport type PodResult<T> = { ok: true; data: T } | { ok: false; error: PodError };\n\nexport type PodWriteResult<T> =\n | { ok: true; status: \"applied\"; data: T }\n | { ok: true; status: \"proposed\"; proposalId: string | null; reviewUrl: string; data: T }\n | { ok: false; error: PodError };\n\nexport type QueryValue = string | number | boolean | null | undefined | ReadonlyArray<string | number | boolean>;\n\n/** One pending proposal, the pod's summary row (`GET /proposals?view=basic`). */\nexport interface PendingProposal {\n id: string;\n proposalType: string;\n targetType: string;\n targetId: string | null;\n workspaceId: string | null;\n /** ISO 8601, as the pod sent it. */\n createdAt: string;\n /** The agent that filed it (this app's agent, or another one), when known. */\n agentUserId: string | null;\n sessionId: string | null;\n summary?: string;\n /** Where the person reviews it: the pod's deep link. */\n reviewUrl: string;\n}\n\nexport interface PendingProposals {\n /** Every pending proposal the connection can see, not only this page (the pod COUNTs it). */\n total: number;\n proposals: PendingProposal[];\n hasMore: boolean;\n}\n\nexport interface ListPendingProposalsOptions {\n /** One granted space only. */\n workspaceId?: string;\n /** 1-200, default 20. */\n limit?: number;\n offset?: number;\n /**\n * Only these proposals (at most 100 ids; the pod's `proposalIds` filter,\n * pods that do not filter are filtered here). An empty list matches nothing (answered without a request).\n */\n proposalIds?: readonly string[];\n}\n\nexport interface PodClientOptions {\n fetch?: typeof fetch;\n now?: () => number;\n /** Per Hub request, default 10 000 ms; a request past it is `pod_unreachable`. */\n timeoutMs?: number;\n /** The most a Hub answer may weigh, default 8 MiB; a bigger one is `server` (the body is not read further). */\n maxResponseBytes?: number;\n /**\n * Renew the connection after the pod refused its access token (401) while it\n * holds a refresh token: rotate it, PERSIST the result (the old refresh token\n * is spent once the pod answers), then return it; throw the refresh's\n * `ConnectError` when it fails. The client calls it at most once (concurrent\n * 401s share the call), retries the refused request once with the result, and\n * uses it from then on. `PodAppAuth.client(connection, { persist })` builds\n * one. Without it a 401 stays `unauthorized` (with `refreshable`).\n */\n renew?: (connection: PodConnection) => Promise<PodConnection>;\n}\n\nexport interface PodClient {\n podUrl: string;\n /** The connection's, or the renewed one's after a 401 (`renew`). */\n readonly user: PodConnection[\"user\"];\n readonly grant: PodGrant[];\n /** Who the token acts as (`connectionActsAs`): `\"agent\"` unless the pod said `\"person\"`. */\n readonly actsAs: PodActsAs;\n /** Whether the connection was granted `permission` (in `workspaceId` when given). */\n can(permission: string, workspaceId?: string): boolean;\n /** The granted spaces, each with its permissions (`spaces(connection)`). */\n spaces(): PodSpace[];\n /**\n * \"Waiting for review: N\". The person's pending proposals in the spaces this\n * connection may read proposals in (any agent's, not only this app's), from\n * the pod's seamed `GET /proposals` door. Needs the `proposal.read`\n * permission (ask for it in `requests`): without it the pod would answer an\n * empty list, so this answers `forbidden` instead, without a request.\n */\n listPendingProposals(opts?: ListPendingProposalsOptions): Promise<PodResult<PendingProposals>>;\n /** `GET /entities` (no full-text `q`: a space-scoped token may list, never search). */\n listEntities(opts?: ListEntitiesOptions): Promise<PodResult<PodEntity[]>>;\n /** `GET /entities/:id`; `not_found` when it does not exist or is not readable. */\n getEntity(id: string): Promise<PodResult<PodEntity>>;\n /** `GET /entities/:id/connections`: its relations, property links, threads and sessions. */\n getEntityConnections(id: string, opts?: GetEntityConnectionsOptions): Promise<PodResult<PodEntityConnections>>;\n /** `GET /documents/:id`: content, revision, sections. */\n getDocument(id: string, opts?: GetDocumentOptions): Promise<PodResult<PodDocument>>;\n /**\n * `GET /focus-sessions` in a space (or a project), newest first, with their\n * slots (`expectedOutputs`; `humanSlots(session)` = what waits on the person).\n * `invalid` without a request when neither `workspaceId` nor `projectId` is given.\n */\n listFocusSessions(opts: ListFocusSessionsOptions): Promise<PodResult<PodFocusSession[]>>;\n /** `GET /files/:id/url`: a signed download URL for a file entity (about an hour). */\n getFileUrl(entityId: string): Promise<PodResult<PodFileUrl>>;\n /**\n * `POST /proposals/:id/approve`, as the person. Only a PERSON connection\n * (`actsAs: \"person\"`) holding `proposal.approve` can: an agent connection\n * gets `forbidden` / `agent_cannot_approve` and NOTHING is sent (the pod\n * never lets an agent approve); without the permission, `forbidden` /\n * `not_granted`, also without a request.\n */\n approveProposal(id: string, opts?: ApproveProposalOptions): Promise<PodResult<PodApproval>>;\n get<T = unknown>(path: string, query?: Record<string, QueryValue>): Promise<PodResult<T>>;\n post<T = unknown>(path: string, body?: unknown): Promise<PodWriteResult<T>>;\n patch<T = unknown>(path: string, body?: unknown): Promise<PodWriteResult<T>>;\n put<T = unknown>(path: string, body?: unknown): Promise<PodWriteResult<T>>;\n delete<T = unknown>(path: string, body?: unknown): Promise<PodWriteResult<T>>;\n}\n\n/** Who `connection`'s token acts as: `\"person\"` only when the pod said so, else `\"agent\"`. */\nexport function connectionActsAs(connection: Pick<PodConnection, \"actsAs\">): PodActsAs {\n return connection.actsAs === \"person\" ? \"person\" : \"agent\";\n}\n\n/**\n * The slots of `session` that wait on the person (Home's \"needs you\"):\n * `owner: \"human\"`, not done, not retired.\n */\nexport function humanSlots(session: Pick<PodFocusSession, \"expectedOutputs\">): PodSessionSlot[] {\n return session.expectedOutputs.filter((slot) => slot.owner === \"human\" && slot.status !== \"done\" && !slot.retiredAt);\n}\n\n/** Whether `connection` was granted `permission` (in `workspaceId` when given). Exact match: grants carry no wildcard. */\nexport function can(connection: Pick<PodConnection, \"grant\">, permission: string, workspaceId?: string): boolean {\n return connection.grant.some((g) => g.permission === permission && (workspaceId === undefined || g.workspaceId === workspaceId));\n}\n\n/**\n * The grant by space, in the grant's order, each permission once. The pod\n * sends one row per permission per space; a space switcher wants one per space.\n */\nexport function spaces(connection: Pick<PodConnection, \"grant\">): PodSpace[] {\n const bySpace = new Map<string, PodSpace>();\n for (const g of connection.grant) {\n const space = bySpace.get(g.workspaceId) ?? { workspaceId: g.workspaceId, workspaceName: g.workspaceName, permissions: [] };\n if (!space.workspaceName && g.workspaceName) space.workspaceName = g.workspaceName;\n if (!space.templateSlug && g.templateSlug) space.templateSlug = g.templateSlug;\n if (!space.permissions.includes(g.permission)) space.permissions.push(g.permission);\n bySpace.set(g.workspaceId, space);\n }\n return [...bySpace.values()];\n}\n\n/** An id the SDK puts in a path: uuids, the pod's short ids, the mock's ids. */\nconst ID_RE = /^[A-Za-z0-9_-]{1,128}$/;\nconst PROPOSAL_IDS_MAX = 100;\n\nconst SAFE_PATH = /^\\/(?!\\/)[A-Za-z0-9\\-._~!$&'()*+,;=:@%/]*$/;\nconst TIMEOUT_MS = 10_000;\nconst MAX_RESPONSE_BYTES = 8 * 1024 * 1024;\n\n/**\n * `<pod>/api/hub<path>`, or null when `path` could leave the Hub API: not\n * pod-relative, a dot segment, plain or percent-encoded (WHATWG resolves\n * `%2e%2e` as `..`), a backslash, a query. The resolved URL is checked too.\n */\nfunction hubUrl(podUrl: string, path: string, query?: Record<string, QueryValue>): URL | null {\n if (!SAFE_PATH.test(path) || /%2e/i.test(path) || /(^|\\/)\\.\\.?(\\/|$)/.test(path) || path.includes(\"\\\\\")) return null;\n let url: URL;\n try {\n url = new URL(`${podUrl}/api/hub${path}`);\n } catch {\n return null;\n }\n if (url.origin !== new URL(podUrl).origin || !(url.pathname === \"/api/hub\" || url.pathname.startsWith(\"/api/hub/\"))) return null;\n for (const [key, value] of Object.entries(query ?? {})) {\n if (value === undefined || value === null) continue;\n for (const v of Array.isArray(value) ? value : [value]) url.searchParams.append(key, String(v));\n }\n return url;\n}\n\n/** A failed renewal (`renew` threw) as the result of the request it was for. */\nfunction renewalError(err: unknown): PodError {\n const e: ConnectError | null = isConnectError(err) ? err : null;\n const detail = e?.message ?? (err instanceof Error ? err.message : \"The connection could not be renewed\");\n switch (e?.code) {\n case \"revoked\":\n // The pod refused the connection itself: connect again.\n return { code: \"unauthorized\", status: 401, detail };\n case \"expired\":\n // It lapsed, or its renewal could not be stored: connect again.\n return { code: \"expired\", status: null, detail };\n case \"rate_limited\":\n return { code: \"rate_limited\", status: null, detail, ...(e.retryAfterSec !== undefined ? { retryAfterSec: e.retryAfterSec } : {}) };\n case \"pod_unreachable\":\n return { code: \"pod_unreachable\", status: null, detail };\n case \"pod_moved\":\n return { code: \"pod_moved\", status: null, detail };\n default:\n return { code: \"server\", status: null, detail };\n }\n}\n\nfunction errorFor(status: number, body: unknown, retryAfter: string | null, now: number): PodError {\n const obj = body && typeof body === \"object\" ? (body as Record<string, unknown>) : {};\n const detail = typeof obj.error === \"string\" ? obj.error : typeof obj.message === \"string\" ? obj.message : undefined;\n const base = { status, ...(detail !== undefined ? { detail } : {}) };\n if (status === 401) return { code: \"unauthorized\", ...base };\n if (status === 403) return { code: obj.code === \"GRANT_DOOR_UNSUPPORTED\" ? \"door_unsupported\" : \"forbidden\", ...base };\n if (status === 404) return { code: \"not_found\", ...base };\n if (status === 409) return { code: \"conflict\", ...base };\n if (status === 400 || status === 422) return { code: \"invalid\", ...base };\n if (status === 429) {\n const retryAfterSec = retryAfterSeconds(retryAfter, now);\n return { code: \"rate_limited\", ...base, ...(retryAfterSec !== undefined ? { retryAfterSec } : {}) };\n }\n if (status === 502 || status === 503 || status === 504) return { code: \"pod_unreachable\", ...base };\n if (status >= 300 && status < 400) return { code: \"pod_moved\", ...base };\n return { code: \"server\", ...base };\n}\n\n/**\n * Where to review a proposal: the pod's answer, resolved against the pod and\n * kept only on the pod's own origin; else the pod's deep link `/open/<id>`.\n */\nexport function reviewUrlFor(podUrl: string, reviewUrl: unknown, proposalId: string | null): string {\n if (typeof reviewUrl === \"string\" && reviewUrl) {\n try {\n const url = new URL(reviewUrl, `${podUrl}/`);\n if (url.origin === new URL(podUrl).origin) return url.href;\n } catch {\n // fall through to the deep link\n }\n }\n return proposalId ? `${podUrl}/open/${encodeURIComponent(proposalId)}` : podUrl;\n}\n\nfunction refused(code: PodErrorCode, reason: PodRefusalReason, detail: string): { ok: false; error: PodError } {\n return { ok: false, error: { code, status: null, detail, reason } };\n}\n\nfunction asObject(value: unknown): Record<string, unknown> | null {\n return value && typeof value === \"object\" && !Array.isArray(value) ? (value as Record<string, unknown>) : null;\n}\n\nfunction hasId(value: unknown): value is Record<string, unknown> & { id: string } {\n return asObject(value) !== null && typeof (value as Record<string, unknown>).id === \"string\";\n}\n\nexport function createPodClient(connection: PodConnection, opts: PodClientOptions = {}): PodClient {\n const now = opts.now ?? Date.now;\n const doFetch = () => opts.fetch ?? fetch;\n /** The connection in use: `connection`, or what `renew` returned after a 401. */\n let current = connection;\n /** The one renewal this client may run: a 401 forces ONE refresh, never a loop. */\n let renewal: Promise<PodResult<PodConnection>> | null = null;\n\n function renewOnce(refused: PodConnection): Promise<PodResult<PodConnection>> {\n if (current !== refused) return Promise.resolve({ ok: true, data: current });\n renewal ??= opts.renew!(refused).then(\n (next): PodResult<PodConnection> => {\n current = next;\n return { ok: true, data: next };\n },\n (err: unknown): PodResult<PodConnection> => ({ ok: false, error: renewalError(err) }),\n );\n return renewal;\n }\n\n async function request(method: string, path: string, body: unknown, query?: Record<string, QueryValue>, retried = false): Promise<PodResult<unknown>> {\n const sent = current;\n const url = hubUrl(sent.podUrl, path, query);\n if (!url) return refused(\"invalid\", \"invalid_input\", \"Not a Hub API path: encode each path segment (encodeURIComponent)\");\n if (sent.accessExpiresAt <= now()) return { ok: false, error: { code: \"expired\", status: null } };\n const headers: Record<string, string> = { Accept: \"application/json\", Authorization: `Bearer ${sent.accessToken}` };\n if (body !== undefined) headers[\"Content-Type\"] = \"application/json\";\n let res: Response;\n try {\n res = await doFetch()(url.href, {\n method,\n headers,\n body: body === undefined ? undefined : JSON.stringify(body),\n redirect: \"manual\",\n signal: timeoutSignal(opts.timeoutMs ?? TIMEOUT_MS),\n });\n } catch {\n return { ok: false, error: { code: \"pod_unreachable\", status: null } };\n }\n const read = await readCapped(res, opts.maxResponseBytes ?? MAX_RESPONSE_BYTES);\n if (!read.ok) {\n return read.reason === \"too_large\"\n ? { ok: false, error: { code: \"server\", status: res.status, detail: \"The pod's answer is larger than maxResponseBytes\" } }\n : { ok: false, error: { code: \"pod_unreachable\", status: res.status, detail: \"The pod's answer broke off\" } };\n }\n let data: unknown = null;\n if (read.text) {\n try {\n data = JSON.parse(read.text);\n } catch {\n data = null;\n }\n }\n if (res.status >= 200 && res.status < 300) return { ok: true, data };\n if (res.status === 401 && sent.refreshToken) {\n // The key was refused, not necessarily the connection (a narrow or a pod-side \"Revoke key\" ends the key only).\n if (!opts.renew) return { ok: false, error: { ...errorFor(401, data, null, now()), refreshable: true } };\n if (!retried) {\n const renewed = await renewOnce(sent);\n if (!renewed.ok) return renewed;\n return request(method, path, body, query, true);\n }\n }\n return { ok: false, error: errorFor(res.status, data, res.headers.get(\"retry-after\"), now()) };\n }\n\n async function write<T>(method: string, path: string, body: unknown): Promise<PodWriteResult<T>> {\n const result = await request(method, path, body);\n if (!result.ok) return result;\n const obj = result.data && typeof result.data === \"object\" ? (result.data as Record<string, unknown>) : {};\n const proposalId = typeof obj.proposalId === \"string\" && obj.proposalId ? obj.proposalId : null;\n if (obj.status === \"proposed\" || (obj.status === \"pending\" && proposalId)) {\n return {\n ok: true,\n status: \"proposed\",\n proposalId,\n reviewUrl: reviewUrlFor(current.podUrl, obj.reviewUrl, proposalId),\n data: result.data as T,\n };\n }\n return { ok: true, status: \"applied\", data: result.data as T };\n }\n\n async function listPendingProposals(opts: ListPendingProposalsOptions = {}): Promise<PodResult<PendingProposals>> {\n if (!can(current, \"proposal.read\", opts.workspaceId)) {\n return refused(\"forbidden\", \"not_granted\", \"The connection was not granted proposal.read in that space\");\n }\n let only: Set<string> | null = null;\n if (opts.proposalIds !== undefined) {\n if (opts.proposalIds.length > PROPOSAL_IDS_MAX || opts.proposalIds.some((id) => !ID_RE.test(id))) {\n return refused(\"invalid\", \"invalid_input\", `proposalIds must be at most ${PROPOSAL_IDS_MAX} proposal ids`);\n }\n // `?proposalIds=` matches nothing on the pod; our query builder would drop it and ask for ALL.\n if (opts.proposalIds.length === 0) return { ok: true, data: { total: 0, proposals: [], hasMore: false } };\n only = new Set(opts.proposalIds);\n }\n const limit = Math.min(Math.max(Math.floor(opts.limit ?? 20), 1), 200);\n const offset = Math.max(Math.floor(opts.offset ?? 0), 0);\n const result = await request(\"GET\", \"/proposals\", undefined, {\n status: \"pending\",\n view: \"basic\",\n limit,\n offset,\n workspaceId: opts.workspaceId,\n ...(only ? { proposalIds: [...only].join(\",\") } : {}),\n });\n if (!result.ok) return result;\n const obj = result.data && typeof result.data === \"object\" ? (result.data as Record<string, unknown>) : {};\n if (!Array.isArray(obj.proposals) || typeof obj.total !== \"number\") {\n return { ok: false, error: { code: \"server\", status: 200, detail: \"The pod's /proposals answer has no proposals or total\" } };\n }\n const str = (v: unknown): string | null => (typeof v === \"string\" && v ? v : null);\n const proposals: PendingProposal[] = [];\n for (const row of obj.proposals as unknown[]) {\n if (!row || typeof row !== \"object\") continue;\n const r = row as Record<string, unknown>;\n const id = str(r.id);\n // A pod older than the filter ignores it: never hand back a proposal that was not asked for.\n if (!id || (only && !only.has(id))) continue;\n proposals.push({\n id,\n proposalType: str(r.proposalType) ?? \"\",\n targetType: str(r.targetType) ?? \"\",\n targetId: str(r.targetId),\n workspaceId: str(r.workspaceId),\n createdAt: str(r.createdAt) ?? \"\",\n agentUserId: str(r.agentUserId),\n sessionId: str(r.sessionId),\n ...(str(r.summary) ? { summary: str(r.summary)! } : {}),\n reviewUrl: reviewUrlFor(current.podUrl, null, id),\n });\n }\n return {\n ok: true,\n data: { total: obj.total, proposals, hasMore: obj.hasMore === true || offset + proposals.length < obj.total },\n };\n }\n\n /** A read whose 2xx body must pass `shape`; anything else is the pod answering what the SDK cannot use. */\n async function read<T>(path: string, query: Record<string, QueryValue> | undefined, shape: (data: unknown) => T | null): Promise<PodResult<T>> {\n const result = await request(\"GET\", path, undefined, query);\n if (!result.ok) return result;\n const data = shape(result.data);\n if (data === null) return { ok: false, error: { code: \"server\", status: 200, detail: `The pod's ${path.split(\"/\")[1]} answer is not the expected shape` } };\n return { ok: true, data };\n }\n\n const badId = (what: string) => refused(\"invalid\", \"invalid_input\", `Not a ${what} id`);\n const clamp = (n: number | undefined, min: number, max: number) => (n === undefined ? undefined : Math.min(Math.max(Math.floor(n), min), max));\n\n function listEntities(opts: ListEntitiesOptions = {}): Promise<PodResult<PodEntity[]>> {\n return read(\"/entities\", {\n workspaceId: opts.workspaceId,\n profileSlug: opts.profileSlug,\n limit: clamp(opts.limit, 1, 1000),\n offset: clamp(opts.offset, 0, Number.MAX_SAFE_INTEGER),\n sort: opts.sort,\n includePodWide: opts.includePodWide,\n facetSlug: opts.facetSlug,\n // Sent only when asked: the default request stays the 1.5.0 one.\n includeFacets: opts.includeFacets ? true : undefined,\n }, (data) => (Array.isArray(data) ? (data.filter(hasId) as PodEntity[]) : null));\n }\n\n async function getEntity(id: string): Promise<PodResult<PodEntity>> {\n if (!ID_RE.test(id)) return badId(\"entity\");\n return read(`/entities/${id}`, undefined, (data) => (hasId(data) ? (data as PodEntity) : null));\n }\n\n async function getEntityConnections(id: string, opts: GetEntityConnectionsOptions = {}): Promise<PodResult<PodEntityConnections>> {\n if (!ID_RE.test(id)) return badId(\"entity\");\n return read(`/entities/${id}/connections`, { workspaceId: opts.workspaceId, limit: clamp(opts.limit, 1, 200) }, (data) => {\n const obj = asObject(data);\n if (!obj || !Array.isArray(obj.connections)) return null;\n const connections = obj.connections.filter(\n (c): c is PodEntityConnection => !!c && typeof c === \"object\" && typeof (c as Record<string, unknown>).entityId === \"string\",\n );\n const counts = asObject(obj.counts) ?? {};\n const count = (key: string, source: string) =>\n typeof counts[key] === \"number\" ? (counts[key] as number) : connections.filter((c) => c.source === source).length;\n return {\n connections,\n counts: {\n total: typeof counts.total === \"number\" ? counts.total : connections.length,\n graph: count(\"graph\", \"graph\"),\n structural: count(\"structural\", \"property\"),\n threads: count(\"threads\", \"thread\"),\n contextChannels: count(\"contextChannels\", \"context_channel\"),\n focusSessions: count(\"focusSessions\", \"focus_session\"),\n },\n };\n });\n }\n\n async function getDocument(id: string, opts: GetDocumentOptions = {}): Promise<PodResult<PodDocument>> {\n if (!ID_RE.test(id)) return badId(\"document\");\n return read(`/documents/${id}`, { format: opts.format }, (data) => {\n const doc = asObject(asObject(data)?.document);\n if (!doc || typeof doc.id !== \"string\" || typeof doc.content !== \"string\") return null;\n return { ...doc, sections: Array.isArray(doc.sections) ? doc.sections : [] } as PodDocument;\n });\n }\n\n async function listFocusSessions(opts: ListFocusSessionsOptions): Promise<PodResult<PodFocusSession[]>> {\n if (!opts?.workspaceId && !opts?.projectId) return refused(\"invalid\", \"invalid_input\", \"workspaceId or projectId is required\");\n return read(\"/focus-sessions\", {\n workspaceId: opts.workspaceId,\n projectId: opts.projectId,\n status: opts.status,\n limit: clamp(opts.limit, 1, 50),\n lens: opts.lens,\n kind: opts.kind,\n subjectEntityId: opts.subjectEntityId,\n }, (data) => {\n if (!Array.isArray(data)) return null;\n return data.filter(hasId).map((row) => ({\n ...row,\n expectedOutputs: Array.isArray(row.expectedOutputs)\n ? row.expectedOutputs.filter((s): s is PodSessionSlot => !!s && typeof s === \"object\" && typeof (s as Record<string, unknown>).label === \"string\")\n : [],\n })) as PodFocusSession[];\n });\n }\n\n async function getFileUrl(entityId: string): Promise<PodResult<PodFileUrl>> {\n if (!ID_RE.test(entityId)) return badId(\"file\");\n return read(`/files/${entityId}/url`, undefined, (data) => {\n const obj = asObject(data);\n if (!obj || typeof obj.url !== \"string\" || typeof obj.expiresAt !== \"string\") return null;\n return { url: obj.url, expiresAt: obj.expiresAt, mimeType: typeof obj.mimeType === \"string\" ? obj.mimeType : null };\n });\n }\n\n async function approveProposal(id: string, opts: ApproveProposalOptions = {}): Promise<PodResult<PodApproval>> {\n // Checked first, before anything else: an agent credential never reaches the approve door.\n if (connectionActsAs(current) !== \"person\") {\n return refused(\"forbidden\", \"agent_cannot_approve\", \"An agent connection can never approve: approval is the person's\");\n }\n if (!can(current, \"proposal.approve\", opts.workspaceId)) {\n return refused(\"forbidden\", \"not_granted\", \"The connection was not granted proposal.approve in that space\");\n }\n if (!ID_RE.test(id)) return badId(\"proposal\");\n const result = await request(\"POST\", `/proposals/${id}/approve`, {});\n if (!result.ok) return result;\n const obj = asObject(result.data);\n if (!obj || typeof obj.proposalId !== \"string\") {\n return { ok: false, error: { code: \"server\", status: 200, detail: \"The pod's approve answer names no proposalId\" } };\n }\n return {\n ok: true,\n data: {\n proposalId: obj.proposalId,\n ...(typeof obj.primaryId === \"string\" ? { primaryId: obj.primaryId } : {}),\n ...(typeof obj.linked === \"number\" ? { linked: obj.linked } : {}),\n proposal: asObject(obj.proposal),\n },\n };\n }\n\n return {\n podUrl: connection.podUrl,\n get user() {\n return current.user;\n },\n get grant() {\n return current.grant;\n },\n get actsAs() {\n return connectionActsAs(current);\n },\n can: (permission, workspaceId) => can(current, permission, workspaceId),\n spaces: () => spaces(current),\n listPendingProposals,\n listEntities,\n getEntity,\n getEntityConnections,\n getDocument,\n listFocusSessions,\n getFileUrl,\n approveProposal,\n get<T>(path: string, query?: Record<string, QueryValue>) {\n return request(\"GET\", path, undefined, query) as Promise<PodResult<T>>;\n },\n post<T>(path: string, body?: unknown) {\n return write<T>(\"POST\", path, body);\n },\n patch<T>(path: string, body?: unknown) {\n return write<T>(\"PATCH\", path, body);\n },\n put<T>(path: string, body?: unknown) {\n return write<T>(\"PUT\", path, body);\n },\n delete<T>(path: string, body?: unknown) {\n return write<T>(\"DELETE\", path, body);\n },\n };\n}\n","/**\n * The OAuth 2.1 authorization-code flow of a pod app, as\n * plain functions over a SEALED transaction string. `createPodAppAuth` wires\n * them to cookies and route handlers; use these directly for another store.\n *\n * Security properties (each pinned by a test in src/__tests__/connect.*):\n * - PKCE S256 with a fresh 32-byte verifier per sign-in;\n * - a fresh 32-byte `state`, compared in constant time on the callback;\n * - mix-up defence (RFC 9207): the callback's `iss` must equal the issuer the\n * transaction was started with; a missing `iss` is refused too;\n * - the transaction lives 10 minutes and is sealed (AES-GCM) under the app\n * secret with its own purpose: tampered, foreign or stale → refused;\n * - the `error` of a callback is honoured only AFTER `iss` and `state` match,\n * so a forged error cannot end someone else's sign-in;\n * - tokens never leave the server: nothing here returns a value meant for a\n * browser except the authorize URL.\n */\nimport { ConnectError, fromOAuthError, retryAfterSeconds } from \"./errors\";\nimport { constantTimeEqual, pkceChallenge, randomToken } from \"./encoding\";\nimport { discoverPod, type DiscoverOptions } from \"./discovery\";\nimport { readCapped, timeoutSignal } from \"./http\";\nimport { LOOPBACK_HOSTS } from \"./rules\";\nimport { seal, unseal, type AppSecret } from \"./seal\";\nimport type { PodActsAs, PodConnection, PodGrant, PodIssuer, PodUser } from \"./types\";\n\n/** How long a started sign-in stays valid. */\nexport const TRANSACTION_TTL_MS = 10 * 60 * 1000;\n/** The refresh token's idle life when the pod does not say (the pod's: 30 days). */\nexport const DEFAULT_REFRESH_IDLE_MS = 30 * 24 * 60 * 60 * 1000;\nconst DEFAULT_ACCESS_TTL_SEC = 3600;\nconst TIMEOUT_MS = 10_000;\n/** A token response is a few KB; more is not a token response. */\nconst MAX_TOKEN_RESPONSE_BYTES = 64 * 1024;\n\n/** A sealed `ConnectTransaction`: opaque, safe to put in an HttpOnly cookie. */\nexport type SealedTransaction = string & { readonly __sealedTransaction: true };\n\n/** What a started sign-in remembers (sealed; never sent to the pod). */\nexport interface ConnectTransaction {\n v: 1;\n issuer: string;\n tokenEndpoint: string;\n revocationEndpoint: string | null;\n clientId: string;\n redirectUri: string;\n state: string;\n verifier: string;\n resource: string | null;\n /** A same-origin path to land on afterwards. */\n next: string | null;\n createdAt: number;\n}\n\nexport interface FlowOptions {\n fetch?: typeof fetch;\n now?: () => number;\n}\n\nexport interface StartConnectOptions extends FlowOptions {\n /** A validated issuer (`discoverPod`), or an address / launch `iss` to discover. */\n pod: PodIssuer | string;\n clientId: string;\n redirectUri: string;\n /** Grant-grammar permissions, each declared in the app's client metadata. */\n permissions: readonly string[];\n secret: AppSecret;\n /** RFC 8707 resource: the pod's Hub API (default) or its MCP endpoint; null = none. */\n resource?: \"api/hub\" | \"mcp\" | null;\n /** A space to preselect on consent. */\n spaceHint?: string | null;\n /** A same-origin path to land on afterwards (validate it first: `safeNextPath`). */\n next?: string | null;\n /**\n * `\"consent\"`: the person asked to see the consent screen again (to widen or\n * narrow what the app may touch). Sent as OIDC `prompt=consent`; the sign-in\n * is otherwise the same (fresh PKCE verifier and state).\n */\n prompt?: \"consent\" | null;\n allowInsecureLoopback?: boolean;\n}\n\nexport interface StartedConnect {\n /** Redirect the browser here (303). */\n authorizeUrl: string;\n /** Store in an HttpOnly cookie until the callback. */\n transaction: SealedTransaction;\n /** ms since epoch. */\n expiresAt: number;\n issuer: PodIssuer;\n}\n\nconst PERMISSION_RE = /^[a-z][a-z0-9_-]*(\\.[a-z0-9_-]+)+$/i;\n\nexport function assertPermissions(permissions: readonly string[]): string[] {\n const unique = [...new Set(permissions)];\n if (unique.length === 0 || unique.length > 32 || unique.some((p) => !PERMISSION_RE.test(p))) {\n throw new ConnectError(\n \"misconfigured\",\n \"permissions must be 1-32 grant-grammar strings such as entity.post.read (no *)\",\n );\n }\n return unique;\n}\n\nexport async function startConnect(opts: StartConnectOptions): Promise<StartedConnect> {\n const now = (opts.now ?? Date.now)();\n const permissions = assertPermissions(opts.permissions);\n const issuer =\n typeof opts.pod === \"string\"\n ? await discoverPod(opts.pod, { fetch: opts.fetch, now: opts.now, allowInsecureLoopback: opts.allowInsecureLoopback } satisfies DiscoverOptions)\n : opts.pod;\n const verifier = randomToken();\n const state = randomToken();\n const resourceKind = opts.resource === undefined ? \"api/hub\" : opts.resource;\n const resource = resourceKind ? `${issuer.issuer}/${resourceKind}` : null;\n\n const url = new URL(issuer.authorizationEndpoint);\n url.searchParams.set(\"response_type\", \"code\");\n url.searchParams.set(\"client_id\", opts.clientId);\n url.searchParams.set(\"redirect_uri\", opts.redirectUri);\n url.searchParams.set(\"scope\", permissions.join(\" \"));\n url.searchParams.set(\"state\", state);\n url.searchParams.set(\"code_challenge\", await pkceChallenge(verifier));\n url.searchParams.set(\"code_challenge_method\", \"S256\");\n if (resource) url.searchParams.set(\"resource\", resource);\n if (opts.spaceHint) url.searchParams.set(\"synap_space_hint\", opts.spaceHint);\n if (opts.prompt === \"consent\") url.searchParams.set(\"prompt\", \"consent\");\n\n const tx: ConnectTransaction = {\n v: 1,\n issuer: issuer.issuer,\n tokenEndpoint: issuer.tokenEndpoint,\n revocationEndpoint: issuer.revocationEndpoint,\n clientId: opts.clientId,\n redirectUri: opts.redirectUri,\n state,\n verifier,\n resource,\n next: opts.next ?? null,\n createdAt: now,\n };\n const expiresAt = now + TRANSACTION_TTL_MS;\n const transaction = (await seal(tx, {\n secret: opts.secret,\n purpose: \"synap-connect/transaction\",\n expiresAt,\n })) as SealedTransaction;\n return { authorizeUrl: url.toString(), transaction, expiresAt, issuer };\n}\n\n/** Open a sealed transaction; refuses tampered, foreign and stale ones. */\nexport async function openTransaction(\n sealed: string | null | undefined,\n opts: { secret: AppSecret; now?: () => number },\n): Promise<ConnectTransaction> {\n if (!sealed) throw new ConnectError(\"expired_transaction\", \"No sign-in in progress in this browser\");\n const opened = await unseal<ConnectTransaction>(sealed, {\n secret: opts.secret,\n purpose: \"synap-connect/transaction\",\n now: (opts.now ?? Date.now)(),\n });\n if (!opened.ok) {\n throw new ConnectError(\n \"expired_transaction\",\n opened.reason === \"expired\" ? \"The sign-in is older than 10 minutes\" : \"The sign-in cookie is not one this app sealed\",\n );\n }\n const tx = opened.value;\n if (!tx || tx.v !== 1 || typeof tx.state !== \"string\" || typeof tx.issuer !== \"string\") {\n throw new ConnectError(\"expired_transaction\", \"The sign-in cookie is not one this app sealed\");\n }\n return tx;\n}\n\nexport interface HandleCallbackOptions extends FlowOptions {\n /** The full URL the pod redirected the browser to. */\n callbackUrl: string | URL;\n transaction: string | null | undefined;\n clientId: string;\n redirectUri: string;\n secret: AppSecret;\n}\n\nexport interface CallbackResult {\n connection: PodConnection;\n /** The same-origin path the sign-in was started for. */\n next: string | null;\n}\n\nexport async function handleCallback(opts: HandleCallbackOptions): Promise<CallbackResult> {\n const tx = await openTransaction(opts.transaction, opts);\n if (tx.clientId !== opts.clientId || tx.redirectUri !== opts.redirectUri) {\n throw new ConnectError(\"misconfigured\", \"The sign-in was started for another client_id or redirect_uri\");\n }\n const params = new URL(opts.callbackUrl).searchParams;\n\n // RFC 9207: the answer must name the AS this sign-in went to. Absent = refused\n // (every Synap pod sends it, and discovery required it).\n const iss = params.get(\"iss\");\n if (iss === null || iss !== tx.issuer) {\n throw new ConnectError(\"mismatch\", iss === null ? \"The answer names no issuer\" : \"The answer comes from another pod than the one asked\");\n }\n const state = params.get(\"state\");\n if (state === null || !constantTimeEqual(state, tx.state)) {\n throw new ConnectError(\"mismatch\", \"The answer does not belong to the sign-in this browser started\");\n }\n const error = params.get(\"error\");\n if (error !== null) {\n throw new ConnectError(fromOAuthError(error, \"authorize\"), `The pod answered ${error}`, { oauthError: error });\n }\n const code = params.get(\"code\");\n if (!code) throw new ConnectError(\"server_error\", \"The answer carries no code\");\n\n const body = new URLSearchParams({\n grant_type: \"authorization_code\",\n code,\n redirect_uri: tx.redirectUri,\n client_id: tx.clientId,\n code_verifier: tx.verifier,\n });\n if (tx.resource) body.set(\"resource\", tx.resource);\n const json = await tokenRequest(tx.tokenEndpoint, body, \"token\", opts);\n const connection = parseTokenResponse(json, {\n podUrl: tx.issuer,\n clientId: tx.clientId,\n tokenEndpoint: tx.tokenEndpoint,\n revocationEndpoint: tx.revocationEndpoint,\n now: (opts.now ?? Date.now)(),\n });\n return { connection, next: tx.next };\n}\n\nasync function tokenRequest(\n endpoint: string,\n body: URLSearchParams,\n during: \"token\" | \"refresh\",\n opts: FlowOptions,\n): Promise<Record<string, unknown>> {\n const doFetch = opts.fetch ?? fetch;\n let res: Response;\n try {\n res = await doFetch(endpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\", Accept: \"application/json\" },\n body: body.toString(),\n redirect: \"manual\",\n signal: timeoutSignal(TIMEOUT_MS),\n });\n } catch {\n throw new ConnectError(\"pod_unreachable\", \"The pod's token endpoint did not answer\");\n }\n if (res.status >= 300 && res.status < 400) {\n await res.body?.cancel().catch(() => {});\n throw new ConnectError(\"pod_moved\", `The token endpoint answered ${res.status}: the pod moved; connect again with its new address`);\n }\n if (res.status === 429) {\n throw new ConnectError(\"rate_limited\", \"The pod asked to slow down\", {\n retryAfterSec: retryAfterSeconds(res.headers.get(\"retry-after\"), (opts.now ?? Date.now)()),\n });\n }\n if (res.status >= 500) throw new ConnectError(\"pod_unreachable\", `The token endpoint answered ${res.status}`);\n const read = await readCapped(res, MAX_TOKEN_RESPONSE_BYTES);\n if (!read.ok) {\n throw read.reason === \"too_large\"\n ? new ConnectError(\"server_error\", \"The token endpoint's answer is too large\")\n : new ConnectError(\"pod_unreachable\", \"The token endpoint's answer broke off\");\n }\n let json: unknown;\n try {\n json = JSON.parse(read.text);\n } catch {\n throw new ConnectError(\"server_error\", `The token endpoint answered ${res.status} without JSON`);\n }\n const obj = json && typeof json === \"object\" && !Array.isArray(json) ? (json as Record<string, unknown>) : {};\n if (res.status !== 200) {\n const oauthError = typeof obj.error === \"string\" ? obj.error : \"server_error\";\n throw new ConnectError(fromOAuthError(oauthError, during), `The token endpoint answered ${oauthError}`, { oauthError });\n }\n return obj;\n}\n\nfunction parseGrant(value: unknown): PodGrant[] | null {\n if (!Array.isArray(value)) return null;\n const out: PodGrant[] = [];\n for (const g of value) {\n if (!g || typeof g !== \"object\") return null;\n const { permission, workspaceId, workspaceName, templateSlug } = g as Record<string, unknown>;\n if (typeof permission !== \"string\" || typeof workspaceId !== \"string\") return null;\n const row: PodGrant = { permission, workspaceId, workspaceName: typeof workspaceName === \"string\" ? workspaceName : \"\" };\n // The space's template, omitted when it came from none.\n if (typeof templateSlug === \"string\" && templateSlug.length > 0 && templateSlug.length <= 200) row.templateSlug = templateSlug;\n out.push(row);\n }\n return out;\n}\n\n/**\n * `synap_acts_as`: read from EVERY token response, never carried\n * over from the previous one, so a pod that stops saying \"person\" (or never\n * did) gives an agent connection.\n */\nfunction parseActsAs(value: unknown): PodActsAs | undefined {\n return value === \"person\" || value === \"agent\" ? value : undefined;\n}\n\nconst APP_ID_RE = /^[A-Za-z0-9_-]{1,128}$/;\n\n/**\n * `synap_app` (Synap extension): the pod's id for this app connection and its\n * page for it. The page is on the pod's console, which may be another host\n * than the issuer, so it is kept when it is an absolute https URL (http only on\n * loopback) without credentials; anything else is dropped.\n */\nfunction parseApp(value: unknown): { appId?: string; manageUrl?: string } {\n if (!value || typeof value !== \"object\") return {};\n const { id, manageUrl } = value as Record<string, unknown>;\n const out: { appId?: string; manageUrl?: string } = {};\n if (typeof id === \"string\" && APP_ID_RE.test(id)) out.appId = id;\n if (typeof manageUrl === \"string\" && manageUrl.length <= 2048) {\n try {\n const url = new URL(manageUrl);\n const secure = url.protocol === \"https:\" || (url.protocol === \"http:\" && LOOPBACK_HOSTS.has(url.hostname));\n if (secure && !url.username && !url.password) out.manageUrl = url.href;\n } catch {\n // not a URL: no link\n }\n }\n return out;\n}\n\nfunction parseUser(value: unknown): PodUser | null {\n if (!value || typeof value !== \"object\") return null;\n const { id, name } = value as Record<string, unknown>;\n return typeof id === \"string\" ? { id, name: typeof name === \"string\" ? name : \"\" } : null;\n}\n\n/**\n * A token response → a connection. On a refresh, `previous` supplies what the\n * response may omit (grant, user, app; never `actsAs`). `expires_in` is read as sent: a refresh\n * answered within the pod's reuse grace hands back the live key the\n * first redeem got, with less than an hour left. A refresh response WITHOUT a new\n * refresh_token drops the old one: with single-use rotation, resending a\n * consumed token reads as theft and revokes the whole family.\n */\nexport function parseTokenResponse(\n body: Record<string, unknown>,\n ctx: {\n podUrl: string;\n clientId: string;\n tokenEndpoint: string;\n revocationEndpoint: string | null;\n now: number;\n previous?: PodConnection;\n },\n): PodConnection {\n const { access_token, token_type, expires_in, scope, refresh_token, refresh_token_expires_in } = body;\n if (typeof access_token !== \"string\" || access_token === \"\") {\n throw new ConnectError(\"server_error\", \"The token response has no access_token\");\n }\n if (typeof token_type !== \"string\" || token_type.toLowerCase() !== \"bearer\") {\n throw new ConnectError(\"server_error\", \"The token response is not a Bearer token\");\n }\n if (expires_in !== undefined && (typeof expires_in !== \"number\" || !Number.isFinite(expires_in) || expires_in < 0)) {\n throw new ConnectError(\"server_error\", \"The token response has an invalid expires_in\");\n }\n const grant = parseGrant(body.synap_grant) ?? ctx.previous?.grant ?? null;\n const user = parseUser(body.synap_user) ?? ctx.previous?.user ?? null;\n if (!grant || !user) {\n throw new ConnectError(\"pod_unsupported\", \"The token response lacks synap_grant / synap_user (pod older than app sign-in)\");\n }\n const scopes =\n typeof scope === \"string\" ? scope.split(/\\s+/).filter(Boolean) : (ctx.previous?.scope ?? [...new Set(grant.map((g) => g.permission))]);\n const connection: PodConnection = {\n podUrl: ctx.podUrl,\n clientId: ctx.clientId,\n tokenEndpoint: ctx.tokenEndpoint,\n revocationEndpoint: ctx.revocationEndpoint,\n accessToken: access_token,\n accessExpiresAt: ctx.now + (typeof expires_in === \"number\" ? expires_in : DEFAULT_ACCESS_TTL_SEC) * 1000,\n scope: scopes,\n grant,\n user,\n connectedAt: ctx.previous?.connectedAt ?? ctx.now,\n };\n const app = parseApp(body.synap_app);\n const appId = app.appId ?? ctx.previous?.appId;\n const manageUrl = app.manageUrl ?? ctx.previous?.manageUrl;\n if (appId !== undefined) connection.appId = appId;\n if (manageUrl !== undefined) connection.manageUrl = manageUrl;\n const actsAs = parseActsAs(body.synap_acts_as);\n if (actsAs !== undefined) connection.actsAs = actsAs;\n if (typeof refresh_token === \"string\" && refresh_token !== \"\") {\n connection.refreshToken = refresh_token;\n const idle =\n typeof refresh_token_expires_in === \"number\" && Number.isFinite(refresh_token_expires_in) && refresh_token_expires_in > 0\n ? refresh_token_expires_in * 1000\n : DEFAULT_REFRESH_IDLE_MS;\n connection.refreshExpiresAt = ctx.now + idle;\n }\n return connection;\n}\n\n/**\n * How long a rotation's result is remembered for its SPENT refresh token.\n * The pod refuses a concurrent second redeem softly, but a token\n * presented AGAIN after its use is a reuse: the whole family and the live\n * access key are revoked. A browser easily sends that replay: parallel\n * requests, a second tab, a response it had not stored yet, all still\n * carrying the old cookie. Within this window such a request gets the\n * rotation's result (and re-sends the new cookie) instead of reaching the pod.\n */\nexport const ROTATION_MEMORY_MS = 2 * 60 * 1000;\n\ninterface Rotation {\n result: Promise<PodConnection>;\n /** When it succeeded (ms, the caller's clock); undefined while in flight. */\n settledAt?: number;\n}\n\n/** Spent refresh token → its rotation, per process. */\nconst rotations = new Map<string, Rotation>();\n\nfunction pruneRotations(now: number): void {\n for (const [token, r] of rotations) {\n if (r.settledAt !== undefined && now - r.settledAt > ROTATION_MEMORY_MS) rotations.delete(token);\n }\n}\n\n/**\n * Exchange the refresh token for a new access token and the rotated refresh\n * token (single-use, 30 days from issue, its reach re-derived by the pod).\n *\n * SINGLE-FLIGHT per connection within this process: concurrent callers share\n * one request, and for `ROTATION_MEMORY_MS` after it a caller still holding\n * the spent token gets the same result, so this process never replays a\n * spent token. Across processes nothing here can coordinate: a server-side\n * `ConnectionStore` (one row per connection) is the way to rule that out.\n * The caller must PERSIST the result before using its access token\n * (`refreshIfNeeded` does).\n *\n * Throws `expired` (no refresh token, or past its idle life), `revoked`\n * (invalid_grant while it should live: spent, revoked, the app removed or\n * narrowed to nothing, or a concurrent redeem lost), `misconfigured`\n * (invalid_client, unauthorized_client), `rate_limited`, `pod_unreachable`,\n * `pod_moved` (the token endpoint answered a redirect), `server_error`.\n */\nexport function refreshConnection(connection: PodConnection, opts: FlowOptions = {}): Promise<PodConnection> {\n const clock = opts.now ?? Date.now;\n const now = clock();\n const token = connection.refreshToken;\n if (!token) return Promise.reject(new ConnectError(\"expired\", \"The pod issued no refresh token; connect again\"));\n pruneRotations(now);\n const known = rotations.get(token);\n if (known) return known.result;\n if (connection.refreshExpiresAt !== undefined && connection.refreshExpiresAt <= now) {\n return Promise.reject(new ConnectError(\"expired\", \"The connection was idle too long; connect again\"));\n }\n const body = new URLSearchParams({ grant_type: \"refresh_token\", refresh_token: token, client_id: connection.clientId });\n const rotation: Rotation = {\n result: tokenRequest(connection.tokenEndpoint, body, \"refresh\", opts)\n .then((json) => {\n const next = parseTokenResponse(json, {\n podUrl: connection.podUrl,\n clientId: connection.clientId,\n tokenEndpoint: connection.tokenEndpoint,\n revocationEndpoint: connection.revocationEndpoint,\n now: clock(),\n previous: connection,\n });\n rotation.settledAt = clock();\n return next;\n })\n .catch((err: unknown) => {\n rotations.delete(token);\n // The AS answers invalid_grant to an idle-expired AND a revoked token;\n // the locally known idle deadline tells them apart.\n if (err instanceof ConnectError && err.code === \"revoked\" && connection.refreshExpiresAt !== undefined && connection.refreshExpiresAt <= clock()) {\n throw new ConnectError(\"expired\", err.message, { oauthError: err.oauthError });\n }\n throw err;\n }),\n };\n rotations.set(token, rotation);\n return rotation.result;\n}\n\n/**\n * RFC 7009, best effort: the refresh token (the pod retires its whole family\n * and the live access key) then the access token (for a pod without refresh).\n * Never throws; `revoked` says whether the pod confirmed.\n */\nexport async function revokeConnection(connection: PodConnection, opts: FlowOptions = {}): Promise<{ revoked: boolean }> {\n if (!connection.revocationEndpoint) return { revoked: false };\n const doFetch = opts.fetch ?? fetch;\n let revoked = true;\n const tokens: Array<[string | undefined, string]> = [\n [connection.refreshToken, \"refresh_token\"],\n [connection.accessToken, \"access_token\"],\n ];\n for (const [token, hint] of tokens) {\n if (!token) continue;\n try {\n const res = await doFetch(connection.revocationEndpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({ token, token_type_hint: hint, client_id: connection.clientId }).toString(),\n redirect: \"manual\",\n signal: timeoutSignal(TIMEOUT_MS),\n });\n await res.body?.cancel().catch(() => {});\n if (res.status !== 200) revoked = false;\n } catch {\n revoked = false;\n }\n }\n return { revoked };\n}\n","/**\n * Where a PodConnection lives between requests. The default keeps it in the\n * browser as a SEALED cookie (AES-GCM under the app secret, HttpOnly, Secure\n * on https, SameSite=Lax, Path=/, `__Host-` prefix on https): the browser holds\n * ciphertext it cannot read or forge, the pod token never reaches JS.\n *\n * Bring your own (a database, a KV) by implementing `ConnectionStore`; it\n * answers with the Set-Cookie values to send (e.g. its own session id).\n */\nimport { canonicalPodUrl } from \"./discovery\";\nimport { base64url, fromBase64url, fromUtf8, parseCookies, serializeCookie, utf8 } from \"./encoding\";\nimport { ConnectError } from \"./errors\";\nimport { seal, unseal, type AppSecret } from \"./seal\";\nimport type { PodConnection, PodGrant } from \"./types\";\n\nexport interface ConnectionStore {\n /** The request's connection, or null (absent, tampered, foreign, expired). */\n read(request: Request): Promise<PodConnection | null>;\n /** Persist; returns the Set-Cookie header values to send. */\n write(connection: PodConnection, request: Request): Promise<string[]>;\n /** Forget; returns the Set-Cookie header values to send. */\n clear(request: Request): Promise<string[]>;\n}\n\nexport interface CookieNames {\n session: string;\n transaction: string;\n /** A Synap Cloud sign-in in progress (sealed state, 10 minutes). */\n cloud: string;\n /** \"This browser was connected to <pod>\": the pod's URL only, no secret (`markerCookie`). */\n marker: string;\n}\n\n/** `__Host-` names on https (Secure, Path=/, no Domain: bound to this exact host). */\nexport function cookieNames(secure: boolean): CookieNames {\n return secure\n ? { session: \"__Host-synap_session\", transaction: \"__Host-synap_tx\", cloud: \"__Host-synap_cloud\", marker: \"__Host-synap_seen\" }\n : { session: \"synap_session\", transaction: \"synap_tx\", cloud: \"synap_cloud\", marker: \"synap_seen\" };\n}\n\n/** The marker outlives the connection: as long as browsers keep a cookie (400 days). */\nexport const MARKER_TTL_SEC = 400 * 24 * 60 * 60;\n\n/**\n * The marker cookie: which pod this browser was connected to, so a connection\n * that lapsed (idle past the refresh token's life, its sealed cookie gone)\n * reads as \"expired, connect again to <pod>\" rather than \"never connected\".\n * It holds the pod's public URL and NOTHING else: no token, no user, nothing\n * sealed. A forged value can only pre-fill the address the person confirms.\n */\nexport function markerCookie(podUrl: string, opts: { secure: boolean; name?: string }): string {\n return serializeCookie(opts.name ?? cookieNames(opts.secure).marker, base64url(utf8(podUrl)), {\n maxAgeSec: MARKER_TTL_SEC,\n secure: opts.secure,\n });\n}\n\n/** The marker's pod URL, or null (absent, or not a canonical pod address). */\nexport function readMarker(\n cookieHeader: string | null,\n opts: { secure: boolean; name?: string; allowInsecureLoopback?: boolean },\n): string | null {\n const raw = parseCookies(cookieHeader).get(opts.name ?? cookieNames(opts.secure).marker);\n const bytes = raw ? fromBase64url(raw) : null;\n if (!bytes || bytes.length > 512) return null;\n const podUrl = fromUtf8(bytes);\n try {\n return canonicalPodUrl(podUrl, opts) === podUrl ? podUrl : null;\n } catch {\n return null;\n }\n}\n\n/** Browsers cap a cookie near 4096 bytes; a big grant spans several. */\nconst CHUNK_SIZE = 3800;\nconst MAX_CHUNKS = 4;\n\nfunction chunkName(name: string, i: number): string {\n return i === 0 ? name : `${name}.${i}`;\n}\n\n/** One space of the sealed grant; the template slug only when the pod named one (cookies written by 1.4 have none). */\ntype StoredSpace = [workspaceId: string, workspaceName: string, permissions: string[], templateSlug?: string];\n\n/** The sealed form: the grant grouped per space (it repeats the space otherwise). */\ninterface StoredConnection extends Omit<PodConnection, \"grant\"> {\n g: StoredSpace[];\n}\n\nfunction pack(c: PodConnection): StoredConnection {\n const bySpace = new Map<string, StoredSpace>();\n for (const g of c.grant) {\n const row: StoredSpace = bySpace.get(g.workspaceId) ?? [g.workspaceId, g.workspaceName, []];\n row[2].push(g.permission);\n if (g.templateSlug && row[3] === undefined) row[3] = g.templateSlug;\n bySpace.set(g.workspaceId, row);\n }\n const { grant: _grant, ...rest } = c;\n return { ...rest, g: [...bySpace.values()] };\n}\n\nfunction unpack(s: StoredConnection): PodConnection | null {\n if (!s || typeof s.podUrl !== \"string\" || typeof s.accessToken !== \"string\" || !Array.isArray(s.g)) return null;\n const { g, ...rest } = s;\n const grant: PodGrant[] = g.flatMap(([workspaceId, workspaceName, permissions, templateSlug]) =>\n permissions.map((permission) => ({ permission, workspaceId, workspaceName, ...(typeof templateSlug === \"string\" ? { templateSlug } : {}) })),\n );\n return { ...rest, grant };\n}\n\n/** When the stored value stops being useful. */\nexport function connectionExpiresAt(c: PodConnection): number {\n return c.refreshToken && c.refreshExpiresAt !== undefined ? Math.max(c.refreshExpiresAt, c.accessExpiresAt) : c.accessExpiresAt;\n}\n\nexport interface SealedCookieStoreOptions {\n secret: AppSecret;\n /** True on https (the default when the app URL is https). */\n secure: boolean;\n name?: string;\n now?: () => number;\n}\n\nexport function createSealedCookieStore(opts: SealedCookieStoreOptions): ConnectionStore {\n const name = opts.name ?? cookieNames(opts.secure).session;\n const now = opts.now ?? Date.now;\n\n const presentChunks = (request: Request) => {\n const cookies = parseCookies(request.headers.get(\"cookie\"));\n const names: string[] = [];\n for (let i = 0; i < MAX_CHUNKS; i++) if (cookies.has(chunkName(name, i))) names.push(chunkName(name, i));\n return { cookies, names };\n };\n\n return {\n async read(request) {\n const { cookies } = presentChunks(request);\n let sealed = \"\";\n for (let i = 0; i < MAX_CHUNKS; i++) {\n const part = cookies.get(chunkName(name, i));\n if (part === undefined) break;\n sealed += part;\n }\n if (!sealed) return null;\n const opened = await unseal<StoredConnection>(sealed, { secret: opts.secret, purpose: \"synap-connect/session\", now: now() });\n return opened.ok ? unpack(opened.value) : null;\n },\n\n async write(connection, request) {\n const expiresAt = connectionExpiresAt(connection);\n const sealed = await seal(pack(connection), { secret: opts.secret, purpose: \"synap-connect/session\", expiresAt });\n const parts: string[] = [];\n for (let i = 0; i < sealed.length; i += CHUNK_SIZE) parts.push(sealed.slice(i, i + CHUNK_SIZE));\n if (parts.length > MAX_CHUNKS) {\n throw new ConnectError(\n \"misconfigured\",\n \"The connection is too large for a cookie (a very wide grant); pass a server-side ConnectionStore\",\n );\n }\n const maxAgeSec = (expiresAt - now()) / 1000;\n const out = parts.map((value, i) => serializeCookie(chunkName(name, i), value, { maxAgeSec, secure: opts.secure }));\n // Drop chunks a previous, larger value left behind.\n for (const stale of presentChunks(request).names.slice(parts.length)) {\n out.push(serializeCookie(stale, \"\", { maxAgeSec: 0, secure: opts.secure }));\n }\n return out;\n },\n\n async clear(request) {\n const { names } = presentChunks(request);\n return (names.length > 0 ? names : [name]).map((n) => serializeCookie(n, \"\", { maxAgeSec: 0, secure: opts.secure }));\n },\n };\n}\n\n/**\n * Apply Set-Cookie values to a request's Cookie header, so code that runs\n * later IN THE SAME REQUEST (a server component after `proxy.ts`) reads the\n * refreshed value. A `Max-Age=0` removes the cookie.\n */\nexport function applySetCookies(cookieHeader: string | null, setCookies: readonly string[]): string {\n const cookies = parseCookies(cookieHeader);\n for (const sc of setCookies) {\n const [pair, ...attrs] = sc.split(\";\");\n const eq = pair.indexOf(\"=\");\n if (eq < 0) continue;\n const name = pair.slice(0, eq).trim();\n const removed = attrs.some((a) => /^\\s*max-age\\s*=\\s*0\\s*$/i.test(a));\n if (removed) cookies.delete(name);\n else cookies.set(name, pair.slice(eq + 1).trim());\n }\n return [...cookies].map(([k, v]) => `${k}=${v}`).join(\"; \");\n}\n","/**\n * `createPodAppAuth(config)`: pod-app sign-in as Web-standard route handlers\n * (`Request → Response`, so Next route handlers, Hono, Remix, workers) plus\n * the `proxy.ts` refresh helper. The route files stay two lines:\n *\n * // app/api/synap/connect/route.ts\n * export const POST = synap.handlers.connect;\n *\n * Handlers:\n * GET <metadata> the app's client metadata document (its client_id URL)\n * POST <connect> form { pod, next?, space?, prompt? } → 303 to the pod's /authorize\n * GET <callback> the pod's answer → sealed session cookie → 303 to `next`\n * POST <disconnect> revoke at the pod (best effort), clear the cookie → 303\n * POST <cloud> form { next?, space?, prompt? } → 303 to the Synap Cloud pod chooser\n * GET <cloud> the chooser's return (?iss&state, state checked) → 303 to the pod's /authorize\n *\n * `prompt=consent` asks for the pod's consent screen again (widen or narrow\n * what the app may touch); without it a person already connected to that pod\n * goes straight to `next`. Either way a sign-in is a fresh PKCE + state flow.\n *\n * `connect` and `disconnect` change state, so they are POST and same-origin\n * only (Origin, else Sec-Fetch-Site): a third-party page cannot start a\n * sign-in into an attacker's pod for a visitor (login CSRF) or sign them out.\n * Failures redirect with `?error=<ConnectErrorCode>` and nothing else.\n *\n * Each successful sign-in or refresh also sets a marker cookie holding the\n * pod's URL and no secret (`markerCookie`), so a connection that lapsed reads\n * as \"expired\" (`readLapsed`, `RefreshOutcome.lapsed`), not \"never connected\".\n */\nimport { acceptCloudReturn, cloudBaseUrl, CLOUD_TRANSACTION_TTL_MS, startCloudConnect } from \"./cloud\";\nimport { safeNextPath, canonicalPodUrl } from \"./discovery\";\nimport { ConnectError, isConnectError } from \"./errors\";\nimport { serializeCookie, parseCookies } from \"./encoding\";\nimport { createPodClient, type PodClient, type PodClientOptions } from \"./client\";\nimport { assertPermissions, handleCallback, refreshConnection, revokeConnection, startConnect, TRANSACTION_TTL_MS } from \"./flow\";\nimport { assertServerRuntime } from \"./guard\";\nimport { isIpLiteral, LOOPBACK_HOSTS, SPACE_HINT_RE } from \"./rules\";\nimport { assertAppSecret, type AppSecret } from \"./seal\";\nimport { applySetCookies, cookieNames, createSealedCookieStore, markerCookie, readMarker, type ConnectionStore } from \"./store\";\nimport type { AppRequest, ClientMetadataDocument, PodConnection } from \"./types\";\n\nexport interface PodAppPaths {\n /** The client metadata document; its absolute URL is the client_id. */\n metadata: string;\n connect: string;\n callback: string;\n disconnect: string;\n /** Synap Cloud: POST starts it, GET is the pod chooser's return (forwarded from the connect page). */\n cloud: string;\n /** The app's connect page (also the CIMD `initiate_login_uri`). */\n connectPage: string;\n /** Where a failed callback lands, with `?error=<code>`. */\n resultPage: string;\n /** Where a sign-in lands when it named no `next`. */\n afterConnect: string;\n}\n\nexport const DEFAULT_PATHS: PodAppPaths = {\n metadata: \"/.well-known/synap-client.json\",\n connect: \"/api/synap/connect\",\n callback: \"/api/synap/callback\",\n disconnect: \"/api/synap/disconnect\",\n cloud: \"/api/synap/cloud\",\n connectPage: \"/connect\",\n resultPage: \"/connect/result\",\n afterConnect: \"/\",\n};\n\n/** Refresh only inside the last 5 minutes of the access token's life. */\nexport const REFRESH_WINDOW_MS = 5 * 60 * 1000;\n\nexport interface PodAppAuthConfig {\n /**\n * The app's origin, e.g. https://studio.example.com. `http://localhost` is\n * accepted for the SDK's MOCK pod only: every real pod, a local one\n * included, refuses an http client_id and redirect_uri, so an app that signs\n * in to a real pod runs on https (a tunnel in development).\n */\n appUrl: string;\n /** Shown on the pod's consent screen (at most 100 characters). */\n clientName: string;\n /** What the app asks for, each with the reason the consent screen shows. */\n requests: readonly AppRequest[];\n /** Workspace-template slugs the consent may offer to create (`synap_templates`). */\n templates?: readonly string[];\n /** SERVER-ONLY secret, at least 32 characters (SYNAP_APP_SESSION_SECRET). Several to rotate. */\n secret: AppSecret;\n paths?: Partial<PodAppPaths>;\n /** Defaults to the sealed session cookie. */\n store?: ConnectionStore;\n /** RFC 8707 resource asked for (default the Hub API). */\n resource?: \"api/hub\" | \"mcp\" | null;\n /**\n * The Synap Control Plane's URL (e.g. https://api.synap.live): offers\n * \"Continue with Synap Cloud\" (`cloudConnectAvailable`). Unset = address only.\n */\n cloudUrl?: string | null;\n /**\n * Accept a pod (and a Control Plane) on http://localhost: development\n * against the SDK's mock pod (`@synap-core/sdk/testing`). A real local pod\n * still refuses an app that is not on https (see `appUrl`).\n */\n allowInsecureLoopback?: boolean;\n /** Developer log of every refused sign-in; messages never carry a token. Default: console.warn. */\n onError?: (error: ConnectError, where: \"connect\" | \"callback\" | \"disconnect\" | \"refresh\") => void;\n fetch?: typeof fetch;\n now?: () => number;\n}\n\nexport type RefreshStatus =\n /** No connection in this request. */\n | \"none\"\n /** Prefetch or static asset: nothing refreshed (the connection is read as is). */\n | \"skipped\"\n /** Valid beyond the refresh window, or no refresh token to use. */\n | \"fresh\"\n /** Rotated: send `setCookies`, forward `cookieHeader`. */\n | \"refreshed\"\n /** The refresh failed (a lost rotation race, the pod busy) but the access token still works: keep it, set nothing. */\n | \"kept\"\n /** The access token is spent and cannot be renewed (lapsed, or the pod moved: `error.code`): connect again. The session cookie is cleared (the marker stays). */\n | \"expired\"\n /** The pod refused the refresh token (access removed): connect again. The session cookie is cleared (the marker stays). */\n | \"revoked\"\n /** The pod could not be reached and the access token is spent: try later. */\n | \"unavailable\";\n\nexport interface RefreshOutcome {\n status: RefreshStatus;\n /** A usable connection, or null. */\n connection: PodConnection | null;\n /** Set-Cookie values to append to the response. */\n setCookies: string[];\n /** The request's Cookie header with the refreshed value, to forward to the render (null = unchanged). */\n cookieHeader: string | null;\n error?: ConnectError;\n /**\n * With `none`: this browser WAS connected to that pod and the connection\n * lapsed (its sealed cookie is gone, idle past its life). Say \"expired,\n * connect again\" with the pod pre-filled, not \"connect your pod\".\n */\n lapsed?: { podUrl: string };\n}\n\nexport interface RefreshOptions {\n /**\n * Rotate now, whatever the access token's expiry says: the pod refused it\n * (a Hub call answered `unauthorized` with `refreshable`). Needs a refresh\n * token. If the pod refuses the refresh too, the outcome is `revoked` (or\n * `expired`), never `kept`: the access token is known dead.\n */\n force?: boolean;\n}\n\nexport interface PodAppClientOptions extends PodClientOptions {\n /**\n * Where the client may set cookies (a route handler, a server action): after\n * a 401 it then renews the connection ONCE (rotate, seal, `persist` the\n * Set-Cookie values, retry the request once). The rotation spends the old\n * refresh token, so it is never done where its result cannot be stored: a\n * server component passes no `persist`, and a 401 there answers\n * `unauthorized` with `refreshable` (see `RefreshOptions.force`).\n */\n persist?: (setCookies: string[]) => void | Promise<void>;\n /** The request (or its Cookie header) the connection came from: chunks a larger old cookie left are cleared. */\n request?: Request | string | null;\n}\n\nexport interface PodAppAuth {\n clientId: string;\n redirectUri: string;\n metadata: ClientMetadataDocument;\n paths: PodAppPaths;\n /** Whether \"Continue with Synap Cloud\" can be offered (`cloudUrl` is configured). */\n cloudConnectAvailable: boolean;\n handlers: {\n clientMetadata(request: Request): Promise<Response>;\n connect(request: Request): Promise<Response>;\n callback(request: Request): Promise<Response>;\n disconnect(request: Request): Promise<Response>;\n /** POST: start a Synap Cloud sign-in (same-origin, form { next?, space?, prompt? }). */\n cloudConnect(request: Request): Promise<Response>;\n /** GET: the Synap Cloud pod chooser's return (`?iss&state`), checked against this browser's cookie. */\n cloudReturn(request: Request): Promise<Response>;\n };\n /**\n * The stored connection (no refresh, no expiry check), or null. Takes the\n * request, or just its Cookie header (a server component has no Request:\n * pass `(await headers()).get(\"cookie\")`).\n */\n readConnection(source: Request | string | null): Promise<PodConnection | null>;\n /**\n * The pod this browser WAS connected to, when no connection is stored any\n * more (it lapsed); null when one is stored or this browser never connected\n * (or disconnected). Same sources as `readConnection`.\n */\n readLapsed(source: Request | string | null): Promise<{ podUrl: string } | null>;\n /** For `proxy.ts` / a route handler: rotate near expiry (or now, `force`), lost-race safe. */\n refreshIfNeeded(request: Request, opts?: RefreshOptions): Promise<RefreshOutcome>;\n /** A Hub REST client for the connection (renewing it after a 401 when it can `persist`). */\n client(connection: PodConnection, opts?: PodAppClientOptions): PodClient;\n}\n\nconst TEMPLATE_SLUG_RE = /^[a-z0-9][a-z0-9._/-]{0,99}$/;\nconst MAX_METADATA_BYTES = 5 * 1024;\nconst MAX_REQUESTS = 32;\n/** How long a refused refresh is not asked again while the access token still works (\"kept\"). */\nconst KEPT_RETRY_MS = 60_000;\n\n/** The pod's grant actions: the only verbs a pod app may ask for (client-metadata.ts `assertAppPermission`). */\nconst GRANT_ACTIONS = [\"read\", \"create\", \"update\", \"delete\"];\n/** The review verbs only an official Synap app (`https://<id>.synap.live`) may ask for, spelled exactly. */\nconst SYNAP_APP_REVIEW_PERMISSIONS = [\"proposal.approve\", \"proposal.reject\"];\nconst PERMISSION_SEGMENT_RE = /^[a-z0-9][a-z0-9_-]*$/;\n\nfunction isSynapAppOrigin(origin: string): boolean {\n return /^https:\\/\\/[a-z0-9]([a-z0-9-]*[a-z0-9])?\\.synap\\.live$/.test(origin);\n}\n\n/**\n * Mock mode's origin (`http://localhost`): it signs in to the SDK's mock pod\n * only, since a real pod refuses an http client_id before it reads a single\n * permission (synap-backend `routers/oauth/client-id-url.ts`\n * `assertClientMetadataUrl`: \"client_id URL must use https\"). So the review\n * verbs are not refused here, and an app that asks for them can run on the mock.\n */\nfunction isMockModeOrigin(origin: string): boolean {\n const url = new URL(origin);\n return url.protocol === \"http:\" && LOOPBACK_HOSTS.has(url.hostname);\n}\n\n/**\n * One `synap_requests[].permission`, refused here exactly when the pod would\n * refuse it: `<subject>[.<kind>].<action>`, lowercase, a kind only on\n * `entity`, an action in the pod's grant actions (the review verbs only for a\n * Synap app, or on mock mode's loopback origin). Stricter on one point: no `*`\n * at all (`can()` matches exactly).\n */\nfunction assertAppPermission(permission: string, origin: string): void {\n const segs = permission.split(\".\");\n const fail = (why: string) => {\n throw new ConnectError(\"misconfigured\", `requests: \"${permission}\" ${why}`);\n };\n if (segs.length < 2 || segs.length > 3 || segs.some((seg) => !PERMISSION_SEGMENT_RE.test(seg))) {\n fail(\"is not <subject>[.<kind>].<action> in lowercase (no *)\");\n }\n if (segs.length === 3 && segs[0] !== \"entity\") fail(`names a kind, but \"${segs[0]}\" has none (only entity does)`);\n if (SYNAP_APP_REVIEW_PERMISSIONS.includes(permission) && (isSynapAppOrigin(origin) || isMockModeOrigin(origin))) return;\n if (!GRANT_ACTIONS.includes(segs[segs.length - 1])) {\n fail(`asks for \"${segs[segs.length - 1]}\", which an app cannot be granted (read, create, update, delete)`);\n }\n}\n\nfunction appOriginOf(appUrl: string): { origin: string; secure: boolean } {\n let url: URL;\n try {\n url = new URL(appUrl);\n } catch {\n throw new ConnectError(\"misconfigured\", \"appUrl is not a URL\");\n }\n const secure = url.protocol === \"https:\";\n if (!secure && !(url.protocol === \"http:\" && LOOPBACK_HOSTS.has(url.hostname))) {\n throw new ConnectError(\"misconfigured\", \"appUrl must be https (http only on localhost, for the SDK's mock pod)\");\n }\n if (secure && isIpLiteral(url.hostname)) {\n throw new ConnectError(\"misconfigured\", \"appUrl must use a domain name: the pod refuses an IP address client_id\");\n }\n if (url.pathname !== \"/\" || url.search || url.hash || url.username || url.password) {\n throw new ConnectError(\"misconfigured\", \"appUrl is an origin: no path, query, fragment or credentials\");\n }\n return { origin: url.origin, secure };\n}\n\nfunction assertPath(name: string, path: string): void {\n if (!path.startsWith(\"/\") || path.startsWith(\"//\") || /[?#\\\\]/.test(path)) {\n throw new ConnectError(\"misconfigured\", `paths.${name} must be an absolute path on the app`);\n }\n}\n\n/**\n * The app's client metadata document (its client_id URL serves it). Refuses,\n * at setup, every document the pod would refuse at sign-in: the permissions\n * (`assertAppPermission`), at most 32 requests counted as sent, duplicates\n * included, an IP address origin, the 5 KB cap.\n */\nexport function buildClientMetadata(opts: {\n appUrl: string;\n clientName: string;\n requests: readonly AppRequest[];\n templates?: readonly string[];\n paths?: Partial<PodAppPaths>;\n}): ClientMetadataDocument {\n const { origin } = appOriginOf(opts.appUrl);\n const paths = { ...DEFAULT_PATHS, ...opts.paths };\n for (const [name, path] of Object.entries(paths)) assertPath(name, path);\n const clientName = opts.clientName.trim();\n if (!clientName || clientName.length > 100) throw new ConnectError(\"misconfigured\", \"clientName must be 1-100 characters\");\n // The pod counts the entries as sent, before it drops duplicates.\n if (opts.requests.length === 0 || opts.requests.length > MAX_REQUESTS) {\n throw new ConnectError(\"misconfigured\", `requests must hold 1-${MAX_REQUESTS} entries (duplicates count)`);\n }\n assertPermissions(opts.requests.map((r) => r.permission));\n for (const r of opts.requests) {\n assertAppPermission(r.permission, origin);\n if (typeof r.reason !== \"string\" || !r.reason.trim() || r.reason.length > 200) {\n throw new ConnectError(\"misconfigured\", `the reason for ${r.permission} must be 1-200 characters`);\n }\n }\n const templates = [...new Set(opts.templates ?? [])];\n if (templates.length > 8 || templates.some((t) => !TEMPLATE_SLUG_RE.test(t))) {\n throw new ConnectError(\"misconfigured\", \"at most 8 template slugs (lowercase letters, digits, . _ / -)\");\n }\n const doc: ClientMetadataDocument = {\n client_id: `${origin}${paths.metadata}`,\n client_name: clientName,\n client_uri: origin,\n redirect_uris: [`${origin}${paths.callback}`],\n grant_types: [\"authorization_code\", \"refresh_token\"],\n response_types: [\"code\"],\n token_endpoint_auth_method: \"none\",\n initiate_login_uri: `${origin}${paths.connectPage}`,\n synap_requests: opts.requests.map((r) => ({ permission: r.permission, reason: r.reason.trim() })),\n ...(templates.length > 0 ? { synap_templates: templates } : {}),\n };\n if (new TextEncoder().encode(JSON.stringify(doc)).length > MAX_METADATA_BYTES) {\n throw new ConnectError(\"misconfigured\", \"the client metadata document exceeds the pod's 5 KB limit\");\n }\n return doc;\n}\n\nfunction redirect(location: string, setCookies: readonly string[] = []): Response {\n const headers = new Headers({ Location: location, \"Cache-Control\": \"no-store\" });\n for (const c of setCookies) headers.append(\"Set-Cookie\", c);\n return new Response(null, { status: 303, headers });\n}\n\nfunction withQuery(origin: string, path: string, params: Record<string, string | null | undefined>): string {\n const url = new URL(path, origin);\n for (const [k, v] of Object.entries(params)) if (v) url.searchParams.set(k, v);\n return url.href;\n}\n\n/** A state-changing request must come from the app's own pages. */\nfunction sameOrigin(request: Request, origin: string): boolean {\n const from = request.headers.get(\"origin\");\n if (from !== null) return from === origin;\n return request.headers.get(\"sec-fetch-site\") === \"same-origin\";\n}\n\nfunction formField(form: FormData | null, name: string): string | null {\n const v = form?.get(name);\n return typeof v === \"string\" ? v : null;\n}\n\nasync function readForm(request: Request): Promise<FormData | null> {\n try {\n return await request.formData();\n } catch {\n return null;\n }\n}\n\n/**\n * A prefetch or a static asset: `refreshIfNeeded` answers `skipped` for these.\n *\n * LIMIT (Next.js 16 `proxy.ts` on the Node runtime): Next strips its router\n * headers (`next-router-prefetch`, `rsc`, …, `FLIGHT_HEADERS` in\n * next/dist/server/web/adapter.js) and the `_rsc` query BEFORE the proxy runs,\n * so a router prefetch is invisible here. Skip prefetches in the proxy\n * MATCHER, which still sees the raw headers:\n *\n * export const config = { matcher: [{ source: \"/((?!_next/static|_next/image|.*\\\\.\\\\w+$).*)\",\n * missing: [{ type: \"header\", key: \"next-router-prefetch\" }, { type: \"header\", key: \"purpose\", value: \"prefetch\" }] }] };\n *\n * Route handlers and other frameworks see the headers, and this still checks them.\n */\nexport function isPrefetchOrAsset(request: Request): boolean {\n const h = request.headers;\n if (h.get(\"next-router-prefetch\") || h.get(\"next-router-segment-prefetch\") || h.get(\"x-middleware-prefetch\")) return true;\n if (/prefetch/i.test(h.get(\"purpose\") ?? \"\") || /prefetch/i.test(h.get(\"sec-purpose\") ?? \"\")) return true;\n const path = new URL(request.url).pathname;\n return path.startsWith(\"/_next/\") || /\\.[A-Za-z0-9]{1,8}$/.test(path);\n}\n\nexport function createPodAppAuth(config: PodAppAuthConfig): PodAppAuth {\n assertServerRuntime();\n assertAppSecret(config.secret);\n const { origin, secure } = appOriginOf(config.appUrl);\n const paths: PodAppPaths = { ...DEFAULT_PATHS, ...config.paths };\n const metadata = buildClientMetadata({ ...config, paths });\n const cloudUrl = config.cloudUrl?.trim() ? cloudBaseUrl(config.cloudUrl, config) : null;\n const clientId = metadata.client_id;\n const redirectUri = metadata.redirect_uris[0];\n const permissions = config.requests.map((r) => r.permission);\n const names = cookieNames(secure);\n const now = config.now ?? Date.now;\n const store = config.store ?? createSealedCookieStore({ secret: config.secret, secure, now });\n const flow = { fetch: config.fetch, now: config.now };\n const report =\n config.onError ??\n ((err: ConnectError, where: string) => {\n console.warn(`[synap/connect] ${where}: ${err.code}: ${err.message}`);\n });\n const fail = (err: unknown, where: \"connect\" | \"callback\" | \"disconnect\" | \"refresh\"): ConnectError => {\n const e = isConnectError(err) ? err : new ConnectError(\"server_error\", err instanceof Error ? err.message : \"Unexpected failure\");\n report(e, where);\n return e;\n };\n const clearTx = serializeCookie(names.transaction, \"\", { maxAgeSec: 0, secure });\n const clearCloud = serializeCookie(names.cloud, \"\", { maxAgeSec: 0, secure });\n const clearMarker = serializeCookie(names.marker, \"\", { maxAgeSec: 0, secure });\n const marker = (podUrl: string) => markerCookie(podUrl, { secure, name: names.marker });\n const asRequest = (source: Request | string | null) =>\n source instanceof Request ? source : new Request(origin, { headers: source ? { cookie: source } : {} });\n const lapsedIn = (request: Request) => {\n const podUrl = readMarker(request.headers.get(\"cookie\"), { secure, name: names.marker, allowInsecureLoopback: config.allowInsecureLoopback });\n return podUrl ? { podUrl } : null;\n };\n\n /** Off to the pod's consent: fresh PKCE + state, the transaction cookie set (extra cookies first). */\n async function toPod(opts: { pod: string; next: string; space: string | null; prompt: \"consent\" | null }, extra: string[] = []) {\n const started = await startConnect({\n ...flow,\n pod: opts.pod,\n clientId,\n redirectUri,\n permissions,\n secret: config.secret,\n resource: config.resource,\n spaceHint: opts.space && SPACE_HINT_RE.test(opts.space) ? opts.space : null,\n next: opts.next,\n prompt: opts.prompt,\n allowInsecureLoopback: config.allowInsecureLoopback,\n });\n return redirect(started.authorizeUrl, [\n ...extra,\n serializeCookie(names.transaction, started.transaction, { maxAgeSec: TRANSACTION_TTL_MS / 1000, secure }),\n ]);\n }\n\n /** A returning person already connected to this pod goes straight on (unless they asked to see the consent again). */\n async function liveConnectionTo(request: Request, pod: string): Promise<boolean> {\n const existing = await store.read(request);\n return Boolean(existing && existing.accessExpiresAt > now() && existing.podUrl === canonicalPodUrl(pod, config));\n }\n\n async function clientMetadata(): Promise<Response> {\n return new Response(JSON.stringify(metadata), {\n status: 200,\n headers: { \"Content-Type\": \"application/json\", \"Cache-Control\": \"public, max-age=300\" },\n });\n }\n\n async function connect(request: Request): Promise<Response> {\n if (request.method !== \"POST\") return new Response(\"Method Not Allowed\", { status: 405, headers: { Allow: \"POST\" } });\n if (!sameOrigin(request, origin)) return new Response(\"Forbidden\", { status: 403 });\n const form = await readForm(request);\n const pod = formField(form, \"pod\")?.slice(0, 512) ?? \"\";\n const next = safeNextPath(formField(form, \"next\"), origin) ?? paths.afterConnect;\n const space = formField(form, \"space\");\n const prompt = formField(form, \"prompt\") === \"consent\" ? \"consent\" : null;\n try {\n if (!prompt && (await liveConnectionTo(request, pod))) return redirect(new URL(next, origin).href);\n return await toPod({ pod, next, space, prompt });\n } catch (err) {\n const e = fail(err, \"connect\");\n return redirect(withQuery(origin, paths.connectPage, { error: e.code, pod }));\n }\n }\n\n async function callback(request: Request): Promise<Response> {\n if (request.method !== \"GET\") return new Response(\"Method Not Allowed\", { status: 405, headers: { Allow: \"GET\" } });\n const transaction = parseCookies(request.headers.get(\"cookie\")).get(names.transaction) ?? null;\n try {\n const { connection, next } = await handleCallback({\n ...flow,\n callbackUrl: request.url,\n transaction,\n clientId,\n redirectUri,\n secret: config.secret,\n });\n const cookies = await store.write(connection, request);\n return redirect(new URL(next ?? paths.afterConnect, origin).href, [clearTx, ...cookies, marker(connection.podUrl)]);\n } catch (err) {\n const e = fail(err, \"callback\");\n return redirect(withQuery(origin, paths.resultPage, { error: e.code }), [clearTx]);\n }\n }\n\n async function disconnect(request: Request): Promise<Response> {\n if (request.method !== \"POST\") return new Response(\"Method Not Allowed\", { status: 405, headers: { Allow: \"POST\" } });\n if (!sameOrigin(request, origin)) return new Response(\"Forbidden\", { status: 403 });\n const next = safeNextPath(formField(await readForm(request), \"next\"), origin) ?? paths.connectPage;\n const connection = await store.read(request);\n if (connection) {\n const { revoked } = await revokeConnection(connection, flow);\n if (!revoked) report(new ConnectError(\"pod_unreachable\", \"The pod did not confirm the revocation; the cookie is cleared anyway\"), \"disconnect\");\n }\n // A sign-out is not a lapse: the marker goes too.\n return redirect(new URL(next, origin).href, [...(await store.clear(request)), clearMarker]);\n }\n\n async function cloudConnect(request: Request): Promise<Response> {\n if (request.method !== \"POST\") return new Response(\"Method Not Allowed\", { status: 405, headers: { Allow: \"POST\" } });\n if (!sameOrigin(request, origin)) return new Response(\"Forbidden\", { status: 403 });\n const form = await readForm(request);\n const next = safeNextPath(formField(form, \"next\"), origin) ?? paths.afterConnect;\n const space = formField(form, \"space\");\n try {\n const started = await startCloudConnect({\n cloudUrl,\n allowInsecureLoopback: config.allowInsecureLoopback,\n clientId,\n secret: config.secret,\n next,\n spaceHint: space && SPACE_HINT_RE.test(space) ? space : null,\n prompt: formField(form, \"prompt\") === \"consent\" ? \"consent\" : null,\n now,\n });\n return redirect(started.url, [\n serializeCookie(names.cloud, started.transaction, { maxAgeSec: CLOUD_TRANSACTION_TTL_MS / 1000, secure }),\n ]);\n } catch (err) {\n const e = fail(err, \"connect\");\n return redirect(withQuery(origin, paths.connectPage, { error: e.code }));\n }\n }\n\n async function cloudReturn(request: Request): Promise<Response> {\n if (request.method !== \"GET\") return new Response(\"Method Not Allowed\", { status: 405, headers: { Allow: \"GET\" } });\n let pod: string | null = null;\n try {\n // The state is checked against THIS browser's cookie before the pod is used.\n const back = await acceptCloudReturn({\n url: request.url,\n transaction: parseCookies(request.headers.get(\"cookie\")).get(names.cloud) ?? null,\n clientId,\n secret: config.secret,\n allowInsecureLoopback: config.allowInsecureLoopback,\n now,\n });\n pod = back.pod;\n const next = back.next ?? paths.afterConnect;\n if (!back.prompt && (await liveConnectionTo(request, back.pod))) return redirect(new URL(next, origin).href, [clearCloud]);\n return await toPod({ pod: back.pod, next, space: back.spaceHint, prompt: back.prompt }, [clearCloud]);\n } catch (err) {\n const e = fail(err, \"connect\");\n // The pod is echoed only once the state matched: a forged return pre-fills nothing.\n return redirect(withQuery(origin, paths.connectPage, { error: e.code, pod: pod ? new URL(pod).host : null }), [clearCloud]);\n }\n }\n\n /** Refresh tokens whose refresh failed while their access token still worked (\"kept\"): not asked again for a minute. */\n const keptFailures = new Map<string, { error: ConnectError; at: number }>();\n\n async function refreshIfNeeded(request: Request, opts: RefreshOptions = {}): Promise<RefreshOutcome> {\n const connection = await store.read(request);\n const unchanged = (status: RefreshStatus, conn: PodConnection | null, error?: ConnectError): RefreshOutcome => ({\n status,\n connection: conn,\n setCookies: [],\n cookieHeader: null,\n ...(error ? { error } : {}),\n });\n if (!connection) {\n const lapsed = lapsedIn(request);\n return lapsed ? { ...unchanged(\"none\", null), lapsed } : unchanged(\"none\", null);\n }\n if (isPrefetchOrAsset(request)) return unchanged(\"skipped\", connection);\n const t = now();\n // Forced: the pod already refused the access token, whatever its expiry says.\n const accessLives = !opts.force && connection.accessExpiresAt > t;\n if (!opts.force && connection.accessExpiresAt - t > REFRESH_WINDOW_MS) return unchanged(\"fresh\", connection);\n if (!connection.refreshToken) {\n // The pod issued none: the connection ends with its access token.\n return accessLives ? unchanged(\"fresh\", connection) : unchanged(\"expired\", null, new ConnectError(\"expired\", \"No refresh token; connect again\"));\n }\n for (const [token, f] of keptFailures) if (t - f.at >= KEPT_RETRY_MS) keptFailures.delete(token);\n const keptFailure = accessLives ? keptFailures.get(connection.refreshToken) : undefined;\n if (keptFailure) return unchanged(\"kept\", connection, keptFailure.error);\n let next: PodConnection;\n try {\n next = await refreshConnection(connection, flow);\n } catch (err) {\n const e = fail(err, \"refresh\");\n // LOST-RACE SAFE: another instance may have redeemed the token at the\n // same moment; the pod refuses the concurrent loser without a family\n // revoke, and the access token we hold is still good. Keep it and set\n // NOTHING, so the winner's cookie stands. (Within one process the race\n // never reaches the pod: `refreshConnection` is single-flight.) Never\n // clear the cookie here either: a clear racing the winner's Set-Cookie\n // would sign the person out. The failure is remembered for a minute, so\n // each page view does not ask the pod again.\n if (accessLives) {\n keptFailures.set(connection.refreshToken, { error: e, at: t });\n return unchanged(\"kept\", connection, e);\n }\n // Spent and refused for good: clear the session cookie, so every later\n // page view does not post the dead token to /token again (that budget is\n // per caller IP: the app server, shared by all its visitors). The marker\n // stays: the person still reads \"expired, connect again to <pod>\".\n if (e.code === \"revoked\" || e.code === \"expired\" || e.code === \"pod_moved\") {\n const cleared = await store.clear(request).catch(() => [] as string[]);\n return { status: e.code === \"revoked\" ? \"revoked\" : \"expired\", connection: null, setCookies: cleared, cookieHeader: applySetCookies(request.headers.get(\"cookie\"), cleared), error: e };\n }\n return unchanged(\"unavailable\", null, e);\n }\n // PERSIST BEFORE USE: the old refresh token is spent now; only a stored\n // successor keeps the connection. If it cannot be stored, the new tokens\n // are not handed out, and the stored (spent) one is cleared so no later\n // request replays it (a replay revokes the family and the live key).\n let setCookies: string[];\n try {\n setCookies = await store.write(next, request);\n } catch (err) {\n const e = fail(err, \"refresh\");\n const cleared = await store.clear(request).catch(() => [] as string[]);\n return { status: \"expired\", connection: null, setCookies: cleared, cookieHeader: null, error: e };\n }\n setCookies = [...setCookies, marker(next.podUrl)];\n return {\n status: \"refreshed\",\n connection: next,\n setCookies,\n cookieHeader: applySetCookies(request.headers.get(\"cookie\"), setCookies),\n };\n }\n\n /**\n * The client's renewal after a 401: rotate (single-flight per\n * process), PERSIST through `persist`, then hand the new connection over.\n * A rotation that cannot be persisted is not used (persist before use).\n */\n function renewer(persist: NonNullable<PodAppClientOptions[\"persist\"]>, source: Request | string | null | undefined) {\n return async (connection: PodConnection): Promise<PodConnection> => {\n let next: PodConnection;\n try {\n next = await refreshConnection(connection, flow);\n } catch (err) {\n throw fail(err, \"refresh\");\n }\n try {\n const cookies = await store.write(next, asRequest(source ?? null));\n await persist([...cookies, marker(next.podUrl)]);\n } catch (err) {\n fail(err, \"refresh\");\n throw new ConnectError(\"expired\", \"The renewed connection could not be stored; connect again\");\n }\n return next;\n };\n }\n\n return {\n clientId,\n redirectUri,\n metadata,\n paths,\n cloudConnectAvailable: cloudUrl !== null,\n handlers: { clientMetadata, connect, callback, disconnect, cloudConnect, cloudReturn },\n readConnection: (source) => store.read(asRequest(source)),\n async readLapsed(source) {\n const request = asRequest(source);\n return (await store.read(request)) ? null : lapsedIn(request);\n },\n refreshIfNeeded,\n client(connection, opts = {}) {\n const { persist, request, ...clientOpts } = opts;\n return createPodClient(connection, {\n fetch: config.fetch,\n now: config.now,\n ...(persist ? { renew: renewer(persist, request) } : {}),\n ...clientOpts,\n });\n },\n };\n}\n","/**\n * `@synap-core/sdk/connect` — pod-app sign-in, SERVER-ONLY: an app connects to a person's own pod as an OAuth 2.1 public\n * client (CIMD client_id, PKCE S256, `iss`-checked answers), keeps the pod\n * token on its server in a sealed HttpOnly cookie, refreshes it in `proxy.ts`,\n * and reads/writes the pod's Hub API with typed results.\n *\n * Framework-free and Web-standard (Request/Response, WebCrypto): Node 20+,\n * edge runtimes, workers. Throws when loaded in a browser (./guard.ts).\n *\n * Invariant (pinned by src/__tests__/connect.guard.test.ts): every file of\n * this entry imports only its siblings.\n */\nimport { assertServerRuntime } from \"./guard\";\n\nassertServerRuntime();\n\n// What an app on createPodAppAuth uses.\nexport { createPodAppAuth, buildClientMetadata, isPrefetchOrAsset, DEFAULT_PATHS, REFRESH_WINDOW_MS } from \"./auth\";\nexport type { PodAppAuth, PodAppAuthConfig, PodAppClientOptions, PodAppPaths, RefreshOptions, RefreshOutcome, RefreshStatus } from \"./auth\";\nexport { acceptLaunch, safeNextPath } from \"./discovery\";\nexport type { LaunchHint } from \"./discovery\";\nexport { cloudConnectAvailable } from \"./cloud\";\nexport type { CloudConnectConfig } from \"./cloud\";\nexport { createPodClient, can, spaces, reviewUrlFor, connectionActsAs, humanSlots } from \"./client\";\nexport type {\n ListPendingProposalsOptions,\n PendingProposal,\n PendingProposals,\n PodClient,\n PodClientOptions,\n PodError,\n PodErrorCode,\n PodRefusalReason,\n PodResult,\n PodWriteResult,\n QueryValue,\n} from \"./client\";\nexport type {\n ApproveProposalOptions,\n GetDocumentOptions,\n GetEntityConnectionsOptions,\n ListEntitiesOptions,\n ListFocusSessionsOptions,\n PodApproval,\n PodConnectionSource,\n PodDocument,\n PodDocumentSection,\n PodEntity,\n PodEntityConnection,\n PodEntityConnections,\n PodEntityFacet,\n PodFileUrl,\n PodFocusSession,\n PodSessionSlot,\n PodSessionStatus,\n} from \"./hub-types\";\nexport { ConnectError, isConnectError, parseConnectErrorCode, CONNECT_ERROR_CODES } from \"./errors\";\nexport type { ConnectErrorCode } from \"./errors\";\nexport { isBrowserRuntime } from \"./guard\";\nexport type { AppRequest, ClientMetadataDocument, PodActsAs, PodConnection, PodGrant, PodIssuer, PodSpace, PodUser } from \"./types\";\nexport type { ConnectionStore } from \"./store\";\nexport { createSealedCookieStore } from \"./store\";\nexport type { SealedCookieStoreOptions } from \"./store\";\nexport type { AppSecret } from \"./seal\";\n\n// Building blocks: what createPodAppAuth is made of, for another framework or\n// store. INTERNAL, not the supported surface: an app on createPodAppAuth needs\n// none of them, and no new export is added here. They stay exported only for\n// 1.x semver; 2.0 moves them to their own entry. (ConnectionStore and\n// createSealedCookieStore above are the supported custom-store seam.)\nexport { discoverPod, canonicalPodUrl } from \"./discovery\";\nexport type { DiscoverOptions } from \"./discovery\";\nexport { startCloudConnect, acceptCloudReturn, cloudBaseUrl, CLOUD_TRANSACTION_TTL_MS } from \"./cloud\";\nexport type {\n AcceptCloudReturnOptions,\n CloudReturn,\n CloudTransaction,\n SealedCloudTransaction,\n StartCloudConnectOptions,\n StartedCloudConnect,\n} from \"./cloud\";\nexport {\n startConnect,\n openTransaction,\n handleCallback,\n refreshConnection,\n revokeConnection,\n parseTokenResponse,\n TRANSACTION_TTL_MS,\n DEFAULT_REFRESH_IDLE_MS,\n ROTATION_MEMORY_MS,\n} from \"./flow\";\nexport type {\n CallbackResult,\n ConnectTransaction,\n FlowOptions,\n HandleCallbackOptions,\n SealedTransaction,\n StartConnectOptions,\n StartedConnect,\n} from \"./flow\";\nexport { cookieNames, applySetCookies, connectionExpiresAt, markerCookie, readMarker, MARKER_TTL_SEC } from \"./store\";\nexport type { CookieNames } from \"./store\";\nexport { seal, unseal, MIN_SECRET_LENGTH } from \"./seal\";\nexport type { Opened, SealPurpose } from \"./seal\";\nexport { pkceChallenge } from \"./encoding\";\n"],"mappings":"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","names":["TIMEOUT_MS","refused","read","opts","TIMEOUT_MS","MAX_METADATA_BYTES"]}
1
+ {"version":3,"sources":["../src/connect/guard.ts","../src/connect/errors.ts","../src/connect/http.ts","../src/connect/discovery.ts","../src/connect/seal.ts","../src/connect/cloud.ts","../src/connect/client.ts","../src/connect/flow.ts","../src/connect/store.ts","../src/connect/auth.ts","../src/connect/index.ts"],"sourcesContent":["/**\n * `@synap-core/sdk/connect` holds pod tokens and the app secret: it must never\n * run in a browser. Two guards:\n *\n * - BUILD: the package's `exports[\"./connect\"]` maps the `browser` condition\n * to `connect.browser` (src/connect/browser.ts), which exports nothing and\n * throws on load, so a client bundle never contains this code and a strict\n * ESM bundler fails on the missing named import. Server and edge\n * conditions (`edge-light`, `workerd`, `import`, `require`) get the real\n * module;\n * - RUNTIME: importing the real module where `window` and `document` exist\n * throws (a test DOM, a misconfigured bundler).\n */\nexport const BROWSER_REFUSAL =\n \"@synap-core/sdk/connect is server-only: it holds the pod's tokens and the app secret. \" +\n \"Import it from route handlers, proxy.ts or server code, never from a client component.\";\n\nexport function isBrowserRuntime(): boolean {\n const g = globalThis as { window?: unknown; document?: unknown };\n return typeof g.window !== \"undefined\" && typeof g.document !== \"undefined\";\n}\n\nexport function assertServerRuntime(): void {\n if (isBrowserRuntime()) throw new Error(BROWSER_REFUSAL);\n}\n","/**\n * The one error type of `@synap-core/sdk/connect`. `code` is the contract a UI\n * branches on; `message` and `detail` are for the developer's logs, never for\n * the person (a sign-in failure page says one sentence per code, never why).\n */\n\nexport type ConnectErrorCode =\n /** The typed pod address is not a usable URL. */\n | \"invalid_pod_address\"\n /** Something answered, but it is not a Synap pod (no AS metadata). */\n | \"not_a_pod\"\n /** The pod did not answer (network, timeout, 5xx). */\n | \"pod_unreachable\"\n /** A Synap pod, but too old for app sign-in (no CIMD, no `iss`, no S256). */\n | \"pod_unsupported\"\n /** Connecting through Synap Cloud is not configured for this app (no Control Plane URL). */\n | \"cp_unavailable\"\n /** The person said no on the consent screen. */\n | \"denied\"\n /** No sign-in in progress here, or it is older than 10 minutes. */\n | \"expired_transaction\"\n /** The answer does not belong to the sign-in this browser started (`state` or `iss`). */\n | \"mismatch\"\n /** The app's own setup is wrong (client metadata, scopes, secret, redirect). */\n | \"misconfigured\"\n /** The pod asked to slow down; see `retryAfterSec`. */\n | \"rate_limited\"\n /** The connection lapsed (refresh token idle past its life, or none was issued). */\n | \"expired\"\n /** The pod refused the connection's refresh token while it should still live. */\n | \"revoked\"\n /**\n * The pod answered a redirect where it must answer itself (a moved pod\n * answers 308 from its old name for a while): connect again with its new\n * address. The new address is never taken from the redirect.\n */\n | \"pod_moved\"\n /** The pod answered something the SDK cannot use. */\n | \"server_error\";\n\nexport class ConnectError extends Error {\n readonly code: ConnectErrorCode;\n /** Seconds to wait, from `Retry-After`, when `code` is `rate_limited`. */\n readonly retryAfterSec?: number;\n /** The OAuth error code the pod sent, when there was one (logs only). */\n readonly oauthError?: string;\n\n constructor(\n code: ConnectErrorCode,\n message: string,\n extra: { retryAfterSec?: number; oauthError?: string } = {},\n ) {\n super(message);\n this.name = \"ConnectError\";\n this.code = code;\n if (extra.retryAfterSec !== undefined) this.retryAfterSec = extra.retryAfterSec;\n if (extra.oauthError !== undefined) this.oauthError = extra.oauthError;\n }\n}\n\nexport function isConnectError(err: unknown): err is ConnectError {\n return err instanceof ConnectError;\n}\n\nexport const CONNECT_ERROR_CODES: readonly ConnectErrorCode[] = [\n \"invalid_pod_address\",\n \"not_a_pod\",\n \"pod_unreachable\",\n \"pod_unsupported\",\n \"cp_unavailable\",\n \"denied\",\n \"expired_transaction\",\n \"mismatch\",\n \"misconfigured\",\n \"rate_limited\",\n \"expired\",\n \"revoked\",\n \"pod_moved\",\n \"server_error\",\n];\n\n/** Parse an untrusted `?error=` value back into a code (null when unknown). */\nexport function parseConnectErrorCode(value: string | null | undefined): ConnectErrorCode | null {\n return (CONNECT_ERROR_CODES as readonly string[]).includes(value ?? \"\") ? (value as ConnectErrorCode) : null;\n}\n\n/**\n * RFC 6749 §4.1.2.1 / §5.2 error codes → ours. `invalid_grant` at the token\n * endpoint during a callback means the code was used, expired or does not\n * match: for the person that is \"the link expired\".\n */\nexport function fromOAuthError(oauthError: string, during: \"authorize\" | \"token\" | \"refresh\"): ConnectErrorCode {\n switch (oauthError) {\n case \"access_denied\":\n return \"denied\";\n case \"temporarily_unavailable\":\n case \"server_error\":\n return \"pod_unreachable\";\n case \"invalid_grant\":\n return during === \"refresh\" ? \"revoked\" : \"expired_transaction\";\n case \"invalid_request\":\n case \"invalid_client\":\n case \"invalid_scope\":\n case \"invalid_target\":\n case \"unauthorized_client\":\n case \"unsupported_grant_type\":\n case \"unsupported_response_type\":\n return \"misconfigured\";\n default:\n return \"server_error\";\n }\n}\n\n/** `Retry-After` as whole seconds (delta-seconds or an HTTP date); undefined when unusable. */\nexport function retryAfterSeconds(header: string | null, now: number): number | undefined {\n if (!header) return undefined;\n const trimmed = header.trim();\n if (/^\\d{1,9}$/.test(trimmed)) return Number(trimmed);\n const at = Date.parse(trimmed);\n if (Number.isNaN(at)) return undefined;\n return Math.max(0, Math.ceil((at - now) / 1000));\n}\n","/**\n * Reading answers from hosts the SDK does not control: a pod a visitor typed,\n * its token endpoint, its Hub API. Bounded in time (`timeoutSignal`) and in\n * bytes (`readCapped`): the bytes are counted on the stream, so an answer past\n * the cap is cancelled there, never buffered whole first.\n */\n\n/** An abort signal after `ms`, where the runtime has `AbortSignal.timeout` (workers and edge runtimes may not). */\nexport function timeoutSignal(ms: number): AbortSignal | undefined {\n return typeof AbortSignal.timeout === \"function\" ? AbortSignal.timeout(ms) : undefined;\n}\n\nexport type CappedRead = { ok: true; text: string } | { ok: false; reason: \"too_large\" | \"unreadable\" };\n\n/** The body as UTF-8 text, at most `maxBytes` bytes. */\nexport async function readCapped(res: Response, maxBytes: number): Promise<CappedRead> {\n const declared = Number(res.headers.get(\"content-length\"));\n if (Number.isFinite(declared) && declared > maxBytes) {\n await res.body?.cancel().catch(() => {});\n return { ok: false, reason: \"too_large\" };\n }\n if (!res.body) return { ok: true, text: \"\" };\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let total = 0;\n let text = \"\";\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n total += value.byteLength;\n if (total > maxBytes) {\n await reader.cancel().catch(() => {});\n return { ok: false, reason: \"too_large\" };\n }\n text += decoder.decode(value, { stream: true });\n }\n } catch {\n return { ok: false, reason: \"unreadable\" };\n }\n return { ok: true, text: text + decoder.decode() };\n}\n","/**\n * Pod discovery: a typed address or a launch `iss` → the pod's authorization\n * server metadata (RFC 8414), validated before any redirect.\n *\n * - the metadata's `issuer` must equal the address it was fetched for, byte\n * for byte (RFC 8414 §3.3): a document naming another issuer is not used;\n * - it must advertise CIMD clients, `iss` on every authorization response\n * (RFC 9207) and PKCE S256, else the pod is too old for app sign-in;\n * - every endpoint must live on the issuer's own origin.\n *\n * The fetch runs on the app's server with an address a visitor typed, so it is\n * fenced: https only (http only on loopback when `allowInsecureLoopback`), no\n * IP literal outside public space, no single-label or `.local`/`.internal`\n * host, no redirect followed, 5 s, 64 KB counted on the stream. At most three\n * addresses are tried per call (`discoverPod`). Residual: a public DNS name\n * that resolves to a private address cannot be refused without a resolver; a\n * deployment that cares adds egress rules, and an app rate-limits the route\n * that calls this (it fetches an address a visitor chose).\n *\n * Connecting through Synap Cloud (the Control Plane's pod chooser) is ./cloud.ts.\n */\nimport { ConnectError, retryAfterSeconds } from \"./errors\";\nimport { readCapped, timeoutSignal } from \"./http\";\nimport { CLOUD_STATE_RE, LOOPBACK_HOSTS, SPACE_HINT_RE } from \"./rules\";\nimport type { PodIssuer } from \"./types\";\n\nconst METADATA_PATH = \"/.well-known/oauth-authorization-server\";\nconst TIMEOUT_MS = 5_000;\nconst MAX_METADATA_BYTES = 64 * 1024;\n\nexport interface DiscoverOptions {\n fetch?: typeof fetch;\n /** Allow `http://localhost` (and 127.0.0.1, [::1]) pods: development only. */\n allowInsecureLoopback?: boolean;\n now?: () => number;\n}\n\nfunction isPrivateIpv4(host: string): boolean {\n const m = /^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/.exec(host);\n if (!m) return false;\n const [a, b] = [Number(m[1]), Number(m[2])];\n return (\n a === 0 ||\n a === 10 ||\n a === 127 ||\n (a === 100 && b >= 64 && b <= 127) ||\n (a === 169 && b === 254) ||\n (a === 172 && b >= 16 && b <= 31) ||\n (a === 192 && b === 168) ||\n a >= 224\n );\n}\n\n/**\n * The canonical issuer for a typed address, or a ConnectError. Adds `https://`\n * when no scheme is given; drops a trailing slash.\n */\nexport function canonicalPodUrl(input: string, opts: { allowInsecureLoopback?: boolean } = {}): string {\n const raw = input.trim();\n if (!raw || raw.length > 512) throw new ConnectError(\"invalid_pod_address\", \"Empty or overlong pod address\");\n let url: URL;\n try {\n url = new URL(/^[a-z][a-z0-9+.-]*:\\/\\//i.test(raw) ? raw : `https://${raw}`);\n } catch {\n throw new ConnectError(\"invalid_pod_address\", \"Not a URL\");\n }\n const host = url.hostname.toLowerCase();\n const loopback = LOOPBACK_HOSTS.has(host); // WHATWG keeps the brackets: \"[::1]\"\n if (url.protocol === \"http:\" && !(loopback && opts.allowInsecureLoopback)) {\n throw new ConnectError(\"invalid_pod_address\", \"A pod address must use https\");\n }\n if (url.protocol !== \"https:\" && url.protocol !== \"http:\") {\n throw new ConnectError(\"invalid_pod_address\", \"A pod address must use https\");\n }\n if (url.username || url.password || url.search || url.hash) {\n throw new ConnectError(\"invalid_pod_address\", \"A pod address has no credentials, query or fragment\");\n }\n if (!(loopback && opts.allowInsecureLoopback)) {\n if (loopback || host.startsWith(\"[\") || isPrivateIpv4(host) || /^\\d+$/.test(host)) {\n throw new ConnectError(\"invalid_pod_address\", \"A pod address must be a public host name\");\n }\n if (!host.includes(\".\") || /\\.(local|internal|localhost|lan|home\\.arpa)$/.test(host)) {\n throw new ConnectError(\"invalid_pod_address\", \"A pod address must be a public host name\");\n }\n }\n const path = url.pathname.replace(/\\/+$/, \"\");\n return `${url.origin}${path}`;\n}\n\nfunction metadataUrl(issuer: string): string {\n const url = new URL(issuer);\n const path = url.pathname === \"/\" ? \"\" : url.pathname;\n return `${url.origin}${METADATA_PATH}${path}`;\n}\n\nfunction sameOriginEndpoint(value: unknown, issuer: string): string | null {\n if (typeof value !== \"string\") return null;\n try {\n const url = new URL(value);\n return url.origin === new URL(issuer).origin && !url.username && !url.hash ? url.href : null;\n } catch {\n return null;\n }\n}\n\nasync function readJsonCapped(res: Response): Promise<unknown> {\n const read = await readCapped(res, MAX_METADATA_BYTES);\n if (!read.ok) {\n throw read.reason === \"too_large\"\n ? new ConnectError(\"not_a_pod\", \"Metadata document too large\")\n : new ConnectError(\"pod_unreachable\", \"The metadata answer broke off\");\n }\n try {\n return JSON.parse(read.text);\n } catch {\n throw new ConnectError(\"not_a_pod\", \"Metadata is not JSON\");\n }\n}\n\n/** Fetch and validate one candidate issuer's metadata. */\nasync function discoverIssuer(issuer: string, opts: DiscoverOptions): Promise<PodIssuer> {\n const doFetch = opts.fetch ?? fetch;\n let res: Response;\n try {\n res = await doFetch(metadataUrl(issuer), {\n method: \"GET\",\n headers: { Accept: \"application/json\" },\n redirect: \"manual\",\n signal: timeoutSignal(TIMEOUT_MS),\n });\n } catch {\n throw new ConnectError(\"pod_unreachable\", \"The pod did not answer\");\n }\n if (res.status >= 300 && res.status < 400) {\n await res.body?.cancel().catch(() => {});\n throw new ConnectError(\"pod_moved\", `The address answered ${res.status}: the pod moved (its new address is not followed)`);\n }\n if (res.status === 429) {\n throw new ConnectError(\"rate_limited\", \"The pod asked to slow down\", {\n retryAfterSec: retryAfterSeconds(res.headers.get(\"retry-after\"), (opts.now ?? Date.now)()),\n });\n }\n if (res.status >= 500) throw new ConnectError(\"pod_unreachable\", `The pod answered ${res.status}`);\n if (res.status !== 200) {\n await res.body?.cancel().catch(() => {});\n throw new ConnectError(\"not_a_pod\", `No authorization server metadata (${res.status})`);\n }\n const doc = await readJsonCapped(res);\n if (!doc || typeof doc !== \"object\" || Array.isArray(doc)) throw new ConnectError(\"not_a_pod\", \"Metadata is not an object\");\n const m = doc as Record<string, unknown>;\n\n // RFC 8414 §3.3 — the mix-up defence at discovery time.\n if (m.issuer !== issuer) {\n throw new ConnectError(\"not_a_pod\", \"The metadata names a different issuer than the address it was fetched from\");\n }\n const list = (v: unknown) => (Array.isArray(v) ? v.filter((x): x is string => typeof x === \"string\") : null);\n const pkce = list(m.code_challenge_methods_supported);\n const responseTypes = list(m.response_types_supported);\n if (\n m.authorization_response_iss_parameter_supported !== true ||\n m.client_id_metadata_document_supported !== true ||\n !pkce?.includes(\"S256\") ||\n (responseTypes !== null && !responseTypes.includes(\"code\"))\n ) {\n throw new ConnectError(\"pod_unsupported\", \"This pod does not support app sign-in (CIMD, iss, S256)\");\n }\n const authorizationEndpoint = sameOriginEndpoint(m.authorization_endpoint, issuer);\n const tokenEndpoint = sameOriginEndpoint(m.token_endpoint, issuer);\n if (!authorizationEndpoint || !tokenEndpoint) {\n throw new ConnectError(\"pod_unsupported\", \"The pod's endpoints are missing or not on its own origin\");\n }\n return {\n issuer,\n authorizationEndpoint,\n tokenEndpoint,\n revocationEndpoint: sameOriginEndpoint(m.revocation_endpoint, issuer),\n refreshSupported: list(m.grant_types_supported)?.includes(\"refresh_token\") ?? false,\n };\n}\n\n/** Synap Cloud's pod domain: pods are `<name>.pod.synap.live`; `<name>.synap.live` and `pod.<name>.synap.live` are older names. */\nconst CLOUD_DOMAIN = \"synap.live\";\nconst CLOUD_POD_ZONE = `pod.${CLOUD_DOMAIN}`;\nconst POD_NAME_RE = /^[a-z0-9]([a-z0-9-]{0,30}[a-z0-9])?$/;\n\n/** The addresses to try after the typed one fails, in order (at most two). */\nfunction fallbackIssuers(first: string): string[] {\n const url = new URL(first);\n const host = url.hostname;\n if (url.pathname !== \"/\" || !host.includes(\".\") || host.endsWith(`.${CLOUD_POD_ZONE}`)) return [];\n const label = host.endsWith(`.${CLOUD_DOMAIN}`) ? host.slice(0, -CLOUD_DOMAIN.length - 1) : null;\n if (label !== null && POD_NAME_RE.test(label)) {\n // A Synap Cloud name typed the old way: the pod's current name, then the old nested one.\n return [`${url.protocol}//${label}.${CLOUD_POD_ZONE}`, `${url.protocol}//pod.${url.host}`];\n }\n return host.startsWith(\"pod.\") ? [] : [`${url.protocol}//pod.${url.host}`];\n}\n\n/**\n * A typed pod address (or a launch `iss`) → its validated authorization server.\n * Tries the address, then the pod names it may stand for: on Synap Cloud,\n * \"you.synap.live\" finds \"you.pod.synap.live\" (and the older\n * \"pod.you.synap.live\"); elsewhere, \"example.com\" finds \"pod.example.com\".\n * A redirect is never followed: an old name that answers 308 counts as a\n * miss (`pod_moved` when nothing else answers).\n */\nexport async function discoverPod(input: string, opts: DiscoverOptions = {}): Promise<PodIssuer> {\n const first = canonicalPodUrl(input, opts);\n let firstError: unknown;\n try {\n return await discoverIssuer(first, opts);\n } catch (err) {\n firstError = err;\n }\n const retryable = (err: unknown) =>\n err instanceof ConnectError && (err.code === \"not_a_pod\" || err.code === \"pod_unreachable\" || err.code === \"pod_moved\");\n if (!retryable(firstError)) throw firstError;\n let tooOld: ConnectError | null = null;\n for (const candidate of fallbackIssuers(first)) {\n try {\n return await discoverIssuer(candidate, opts);\n } catch (err) {\n // A pod that is there but too old says more than \"nothing answered\".\n if (err instanceof ConnectError && err.code === \"pod_unsupported\") tooOld ??= err;\n }\n }\n throw tooOld ?? firstError;\n}\n\n/**\n * A same-origin path to return to after connecting, or null. Accepts a\n * relative path (never `//host` or `/\\host`) or an absolute URL on the app's\n * own origin; anything else is dropped (no open redirect).\n */\nexport function safeNextPath(value: string | null | undefined, appOrigin: string): string | null {\n if (!value || value.length > 2048) return null;\n if (value.startsWith(\"/\") && !value.startsWith(\"//\") && !value.startsWith(\"/\\\\\")) {\n try {\n const url = new URL(value, appOrigin);\n return url.origin === appOrigin ? `${url.pathname}${url.search}${url.hash}` : null;\n } catch {\n return null;\n }\n }\n try {\n const url = new URL(value);\n return url.origin === appOrigin ? `${url.pathname}${url.search}${url.hash}` : null;\n } catch {\n return null;\n }\n}\n\nexport interface LaunchHint {\n /** The pod issuer the launcher named (`iss`), to pre-fill and CONFIRM, never to auto-connect. */\n pod: string | null;\n /** Where to land after connecting (`target_link_uri`), same origin only. */\n next: string | null;\n /** A space hint for the pod's consent (`synap_space_hint`); a pod may ignore it. */\n spaceHint: string | null;\n /**\n * The `state` of a Synap Cloud return (the CP pod chooser answers\n * `initiate_login_uri?iss=…&state=…`, or `?state=…&error=access_denied` when\n * the person cancelled), shape-checked only. NOT verified here:\n * send the request's query to the cloud handler (GET `paths.cloud`), which\n * checks it against this browser's sealed cookie. Never act on `pod` because\n * a state is present.\n */\n cloudState: string | null;\n}\n\n/**\n * Read a third-party-initiated login (OIDC Core §4): the browser\n * opens the app's `initiate_login_uri?iss=…&target_link_uri=…`. One parser for\n * the browser launch and the Synap Cloud chooser's return (`cloudState`).\n */\nexport function acceptLaunch(url: string | URL, appOrigin: string, opts: { allowInsecureLoopback?: boolean } = {}): LaunchHint {\n const params = new URL(url).searchParams;\n let pod: string | null = null;\n const iss = params.get(\"iss\");\n if (iss) {\n try {\n pod = canonicalPodUrl(iss, opts);\n } catch {\n pod = null;\n }\n }\n const hint = params.get(\"synap_space_hint\");\n const state = params.get(\"state\");\n return {\n pod,\n next: safeNextPath(params.get(\"target_link_uri\"), appOrigin),\n spaceHint: hint && SPACE_HINT_RE.test(hint) ? hint : null,\n // A browser launch never carries a state: any well-formed one is a Synap Cloud return\n // (a pick: iss + state; a cancel: state + error=access_denied, no iss).\n cloudState: state && CLOUD_STATE_RE.test(state) ? state : null,\n };\n}\n","/**\n * Sealed values: AES-256-GCM under a key derived (HKDF-SHA-256) from the app\n * secret and a PURPOSE. The purpose is both the HKDF `info` and the GCM\n * additional data, so a value sealed as a sign-in transaction never opens as a\n * session, and the reverse. Any change to the sealed text, a wrong secret or a\n * wrong purpose fails GCM authentication: the value reads as absent.\n *\n * v1.<base64url(iv[12] ‖ ciphertext ‖ tag[16])>\n *\n * The plaintext carries its own expiry (`exp`, ms), checked after decryption,\n * so a cookie kept past its Max-Age by a hostile client still expires.\n */\nimport { ConnectError } from \"./errors\";\nimport { base64url, fromBase64url, fromUtf8, randomBytes, utf8 } from \"./encoding\";\n\nexport type SealPurpose = \"synap-connect/transaction\" | \"synap-connect/session\" | \"synap-connect/cloud\";\n\n/** The app secret: one value, or several to rotate (the first seals, all open). */\nexport type AppSecret = string | readonly string[];\n\nconst VERSION = \"v1\";\nconst IV_BYTES = 12;\nconst TAG_BYTES = 16;\nexport const MIN_SECRET_LENGTH = 32;\nconst SALT = utf8(\"synap-connect/v1\");\n\nconst keyCache = new Map<string, Promise<CryptoKey>>();\n\nfunction secretsOf(secret: AppSecret): readonly string[] {\n const list = typeof secret === \"string\" ? [secret] : secret;\n if (list.length === 0 || list.some((s) => typeof s !== \"string\" || s.length < MIN_SECRET_LENGTH)) {\n throw new ConnectError(\n \"misconfigured\",\n `The app session secret must be at least ${MIN_SECRET_LENGTH} characters (set SYNAP_APP_SESSION_SECRET on the server).`,\n );\n }\n return list;\n}\n\n/** Fail fast at setup when the secret is unusable. */\nexport function assertAppSecret(secret: AppSecret): void {\n secretsOf(secret);\n}\n\nfunction keyFor(secret: string, purpose: SealPurpose): Promise<CryptoKey> {\n const cacheKey = `${purpose}\\u0000${secret}`;\n let key = keyCache.get(cacheKey);\n if (!key) {\n key = crypto.subtle\n .importKey(\"raw\", utf8(secret), \"HKDF\", false, [\"deriveKey\"])\n .then((base) =>\n crypto.subtle.deriveKey(\n { name: \"HKDF\", hash: \"SHA-256\", salt: SALT, info: utf8(purpose) },\n base,\n { name: \"AES-GCM\", length: 256 },\n false,\n [\"encrypt\", \"decrypt\"],\n ),\n );\n keyCache.set(cacheKey, key);\n }\n return key;\n}\n\nexport async function seal(value: unknown, opts: { secret: AppSecret; purpose: SealPurpose; expiresAt: number }): Promise<string> {\n const [current] = secretsOf(opts.secret);\n const iv = randomBytes(IV_BYTES);\n const plaintext = utf8(JSON.stringify({ exp: opts.expiresAt, data: value }));\n const ciphertext = await crypto.subtle.encrypt(\n { name: \"AES-GCM\", iv, additionalData: utf8(opts.purpose) },\n await keyFor(current, opts.purpose),\n plaintext,\n );\n const out = new Uint8Array(IV_BYTES + ciphertext.byteLength);\n out.set(iv, 0);\n out.set(new Uint8Array(ciphertext), IV_BYTES);\n return `${VERSION}.${base64url(out)}`;\n}\n\nexport type Opened<T> = { ok: true; value: T; expiresAt: number } | { ok: false; reason: \"invalid\" | \"expired\" };\n\n/**\n * `invalid` = not ours (malformed, tampered, other secret, other purpose);\n * `expired` = ours, but past its `exp`.\n */\nexport async function unseal<T = unknown>(\n sealed: string,\n opts: { secret: AppSecret; purpose: SealPurpose; now: number },\n): Promise<Opened<T>> {\n const secrets = secretsOf(opts.secret);\n const dot = sealed.indexOf(\".\");\n if (dot < 0 || sealed.slice(0, dot) !== VERSION) return { ok: false, reason: \"invalid\" };\n const bytes = fromBase64url(sealed.slice(dot + 1));\n if (!bytes || bytes.length < IV_BYTES + TAG_BYTES) return { ok: false, reason: \"invalid\" };\n const iv = bytes.slice(0, IV_BYTES);\n const ciphertext = bytes.slice(IV_BYTES);\n for (const secret of secrets) {\n let plaintext: ArrayBuffer;\n try {\n plaintext = await crypto.subtle.decrypt(\n { name: \"AES-GCM\", iv, additionalData: utf8(opts.purpose) },\n await keyFor(secret, opts.purpose),\n ciphertext,\n );\n } catch {\n continue; // not this secret (or tampered): try the next, then refuse\n }\n let parsed: { exp?: unknown; data?: unknown };\n try {\n parsed = JSON.parse(fromUtf8(new Uint8Array(plaintext))) as { exp?: unknown; data?: unknown };\n } catch {\n return { ok: false, reason: \"invalid\" };\n }\n if (typeof parsed.exp !== \"number\") return { ok: false, reason: \"invalid\" };\n if (parsed.exp <= opts.now) return { ok: false, reason: \"expired\" };\n return { ok: true, value: parsed.data as T, expiresAt: parsed.exp };\n }\n return { ok: false, reason: \"invalid\" };\n}\n","/**\n * Connecting through Synap Cloud: the person picks their pod on the Control\n * Plane instead of typing its address.\n *\n * app POST <cloud handler> → 303 <cp>/connect/pod?client_id=<CIMD url>&state=<nonce>\n * CP the person signs in, picks a pod → 302 <initiate_login_uri>?iss=<pod issuer>&state=<nonce>\n * app GET <cloud handler>?iss&state → state checked against this browser's sealed\n * cookie → the SAME sign-in as a typed address\n * (discovery, fresh PKCE + state, `iss` on the answer)\n *\n * The CP never sees a code or a token, and takes the return URL from the\n * app's client metadata, never from the query. `state` is what makes the\n * return trustworthy: it is drawn here, kept sealed in an HttpOnly cookie of\n * this browser, and compared in constant time, so an `iss` link that another\n * site sends a visitor to (login CSRF into an attacker's pod) is refused. A\n * return without a matching `state` never starts a sign-in.\n *\n * The CP's answer only names a pod. The pod itself is then validated exactly\n * like a typed address (`discoverPod`), and its answer like any other.\n */\nimport { canonicalPodUrl } from \"./discovery\";\nimport { constantTimeEqual, randomToken } from \"./encoding\";\nimport { ConnectError, fromOAuthError } from \"./errors\";\nimport { CLOUD_STATE_RE, LOOPBACK_HOSTS } from \"./rules\";\nimport { seal, unseal, type AppSecret } from \"./seal\";\n\n/** How long a Synap Cloud sign-in may take, from the click to the pod chooser's return. */\nexport const CLOUD_TRANSACTION_TTL_MS = 10 * 60 * 1000;\n\n\n/** A sealed `CloudTransaction`: opaque, safe to put in an HttpOnly cookie. */\nexport type SealedCloudTransaction = string & { readonly __sealedCloudTransaction: true };\n\n/** What a started Synap Cloud sign-in remembers (sealed; never sent to the CP). */\nexport interface CloudTransaction {\n v: 1;\n state: string;\n clientId: string;\n /** The CP base it was sent to. */\n cloudUrl: string;\n next: string | null;\n spaceHint: string | null;\n prompt: \"consent\" | null;\n createdAt: number;\n}\n\nexport interface CloudConnectConfig {\n /** The Control Plane's base URL, e.g. https://api.synap.live. Unset = no Synap Cloud option. */\n cloudUrl?: string | null;\n /** Accept a CP on http://localhost (development). */\n allowInsecureLoopback?: boolean;\n}\n\n/** The CP base (no trailing slash), or a ConnectError (`misconfigured`). */\nexport function cloudBaseUrl(cloudUrl: string, opts: { allowInsecureLoopback?: boolean } = {}): string {\n let url: URL;\n try {\n url = new URL(cloudUrl.trim());\n } catch {\n throw new ConnectError(\"misconfigured\", \"cloudUrl is not a URL\");\n }\n const loopback = LOOPBACK_HOSTS.has(url.hostname);\n if (url.protocol !== \"https:\" && !(url.protocol === \"http:\" && loopback && opts.allowInsecureLoopback)) {\n throw new ConnectError(\"misconfigured\", \"cloudUrl must be https (http only on localhost in development)\");\n }\n if (url.username || url.password || url.search || url.hash) {\n throw new ConnectError(\"misconfigured\", \"cloudUrl has no credentials, query or fragment\");\n }\n return `${url.origin}${url.pathname.replace(/\\/+$/, \"\")}`;\n}\n\n/** Whether \"Continue with Synap Cloud\" can be offered: a valid Control Plane URL is configured. */\nexport function cloudConnectAvailable(config?: CloudConnectConfig | null): boolean {\n if (!config?.cloudUrl) return false;\n try {\n cloudBaseUrl(config.cloudUrl, config);\n return true;\n } catch {\n return false;\n }\n}\n\nexport interface StartCloudConnectOptions extends CloudConnectConfig {\n /** The app's client_id (its CIMD URL): the CP reads `initiate_login_uri` from it. */\n clientId: string;\n /**\n * SERVER-ONLY app secret that seals the transaction. Required: without it the call throws `misconfigured`.\n * Optional in the type only for 1.x semver; required in 2.0.\n */\n secret?: AppSecret;\n /** A same-origin path to land on afterwards (validate it first: `safeNextPath`). */\n next?: string | null;\n /** A space hint for the pod's consent (`synap_space_hint`); a pod may ignore it. */\n spaceHint?: string | null;\n /** Show the pod's consent again even if this browser is already connected to the pod the person picks. */\n prompt?: \"consent\" | null;\n /** @deprecated Ignored: the SDK always draws its own 32-byte state. Removed in 2.0. */\n state?: string;\n now?: () => number;\n}\n\nexport interface StartedCloudConnect {\n /** Redirect the browser here (303): the CP's pod chooser. */\n url: string;\n /** Store in an HttpOnly cookie until the CP returns. */\n transaction: SealedCloudTransaction;\n /** ms since epoch. */\n expiresAt: number;\n}\n\n/**\n * Start a Synap Cloud sign-in. Throws `cp_unavailable` at once when no CP URL\n * is configured, `misconfigured` without a secret: both SYNCHRONOUSLY, before\n * a Promise exists, so wrap the call in try/catch rather than relying on\n * `.catch` (kept from 1.3 for compatibility; 2.0 makes it a plain async\n * function with a required secret).\n */\nexport function startCloudConnect(opts: StartCloudConnectOptions): Promise<StartedCloudConnect> {\n if (!cloudConnectAvailable(opts)) {\n throw new ConnectError(\"cp_unavailable\", \"cp: not configured — pass cloudUrl (the Control Plane URL) to offer Synap Cloud\");\n }\n if (!opts.secret) throw new ConnectError(\"misconfigured\", \"startCloudConnect needs the app secret to seal its state\");\n const base = cloudBaseUrl(opts.cloudUrl!, opts);\n const secret = opts.secret;\n return (async () => {\n const now = (opts.now ?? Date.now)();\n const state = randomToken();\n const url = new URL(`${base}/connect/pod`);\n url.searchParams.set(\"client_id\", opts.clientId);\n url.searchParams.set(\"state\", state);\n const tx: CloudTransaction = {\n v: 1,\n state,\n clientId: opts.clientId,\n cloudUrl: base,\n next: opts.next ?? null,\n spaceHint: opts.spaceHint ?? null,\n prompt: opts.prompt === \"consent\" ? \"consent\" : null,\n createdAt: now,\n };\n const expiresAt = now + CLOUD_TRANSACTION_TTL_MS;\n const transaction = (await seal(tx, { secret, purpose: \"synap-connect/cloud\", expiresAt })) as SealedCloudTransaction;\n return { url: url.href, transaction, expiresAt };\n })();\n}\n\nexport interface AcceptCloudReturnOptions {\n /** The URL the CP sent the browser back to (`?iss=…&state=…`). */\n url: string | URL;\n transaction: string | null | undefined;\n clientId: string;\n secret: AppSecret;\n allowInsecureLoopback?: boolean;\n now?: () => number;\n}\n\nexport interface CloudReturn {\n /** The pod the person picked (canonical issuer), still to be discovered and validated. */\n pod: string;\n next: string | null;\n spaceHint: string | null;\n prompt: \"consent\" | null;\n}\n\n/**\n * Check the CP's return against the sealed transaction of THIS browser.\n * Throws `expired_transaction` (none, foreign, tampered or older than 10\n * minutes), `mismatch` (the state is not this browser's, or no pod is named),\n * `denied` (the person cancelled on the chooser: `error=access_denied`, no\n * `iss`; nothing is started), `invalid_pod_address`.\n * `state` is compared BEFORE anything else in the query is used.\n */\nexport async function acceptCloudReturn(opts: AcceptCloudReturnOptions): Promise<CloudReturn> {\n if (!opts.transaction) throw new ConnectError(\"expired_transaction\", \"No Synap Cloud sign-in in progress in this browser\");\n const opened = await unseal<CloudTransaction>(opts.transaction, {\n secret: opts.secret,\n purpose: \"synap-connect/cloud\",\n now: (opts.now ?? Date.now)(),\n });\n if (!opened.ok) {\n throw new ConnectError(\n \"expired_transaction\",\n opened.reason === \"expired\" ? \"The Synap Cloud sign-in is older than 10 minutes\" : \"The Synap Cloud cookie is not one this app sealed\",\n );\n }\n const tx = opened.value;\n if (!tx || tx.v !== 1 || typeof tx.state !== \"string\") {\n throw new ConnectError(\"expired_transaction\", \"The Synap Cloud cookie is not one this app sealed\");\n }\n if (tx.clientId !== opts.clientId) {\n throw new ConnectError(\"misconfigured\", \"The Synap Cloud sign-in was started for another client_id\");\n }\n const params = new URL(opts.url).searchParams;\n const state = params.get(\"state\");\n if (state === null || !CLOUD_STATE_RE.test(state) || !constantTimeEqual(state, tx.state)) {\n throw new ConnectError(\"mismatch\", \"The Synap Cloud answer does not belong to the sign-in this browser started\");\n }\n const error = params.get(\"error\");\n if (error !== null) {\n throw new ConnectError(fromOAuthError(error, \"authorize\"), `Synap Cloud answered ${error}`, { oauthError: error });\n }\n const iss = params.get(\"iss\");\n if (!iss) throw new ConnectError(\"mismatch\", \"The Synap Cloud answer names no pod\");\n return {\n pod: canonicalPodUrl(iss, opts),\n next: tx.next,\n spaceHint: tx.spaceHint,\n prompt: tx.prompt === \"consent\" ? \"consent\" : null,\n };\n}\n","/**\n * A server-side Hub REST client for one PodConnection: `<pod>/api/hub/*` with\n * the connection's Bearer token. Results, not throws: `{ ok: true, … }` or\n * `{ ok: false, error }` with a typed `code`, a malformed path included.\n *\n * - `expired`: the access token is past its expiry here; nothing was sent\n * (refresh in `proxy.ts` / a route handler, or connect again);\n * - `unauthorized`: the pod refused the token. With a `renew` (a client that\n * can persist cookies) the connection is renewed once and the request\n * retried once first; `unauthorized` then means the pod refused the\n * connection itself. Without one, `refreshable` says a renewal may help;\n * - `forbidden`: not granted; `door_unsupported`: a door a space-scoped app\n * token can never use (403 GRANT_DOOR_UNSUPPORTED);\n * - `rate_limited` with `retryAfterSec`; `pod_unreachable` (network, a\n * timeout, 502-504); `pod_moved` (a redirect: connect again);\n * - `invalid`: the pod refused the input (400/422), or the SDK did (a path\n * that could leave the Hub API: encode each segment, `encodeURIComponent`).\n *\n * Writes are governed: `proposed` is SUCCESS (queued for the person's review,\n * never retry it), with `reviewUrl` on the pod. That is the rule of an AGENT\n * connection (`actsAs: \"agent\"`, every app but an official Synap one): its\n * writes are proposals. A PERSON connection (`actsAs: \"person\"`, an official\n * Synap app) writes as the person, directly: its writes apply and come back\n * `applied`, with no review step and no proposal. The result\n * still reports what the pod did, so a `proposed` answer to a person (a space\n * that reviews even its members' changes) is still success, never an error.\n *\n * Typed methods (1.5) for the doors a pod app reads: `listEntities`,\n * `getEntity`, `getEntityConnections`, `getDocument`, `listFocusSessions`,\n * `getFileUrl`, `listPendingProposals`, `approveProposal`. Same results, same\n * codes; a refusal the SDK can decide alone (no grant, an agent approving, a\n * bad id) answers with `status: null` and a `reason`, and sends nothing.\n *\n * The token goes to the pod's own origin only: paths are pod-relative and\n * checked again after URL resolution, no redirect is followed. Each request\n * is bounded: 10 s and 8 MiB by default (`timeoutMs`, `maxResponseBytes`).\n */\nimport { isConnectError, retryAfterSeconds, type ConnectError } from \"./errors\";\nimport { readCapped, timeoutSignal } from \"./http\";\nimport type {\n ApproveProposalOptions,\n GetDocumentOptions,\n GetEntityConnectionsOptions,\n ListEntitiesOptions,\n ListFocusSessionsOptions,\n PodApproval,\n PodDocument,\n PodEntity,\n PodEntityConnection,\n PodEntityConnections,\n PodFileUrl,\n PodFocusSession,\n PodSessionSlot,\n} from \"./hub-types\";\nimport type { PodActsAs, PodConnection, PodGrant, PodSpace } from \"./types\";\n\nexport type PodErrorCode =\n | \"expired\"\n | \"unauthorized\"\n | \"forbidden\"\n | \"door_unsupported\"\n | \"not_found\"\n | \"invalid\"\n | \"conflict\"\n | \"rate_limited\"\n | \"pod_unreachable\"\n /** The pod answered a redirect (a moved pod answers 308 from its old name): connect again at its new address. */\n | \"pod_moved\"\n /**\n * A credential door (approving a proposal that widens who or what can reach\n * the pod) wants the person to re-confirm at the POD within 15 minutes; a\n * Synap Cloud sign-in never counts. Recovery: `prompt_login`.\n */\n | \"step_up_required\"\n /** Synap Cloud refused to vouch a person with no verified email. Recovery: `verify_email`. */\n | \"email_unverified\"\n /** Synap Cloud vouches an app origin over HTTPS only. Recovery: `use_https`. */\n | \"origin_insecure\"\n | \"server\";\n\n/**\n * What a typed refusal asks the app to do:\n * - `prompt_login`: send the person through the connect handler with\n * `prompt=login` (re-confirm at the pod; the SDK redeems it with the\n * device's refresh token), then retry ONCE;\n * - `verify_email`: tell the person to verify their Synap email;\n * - `use_https`: serve the app over HTTPS (`http://localhost` signs in to a\n * local Control Plane started with `CP_ALLOW_DEV_ORIGINS=true`, never to api.synap.live).\n */\nexport type PodRecovery = \"prompt_login\" | \"verify_email\" | \"use_https\";\n\n/**\n * Why the SDK refused before sending anything (`status: null`):\n * - `agent_cannot_approve`: an agent connection asked to approve (the pod\n * never lets an agent credential approve; `code: \"forbidden\"`);\n * - `not_granted`: the connection holds no permission for it (`forbidden`);\n * - `invalid_input`: an id, a path or an option the pod would refuse (`invalid`).\n */\nexport type PodRefusalReason = \"agent_cannot_approve\" | \"not_granted\" | \"invalid_input\";\n\nexport interface PodError {\n code: PodErrorCode;\n /** The HTTP status, or null when nothing was answered. */\n status: number | null;\n retryAfterSec?: number;\n /** The pod's own error text, for logs only. */\n detail?: string;\n /** Set when the SDK refused locally (1.5). */\n reason?: PodRefusalReason;\n /** What to do next, on `step_up_required`, `email_unverified` and `origin_insecure`. */\n recovery?: PodRecovery;\n /**\n * On `unauthorized` (1.5): the pod refused the access token, the connection\n * still holds a refresh token, and this client could not renew it (no\n * `renew`). A forced refresh may restore it (a narrow, or a pod-side \"Revoke\n * key\", ends the key, not the connection): run `refreshIfNeeded(request,\n * { force: true })` where cookies can be set. Absent = the pod refused the\n * connection itself: connect again.\n */\n refreshable?: true;\n}\n\nexport type PodResult<T> = { ok: true; data: T } | { ok: false; error: PodError };\n\nexport type PodWriteResult<T> =\n | { ok: true; status: \"applied\"; data: T }\n | { ok: true; status: \"proposed\"; proposalId: string | null; reviewUrl: string; data: T }\n | { ok: false; error: PodError };\n\nexport type QueryValue = string | number | boolean | null | undefined | ReadonlyArray<string | number | boolean>;\n\n/** One pending proposal, the pod's summary row (`GET /proposals?view=basic`). */\nexport interface PendingProposal {\n id: string;\n proposalType: string;\n targetType: string;\n targetId: string | null;\n workspaceId: string | null;\n /** ISO 8601, as the pod sent it. */\n createdAt: string;\n /** The agent that filed it (this app's agent, or another one), when known. */\n agentUserId: string | null;\n sessionId: string | null;\n summary?: string;\n /** Where the person reviews it: the pod's deep link. */\n reviewUrl: string;\n}\n\nexport interface PendingProposals {\n /** Every pending proposal the connection can see, not only this page (the pod COUNTs it). */\n total: number;\n proposals: PendingProposal[];\n hasMore: boolean;\n}\n\nexport interface ListPendingProposalsOptions {\n /** One granted space only. */\n workspaceId?: string;\n /** 1-200, default 20. */\n limit?: number;\n offset?: number;\n /**\n * Only these proposals (at most 100 ids; the pod's `proposalIds` filter,\n * pods that do not filter are filtered here). An empty list matches nothing (answered without a request).\n */\n proposalIds?: readonly string[];\n}\n\nexport interface PodClientOptions {\n fetch?: typeof fetch;\n now?: () => number;\n /** Per Hub request, default 10 000 ms; a request past it is `pod_unreachable`. */\n timeoutMs?: number;\n /** The most a Hub answer may weigh, default 8 MiB; a bigger one is `server` (the body is not read further). */\n maxResponseBytes?: number;\n /**\n * Renew the connection after the pod refused its access token (401) while it\n * holds a refresh token: rotate it, PERSIST the result (the old refresh token\n * is spent once the pod answers), then return it; throw the refresh's\n * `ConnectError` when it fails. The client calls it at most once (concurrent\n * 401s share the call), retries the refused request once with the result, and\n * uses it from then on. `PodAppAuth.client(connection, { persist })` builds\n * one. Without it a 401 stays `unauthorized` (with `refreshable`).\n */\n renew?: (connection: PodConnection) => Promise<PodConnection>;\n}\n\nexport interface PodClient {\n podUrl: string;\n /** The connection's, or the renewed one's after a 401 (`renew`). */\n readonly user: PodConnection[\"user\"];\n readonly grant: PodGrant[];\n /** Who the token acts as (`connectionActsAs`): `\"agent\"` unless the pod said `\"person\"`. */\n readonly actsAs: PodActsAs;\n /** Whether the connection was granted `permission` (in `workspaceId` when given). */\n can(permission: string, workspaceId?: string): boolean;\n /** The granted spaces, each with its permissions (`spaces(connection)`). */\n spaces(): PodSpace[];\n /**\n * \"Waiting for review: N\". The person's pending proposals in the spaces this\n * connection may read proposals in (any agent's, not only this app's), from\n * the pod's seamed `GET /proposals` door. Needs the `proposal.read`\n * permission (ask for it in `requests`): without it the pod would answer an\n * empty list, so this answers `forbidden` instead, without a request.\n */\n listPendingProposals(opts?: ListPendingProposalsOptions): Promise<PodResult<PendingProposals>>;\n /** `GET /entities` (no full-text `q`: a space-scoped token may list, never search). */\n listEntities(opts?: ListEntitiesOptions): Promise<PodResult<PodEntity[]>>;\n /** `GET /entities/:id`; `not_found` when it does not exist or is not readable. */\n getEntity(id: string): Promise<PodResult<PodEntity>>;\n /** `GET /entities/:id/connections`: its relations, property links, threads and sessions. */\n getEntityConnections(id: string, opts?: GetEntityConnectionsOptions): Promise<PodResult<PodEntityConnections>>;\n /** `GET /documents/:id`: content, revision, sections. */\n getDocument(id: string, opts?: GetDocumentOptions): Promise<PodResult<PodDocument>>;\n /**\n * `GET /focus-sessions` in a space (or a project), newest first, with their\n * slots (`expectedOutputs`; `humanSlots(session)` = what waits on the person).\n * `invalid` without a request when neither `workspaceId` nor `projectId` is given.\n */\n listFocusSessions(opts: ListFocusSessionsOptions): Promise<PodResult<PodFocusSession[]>>;\n /** `GET /files/:id/url`: a signed download URL for a file entity (about an hour). */\n getFileUrl(entityId: string): Promise<PodResult<PodFileUrl>>;\n /**\n * `POST /proposals/:id/approve`, as the person. Only a PERSON connection\n * (`actsAs: \"person\"`) holding `proposal.approve` can: an agent connection\n * gets `forbidden` / `agent_cannot_approve` and NOTHING is sent (the pod\n * never lets an agent approve); without the permission, `forbidden` /\n * `not_granted`, also without a request.\n */\n approveProposal(id: string, opts?: ApproveProposalOptions): Promise<PodResult<PodApproval>>;\n get<T = unknown>(path: string, query?: Record<string, QueryValue>): Promise<PodResult<T>>;\n post<T = unknown>(path: string, body?: unknown): Promise<PodWriteResult<T>>;\n patch<T = unknown>(path: string, body?: unknown): Promise<PodWriteResult<T>>;\n put<T = unknown>(path: string, body?: unknown): Promise<PodWriteResult<T>>;\n delete<T = unknown>(path: string, body?: unknown): Promise<PodWriteResult<T>>;\n}\n\n/** Who `connection`'s token acts as: `\"person\"` only when the pod said so, else `\"agent\"`. */\nexport function connectionActsAs(connection: Pick<PodConnection, \"actsAs\">): PodActsAs {\n return connection.actsAs === \"person\" ? \"person\" : \"agent\";\n}\n\n/**\n * The slots of `session` that wait on the person (Home's \"needs you\"):\n * `owner: \"human\"`, not done, not retired.\n */\nexport function humanSlots(session: Pick<PodFocusSession, \"expectedOutputs\">): PodSessionSlot[] {\n return session.expectedOutputs.filter((slot) => slot.owner === \"human\" && slot.status !== \"done\" && !slot.retiredAt);\n}\n\n/** Whether `connection` was granted `permission` (in `workspaceId` when given). Exact match: grants carry no wildcard. */\nexport function can(connection: Pick<PodConnection, \"grant\">, permission: string, workspaceId?: string): boolean {\n return connection.grant.some((g) => g.permission === permission && (workspaceId === undefined || g.workspaceId === workspaceId));\n}\n\n/**\n * The grant by space, in the grant's order, each permission once. The pod\n * sends one row per permission per space; a space switcher wants one per space.\n */\nexport function spaces(connection: Pick<PodConnection, \"grant\">): PodSpace[] {\n const bySpace = new Map<string, PodSpace>();\n for (const g of connection.grant) {\n const space = bySpace.get(g.workspaceId) ?? { workspaceId: g.workspaceId, workspaceName: g.workspaceName, permissions: [] };\n if (!space.workspaceName && g.workspaceName) space.workspaceName = g.workspaceName;\n if (!space.templateSlug && g.templateSlug) space.templateSlug = g.templateSlug;\n if (!space.permissions.includes(g.permission)) space.permissions.push(g.permission);\n bySpace.set(g.workspaceId, space);\n }\n return [...bySpace.values()];\n}\n\n/** An id the SDK puts in a path: uuids, the pod's short ids, the mock's ids. */\nconst ID_RE = /^[A-Za-z0-9_-]{1,128}$/;\nconst PROPOSAL_IDS_MAX = 100;\n\nconst SAFE_PATH = /^\\/(?!\\/)[A-Za-z0-9\\-._~!$&'()*+,;=:@%/]*$/;\nconst TIMEOUT_MS = 10_000;\nconst MAX_RESPONSE_BYTES = 8 * 1024 * 1024;\n\n/**\n * `<pod>/api/hub<path>`, or null when `path` could leave the Hub API: not\n * pod-relative, a dot segment, plain or percent-encoded (WHATWG resolves\n * `%2e%2e` as `..`), a backslash, a query. The resolved URL is checked too.\n */\nfunction hubUrl(podUrl: string, path: string, query?: Record<string, QueryValue>): URL | null {\n if (!SAFE_PATH.test(path) || /%2e/i.test(path) || /(^|\\/)\\.\\.?(\\/|$)/.test(path) || path.includes(\"\\\\\")) return null;\n let url: URL;\n try {\n url = new URL(`${podUrl}/api/hub${path}`);\n } catch {\n return null;\n }\n if (url.origin !== new URL(podUrl).origin || !(url.pathname === \"/api/hub\" || url.pathname.startsWith(\"/api/hub/\"))) return null;\n for (const [key, value] of Object.entries(query ?? {})) {\n if (value === undefined || value === null) continue;\n for (const v of Array.isArray(value) ? value : [value]) url.searchParams.append(key, String(v));\n }\n return url;\n}\n\n/** A failed renewal (`renew` threw) as the result of the request it was for. */\nfunction renewalError(err: unknown): PodError {\n const e: ConnectError | null = isConnectError(err) ? err : null;\n const detail = e?.message ?? (err instanceof Error ? err.message : \"The connection could not be renewed\");\n switch (e?.code) {\n case \"revoked\":\n // The pod refused the connection itself: connect again.\n return { code: \"unauthorized\", status: 401, detail };\n case \"expired\":\n // It lapsed, or its renewal could not be stored: connect again.\n return { code: \"expired\", status: null, detail };\n case \"rate_limited\":\n return { code: \"rate_limited\", status: null, detail, ...(e.retryAfterSec !== undefined ? { retryAfterSec: e.retryAfterSec } : {}) };\n case \"pod_unreachable\":\n return { code: \"pod_unreachable\", status: null, detail };\n case \"pod_moved\":\n return { code: \"pod_moved\", status: null, detail };\n default:\n return { code: \"server\", status: null, detail };\n }\n}\n\n/** The pod's step-up refusal (backend `STEP_UP_REQUIRED_MESSAGE`); the Hub's REST body carries only this text. */\nconst STEP_UP_MESSAGE = \"Confirm it's you before changing who or what can reach this pod.\";\n\n/** The Control Plane's public problem codes (409, `{ error, code, recovery }`). */\nconst CP_PROBLEMS: Readonly<Record<string, { code: PodErrorCode; recovery: PodRecovery }>> = {\n CP_ACCOUNT_EMAIL_REQUIRED: { code: \"email_unverified\", recovery: \"verify_email\" },\n FEDERATED_APPLICATION_ORIGIN_INSECURE: { code: \"origin_insecure\", recovery: \"use_https\" },\n};\n\nfunction isStepUp(obj: Record<string, unknown>): boolean {\n const data = (v: unknown) => (v && typeof v === \"object\" ? (v as Record<string, unknown>) : {});\n // tRPC shape (`error.data.reasonCode`), a flat reasonCode, or the Hub REST body: `{ error: <message> }`.\n const nested = data(data(obj.error).data).reasonCode ?? data(obj.data).reasonCode ?? obj.reasonCode;\n return nested === \"STEP_UP_REQUIRED\" || obj.error === STEP_UP_MESSAGE || obj.message === STEP_UP_MESSAGE;\n}\n\n/**\n * A 403 or 409 body → its typed code. A step-up is read on a 403 only, a Control\n * Plane problem on a 409 only; everything else keeps the plain `forbidden` /\n * `door_unsupported` / `conflict`, so a typed refusal is never read as a generic one.\n */\nexport function classifyRefusal(status: 403 | 409 | number, body: unknown): { code: PodErrorCode; recovery?: PodRecovery } {\n const obj = body && typeof body === \"object\" ? (body as Record<string, unknown>) : {};\n if (status === 403) {\n if (isStepUp(obj)) return { code: \"step_up_required\", recovery: \"prompt_login\" };\n return { code: obj.code === \"GRANT_DOOR_UNSUPPORTED\" ? \"door_unsupported\" : \"forbidden\" };\n }\n const problem = typeof obj.code === \"string\" ? CP_PROBLEMS[obj.code] : undefined;\n return problem ?? { code: \"conflict\" };\n}\n\nfunction errorFor(status: number, body: unknown, retryAfter: string | null, now: number): PodError {\n const obj = body && typeof body === \"object\" ? (body as Record<string, unknown>) : {};\n const detail = typeof obj.error === \"string\" ? obj.error : typeof obj.message === \"string\" ? obj.message : undefined;\n const base = { status, ...(detail !== undefined ? { detail } : {}) };\n if (status === 401) return { code: \"unauthorized\", ...base };\n if (status === 403 || status === 409) return { ...classifyRefusal(status, body), ...base };\n if (status === 404) return { code: \"not_found\", ...base };\n if (status === 400 || status === 422) return { code: \"invalid\", ...base };\n if (status === 429) {\n const retryAfterSec = retryAfterSeconds(retryAfter, now);\n return { code: \"rate_limited\", ...base, ...(retryAfterSec !== undefined ? { retryAfterSec } : {}) };\n }\n if (status === 502 || status === 503 || status === 504) return { code: \"pod_unreachable\", ...base };\n if (status >= 300 && status < 400) return { code: \"pod_moved\", ...base };\n return { code: \"server\", ...base };\n}\n\n/**\n * Where to review a proposal: the pod's answer, resolved against the pod and\n * kept only on the pod's own origin; else the pod's deep link `/open/<id>`.\n */\nexport function reviewUrlFor(podUrl: string, reviewUrl: unknown, proposalId: string | null): string {\n if (typeof reviewUrl === \"string\" && reviewUrl) {\n try {\n const url = new URL(reviewUrl, `${podUrl}/`);\n if (url.origin === new URL(podUrl).origin) return url.href;\n } catch {\n // fall through to the deep link\n }\n }\n return proposalId ? `${podUrl}/open/${encodeURIComponent(proposalId)}` : podUrl;\n}\n\nfunction refused(code: PodErrorCode, reason: PodRefusalReason, detail: string): { ok: false; error: PodError } {\n return { ok: false, error: { code, status: null, detail, reason } };\n}\n\nfunction asObject(value: unknown): Record<string, unknown> | null {\n return value && typeof value === \"object\" && !Array.isArray(value) ? (value as Record<string, unknown>) : null;\n}\n\nfunction hasId(value: unknown): value is Record<string, unknown> & { id: string } {\n return asObject(value) !== null && typeof (value as Record<string, unknown>).id === \"string\";\n}\n\nexport function createPodClient(connection: PodConnection, opts: PodClientOptions = {}): PodClient {\n const now = opts.now ?? Date.now;\n const doFetch = () => opts.fetch ?? fetch;\n /** The connection in use: `connection`, or what `renew` returned after a 401. */\n let current = connection;\n /** The one renewal this client may run: a 401 forces ONE refresh, never a loop. */\n let renewal: Promise<PodResult<PodConnection>> | null = null;\n\n function renewOnce(refused: PodConnection): Promise<PodResult<PodConnection>> {\n if (current !== refused) return Promise.resolve({ ok: true, data: current });\n renewal ??= opts.renew!(refused).then(\n (next): PodResult<PodConnection> => {\n current = next;\n return { ok: true, data: next };\n },\n (err: unknown): PodResult<PodConnection> => ({ ok: false, error: renewalError(err) }),\n );\n return renewal;\n }\n\n async function request(method: string, path: string, body: unknown, query?: Record<string, QueryValue>, retried = false): Promise<PodResult<unknown>> {\n const sent = current;\n const url = hubUrl(sent.podUrl, path, query);\n if (!url) return refused(\"invalid\", \"invalid_input\", \"Not a Hub API path: encode each path segment (encodeURIComponent)\");\n if (sent.accessExpiresAt <= now()) return { ok: false, error: { code: \"expired\", status: null } };\n const headers: Record<string, string> = { Accept: \"application/json\", Authorization: `Bearer ${sent.accessToken}` };\n if (body !== undefined) headers[\"Content-Type\"] = \"application/json\";\n let res: Response;\n try {\n res = await doFetch()(url.href, {\n method,\n headers,\n body: body === undefined ? undefined : JSON.stringify(body),\n redirect: \"manual\",\n signal: timeoutSignal(opts.timeoutMs ?? TIMEOUT_MS),\n });\n } catch {\n return { ok: false, error: { code: \"pod_unreachable\", status: null } };\n }\n const read = await readCapped(res, opts.maxResponseBytes ?? MAX_RESPONSE_BYTES);\n if (!read.ok) {\n return read.reason === \"too_large\"\n ? { ok: false, error: { code: \"server\", status: res.status, detail: \"The pod's answer is larger than maxResponseBytes\" } }\n : { ok: false, error: { code: \"pod_unreachable\", status: res.status, detail: \"The pod's answer broke off\" } };\n }\n let data: unknown = null;\n if (read.text) {\n try {\n data = JSON.parse(read.text);\n } catch {\n data = null;\n }\n }\n if (res.status >= 200 && res.status < 300) return { ok: true, data };\n if (res.status === 401 && sent.refreshToken) {\n // The key was refused, not necessarily the connection (a narrow or a pod-side \"Revoke key\" ends the key only).\n if (!opts.renew) return { ok: false, error: { ...errorFor(401, data, null, now()), refreshable: true } };\n if (!retried) {\n const renewed = await renewOnce(sent);\n if (!renewed.ok) return renewed;\n return request(method, path, body, query, true);\n }\n }\n return { ok: false, error: errorFor(res.status, data, res.headers.get(\"retry-after\"), now()) };\n }\n\n async function write<T>(method: string, path: string, body: unknown): Promise<PodWriteResult<T>> {\n const result = await request(method, path, body);\n if (!result.ok) return result;\n const obj = result.data && typeof result.data === \"object\" ? (result.data as Record<string, unknown>) : {};\n const proposalId = typeof obj.proposalId === \"string\" && obj.proposalId ? obj.proposalId : null;\n if (obj.status === \"proposed\" || (obj.status === \"pending\" && proposalId)) {\n return {\n ok: true,\n status: \"proposed\",\n proposalId,\n reviewUrl: reviewUrlFor(current.podUrl, obj.reviewUrl, proposalId),\n data: result.data as T,\n };\n }\n return { ok: true, status: \"applied\", data: result.data as T };\n }\n\n async function listPendingProposals(opts: ListPendingProposalsOptions = {}): Promise<PodResult<PendingProposals>> {\n if (!can(current, \"proposal.read\", opts.workspaceId)) {\n return refused(\"forbidden\", \"not_granted\", \"The connection was not granted proposal.read in that space\");\n }\n let only: Set<string> | null = null;\n if (opts.proposalIds !== undefined) {\n if (opts.proposalIds.length > PROPOSAL_IDS_MAX || opts.proposalIds.some((id) => !ID_RE.test(id))) {\n return refused(\"invalid\", \"invalid_input\", `proposalIds must be at most ${PROPOSAL_IDS_MAX} proposal ids`);\n }\n // `?proposalIds=` matches nothing on the pod; our query builder would drop it and ask for ALL.\n if (opts.proposalIds.length === 0) return { ok: true, data: { total: 0, proposals: [], hasMore: false } };\n only = new Set(opts.proposalIds);\n }\n const limit = Math.min(Math.max(Math.floor(opts.limit ?? 20), 1), 200);\n const offset = Math.max(Math.floor(opts.offset ?? 0), 0);\n const result = await request(\"GET\", \"/proposals\", undefined, {\n status: \"pending\",\n view: \"basic\",\n limit,\n offset,\n workspaceId: opts.workspaceId,\n ...(only ? { proposalIds: [...only].join(\",\") } : {}),\n });\n if (!result.ok) return result;\n const obj = result.data && typeof result.data === \"object\" ? (result.data as Record<string, unknown>) : {};\n if (!Array.isArray(obj.proposals) || typeof obj.total !== \"number\") {\n return { ok: false, error: { code: \"server\", status: 200, detail: \"The pod's /proposals answer has no proposals or total\" } };\n }\n const str = (v: unknown): string | null => (typeof v === \"string\" && v ? v : null);\n const proposals: PendingProposal[] = [];\n for (const row of obj.proposals as unknown[]) {\n if (!row || typeof row !== \"object\") continue;\n const r = row as Record<string, unknown>;\n const id = str(r.id);\n // A pod older than the filter ignores it: never hand back a proposal that was not asked for.\n if (!id || (only && !only.has(id))) continue;\n proposals.push({\n id,\n proposalType: str(r.proposalType) ?? \"\",\n targetType: str(r.targetType) ?? \"\",\n targetId: str(r.targetId),\n workspaceId: str(r.workspaceId),\n createdAt: str(r.createdAt) ?? \"\",\n agentUserId: str(r.agentUserId),\n sessionId: str(r.sessionId),\n ...(str(r.summary) ? { summary: str(r.summary)! } : {}),\n reviewUrl: reviewUrlFor(current.podUrl, null, id),\n });\n }\n return {\n ok: true,\n data: { total: obj.total, proposals, hasMore: obj.hasMore === true || offset + proposals.length < obj.total },\n };\n }\n\n /** A read whose 2xx body must pass `shape`; anything else is the pod answering what the SDK cannot use. */\n async function read<T>(path: string, query: Record<string, QueryValue> | undefined, shape: (data: unknown) => T | null): Promise<PodResult<T>> {\n const result = await request(\"GET\", path, undefined, query);\n if (!result.ok) return result;\n const data = shape(result.data);\n if (data === null) return { ok: false, error: { code: \"server\", status: 200, detail: `The pod's ${path.split(\"/\")[1]} answer is not the expected shape` } };\n return { ok: true, data };\n }\n\n const badId = (what: string) => refused(\"invalid\", \"invalid_input\", `Not a ${what} id`);\n const clamp = (n: number | undefined, min: number, max: number) => (n === undefined ? undefined : Math.min(Math.max(Math.floor(n), min), max));\n\n function listEntities(opts: ListEntitiesOptions = {}): Promise<PodResult<PodEntity[]>> {\n return read(\"/entities\", {\n workspaceId: opts.workspaceId,\n profileSlug: opts.profileSlug,\n limit: clamp(opts.limit, 1, 1000),\n offset: clamp(opts.offset, 0, Number.MAX_SAFE_INTEGER),\n sort: opts.sort,\n includePodWide: opts.includePodWide,\n facetSlug: opts.facetSlug,\n // Sent only when asked: the default request stays the 1.5.0 one.\n includeFacets: opts.includeFacets ? true : undefined,\n }, (data) => (Array.isArray(data) ? (data.filter(hasId) as PodEntity[]) : null));\n }\n\n async function getEntity(id: string): Promise<PodResult<PodEntity>> {\n if (!ID_RE.test(id)) return badId(\"entity\");\n return read(`/entities/${id}`, undefined, (data) => (hasId(data) ? (data as PodEntity) : null));\n }\n\n async function getEntityConnections(id: string, opts: GetEntityConnectionsOptions = {}): Promise<PodResult<PodEntityConnections>> {\n if (!ID_RE.test(id)) return badId(\"entity\");\n return read(`/entities/${id}/connections`, { workspaceId: opts.workspaceId, limit: clamp(opts.limit, 1, 200) }, (data) => {\n const obj = asObject(data);\n if (!obj || !Array.isArray(obj.connections)) return null;\n const connections = obj.connections.filter(\n (c): c is PodEntityConnection => !!c && typeof c === \"object\" && typeof (c as Record<string, unknown>).entityId === \"string\",\n );\n const counts = asObject(obj.counts) ?? {};\n const count = (key: string, source: string) =>\n typeof counts[key] === \"number\" ? (counts[key] as number) : connections.filter((c) => c.source === source).length;\n return {\n connections,\n counts: {\n total: typeof counts.total === \"number\" ? counts.total : connections.length,\n graph: count(\"graph\", \"graph\"),\n structural: count(\"structural\", \"property\"),\n threads: count(\"threads\", \"thread\"),\n contextChannels: count(\"contextChannels\", \"context_channel\"),\n focusSessions: count(\"focusSessions\", \"focus_session\"),\n },\n };\n });\n }\n\n async function getDocument(id: string, opts: GetDocumentOptions = {}): Promise<PodResult<PodDocument>> {\n if (!ID_RE.test(id)) return badId(\"document\");\n return read(`/documents/${id}`, { format: opts.format }, (data) => {\n const doc = asObject(asObject(data)?.document);\n if (!doc || typeof doc.id !== \"string\" || typeof doc.content !== \"string\") return null;\n return { ...doc, sections: Array.isArray(doc.sections) ? doc.sections : [] } as PodDocument;\n });\n }\n\n async function listFocusSessions(opts: ListFocusSessionsOptions): Promise<PodResult<PodFocusSession[]>> {\n if (!opts?.workspaceId && !opts?.projectId) return refused(\"invalid\", \"invalid_input\", \"workspaceId or projectId is required\");\n return read(\"/focus-sessions\", {\n workspaceId: opts.workspaceId,\n projectId: opts.projectId,\n status: opts.status,\n limit: clamp(opts.limit, 1, 50),\n lens: opts.lens,\n kind: opts.kind,\n subjectEntityId: opts.subjectEntityId,\n }, (data) => {\n if (!Array.isArray(data)) return null;\n return data.filter(hasId).map((row) => ({\n ...row,\n expectedOutputs: Array.isArray(row.expectedOutputs)\n ? row.expectedOutputs.filter((s): s is PodSessionSlot => !!s && typeof s === \"object\" && typeof (s as Record<string, unknown>).label === \"string\")\n : [],\n })) as PodFocusSession[];\n });\n }\n\n async function getFileUrl(entityId: string): Promise<PodResult<PodFileUrl>> {\n if (!ID_RE.test(entityId)) return badId(\"file\");\n return read(`/files/${entityId}/url`, undefined, (data) => {\n const obj = asObject(data);\n if (!obj || typeof obj.url !== \"string\" || typeof obj.expiresAt !== \"string\") return null;\n return { url: obj.url, expiresAt: obj.expiresAt, mimeType: typeof obj.mimeType === \"string\" ? obj.mimeType : null };\n });\n }\n\n async function approveProposal(id: string, opts: ApproveProposalOptions = {}): Promise<PodResult<PodApproval>> {\n // Checked first, before anything else: an agent credential never reaches the approve door.\n if (connectionActsAs(current) !== \"person\") {\n return refused(\"forbidden\", \"agent_cannot_approve\", \"An agent connection can never approve: approval is the person's\");\n }\n if (!can(current, \"proposal.approve\", opts.workspaceId)) {\n return refused(\"forbidden\", \"not_granted\", \"The connection was not granted proposal.approve in that space\");\n }\n if (!ID_RE.test(id)) return badId(\"proposal\");\n const result = await request(\"POST\", `/proposals/${id}/approve`, {});\n if (!result.ok) return result;\n const obj = asObject(result.data);\n if (!obj || typeof obj.proposalId !== \"string\") {\n return { ok: false, error: { code: \"server\", status: 200, detail: \"The pod's approve answer names no proposalId\" } };\n }\n return {\n ok: true,\n data: {\n proposalId: obj.proposalId,\n ...(typeof obj.primaryId === \"string\" ? { primaryId: obj.primaryId } : {}),\n ...(typeof obj.linked === \"number\" ? { linked: obj.linked } : {}),\n proposal: asObject(obj.proposal),\n },\n };\n }\n\n return {\n podUrl: connection.podUrl,\n get user() {\n return current.user;\n },\n get grant() {\n return current.grant;\n },\n get actsAs() {\n return connectionActsAs(current);\n },\n can: (permission, workspaceId) => can(current, permission, workspaceId),\n spaces: () => spaces(current),\n listPendingProposals,\n listEntities,\n getEntity,\n getEntityConnections,\n getDocument,\n listFocusSessions,\n getFileUrl,\n approveProposal,\n get<T>(path: string, query?: Record<string, QueryValue>) {\n return request(\"GET\", path, undefined, query) as Promise<PodResult<T>>;\n },\n post<T>(path: string, body?: unknown) {\n return write<T>(\"POST\", path, body);\n },\n patch<T>(path: string, body?: unknown) {\n return write<T>(\"PATCH\", path, body);\n },\n put<T>(path: string, body?: unknown) {\n return write<T>(\"PUT\", path, body);\n },\n delete<T>(path: string, body?: unknown) {\n return write<T>(\"DELETE\", path, body);\n },\n };\n}\n","/**\n * The OAuth 2.1 authorization-code flow of a pod app, as\n * plain functions over a SEALED transaction string. `createPodAppAuth` wires\n * them to cookies and route handlers; use these directly for another store.\n *\n * Security properties (each pinned by a test in src/__tests__/connect.*):\n * - PKCE S256 with a fresh 32-byte verifier per sign-in;\n * - a fresh 32-byte `state`, compared in constant time on the callback;\n * - mix-up defence (RFC 9207): the callback's `iss` must equal the issuer the\n * transaction was started with; a missing `iss` is refused too;\n * - the transaction lives 10 minutes and is sealed (AES-GCM) under the app\n * secret with its own purpose: tampered, foreign or stale → refused;\n * - the `error` of a callback is honoured only AFTER `iss` and `state` match,\n * so a forged error cannot end someone else's sign-in;\n * - tokens never leave the server: nothing here returns a value meant for a\n * browser except the authorize URL.\n */\nimport { ConnectError, fromOAuthError, retryAfterSeconds } from \"./errors\";\nimport { constantTimeEqual, pkceChallenge, randomToken } from \"./encoding\";\nimport { discoverPod, type DiscoverOptions } from \"./discovery\";\nimport { readCapped, timeoutSignal } from \"./http\";\nimport { LOOPBACK_HOSTS } from \"./rules\";\nimport { seal, unseal, type AppSecret } from \"./seal\";\nimport type { PodActsAs, PodConnection, PodGrant, PodIssuer, PodUser } from \"./types\";\n\n/** How long a started sign-in stays valid. */\nexport const TRANSACTION_TTL_MS = 10 * 60 * 1000;\n/** The refresh token's idle life when the pod does not say (the pod's: 30 days). */\nexport const DEFAULT_REFRESH_IDLE_MS = 30 * 24 * 60 * 60 * 1000;\nconst DEFAULT_ACCESS_TTL_SEC = 3600;\nconst TIMEOUT_MS = 10_000;\n/** A token response is a few KB; more is not a token response. */\nconst MAX_TOKEN_RESPONSE_BYTES = 64 * 1024;\n\n/** A sealed `ConnectTransaction`: opaque, safe to put in an HttpOnly cookie. */\nexport type SealedTransaction = string & { readonly __sealedTransaction: true };\n\n/** What a started sign-in remembers (sealed; never sent to the pod). */\nexport interface ConnectTransaction {\n v: 1;\n issuer: string;\n tokenEndpoint: string;\n revocationEndpoint: string | null;\n clientId: string;\n redirectUri: string;\n state: string;\n verifier: string;\n resource: string | null;\n /** A same-origin path to land on afterwards. */\n next: string | null;\n /** The sign-in re-confirms a device that is already connected (`prompt=login`). */\n stepUp?: boolean;\n createdAt: number;\n}\n\nexport interface FlowOptions {\n fetch?: typeof fetch;\n now?: () => number;\n}\n\nexport interface StartConnectOptions extends FlowOptions {\n /** A validated issuer (`discoverPod`), or an address / launch `iss` to discover. */\n pod: PodIssuer | string;\n clientId: string;\n redirectUri: string;\n /** Grant-grammar permissions, each declared in the app's client metadata. */\n permissions: readonly string[];\n secret: AppSecret;\n /** RFC 8707 resource: the pod's Hub API (default) or its MCP endpoint; null = none. */\n resource?: \"api/hub\" | \"mcp\" | null;\n /** A space to preselect on consent. */\n spaceHint?: string | null;\n /** A same-origin path to land on afterwards (validate it first: `safeNextPath`). */\n next?: string | null;\n /**\n * `\"consent\"`: the person asked to see the consent screen again (to widen or\n * narrow what the app may touch). Sent as OIDC `prompt=consent`; the sign-in\n * is otherwise the same (fresh PKCE verifier and state).\n *\n * `\"login\"`: a step-up. A credential door refused a stale device\n * (`step_up_required`): the person re-confirms at the pod (OIDC\n * `prompt=login`) and the callback redeems the code WITH the connection's\n * refresh token (`synap_device_refresh_token`; see `HandleCallbackOptions.current`).\n */\n prompt?: \"consent\" | \"login\" | null;\n allowInsecureLoopback?: boolean;\n}\n\nexport interface StartedConnect {\n /** Redirect the browser here (303). */\n authorizeUrl: string;\n /** Store in an HttpOnly cookie until the callback. */\n transaction: SealedTransaction;\n /** ms since epoch. */\n expiresAt: number;\n issuer: PodIssuer;\n}\n\nconst PERMISSION_RE = /^[a-z][a-z0-9_-]*(\\.[a-z0-9_-]+)+$/i;\n\nexport function assertPermissions(permissions: readonly string[]): string[] {\n const unique = [...new Set(permissions)];\n if (unique.length === 0 || unique.length > 32 || unique.some((p) => !PERMISSION_RE.test(p))) {\n throw new ConnectError(\n \"misconfigured\",\n \"permissions must be 1-32 grant-grammar strings such as entity.post.read (no *)\",\n );\n }\n return unique;\n}\n\nexport async function startConnect(opts: StartConnectOptions): Promise<StartedConnect> {\n const now = (opts.now ?? Date.now)();\n const permissions = assertPermissions(opts.permissions);\n const issuer =\n typeof opts.pod === \"string\"\n ? await discoverPod(opts.pod, { fetch: opts.fetch, now: opts.now, allowInsecureLoopback: opts.allowInsecureLoopback } satisfies DiscoverOptions)\n : opts.pod;\n const verifier = randomToken();\n const state = randomToken();\n const resourceKind = opts.resource === undefined ? \"api/hub\" : opts.resource;\n const resource = resourceKind ? `${issuer.issuer}/${resourceKind}` : null;\n\n const url = new URL(issuer.authorizationEndpoint);\n url.searchParams.set(\"response_type\", \"code\");\n url.searchParams.set(\"client_id\", opts.clientId);\n url.searchParams.set(\"redirect_uri\", opts.redirectUri);\n url.searchParams.set(\"scope\", permissions.join(\" \"));\n url.searchParams.set(\"state\", state);\n url.searchParams.set(\"code_challenge\", await pkceChallenge(verifier));\n url.searchParams.set(\"code_challenge_method\", \"S256\");\n if (resource) url.searchParams.set(\"resource\", resource);\n if (opts.spaceHint) url.searchParams.set(\"synap_space_hint\", opts.spaceHint);\n if (opts.prompt === \"consent\" || opts.prompt === \"login\") url.searchParams.set(\"prompt\", opts.prompt);\n\n const tx: ConnectTransaction = {\n v: 1,\n issuer: issuer.issuer,\n tokenEndpoint: issuer.tokenEndpoint,\n revocationEndpoint: issuer.revocationEndpoint,\n clientId: opts.clientId,\n redirectUri: opts.redirectUri,\n state,\n verifier,\n resource,\n next: opts.next ?? null,\n ...(opts.prompt === \"login\" ? { stepUp: true } : {}),\n createdAt: now,\n };\n const expiresAt = now + TRANSACTION_TTL_MS;\n const transaction = (await seal(tx, {\n secret: opts.secret,\n purpose: \"synap-connect/transaction\",\n expiresAt,\n })) as SealedTransaction;\n return { authorizeUrl: url.toString(), transaction, expiresAt, issuer };\n}\n\n/** Open a sealed transaction; refuses tampered, foreign and stale ones. */\nexport async function openTransaction(\n sealed: string | null | undefined,\n opts: { secret: AppSecret; now?: () => number },\n): Promise<ConnectTransaction> {\n if (!sealed) throw new ConnectError(\"expired_transaction\", \"No sign-in in progress in this browser\");\n const opened = await unseal<ConnectTransaction>(sealed, {\n secret: opts.secret,\n purpose: \"synap-connect/transaction\",\n now: (opts.now ?? Date.now)(),\n });\n if (!opened.ok) {\n throw new ConnectError(\n \"expired_transaction\",\n opened.reason === \"expired\" ? \"The sign-in is older than 10 minutes\" : \"The sign-in cookie is not one this app sealed\",\n );\n }\n const tx = opened.value;\n if (!tx || tx.v !== 1 || typeof tx.state !== \"string\" || typeof tx.issuer !== \"string\") {\n throw new ConnectError(\"expired_transaction\", \"The sign-in cookie is not one this app sealed\");\n }\n return tx;\n}\n\nexport interface HandleCallbackOptions extends FlowOptions {\n /** The full URL the pod redirected the browser to. */\n callbackUrl: string | URL;\n transaction: string | null | undefined;\n clientId: string;\n redirectUri: string;\n secret: AppSecret;\n /**\n * The connection this browser holds now. A step-up (`prompt=login`) of a\n * Synap app's device is redeemed with ITS refresh token (the pod moves that\n * device's clock instead of making a second device); without one, or for an\n * app that is not a person's device, the code is redeemed as any other.\n */\n current?: PodConnection | null;\n}\n\nexport interface CallbackResult {\n connection: PodConnection;\n /** The same-origin path the sign-in was started for. */\n next: string | null;\n}\n\nexport async function handleCallback(opts: HandleCallbackOptions): Promise<CallbackResult> {\n const tx = await openTransaction(opts.transaction, opts);\n if (tx.clientId !== opts.clientId || tx.redirectUri !== opts.redirectUri) {\n throw new ConnectError(\"misconfigured\", \"The sign-in was started for another client_id or redirect_uri\");\n }\n const params = new URL(opts.callbackUrl).searchParams;\n\n // RFC 9207: the answer must name the AS this sign-in went to. Absent = refused\n // (every Synap pod sends it, and discovery required it).\n const iss = params.get(\"iss\");\n if (iss === null || iss !== tx.issuer) {\n throw new ConnectError(\"mismatch\", iss === null ? \"The answer names no issuer\" : \"The answer comes from another pod than the one asked\");\n }\n const state = params.get(\"state\");\n if (state === null || !constantTimeEqual(state, tx.state)) {\n throw new ConnectError(\"mismatch\", \"The answer does not belong to the sign-in this browser started\");\n }\n const error = params.get(\"error\");\n if (error !== null) {\n throw new ConnectError(fromOAuthError(error, \"authorize\"), `The pod answered ${error}`, { oauthError: error });\n }\n const code = params.get(\"code\");\n if (!code) throw new ConnectError(\"server_error\", \"The answer carries no code\");\n\n const body = new URLSearchParams({\n grant_type: \"authorization_code\",\n code,\n redirect_uri: tx.redirectUri,\n client_id: tx.clientId,\n code_verifier: tx.verifier,\n });\n if (tx.resource) body.set(\"resource\", tx.resource);\n const device =\n tx.stepUp && opts.current && opts.current.actsAs === \"person\" && opts.current.podUrl === tx.issuer && opts.current.clientId === tx.clientId\n ? opts.current\n : null;\n if (device?.refreshToken) body.set(\"synap_device_refresh_token\", device.refreshToken);\n const json = await tokenRequest(tx.tokenEndpoint, body, \"token\", opts);\n const connection = parseTokenResponse(json, {\n podUrl: tx.issuer,\n clientId: tx.clientId,\n tokenEndpoint: tx.tokenEndpoint,\n revocationEndpoint: tx.revocationEndpoint,\n now: (opts.now ?? Date.now)(),\n ...(device ? { previous: device } : {}),\n });\n return { connection, next: tx.next };\n}\n\nasync function tokenRequest(\n endpoint: string,\n body: URLSearchParams,\n during: \"token\" | \"refresh\",\n opts: FlowOptions,\n): Promise<Record<string, unknown>> {\n const doFetch = opts.fetch ?? fetch;\n let res: Response;\n try {\n res = await doFetch(endpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\", Accept: \"application/json\" },\n body: body.toString(),\n redirect: \"manual\",\n signal: timeoutSignal(TIMEOUT_MS),\n });\n } catch {\n throw new ConnectError(\"pod_unreachable\", \"The pod's token endpoint did not answer\");\n }\n if (res.status >= 300 && res.status < 400) {\n await res.body?.cancel().catch(() => {});\n throw new ConnectError(\"pod_moved\", `The token endpoint answered ${res.status}: the pod moved; connect again with its new address`);\n }\n if (res.status === 429) {\n throw new ConnectError(\"rate_limited\", \"The pod asked to slow down\", {\n retryAfterSec: retryAfterSeconds(res.headers.get(\"retry-after\"), (opts.now ?? Date.now)()),\n });\n }\n if (res.status >= 500) throw new ConnectError(\"pod_unreachable\", `The token endpoint answered ${res.status}`);\n const read = await readCapped(res, MAX_TOKEN_RESPONSE_BYTES);\n if (!read.ok) {\n throw read.reason === \"too_large\"\n ? new ConnectError(\"server_error\", \"The token endpoint's answer is too large\")\n : new ConnectError(\"pod_unreachable\", \"The token endpoint's answer broke off\");\n }\n let json: unknown;\n try {\n json = JSON.parse(read.text);\n } catch {\n throw new ConnectError(\"server_error\", `The token endpoint answered ${res.status} without JSON`);\n }\n const obj = json && typeof json === \"object\" && !Array.isArray(json) ? (json as Record<string, unknown>) : {};\n if (res.status !== 200) {\n const oauthError = typeof obj.error === \"string\" ? obj.error : \"server_error\";\n throw new ConnectError(fromOAuthError(oauthError, during), `The token endpoint answered ${oauthError}`, { oauthError });\n }\n return obj;\n}\n\nfunction parseGrant(value: unknown): PodGrant[] | null {\n if (!Array.isArray(value)) return null;\n const out: PodGrant[] = [];\n for (const g of value) {\n if (!g || typeof g !== \"object\") return null;\n const { permission, workspaceId, workspaceName, templateSlug } = g as Record<string, unknown>;\n if (typeof permission !== \"string\" || typeof workspaceId !== \"string\") return null;\n const row: PodGrant = { permission, workspaceId, workspaceName: typeof workspaceName === \"string\" ? workspaceName : \"\" };\n // The space's template, omitted when it came from none.\n if (typeof templateSlug === \"string\" && templateSlug.length > 0 && templateSlug.length <= 200) row.templateSlug = templateSlug;\n out.push(row);\n }\n return out;\n}\n\n/**\n * `synap_acts_as`: read from EVERY token response, never carried\n * over from the previous one, so a pod that stops saying \"person\" (or never\n * did) gives an agent connection.\n */\nfunction parseActsAs(value: unknown): PodActsAs | undefined {\n return value === \"person\" || value === \"agent\" ? value : undefined;\n}\n\nconst APP_ID_RE = /^[A-Za-z0-9_-]{1,128}$/;\n\n/**\n * `synap_app` (Synap extension): the pod's id for this app connection and its\n * page for it. The page is on the pod's console, which may be another host\n * than the issuer, so it is kept when it is an absolute https URL (http only on\n * loopback) without credentials; anything else is dropped.\n */\nfunction parseApp(value: unknown): { appId?: string; manageUrl?: string } {\n if (!value || typeof value !== \"object\") return {};\n const { id, manageUrl } = value as Record<string, unknown>;\n const out: { appId?: string; manageUrl?: string } = {};\n if (typeof id === \"string\" && APP_ID_RE.test(id)) out.appId = id;\n if (typeof manageUrl === \"string\" && manageUrl.length <= 2048) {\n try {\n const url = new URL(manageUrl);\n const secure = url.protocol === \"https:\" || (url.protocol === \"http:\" && LOOPBACK_HOSTS.has(url.hostname));\n if (secure && !url.username && !url.password) out.manageUrl = url.href;\n } catch {\n // not a URL: no link\n }\n }\n return out;\n}\n\nfunction parseUser(value: unknown): PodUser | null {\n if (!value || typeof value !== \"object\") return null;\n const { id, name } = value as Record<string, unknown>;\n return typeof id === \"string\" ? { id, name: typeof name === \"string\" ? name : \"\" } : null;\n}\n\n/**\n * A token response → a connection. On a refresh, `previous` supplies what the\n * response may omit (grant, user, app; never `actsAs`). `expires_in` is read as sent: a refresh\n * answered within the pod's reuse grace hands back the live key the\n * first redeem got, with less than an hour left. A refresh response WITHOUT a new\n * refresh_token drops the old one: with single-use rotation, resending a\n * consumed token reads as theft and revokes the whole family.\n */\nexport function parseTokenResponse(\n body: Record<string, unknown>,\n ctx: {\n podUrl: string;\n clientId: string;\n tokenEndpoint: string;\n revocationEndpoint: string | null;\n now: number;\n previous?: PodConnection;\n },\n): PodConnection {\n const { access_token, token_type, expires_in, scope, refresh_token, refresh_token_expires_in } = body;\n if (typeof access_token !== \"string\" || access_token === \"\") {\n throw new ConnectError(\"server_error\", \"The token response has no access_token\");\n }\n if (typeof token_type !== \"string\" || token_type.toLowerCase() !== \"bearer\") {\n throw new ConnectError(\"server_error\", \"The token response is not a Bearer token\");\n }\n if (expires_in !== undefined && (typeof expires_in !== \"number\" || !Number.isFinite(expires_in) || expires_in < 0)) {\n throw new ConnectError(\"server_error\", \"The token response has an invalid expires_in\");\n }\n const grant = parseGrant(body.synap_grant) ?? ctx.previous?.grant ?? null;\n const user = parseUser(body.synap_user) ?? ctx.previous?.user ?? null;\n if (!grant || !user) {\n throw new ConnectError(\"pod_unsupported\", \"The token response lacks synap_grant / synap_user (pod older than app sign-in)\");\n }\n const scopes =\n typeof scope === \"string\" ? scope.split(/\\s+/).filter(Boolean) : (ctx.previous?.scope ?? [...new Set(grant.map((g) => g.permission))]);\n const connection: PodConnection = {\n podUrl: ctx.podUrl,\n clientId: ctx.clientId,\n tokenEndpoint: ctx.tokenEndpoint,\n revocationEndpoint: ctx.revocationEndpoint,\n accessToken: access_token,\n accessExpiresAt: ctx.now + (typeof expires_in === \"number\" ? expires_in : DEFAULT_ACCESS_TTL_SEC) * 1000,\n scope: scopes,\n grant,\n user,\n connectedAt: ctx.previous?.connectedAt ?? ctx.now,\n };\n const app = parseApp(body.synap_app);\n const appId = app.appId ?? ctx.previous?.appId;\n const manageUrl = app.manageUrl ?? ctx.previous?.manageUrl;\n if (appId !== undefined) connection.appId = appId;\n if (manageUrl !== undefined) connection.manageUrl = manageUrl;\n const actsAs = parseActsAs(body.synap_acts_as);\n if (actsAs !== undefined) connection.actsAs = actsAs;\n if (typeof refresh_token === \"string\" && refresh_token !== \"\") {\n connection.refreshToken = refresh_token;\n const idle =\n typeof refresh_token_expires_in === \"number\" && Number.isFinite(refresh_token_expires_in) && refresh_token_expires_in > 0\n ? refresh_token_expires_in * 1000\n : DEFAULT_REFRESH_IDLE_MS;\n connection.refreshExpiresAt = ctx.now + idle;\n }\n return connection;\n}\n\n/**\n * How long a rotation's result is remembered for its SPENT refresh token.\n * The pod refuses a concurrent second redeem softly, but a token\n * presented AGAIN after its use is a reuse: the whole family and the live\n * access key are revoked. A browser easily sends that replay: parallel\n * requests, a second tab, a response it had not stored yet, all still\n * carrying the old cookie. Within this window such a request gets the\n * rotation's result (and re-sends the new cookie) instead of reaching the pod.\n */\nexport const ROTATION_MEMORY_MS = 2 * 60 * 1000;\n\ninterface Rotation {\n result: Promise<PodConnection>;\n /** When it succeeded (ms, the caller's clock); undefined while in flight. */\n settledAt?: number;\n}\n\n/** Spent refresh token → its rotation, per process. */\nconst rotations = new Map<string, Rotation>();\n\nfunction pruneRotations(now: number): void {\n for (const [token, r] of rotations) {\n if (r.settledAt !== undefined && now - r.settledAt > ROTATION_MEMORY_MS) rotations.delete(token);\n }\n}\n\n/**\n * Exchange the refresh token for a new access token and the rotated refresh\n * token (single-use, 30 days from issue, its reach re-derived by the pod).\n *\n * SINGLE-FLIGHT per connection within this process: concurrent callers share\n * one request, and for `ROTATION_MEMORY_MS` after it a caller still holding\n * the spent token gets the same result, so this process never replays a\n * spent token. Across processes nothing here can coordinate: a server-side\n * `ConnectionStore` (one row per connection) is the way to rule that out.\n * The caller must PERSIST the result before using its access token\n * (`refreshIfNeeded` does).\n *\n * Throws `expired` (no refresh token, or past its idle life), `revoked`\n * (invalid_grant while it should live: spent, revoked, the app removed or\n * narrowed to nothing, or a concurrent redeem lost), `misconfigured`\n * (invalid_client, unauthorized_client), `rate_limited`, `pod_unreachable`,\n * `pod_moved` (the token endpoint answered a redirect), `server_error`.\n */\nexport function refreshConnection(connection: PodConnection, opts: FlowOptions = {}): Promise<PodConnection> {\n const clock = opts.now ?? Date.now;\n const now = clock();\n const token = connection.refreshToken;\n if (!token) return Promise.reject(new ConnectError(\"expired\", \"The pod issued no refresh token; connect again\"));\n pruneRotations(now);\n const known = rotations.get(token);\n if (known) return known.result;\n if (connection.refreshExpiresAt !== undefined && connection.refreshExpiresAt <= now) {\n return Promise.reject(new ConnectError(\"expired\", \"The connection was idle too long; connect again\"));\n }\n const body = new URLSearchParams({ grant_type: \"refresh_token\", refresh_token: token, client_id: connection.clientId });\n const rotation: Rotation = {\n result: tokenRequest(connection.tokenEndpoint, body, \"refresh\", opts)\n .then((json) => {\n const next = parseTokenResponse(json, {\n podUrl: connection.podUrl,\n clientId: connection.clientId,\n tokenEndpoint: connection.tokenEndpoint,\n revocationEndpoint: connection.revocationEndpoint,\n now: clock(),\n previous: connection,\n });\n rotation.settledAt = clock();\n return next;\n })\n .catch((err: unknown) => {\n rotations.delete(token);\n // The AS answers invalid_grant to an idle-expired AND a revoked token;\n // the locally known idle deadline tells them apart.\n if (err instanceof ConnectError && err.code === \"revoked\" && connection.refreshExpiresAt !== undefined && connection.refreshExpiresAt <= clock()) {\n throw new ConnectError(\"expired\", err.message, { oauthError: err.oauthError });\n }\n throw err;\n }),\n };\n rotations.set(token, rotation);\n return rotation.result;\n}\n\n/**\n * RFC 7009, best effort: the refresh token (the pod retires its whole family\n * and the live access key) then the access token (for a pod without refresh).\n * Never throws; `revoked` says whether the pod confirmed.\n */\nexport async function revokeConnection(connection: PodConnection, opts: FlowOptions = {}): Promise<{ revoked: boolean }> {\n if (!connection.revocationEndpoint) return { revoked: false };\n const doFetch = opts.fetch ?? fetch;\n let revoked = true;\n const tokens: Array<[string | undefined, string]> = [\n [connection.refreshToken, \"refresh_token\"],\n [connection.accessToken, \"access_token\"],\n ];\n for (const [token, hint] of tokens) {\n if (!token) continue;\n try {\n const res = await doFetch(connection.revocationEndpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({ token, token_type_hint: hint, client_id: connection.clientId }).toString(),\n redirect: \"manual\",\n signal: timeoutSignal(TIMEOUT_MS),\n });\n await res.body?.cancel().catch(() => {});\n if (res.status !== 200) revoked = false;\n } catch {\n revoked = false;\n }\n }\n return { revoked };\n}\n","/**\n * Where a PodConnection lives between requests. The default keeps it in the\n * browser as a SEALED cookie (AES-GCM under the app secret, HttpOnly, Secure\n * on https, SameSite=Lax, Path=/, `__Host-` prefix on https): the browser holds\n * ciphertext it cannot read or forge, the pod token never reaches JS.\n *\n * Bring your own (a database, a KV) by implementing `ConnectionStore`; it\n * answers with the Set-Cookie values to send (e.g. its own session id).\n */\nimport { canonicalPodUrl } from \"./discovery\";\nimport { base64url, fromBase64url, fromUtf8, parseCookies, serializeCookie, utf8 } from \"./encoding\";\nimport { ConnectError } from \"./errors\";\nimport { seal, unseal, type AppSecret } from \"./seal\";\nimport type { PodConnection, PodGrant } from \"./types\";\n\nexport interface ConnectionStore {\n /** The request's connection, or null (absent, tampered, foreign, expired). */\n read(request: Request): Promise<PodConnection | null>;\n /** Persist; returns the Set-Cookie header values to send. */\n write(connection: PodConnection, request: Request): Promise<string[]>;\n /** Forget; returns the Set-Cookie header values to send. */\n clear(request: Request): Promise<string[]>;\n}\n\nexport interface CookieNames {\n session: string;\n transaction: string;\n /** A Synap Cloud sign-in in progress (sealed state, 10 minutes). */\n cloud: string;\n /** \"This browser was connected to <pod>\": the pod's URL only, no secret (`markerCookie`). */\n marker: string;\n}\n\n/** `__Host-` names on https (Secure, Path=/, no Domain: bound to this exact host). */\nexport function cookieNames(secure: boolean): CookieNames {\n return secure\n ? { session: \"__Host-synap_session\", transaction: \"__Host-synap_tx\", cloud: \"__Host-synap_cloud\", marker: \"__Host-synap_seen\" }\n : { session: \"synap_session\", transaction: \"synap_tx\", cloud: \"synap_cloud\", marker: \"synap_seen\" };\n}\n\n/** The marker outlives the connection: as long as browsers keep a cookie (400 days). */\nexport const MARKER_TTL_SEC = 400 * 24 * 60 * 60;\n\n/**\n * The marker cookie: which pod this browser was connected to, so a connection\n * that lapsed (idle past the refresh token's life, its sealed cookie gone)\n * reads as \"expired, connect again to <pod>\" rather than \"never connected\".\n * It holds the pod's public URL and NOTHING else: no token, no user, nothing\n * sealed. A forged value can only pre-fill the address the person confirms.\n */\nexport function markerCookie(podUrl: string, opts: { secure: boolean; name?: string }): string {\n return serializeCookie(opts.name ?? cookieNames(opts.secure).marker, base64url(utf8(podUrl)), {\n maxAgeSec: MARKER_TTL_SEC,\n secure: opts.secure,\n });\n}\n\n/** The marker's pod URL, or null (absent, or not a canonical pod address). */\nexport function readMarker(\n cookieHeader: string | null,\n opts: { secure: boolean; name?: string; allowInsecureLoopback?: boolean },\n): string | null {\n const raw = parseCookies(cookieHeader).get(opts.name ?? cookieNames(opts.secure).marker);\n const bytes = raw ? fromBase64url(raw) : null;\n if (!bytes || bytes.length > 512) return null;\n const podUrl = fromUtf8(bytes);\n try {\n return canonicalPodUrl(podUrl, opts) === podUrl ? podUrl : null;\n } catch {\n return null;\n }\n}\n\n/** Browsers cap a cookie near 4096 bytes; a big grant spans several. */\nconst CHUNK_SIZE = 3800;\nconst MAX_CHUNKS = 4;\n\nfunction chunkName(name: string, i: number): string {\n return i === 0 ? name : `${name}.${i}`;\n}\n\n/** One space of the sealed grant; the template slug only when the pod named one (cookies written by 1.4 have none). */\ntype StoredSpace = [workspaceId: string, workspaceName: string, permissions: string[], templateSlug?: string];\n\n/** The sealed form: the grant grouped per space (it repeats the space otherwise). */\ninterface StoredConnection extends Omit<PodConnection, \"grant\"> {\n g: StoredSpace[];\n}\n\nfunction pack(c: PodConnection): StoredConnection {\n const bySpace = new Map<string, StoredSpace>();\n for (const g of c.grant) {\n const row: StoredSpace = bySpace.get(g.workspaceId) ?? [g.workspaceId, g.workspaceName, []];\n row[2].push(g.permission);\n if (g.templateSlug && row[3] === undefined) row[3] = g.templateSlug;\n bySpace.set(g.workspaceId, row);\n }\n const { grant: _grant, ...rest } = c;\n return { ...rest, g: [...bySpace.values()] };\n}\n\nfunction unpack(s: StoredConnection): PodConnection | null {\n if (!s || typeof s.podUrl !== \"string\" || typeof s.accessToken !== \"string\" || !Array.isArray(s.g)) return null;\n const { g, ...rest } = s;\n const grant: PodGrant[] = g.flatMap(([workspaceId, workspaceName, permissions, templateSlug]) =>\n permissions.map((permission) => ({ permission, workspaceId, workspaceName, ...(typeof templateSlug === \"string\" ? { templateSlug } : {}) })),\n );\n return { ...rest, grant };\n}\n\n/** When the stored value stops being useful. */\nexport function connectionExpiresAt(c: PodConnection): number {\n return c.refreshToken && c.refreshExpiresAt !== undefined ? Math.max(c.refreshExpiresAt, c.accessExpiresAt) : c.accessExpiresAt;\n}\n\nexport interface SealedCookieStoreOptions {\n secret: AppSecret;\n /** True on https (the default when the app URL is https). */\n secure: boolean;\n name?: string;\n now?: () => number;\n}\n\nexport function createSealedCookieStore(opts: SealedCookieStoreOptions): ConnectionStore {\n const name = opts.name ?? cookieNames(opts.secure).session;\n const now = opts.now ?? Date.now;\n\n const presentChunks = (request: Request) => {\n const cookies = parseCookies(request.headers.get(\"cookie\"));\n const names: string[] = [];\n for (let i = 0; i < MAX_CHUNKS; i++) if (cookies.has(chunkName(name, i))) names.push(chunkName(name, i));\n return { cookies, names };\n };\n\n return {\n async read(request) {\n const { cookies } = presentChunks(request);\n let sealed = \"\";\n for (let i = 0; i < MAX_CHUNKS; i++) {\n const part = cookies.get(chunkName(name, i));\n if (part === undefined) break;\n sealed += part;\n }\n if (!sealed) return null;\n const opened = await unseal<StoredConnection>(sealed, { secret: opts.secret, purpose: \"synap-connect/session\", now: now() });\n return opened.ok ? unpack(opened.value) : null;\n },\n\n async write(connection, request) {\n const expiresAt = connectionExpiresAt(connection);\n const sealed = await seal(pack(connection), { secret: opts.secret, purpose: \"synap-connect/session\", expiresAt });\n const parts: string[] = [];\n for (let i = 0; i < sealed.length; i += CHUNK_SIZE) parts.push(sealed.slice(i, i + CHUNK_SIZE));\n if (parts.length > MAX_CHUNKS) {\n throw new ConnectError(\n \"misconfigured\",\n \"The connection is too large for a cookie (a very wide grant); pass a server-side ConnectionStore\",\n );\n }\n const maxAgeSec = (expiresAt - now()) / 1000;\n const out = parts.map((value, i) => serializeCookie(chunkName(name, i), value, { maxAgeSec, secure: opts.secure }));\n // Drop chunks a previous, larger value left behind.\n for (const stale of presentChunks(request).names.slice(parts.length)) {\n out.push(serializeCookie(stale, \"\", { maxAgeSec: 0, secure: opts.secure }));\n }\n return out;\n },\n\n async clear(request) {\n const { names } = presentChunks(request);\n return (names.length > 0 ? names : [name]).map((n) => serializeCookie(n, \"\", { maxAgeSec: 0, secure: opts.secure }));\n },\n };\n}\n\n/**\n * Apply Set-Cookie values to a request's Cookie header, so code that runs\n * later IN THE SAME REQUEST (a server component after `proxy.ts`) reads the\n * refreshed value. A `Max-Age=0` removes the cookie.\n */\nexport function applySetCookies(cookieHeader: string | null, setCookies: readonly string[]): string {\n const cookies = parseCookies(cookieHeader);\n for (const sc of setCookies) {\n const [pair, ...attrs] = sc.split(\";\");\n const eq = pair.indexOf(\"=\");\n if (eq < 0) continue;\n const name = pair.slice(0, eq).trim();\n const removed = attrs.some((a) => /^\\s*max-age\\s*=\\s*0\\s*$/i.test(a));\n if (removed) cookies.delete(name);\n else cookies.set(name, pair.slice(eq + 1).trim());\n }\n return [...cookies].map(([k, v]) => `${k}=${v}`).join(\"; \");\n}\n","/**\n * `createPodAppAuth(config)`: pod-app sign-in as Web-standard route handlers\n * (`Request → Response`, so Next route handlers, Hono, Remix, workers) plus\n * the `proxy.ts` refresh helper. The route files stay two lines:\n *\n * // app/api/synap/connect/route.ts\n * export const POST = synap.handlers.connect;\n *\n * Handlers:\n * GET <metadata> the app's client metadata document (its client_id URL)\n * POST <connect> form { pod, next?, space?, prompt? } → 303 to the pod's /authorize\n * GET <callback> the pod's answer → sealed session cookie → 303 to `next`\n * POST <disconnect> revoke at the pod (best effort), clear the cookie → 303\n * POST <cloud> form { next?, space?, prompt? } → 303 to the Synap Cloud pod chooser\n * GET <cloud> the chooser's return (?iss&state, state checked) → 303 to the pod's /authorize\n *\n * `prompt=consent` asks for the pod's consent screen again (widen or narrow\n * what the app may touch); without it a person already connected to that pod\n * goes straight to `next`. Either way a sign-in is a fresh PKCE + state flow.\n *\n * `prompt=login` is the step-up: a credential door refused a stale device\n * (`step_up_required`), so the person re-confirms at the POD (never through\n * Synap Cloud) and the callback redeems the code with the connection's refresh\n * token. Then retry the refused call once.\n *\n * `connect` and `disconnect` change state, so they are POST and same-origin\n * only (Origin, else Sec-Fetch-Site): a third-party page cannot start a\n * sign-in into an attacker's pod for a visitor (login CSRF) or sign them out.\n * Failures redirect with `?error=<ConnectErrorCode>` and nothing else.\n *\n * Each successful sign-in or refresh also sets a marker cookie holding the\n * pod's URL and no secret (`markerCookie`), so a connection that lapsed reads\n * as \"expired\" (`readLapsed`, `RefreshOutcome.lapsed`), not \"never connected\".\n */\nimport { acceptCloudReturn, cloudBaseUrl, CLOUD_TRANSACTION_TTL_MS, startCloudConnect } from \"./cloud\";\nimport { safeNextPath, canonicalPodUrl } from \"./discovery\";\nimport { ConnectError, isConnectError } from \"./errors\";\nimport { serializeCookie, parseCookies } from \"./encoding\";\nimport { createPodClient, type PodClient, type PodClientOptions } from \"./client\";\nimport { assertPermissions, handleCallback, refreshConnection, revokeConnection, startConnect, TRANSACTION_TTL_MS } from \"./flow\";\nimport { assertServerRuntime } from \"./guard\";\nimport { isIpLiteral, LOOPBACK_HOSTS, SPACE_HINT_RE } from \"./rules\";\nimport { assertAppSecret, type AppSecret } from \"./seal\";\nimport { applySetCookies, cookieNames, createSealedCookieStore, markerCookie, readMarker, type ConnectionStore } from \"./store\";\nimport type { AppRequest, ClientMetadataDocument, PodConnection } from \"./types\";\n\nexport interface PodAppPaths {\n /** The client metadata document; its absolute URL is the client_id. */\n metadata: string;\n connect: string;\n callback: string;\n disconnect: string;\n /** Synap Cloud: POST starts it, GET is the pod chooser's return (forwarded from the connect page). */\n cloud: string;\n /** The app's connect page (also the CIMD `initiate_login_uri`). */\n connectPage: string;\n /** Where a failed callback lands, with `?error=<code>`. */\n resultPage: string;\n /** Where a sign-in lands when it named no `next`. */\n afterConnect: string;\n}\n\nexport const DEFAULT_PATHS: PodAppPaths = {\n metadata: \"/.well-known/synap-client.json\",\n connect: \"/api/synap/connect\",\n callback: \"/api/synap/callback\",\n disconnect: \"/api/synap/disconnect\",\n cloud: \"/api/synap/cloud\",\n connectPage: \"/connect\",\n resultPage: \"/connect/result\",\n afterConnect: \"/\",\n};\n\n/** Refresh only inside the last 5 minutes of the access token's life. */\nexport const REFRESH_WINDOW_MS = 5 * 60 * 1000;\n\nexport interface PodAppAuthConfig {\n /**\n * The app's origin, e.g. https://studio.example.com. `http://localhost` is\n * accepted for the SDK's MOCK pod only: every real pod, a local one\n * included, refuses an http client_id and redirect_uri, so an app that signs\n * in to a real pod runs on https (a tunnel in development).\n */\n appUrl: string;\n /** Shown on the pod's consent screen (at most 100 characters). */\n clientName: string;\n /** What the app asks for, each with the reason the consent screen shows. */\n requests: readonly AppRequest[];\n /** Workspace-template slugs the consent may offer to create (`synap_templates`). */\n templates?: readonly string[];\n /** SERVER-ONLY secret, at least 32 characters (SYNAP_APP_SESSION_SECRET). Several to rotate. */\n secret: AppSecret;\n paths?: Partial<PodAppPaths>;\n /** Defaults to the sealed session cookie. */\n store?: ConnectionStore;\n /** RFC 8707 resource asked for (default the Hub API). */\n resource?: \"api/hub\" | \"mcp\" | null;\n /**\n * The Synap Control Plane's URL (e.g. https://api.synap.live): offers\n * \"Continue with Synap Cloud\" (`cloudConnectAvailable`). Unset = address only.\n */\n cloudUrl?: string | null;\n /**\n * Accept a pod (and a Control Plane) on http://localhost: development\n * against the SDK's mock pod (`@synap-core/sdk/testing`). A real local pod\n * still refuses an app that is not on https (see `appUrl`).\n */\n allowInsecureLoopback?: boolean;\n /** Developer log of every refused sign-in; messages never carry a token. Default: console.warn. */\n onError?: (error: ConnectError, where: \"connect\" | \"callback\" | \"disconnect\" | \"refresh\") => void;\n fetch?: typeof fetch;\n now?: () => number;\n}\n\nexport type RefreshStatus =\n /** No connection in this request. */\n | \"none\"\n /** Prefetch or static asset: nothing refreshed (the connection is read as is). */\n | \"skipped\"\n /** Valid beyond the refresh window, or no refresh token to use. */\n | \"fresh\"\n /** Rotated: send `setCookies`, forward `cookieHeader`. */\n | \"refreshed\"\n /** The refresh failed (a lost rotation race, the pod busy) but the access token still works: keep it, set nothing. */\n | \"kept\"\n /** The access token is spent and cannot be renewed (lapsed, or the pod moved: `error.code`): connect again. The session cookie is cleared (the marker stays). */\n | \"expired\"\n /** The pod refused the refresh token (access removed): connect again. The session cookie is cleared (the marker stays). */\n | \"revoked\"\n /** The pod could not be reached and the access token is spent: try later. */\n | \"unavailable\";\n\nexport interface RefreshOutcome {\n status: RefreshStatus;\n /** A usable connection, or null. */\n connection: PodConnection | null;\n /** Set-Cookie values to append to the response. */\n setCookies: string[];\n /** The request's Cookie header with the refreshed value, to forward to the render (null = unchanged). */\n cookieHeader: string | null;\n error?: ConnectError;\n /**\n * With `none`: this browser WAS connected to that pod and the connection\n * lapsed (its sealed cookie is gone, idle past its life). Say \"expired,\n * connect again\" with the pod pre-filled, not \"connect your pod\".\n */\n lapsed?: { podUrl: string };\n}\n\nexport interface RefreshOptions {\n /**\n * Rotate now, whatever the access token's expiry says: the pod refused it\n * (a Hub call answered `unauthorized` with `refreshable`). Needs a refresh\n * token. If the pod refuses the refresh too, the outcome is `revoked` (or\n * `expired`), never `kept`: the access token is known dead.\n */\n force?: boolean;\n}\n\nexport interface PodAppClientOptions extends PodClientOptions {\n /**\n * Where the client may set cookies (a route handler, a server action): after\n * a 401 it then renews the connection ONCE (rotate, seal, `persist` the\n * Set-Cookie values, retry the request once). The rotation spends the old\n * refresh token, so it is never done where its result cannot be stored: a\n * server component passes no `persist`, and a 401 there answers\n * `unauthorized` with `refreshable` (see `RefreshOptions.force`).\n */\n persist?: (setCookies: string[]) => void | Promise<void>;\n /** The request (or its Cookie header) the connection came from: chunks a larger old cookie left are cleared. */\n request?: Request | string | null;\n}\n\nexport interface PodAppAuth {\n clientId: string;\n redirectUri: string;\n metadata: ClientMetadataDocument;\n paths: PodAppPaths;\n /** Whether \"Continue with Synap Cloud\" can be offered (`cloudUrl` is configured). */\n cloudConnectAvailable: boolean;\n handlers: {\n clientMetadata(request: Request): Promise<Response>;\n connect(request: Request): Promise<Response>;\n callback(request: Request): Promise<Response>;\n disconnect(request: Request): Promise<Response>;\n /** POST: start a Synap Cloud sign-in (same-origin, form { next?, space?, prompt? }). */\n cloudConnect(request: Request): Promise<Response>;\n /** GET: the Synap Cloud pod chooser's return (`?iss&state`), checked against this browser's cookie. */\n cloudReturn(request: Request): Promise<Response>;\n };\n /**\n * The stored connection (no refresh, no expiry check), or null. Takes the\n * request, or just its Cookie header (a server component has no Request:\n * pass `(await headers()).get(\"cookie\")`).\n */\n readConnection(source: Request | string | null): Promise<PodConnection | null>;\n /**\n * The pod this browser WAS connected to, when no connection is stored any\n * more (it lapsed); null when one is stored or this browser never connected\n * (or disconnected). Same sources as `readConnection`.\n */\n readLapsed(source: Request | string | null): Promise<{ podUrl: string } | null>;\n /** For `proxy.ts` / a route handler: rotate near expiry (or now, `force`), lost-race safe. */\n refreshIfNeeded(request: Request, opts?: RefreshOptions): Promise<RefreshOutcome>;\n /** A Hub REST client for the connection (renewing it after a 401 when it can `persist`). */\n client(connection: PodConnection, opts?: PodAppClientOptions): PodClient;\n}\n\nconst TEMPLATE_SLUG_RE = /^[a-z0-9][a-z0-9._/-]{0,99}$/;\nconst MAX_METADATA_BYTES = 5 * 1024;\nconst MAX_REQUESTS = 32;\n/** How long a refused refresh is not asked again while the access token still works (\"kept\"). */\nconst KEPT_RETRY_MS = 60_000;\n\n/** The pod's grant actions: the only verbs a pod app may ask for (client-metadata.ts `assertAppPermission`). */\nconst GRANT_ACTIONS = [\"read\", \"create\", \"update\", \"delete\"];\n/** The review verbs only an official Synap app (`https://<id>.synap.live`) may ask for, spelled exactly. */\nconst SYNAP_APP_REVIEW_PERMISSIONS = [\"proposal.approve\", \"proposal.reject\"];\nconst PERMISSION_SEGMENT_RE = /^[a-z0-9][a-z0-9_-]*$/;\n\nfunction isSynapAppOrigin(origin: string): boolean {\n return /^https:\\/\\/[a-z0-9]([a-z0-9-]*[a-z0-9])?\\.synap\\.live$/.test(origin);\n}\n\n/**\n * Mock mode's origin (`http://localhost`): it signs in to the SDK's mock pod\n * only, since a real pod refuses an http client_id before it reads a single\n * permission (synap-backend `routers/oauth/client-id-url.ts`\n * `assertClientMetadataUrl`: \"client_id URL must use https\"). So the review\n * verbs are not refused here, and an app that asks for them can run on the mock.\n */\nfunction isMockModeOrigin(origin: string): boolean {\n const url = new URL(origin);\n return url.protocol === \"http:\" && LOOPBACK_HOSTS.has(url.hostname);\n}\n\n/**\n * One `synap_requests[].permission`, refused here when the pod would refuse\n * it (at most as strict: any `https://<name>.synap.live` passes the review-verb\n * check here, while the pod admits only its own list of Synap app ids, which\n * this SDK does not copy; the pod's answer is the authority): `<subject>[.<kind>].<action>`, lowercase, a kind only on\n * `entity`, an action in the pod's grant actions (the review verbs only for a\n * Synap app, or on mock mode's loopback origin). Stricter on one point: no `*`\n * at all (`can()` matches exactly).\n */\nfunction assertAppPermission(permission: string, origin: string): void {\n const segs = permission.split(\".\");\n const fail = (why: string) => {\n throw new ConnectError(\"misconfigured\", `requests: \"${permission}\" ${why}`);\n };\n if (segs.length < 2 || segs.length > 3 || segs.some((seg) => !PERMISSION_SEGMENT_RE.test(seg))) {\n fail(\"is not <subject>[.<kind>].<action> in lowercase (no *)\");\n }\n if (segs.length === 3 && segs[0] !== \"entity\") fail(`names a kind, but \"${segs[0]}\" has none (only entity does)`);\n if (SYNAP_APP_REVIEW_PERMISSIONS.includes(permission) && (isSynapAppOrigin(origin) || isMockModeOrigin(origin))) return;\n if (!GRANT_ACTIONS.includes(segs[segs.length - 1])) {\n fail(`asks for \"${segs[segs.length - 1]}\", which an app cannot be granted (read, create, update, delete)`);\n }\n}\n\nfunction appOriginOf(appUrl: string): { origin: string; secure: boolean } {\n let url: URL;\n try {\n url = new URL(appUrl);\n } catch {\n throw new ConnectError(\"misconfigured\", \"appUrl is not a URL\");\n }\n const secure = url.protocol === \"https:\";\n if (!secure && !(url.protocol === \"http:\" && LOOPBACK_HOSTS.has(url.hostname))) {\n throw new ConnectError(\"misconfigured\", \"appUrl must be https (http only on localhost, for the SDK's mock pod)\");\n }\n if (secure && isIpLiteral(url.hostname)) {\n throw new ConnectError(\"misconfigured\", \"appUrl must use a domain name: the pod refuses an IP address client_id\");\n }\n if (url.pathname !== \"/\" || url.search || url.hash || url.username || url.password) {\n throw new ConnectError(\"misconfigured\", \"appUrl is an origin: no path, query, fragment or credentials\");\n }\n return { origin: url.origin, secure };\n}\n\nfunction assertPath(name: string, path: string): void {\n if (!path.startsWith(\"/\") || path.startsWith(\"//\") || /[?#\\\\]/.test(path)) {\n throw new ConnectError(\"misconfigured\", `paths.${name} must be an absolute path on the app`);\n }\n}\n\n/**\n * The app's client metadata document (its client_id URL serves it). Refuses,\n * at setup, every document the pod would refuse at sign-in: the permissions\n * (`assertAppPermission`), at most 32 requests counted as sent, duplicates\n * included, an IP address origin, the 5 KB cap.\n */\nexport function buildClientMetadata(opts: {\n appUrl: string;\n clientName: string;\n requests: readonly AppRequest[];\n templates?: readonly string[];\n paths?: Partial<PodAppPaths>;\n}): ClientMetadataDocument {\n const { origin } = appOriginOf(opts.appUrl);\n const paths = { ...DEFAULT_PATHS, ...opts.paths };\n for (const [name, path] of Object.entries(paths)) assertPath(name, path);\n const clientName = opts.clientName.trim();\n if (!clientName || clientName.length > 100) throw new ConnectError(\"misconfigured\", \"clientName must be 1-100 characters\");\n // The pod counts the entries as sent, before it drops duplicates.\n if (opts.requests.length === 0 || opts.requests.length > MAX_REQUESTS) {\n throw new ConnectError(\"misconfigured\", `requests must hold 1-${MAX_REQUESTS} entries (duplicates count)`);\n }\n assertPermissions(opts.requests.map((r) => r.permission));\n for (const r of opts.requests) {\n assertAppPermission(r.permission, origin);\n if (typeof r.reason !== \"string\" || !r.reason.trim() || r.reason.length > 200) {\n throw new ConnectError(\"misconfigured\", `the reason for ${r.permission} must be 1-200 characters`);\n }\n }\n const templates = [...new Set(opts.templates ?? [])];\n if (templates.length > 8 || templates.some((t) => !TEMPLATE_SLUG_RE.test(t))) {\n throw new ConnectError(\"misconfigured\", \"at most 8 template slugs (lowercase letters, digits, . _ / -)\");\n }\n const doc: ClientMetadataDocument = {\n client_id: `${origin}${paths.metadata}`,\n client_name: clientName,\n client_uri: origin,\n redirect_uris: [`${origin}${paths.callback}`],\n grant_types: [\"authorization_code\", \"refresh_token\"],\n response_types: [\"code\"],\n token_endpoint_auth_method: \"none\",\n initiate_login_uri: `${origin}${paths.connectPage}`,\n synap_requests: opts.requests.map((r) => ({ permission: r.permission, reason: r.reason.trim() })),\n ...(templates.length > 0 ? { synap_templates: templates } : {}),\n };\n if (new TextEncoder().encode(JSON.stringify(doc)).length > MAX_METADATA_BYTES) {\n throw new ConnectError(\"misconfigured\", \"the client metadata document exceeds the pod's 5 KB limit\");\n }\n return doc;\n}\n\nfunction redirect(location: string, setCookies: readonly string[] = []): Response {\n const headers = new Headers({ Location: location, \"Cache-Control\": \"no-store\" });\n for (const c of setCookies) headers.append(\"Set-Cookie\", c);\n return new Response(null, { status: 303, headers });\n}\n\nfunction withQuery(origin: string, path: string, params: Record<string, string | null | undefined>): string {\n const url = new URL(path, origin);\n for (const [k, v] of Object.entries(params)) if (v) url.searchParams.set(k, v);\n return url.href;\n}\n\n/** A state-changing request must come from the app's own pages. */\nfunction sameOrigin(request: Request, origin: string): boolean {\n const from = request.headers.get(\"origin\");\n if (from !== null) return from === origin;\n return request.headers.get(\"sec-fetch-site\") === \"same-origin\";\n}\n\nfunction formField(form: FormData | null, name: string): string | null {\n const v = form?.get(name);\n return typeof v === \"string\" ? v : null;\n}\n\nasync function readForm(request: Request): Promise<FormData | null> {\n try {\n return await request.formData();\n } catch {\n return null;\n }\n}\n\n/**\n * A prefetch or a static asset: `refreshIfNeeded` answers `skipped` for these.\n *\n * LIMIT (Next.js 16 `proxy.ts` on the Node runtime): Next strips its router\n * headers (`next-router-prefetch`, `rsc`, …, `FLIGHT_HEADERS` in\n * next/dist/server/web/adapter.js) and the `_rsc` query BEFORE the proxy runs,\n * so a router prefetch is invisible here. Skip prefetches in the proxy\n * MATCHER, which still sees the raw headers:\n *\n * export const config = { matcher: [{ source: \"/((?!_next/static|_next/image|.*\\\\.\\\\w+$).*)\",\n * missing: [{ type: \"header\", key: \"next-router-prefetch\" }, { type: \"header\", key: \"purpose\", value: \"prefetch\" }] }] };\n *\n * Route handlers and other frameworks see the headers, and this still checks them.\n */\nexport function isPrefetchOrAsset(request: Request): boolean {\n const h = request.headers;\n if (h.get(\"next-router-prefetch\") || h.get(\"next-router-segment-prefetch\") || h.get(\"x-middleware-prefetch\")) return true;\n if (/prefetch/i.test(h.get(\"purpose\") ?? \"\") || /prefetch/i.test(h.get(\"sec-purpose\") ?? \"\")) return true;\n const path = new URL(request.url).pathname;\n return path.startsWith(\"/_next/\") || /\\.[A-Za-z0-9]{1,8}$/.test(path);\n}\n\nexport function createPodAppAuth(config: PodAppAuthConfig): PodAppAuth {\n assertServerRuntime();\n assertAppSecret(config.secret);\n const { origin, secure } = appOriginOf(config.appUrl);\n const paths: PodAppPaths = { ...DEFAULT_PATHS, ...config.paths };\n const metadata = buildClientMetadata({ ...config, paths });\n const cloudUrl = config.cloudUrl?.trim() ? cloudBaseUrl(config.cloudUrl, config) : null;\n const clientId = metadata.client_id;\n const redirectUri = metadata.redirect_uris[0];\n const permissions = config.requests.map((r) => r.permission);\n const names = cookieNames(secure);\n const now = config.now ?? Date.now;\n const store = config.store ?? createSealedCookieStore({ secret: config.secret, secure, now });\n const flow = { fetch: config.fetch, now: config.now };\n const report =\n config.onError ??\n ((err: ConnectError, where: string) => {\n console.warn(`[synap/connect] ${where}: ${err.code}: ${err.message}`);\n });\n const fail = (err: unknown, where: \"connect\" | \"callback\" | \"disconnect\" | \"refresh\"): ConnectError => {\n const e = isConnectError(err) ? err : new ConnectError(\"server_error\", err instanceof Error ? err.message : \"Unexpected failure\");\n report(e, where);\n return e;\n };\n const clearTx = serializeCookie(names.transaction, \"\", { maxAgeSec: 0, secure });\n const clearCloud = serializeCookie(names.cloud, \"\", { maxAgeSec: 0, secure });\n const clearMarker = serializeCookie(names.marker, \"\", { maxAgeSec: 0, secure });\n const marker = (podUrl: string) => markerCookie(podUrl, { secure, name: names.marker });\n const asRequest = (source: Request | string | null) =>\n source instanceof Request ? source : new Request(origin, { headers: source ? { cookie: source } : {} });\n const lapsedIn = (request: Request) => {\n const podUrl = readMarker(request.headers.get(\"cookie\"), { secure, name: names.marker, allowInsecureLoopback: config.allowInsecureLoopback });\n return podUrl ? { podUrl } : null;\n };\n\n /** Off to the pod's consent: fresh PKCE + state, the transaction cookie set (extra cookies first). */\n async function toPod(opts: { pod: string; next: string; space: string | null; prompt: \"consent\" | \"login\" | null }, extra: string[] = []) {\n const started = await startConnect({\n ...flow,\n pod: opts.pod,\n clientId,\n redirectUri,\n permissions,\n secret: config.secret,\n resource: config.resource,\n spaceHint: opts.space && SPACE_HINT_RE.test(opts.space) ? opts.space : null,\n next: opts.next,\n prompt: opts.prompt,\n allowInsecureLoopback: config.allowInsecureLoopback,\n });\n return redirect(started.authorizeUrl, [\n ...extra,\n serializeCookie(names.transaction, started.transaction, { maxAgeSec: TRANSACTION_TTL_MS / 1000, secure }),\n ]);\n }\n\n /** A returning person already connected to this pod goes straight on (unless they asked to see the consent again). */\n async function liveConnectionTo(request: Request, pod: string): Promise<boolean> {\n const existing = await store.read(request);\n return Boolean(existing && existing.accessExpiresAt > now() && existing.podUrl === canonicalPodUrl(pod, config));\n }\n\n async function clientMetadata(): Promise<Response> {\n return new Response(JSON.stringify(metadata), {\n status: 200,\n headers: { \"Content-Type\": \"application/json\", \"Cache-Control\": \"public, max-age=300\" },\n });\n }\n\n async function connect(request: Request): Promise<Response> {\n if (request.method !== \"POST\") return new Response(\"Method Not Allowed\", { status: 405, headers: { Allow: \"POST\" } });\n if (!sameOrigin(request, origin)) return new Response(\"Forbidden\", { status: 403 });\n const form = await readForm(request);\n const pod = formField(form, \"pod\")?.slice(0, 512) ?? \"\";\n const next = safeNextPath(formField(form, \"next\"), origin) ?? paths.afterConnect;\n const space = formField(form, \"space\");\n const asked = formField(form, \"prompt\");\n // `login` is a step-up of the pod's own sign-in; it never goes through Synap Cloud (a Cloud sign-in never counts).\n const prompt = asked === \"consent\" || asked === \"login\" ? asked : null;\n try {\n if (!prompt && (await liveConnectionTo(request, pod))) return redirect(new URL(next, origin).href);\n return await toPod({ pod, next, space, prompt });\n } catch (err) {\n const e = fail(err, \"connect\");\n return redirect(withQuery(origin, paths.connectPage, { error: e.code, pod }));\n }\n }\n\n async function callback(request: Request): Promise<Response> {\n if (request.method !== \"GET\") return new Response(\"Method Not Allowed\", { status: 405, headers: { Allow: \"GET\" } });\n const transaction = parseCookies(request.headers.get(\"cookie\")).get(names.transaction) ?? null;\n try {\n const { connection, next } = await handleCallback({\n ...flow,\n callbackUrl: request.url,\n transaction,\n current: await store.read(request).catch(() => null),\n clientId,\n redirectUri,\n secret: config.secret,\n });\n const cookies = await store.write(connection, request);\n return redirect(new URL(next ?? paths.afterConnect, origin).href, [clearTx, ...cookies, marker(connection.podUrl)]);\n } catch (err) {\n const e = fail(err, \"callback\");\n return redirect(withQuery(origin, paths.resultPage, { error: e.code }), [clearTx]);\n }\n }\n\n async function disconnect(request: Request): Promise<Response> {\n if (request.method !== \"POST\") return new Response(\"Method Not Allowed\", { status: 405, headers: { Allow: \"POST\" } });\n if (!sameOrigin(request, origin)) return new Response(\"Forbidden\", { status: 403 });\n const next = safeNextPath(formField(await readForm(request), \"next\"), origin) ?? paths.connectPage;\n const connection = await store.read(request);\n if (connection) {\n const { revoked } = await revokeConnection(connection, flow);\n if (!revoked) report(new ConnectError(\"pod_unreachable\", \"The pod did not confirm the revocation; the cookie is cleared anyway\"), \"disconnect\");\n }\n // A sign-out is not a lapse: the marker goes too.\n return redirect(new URL(next, origin).href, [...(await store.clear(request)), clearMarker]);\n }\n\n async function cloudConnect(request: Request): Promise<Response> {\n if (request.method !== \"POST\") return new Response(\"Method Not Allowed\", { status: 405, headers: { Allow: \"POST\" } });\n if (!sameOrigin(request, origin)) return new Response(\"Forbidden\", { status: 403 });\n const form = await readForm(request);\n const next = safeNextPath(formField(form, \"next\"), origin) ?? paths.afterConnect;\n const space = formField(form, \"space\");\n try {\n const started = await startCloudConnect({\n cloudUrl,\n allowInsecureLoopback: config.allowInsecureLoopback,\n clientId,\n secret: config.secret,\n next,\n spaceHint: space && SPACE_HINT_RE.test(space) ? space : null,\n prompt: formField(form, \"prompt\") === \"consent\" ? \"consent\" : null,\n now,\n });\n return redirect(started.url, [\n serializeCookie(names.cloud, started.transaction, { maxAgeSec: CLOUD_TRANSACTION_TTL_MS / 1000, secure }),\n ]);\n } catch (err) {\n const e = fail(err, \"connect\");\n return redirect(withQuery(origin, paths.connectPage, { error: e.code }));\n }\n }\n\n async function cloudReturn(request: Request): Promise<Response> {\n if (request.method !== \"GET\") return new Response(\"Method Not Allowed\", { status: 405, headers: { Allow: \"GET\" } });\n let pod: string | null = null;\n try {\n // The state is checked against THIS browser's cookie before the pod is used.\n const back = await acceptCloudReturn({\n url: request.url,\n transaction: parseCookies(request.headers.get(\"cookie\")).get(names.cloud) ?? null,\n clientId,\n secret: config.secret,\n allowInsecureLoopback: config.allowInsecureLoopback,\n now,\n });\n pod = back.pod;\n const next = back.next ?? paths.afterConnect;\n if (!back.prompt && (await liveConnectionTo(request, back.pod))) return redirect(new URL(next, origin).href, [clearCloud]);\n return await toPod({ pod: back.pod, next, space: back.spaceHint, prompt: back.prompt }, [clearCloud]);\n } catch (err) {\n const e = fail(err, \"connect\");\n // The pod is echoed only once the state matched: a forged return pre-fills nothing.\n return redirect(withQuery(origin, paths.connectPage, { error: e.code, pod: pod ? new URL(pod).host : null }), [clearCloud]);\n }\n }\n\n /** Refresh tokens whose refresh failed while their access token still worked (\"kept\"): not asked again for a minute. */\n const keptFailures = new Map<string, { error: ConnectError; at: number }>();\n\n async function refreshIfNeeded(request: Request, opts: RefreshOptions = {}): Promise<RefreshOutcome> {\n const connection = await store.read(request);\n const unchanged = (status: RefreshStatus, conn: PodConnection | null, error?: ConnectError): RefreshOutcome => ({\n status,\n connection: conn,\n setCookies: [],\n cookieHeader: null,\n ...(error ? { error } : {}),\n });\n if (!connection) {\n const lapsed = lapsedIn(request);\n return lapsed ? { ...unchanged(\"none\", null), lapsed } : unchanged(\"none\", null);\n }\n if (isPrefetchOrAsset(request)) return unchanged(\"skipped\", connection);\n const t = now();\n // Forced: the pod already refused the access token, whatever its expiry says.\n const accessLives = !opts.force && connection.accessExpiresAt > t;\n if (!opts.force && connection.accessExpiresAt - t > REFRESH_WINDOW_MS) return unchanged(\"fresh\", connection);\n if (!connection.refreshToken) {\n // The pod issued none: the connection ends with its access token.\n return accessLives ? unchanged(\"fresh\", connection) : unchanged(\"expired\", null, new ConnectError(\"expired\", \"No refresh token; connect again\"));\n }\n for (const [token, f] of keptFailures) if (t - f.at >= KEPT_RETRY_MS) keptFailures.delete(token);\n const keptFailure = accessLives ? keptFailures.get(connection.refreshToken) : undefined;\n if (keptFailure) return unchanged(\"kept\", connection, keptFailure.error);\n let next: PodConnection;\n try {\n next = await refreshConnection(connection, flow);\n } catch (err) {\n const e = fail(err, \"refresh\");\n // LOST-RACE SAFE: another instance may have redeemed the token at the\n // same moment; the pod refuses the concurrent loser without a family\n // revoke, and the access token we hold is still good. Keep it and set\n // NOTHING, so the winner's cookie stands. (Within one process the race\n // never reaches the pod: `refreshConnection` is single-flight.) Never\n // clear the cookie here either: a clear racing the winner's Set-Cookie\n // would sign the person out. The failure is remembered for a minute, so\n // each page view does not ask the pod again.\n if (accessLives) {\n keptFailures.set(connection.refreshToken, { error: e, at: t });\n return unchanged(\"kept\", connection, e);\n }\n // Spent and refused for good: clear the session cookie, so every later\n // page view does not post the dead token to /token again (that budget is\n // per caller IP: the app server, shared by all its visitors). The marker\n // stays: the person still reads \"expired, connect again to <pod>\".\n if (e.code === \"revoked\" || e.code === \"expired\" || e.code === \"pod_moved\") {\n const cleared = await store.clear(request).catch(() => [] as string[]);\n return { status: e.code === \"revoked\" ? \"revoked\" : \"expired\", connection: null, setCookies: cleared, cookieHeader: applySetCookies(request.headers.get(\"cookie\"), cleared), error: e };\n }\n return unchanged(\"unavailable\", null, e);\n }\n // PERSIST BEFORE USE: the old refresh token is spent now; only a stored\n // successor keeps the connection. If it cannot be stored, the new tokens\n // are not handed out, and the stored (spent) one is cleared so no later\n // request replays it (a replay revokes the family and the live key).\n let setCookies: string[];\n try {\n setCookies = await store.write(next, request);\n } catch (err) {\n const e = fail(err, \"refresh\");\n const cleared = await store.clear(request).catch(() => [] as string[]);\n return { status: \"expired\", connection: null, setCookies: cleared, cookieHeader: null, error: e };\n }\n setCookies = [...setCookies, marker(next.podUrl)];\n return {\n status: \"refreshed\",\n connection: next,\n setCookies,\n cookieHeader: applySetCookies(request.headers.get(\"cookie\"), setCookies),\n };\n }\n\n /**\n * The client's renewal after a 401: rotate (single-flight per\n * process), PERSIST through `persist`, then hand the new connection over.\n * A rotation that cannot be persisted is not used (persist before use).\n */\n function renewer(persist: NonNullable<PodAppClientOptions[\"persist\"]>, source: Request | string | null | undefined) {\n return async (connection: PodConnection): Promise<PodConnection> => {\n let next: PodConnection;\n try {\n next = await refreshConnection(connection, flow);\n } catch (err) {\n throw fail(err, \"refresh\");\n }\n try {\n const cookies = await store.write(next, asRequest(source ?? null));\n await persist([...cookies, marker(next.podUrl)]);\n } catch (err) {\n fail(err, \"refresh\");\n throw new ConnectError(\"expired\", \"The renewed connection could not be stored; connect again\");\n }\n return next;\n };\n }\n\n return {\n clientId,\n redirectUri,\n metadata,\n paths,\n cloudConnectAvailable: cloudUrl !== null,\n handlers: { clientMetadata, connect, callback, disconnect, cloudConnect, cloudReturn },\n readConnection: (source) => store.read(asRequest(source)),\n async readLapsed(source) {\n const request = asRequest(source);\n return (await store.read(request)) ? null : lapsedIn(request);\n },\n refreshIfNeeded,\n client(connection, opts = {}) {\n const { persist, request, ...clientOpts } = opts;\n return createPodClient(connection, {\n fetch: config.fetch,\n now: config.now,\n ...(persist ? { renew: renewer(persist, request) } : {}),\n ...clientOpts,\n });\n },\n };\n}\n","/**\n * `@synap-core/sdk/connect` — pod-app sign-in, SERVER-ONLY: an app connects to a person's own pod as an OAuth 2.1 public\n * client (CIMD client_id, PKCE S256, `iss`-checked answers), keeps the pod\n * token on its server in a sealed HttpOnly cookie, refreshes it in `proxy.ts`,\n * and reads/writes the pod's Hub API with typed results.\n *\n * Framework-free and Web-standard (Request/Response, WebCrypto): Node 20+,\n * edge runtimes, workers. Throws when loaded in a browser (./guard.ts).\n *\n * Invariant (pinned by src/__tests__/connect.guard.test.ts): every file of\n * this entry imports only its siblings.\n */\nimport { assertServerRuntime } from \"./guard\";\n\nassertServerRuntime();\n\n// What an app on createPodAppAuth uses.\nexport { createPodAppAuth, buildClientMetadata, isPrefetchOrAsset, DEFAULT_PATHS, REFRESH_WINDOW_MS } from \"./auth\";\nexport type { PodAppAuth, PodAppAuthConfig, PodAppClientOptions, PodAppPaths, RefreshOptions, RefreshOutcome, RefreshStatus } from \"./auth\";\nexport { acceptLaunch, safeNextPath } from \"./discovery\";\nexport type { LaunchHint } from \"./discovery\";\nexport { cloudConnectAvailable } from \"./cloud\";\nexport type { CloudConnectConfig } from \"./cloud\";\nexport { createPodClient, can, spaces, reviewUrlFor, connectionActsAs, humanSlots } from \"./client\";\nexport type {\n ListPendingProposalsOptions,\n PendingProposal,\n PendingProposals,\n PodClient,\n PodClientOptions,\n PodError,\n PodErrorCode,\n PodRecovery,\n PodRefusalReason,\n PodResult,\n PodWriteResult,\n QueryValue,\n} from \"./client\";\nexport type {\n ApproveProposalOptions,\n GetDocumentOptions,\n GetEntityConnectionsOptions,\n ListEntitiesOptions,\n ListFocusSessionsOptions,\n PodApproval,\n PodConnectionSource,\n PodDocument,\n PodDocumentSection,\n PodEntity,\n PodEntityConnection,\n PodEntityConnections,\n PodEntityFacet,\n PodFileUrl,\n PodFocusSession,\n PodSessionSlot,\n PodSessionStatus,\n} from \"./hub-types\";\nexport { ConnectError, isConnectError, parseConnectErrorCode, CONNECT_ERROR_CODES } from \"./errors\";\nexport type { ConnectErrorCode } from \"./errors\";\nexport { isBrowserRuntime } from \"./guard\";\nexport type { AppRequest, ClientMetadataDocument, PodActsAs, PodConnection, PodGrant, PodIssuer, PodSpace, PodUser } from \"./types\";\nexport type { ConnectionStore } from \"./store\";\nexport { createSealedCookieStore } from \"./store\";\nexport type { SealedCookieStoreOptions } from \"./store\";\nexport type { AppSecret } from \"./seal\";\n\n// Building blocks: what createPodAppAuth is made of, for another framework or\n// store. INTERNAL, not the supported surface: an app on createPodAppAuth needs\n// none of them, and no new export is added here. They stay exported only for\n// 1.x semver; 2.0 moves them to their own entry. (ConnectionStore and\n// createSealedCookieStore above are the supported custom-store seam.)\nexport { discoverPod, canonicalPodUrl } from \"./discovery\";\nexport type { DiscoverOptions } from \"./discovery\";\nexport { startCloudConnect, acceptCloudReturn, cloudBaseUrl, CLOUD_TRANSACTION_TTL_MS } from \"./cloud\";\nexport type {\n AcceptCloudReturnOptions,\n CloudReturn,\n CloudTransaction,\n SealedCloudTransaction,\n StartCloudConnectOptions,\n StartedCloudConnect,\n} from \"./cloud\";\nexport {\n startConnect,\n openTransaction,\n handleCallback,\n refreshConnection,\n revokeConnection,\n parseTokenResponse,\n TRANSACTION_TTL_MS,\n DEFAULT_REFRESH_IDLE_MS,\n ROTATION_MEMORY_MS,\n} from \"./flow\";\nexport type {\n CallbackResult,\n ConnectTransaction,\n FlowOptions,\n HandleCallbackOptions,\n SealedTransaction,\n StartConnectOptions,\n StartedConnect,\n} from \"./flow\";\nexport { cookieNames, applySetCookies, connectionExpiresAt, markerCookie, readMarker, MARKER_TTL_SEC } from \"./store\";\nexport type { CookieNames } from \"./store\";\nexport { seal, unseal, MIN_SECRET_LENGTH } from \"./seal\";\nexport type { Opened, SealPurpose } from \"./seal\";\nexport { pkceChallenge } from \"./encoding\";\n"],"mappings":"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","names":["TIMEOUT_MS","refused","read","opts","TIMEOUT_MS","MAX_METADATA_BYTES"]}