@synap-core/sdk 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +80 -0
  2. package/dist/chunk-J7XHAAFJ.js +79 -0
  3. package/dist/chunk-J7XHAAFJ.js.map +1 -0
  4. package/dist/{client-C5sF20f1.d.cts → client-CVUT1Mcf.d.cts} +1 -1
  5. package/dist/{client-C5sF20f1.d.ts → client-CVUT1Mcf.d.ts} +1 -1
  6. package/dist/connect.browser.cjs +22 -0
  7. package/dist/connect.browser.cjs.map +1 -0
  8. package/dist/connect.browser.d.cts +2 -0
  9. package/dist/connect.browser.d.ts +2 -0
  10. package/dist/connect.browser.js +5 -0
  11. package/dist/connect.browser.js.map +1 -0
  12. package/dist/connect.cjs +1206 -0
  13. package/dist/connect.cjs.map +1 -0
  14. package/dist/connect.d.cts +442 -0
  15. package/dist/connect.d.ts +442 -0
  16. package/dist/connect.js +1092 -0
  17. package/dist/connect.js.map +1 -0
  18. package/dist/public.d.cts +2 -2
  19. package/dist/public.d.ts +2 -2
  20. package/dist/testing.cjs +304 -19
  21. package/dist/testing.cjs.map +1 -1
  22. package/dist/testing.d.cts +73 -2
  23. package/dist/testing.d.ts +73 -2
  24. package/dist/testing.js +281 -19
  25. package/dist/testing.js.map +1 -1
  26. package/dist/types-DpqPNaaa.d.cts +76 -0
  27. package/dist/types-DpqPNaaa.d.ts +76 -0
  28. package/package.json +11 -3
  29. package/src/connect/auth.ts +405 -0
  30. package/src/connect/browser.ts +10 -0
  31. package/src/connect/client.ts +193 -0
  32. package/src/connect/discovery.ts +262 -0
  33. package/src/connect/encoding.ts +93 -0
  34. package/src/connect/errors.ts +115 -0
  35. package/src/connect/flow.ts +449 -0
  36. package/src/connect/guard.ts +25 -0
  37. package/src/connect/index.ts +59 -0
  38. package/src/connect/seal.ts +119 -0
  39. package/src/connect/store.ts +151 -0
  40. package/src/connect/types.ts +80 -0
  41. package/src/testing/auth-server.ts +380 -0
  42. package/src/testing/index.ts +5 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/connect/guard.ts","../src/connect/errors.ts","../src/connect/discovery.ts","../src/connect/client.ts","../src/connect/seal.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 available yet. */\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 /** 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 \"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 * Pod discovery (spec 17 §6.3): a typed address or a launch `iss` → the pod's\n * authorization 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. Residual: a public DNS name that\n * resolves to a private address cannot be refused without a resolver; a\n * deployment that cares adds egress rules.\n *\n * Connecting through Synap Cloud (the CP pod chooser, wave C1) is not\n * available yet: `cloudConnectAvailable()` is false and `startCloudConnect`\n * throws `cp_unavailable`.\n */\nimport { ConnectError, retryAfterSeconds } from \"./errors\";\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\nconst LOOPBACK_HOSTS = new Set([\"localhost\", \"127.0.0.1\", \"[::1]\"]);\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 text = await res.text();\n if (text.length > MAX_METADATA_BYTES) throw new ConnectError(\"not_a_pod\", \"Metadata document too large\");\n try {\n return JSON.parse(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: typeof AbortSignal.timeout === \"function\" ? AbortSignal.timeout(TIMEOUT_MS) : undefined,\n });\n } catch {\n throw new ConnectError(\"pod_unreachable\", \"The pod did not answer\");\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) throw new ConnectError(\"not_a_pod\", `No authorization server metadata (${res.status})`);\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/**\n * A typed pod address (or a launch `iss`) → its validated authorization server.\n * Tries the address, then `pod.<host>` (so \"antoine.synap.live\" finds\n * \"pod.antoine.synap.live\").\n */\nexport async function discoverPod(input: string, opts: DiscoverOptions = {}): Promise<PodIssuer> {\n const first = canonicalPodUrl(input, opts);\n try {\n return await discoverIssuer(first, opts);\n } catch (err) {\n const url = new URL(first);\n const retry =\n err instanceof ConnectError &&\n (err.code === \"not_a_pod\" || err.code === \"pod_unreachable\") &&\n !url.hostname.startsWith(\"pod.\") &&\n url.pathname === \"/\" &&\n url.hostname.includes(\".\");\n if (!retry) throw err;\n const second = `${url.protocol}//pod.${url.host}`;\n try {\n return await discoverIssuer(second, opts);\n } catch (err2) {\n // A pod that is there but too old says more than \"nothing answered\".\n throw err2 instanceof ConnectError && err2.code === \"pod_unsupported\" ? err2 : err;\n }\n }\n}\n\n/** Whether \"Continue with Synap Cloud\" can be offered. False until the CP pod chooser (C1) ships. */\nexport function cloudConnectAvailable(): boolean {\n return false;\n}\n\n/** The Synap Cloud pod chooser (C1). Not available yet: always throws `cp_unavailable`. */\nexport function startCloudConnect(_opts: { clientId: string; state?: string }): never {\n throw new ConnectError(\"cp_unavailable\", \"cp: not available — connecting through Synap Cloud is not built yet; use a pod address\");\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 (T6), 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 to preselect on consent (`synap_space_hint`). */\n spaceHint: string | null;\n}\n\n/**\n * Read a third-party-initiated login (OIDC Core §4, spec 17 §7.2): the browser\n * opens the app's `initiate_login_uri?iss=…&target_link_uri=…`. One parser for\n * the browser launch and, later, the Synap Cloud chooser's return.\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 return {\n pod,\n next: safeNextPath(params.get(\"target_link_uri\"), appOrigin),\n spaceHint: hint && /^[A-Za-z0-9_-]{1,64}$/.test(hint) ? hint : 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`.\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 (revoked, or expired there);\n * - `forbidden`: not granted; `door_unsupported`: a door a space-scoped app\n * token can never use (403 GRANT_DOOR_UNSUPPORTED, spec 17 §5);\n * - `rate_limited` with `retryAfterSec`; `pod_unreachable` (network, 502-504).\n *\n * Writes are governed: `proposed` is SUCCESS (queued for the person's review,\n * never retry it), with `reviewUrl` on the pod.\n *\n * The token goes to the pod's own origin only: paths are pod-relative, no\n * redirect is followed.\n */\nimport { retryAfterSeconds } from \"./errors\";\nimport type { PodConnection, PodGrant } 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 | \"server\";\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}\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\nexport interface PodClientOptions {\n fetch?: typeof fetch;\n now?: () => number;\n}\n\nexport interface PodClient {\n podUrl: string;\n user: PodConnection[\"user\"];\n grant: PodGrant[];\n /** Whether the connection was granted `permission` (in `workspaceId` when given). */\n can(permission: string, workspaceId?: string): boolean;\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/** 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\nconst SAFE_PATH = /^\\/(?!\\/)[A-Za-z0-9\\-._~!$&'()*+,;=:@%/]*$/;\n\nfunction hubUrl(podUrl: string, path: string, query?: Record<string, QueryValue>): URL {\n if (!SAFE_PATH.test(path) || /(^|\\/)\\.\\.?(\\/|$)/.test(path) || path.includes(\"\\\\\")) {\n throw new TypeError(`Not a Hub API path: ${path}`);\n }\n const url = new URL(`${podUrl}/api/hub${path}`);\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\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 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\nexport function createPodClient(connection: PodConnection, opts: PodClientOptions = {}): PodClient {\n const now = opts.now ?? Date.now;\n const doFetch = () => opts.fetch ?? fetch;\n\n async function request(method: string, path: string, body: unknown, query?: Record<string, QueryValue>): Promise<PodResult<unknown>> {\n const url = hubUrl(connection.podUrl, path, query);\n if (connection.accessExpiresAt <= now()) return { ok: false, error: { code: \"expired\", status: null } };\n const headers: Record<string, string> = { Accept: \"application/json\", Authorization: `Bearer ${connection.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 });\n } catch {\n return { ok: false, error: { code: \"pod_unreachable\", status: null } };\n }\n let data: unknown = null;\n const text = await res.text().catch(() => \"\");\n if (text) {\n try {\n data = JSON.parse(text);\n } catch {\n data = null;\n }\n }\n if (res.status >= 200 && res.status < 300) return { ok: true, data };\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(connection.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 return {\n podUrl: connection.podUrl,\n user: connection.user,\n grant: connection.grant,\n can: (permission, workspaceId) => can(connection, permission, workspaceId),\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 * 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\";\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 * The OAuth 2.1 authorization-code flow of a pod app (spec 17 §6.4-§6.7), 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 { seal, unseal, type AppSecret } from \"./seal\";\nimport type { 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 (spec 17 F5: 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\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 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\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: typeof AbortSignal.timeout === \"function\" ? AbortSignal.timeout(TIMEOUT_MS) : undefined,\n });\n } catch {\n throw new ConnectError(\"pod_unreachable\", \"The pod's token endpoint did not answer\");\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 let json: unknown;\n try {\n json = await res.json();\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 } = g as Record<string, unknown>;\n if (typeof permission !== \"string\" || typeof workspaceId !== \"string\") return null;\n out.push({ permission, workspaceId, workspaceName: typeof workspaceName === \"string\" ? workspaceName : \"\" });\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). 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 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 (PB3) 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 (PB3: single-use, 30 days from issue, 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 * `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: typeof AbortSignal.timeout === \"function\" ? AbortSignal.timeout(TIMEOUT_MS) : undefined,\n });\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 { parseCookies, serializeCookie } 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}\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\" }\n : { session: \"synap_session\", transaction: \"synap_tx\" };\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/** The sealed form: the grant grouped per space (it repeats the space otherwise). */\ninterface StoredConnection extends Omit<PodConnection, \"grant\"> {\n g: Array<[workspaceId: string, workspaceName: string, permissions: string[]]>;\n}\n\nfunction pack(c: PodConnection): StoredConnection {\n const bySpace = new Map<string, [string, string, string[]]>();\n for (const g of c.grant) {\n const row = bySpace.get(g.workspaceId) ?? [g.workspaceId, g.workspaceName, []];\n row[2].push(g.permission);\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]) =>\n permissions.map((permission) => ({ permission, workspaceId, workspaceName })),\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? } → 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 *\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 */\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 { assertAppSecret, type AppSecret } from \"./seal\";\nimport { applySetCookies, cookieNames, createSealedCookieStore, 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 /** 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 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 /** The app's origin, e.g. https://studio.example.com (http only on localhost). */\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 /** Accept pods on http://localhost (development against a local pod). */\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: connect again. */\n | \"expired\"\n /** The pod refused the refresh token (access removed): connect again. */\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\nexport interface PodAppAuth {\n clientId: string;\n redirectUri: string;\n metadata: ClientMetadataDocument;\n paths: PodAppPaths;\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 };\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 /** For `proxy.ts` / a route handler: rotate near expiry, lost-race safe. */\n refreshIfNeeded(request: Request): Promise<RefreshOutcome>;\n /** A Hub REST client for the connection. */\n client(connection: PodConnection, opts?: PodClientOptions): PodClient;\n}\n\nconst LOOPBACK = new Set([\"localhost\", \"127.0.0.1\", \"[::1]\"]);\nconst TEMPLATE_SLUG_RE = /^[a-z0-9][a-z0-9._/-]{0,99}$/;\nconst MAX_METADATA_BYTES = 5 * 1024;\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.has(url.hostname))) {\n throw new ConnectError(\"misconfigured\", \"appUrl must be https (http only on localhost)\");\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/** The CIMD document (spec 17 §6.1), validated the way the pod will validate it. */\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 assertPermissions(opts.requests.map((r) => r.permission));\n for (const r of opts.requests) {\n if (r.permission.includes(\"*\")) throw new ConnectError(\"misconfigured\", \"a permission cannot contain *\");\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\nfunction isPrefetchOrAsset(request: Request): boolean {\n const h = request.headers;\n if (h.get(\"next-router-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 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\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 try {\n // A returning person already connected to this pod goes straight on.\n const existing = await store.read(request);\n if (existing && existing.accessExpiresAt > now() && existing.podUrl === canonicalPodUrl(pod, config)) {\n return redirect(new URL(next, origin).href);\n }\n const started = await startConnect({\n ...flow,\n pod,\n clientId,\n redirectUri,\n permissions,\n secret: config.secret,\n resource: config.resource,\n spaceHint: space && /^[A-Za-z0-9_-]{1,64}$/.test(space) ? space : null,\n next,\n allowInsecureLoopback: config.allowInsecureLoopback,\n });\n return redirect(started.authorizeUrl, [\n serializeCookie(names.transaction, started.transaction, { maxAgeSec: 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, 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]);\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 return redirect(new URL(next, origin).href, await store.clear(request));\n }\n\n async function refreshIfNeeded(request: Request): 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) return unchanged(\"none\", null);\n if (isPrefetchOrAsset(request)) return unchanged(\"skipped\", connection);\n const t = now();\n const accessLives = connection.accessExpiresAt > t;\n if (connection.accessExpiresAt - t > REFRESH_WINDOW_MS) return unchanged(\"fresh\", connection);\n if (!connection.refreshToken) {\n // The pod issued none (before PB3): 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 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 (PB3), and the access token we hold is still good. Keep it and\n // set NOTHING, so the winner's cookie stands. (Within one process the\n // race never reaches the pod: `refreshConnection` is single-flight.)\n // Never clear the cookie here either: a clear racing the winner's\n // Set-Cookie would sign the person out.\n if (accessLives) return unchanged(\"kept\", connection, e);\n if (e.code === \"revoked\" || e.code === \"expired\") return unchanged(e.code, null, e);\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 return {\n status: \"refreshed\",\n connection: next,\n setCookies,\n cookieHeader: applySetCookies(request.headers.get(\"cookie\"), setCookies),\n };\n }\n\n return {\n clientId,\n redirectUri,\n metadata,\n paths,\n handlers: { clientMetadata, connect, callback, disconnect },\n readConnection: (source) =>\n store.read(source instanceof Request ? source : new Request(origin, { headers: source ? { cookie: source } : {} })),\n refreshIfNeeded,\n client: (connection, opts) => createPodClient(connection, { fetch: config.fetch, now: config.now, ...opts }),\n };\n}\n","/**\n * `@synap-core/sdk/connect` — pod-app sign-in, SERVER-ONLY (spec 17 §10.1,\n * doc 18 § SDK): 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 18+,\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\nexport { createPodAppAuth, buildClientMetadata, DEFAULT_PATHS, REFRESH_WINDOW_MS } from \"./auth\";\nexport type { PodAppAuth, PodAppAuthConfig, PodAppPaths, RefreshOutcome, RefreshStatus } from \"./auth\";\nexport {\n discoverPod,\n canonicalPodUrl,\n acceptLaunch,\n safeNextPath,\n cloudConnectAvailable,\n startCloudConnect,\n} from \"./discovery\";\nexport type { DiscoverOptions, LaunchHint } from \"./discovery\";\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 { createPodClient, can, reviewUrlFor } from \"./client\";\nexport type { PodClient, PodClientOptions, PodError, PodErrorCode, PodResult, PodWriteResult, QueryValue } from \"./client\";\nexport { createSealedCookieStore, cookieNames, applySetCookies, connectionExpiresAt } from \"./store\";\nexport type { ConnectionStore, CookieNames, SealedCookieStoreOptions } from \"./store\";\nexport { seal, unseal, MIN_SECRET_LENGTH } from \"./seal\";\nexport type { AppSecret, Opened, SealPurpose } from \"./seal\";\nexport { pkceChallenge } from \"./encoding\";\nexport { ConnectError, isConnectError, parseConnectErrorCode, CONNECT_ERROR_CODES } from \"./errors\";\nexport type { ConnectErrorCode } from \"./errors\";\nexport { isBrowserRuntime } from \"./guard\";\nexport type { AppRequest, ClientMetadataDocument, PodConnection, PodGrant, PodIssuer, PodUser } from \"./types\";\n"],"mappings":"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","names":["TIMEOUT_MS","MAX_METADATA_BYTES"]}
package/dist/public.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as SubmitResult, P as PublicForm, a as PublicReadError, b as PublicRecordSummary, c as PublicValue, d as PublicCollectionPage, e as PublicRecord, f as PublicResult, g as PublicShare } from './client-C5sF20f1.cjs';
2
- export { C as CollectionQuery, h as PublicClient, i as PublicClientOptions, j as PublicFailure, k as PublicFormField, l as PublicFormFieldType, m as PublicNotModified, n as PublicSite, o as PublicSiteCollection, p as PublicSiteError, q as PublicSiteResult, r as PublicSubmitError, R as Revalidate, s as SITE_MAX_LIMIT, t as SITE_MAX_OFFSET, u as SITE_MAX_SORT_KEYS, v as SITE_PUBLISHED_AT_SORT, w as SubmitFormInput, x as createPublicClient } from './client-C5sF20f1.cjs';
1
+ import { S as SubmitResult, P as PublicForm, c as PublicReadError, d as PublicRecordSummary, e as PublicValue, f as PublicCollectionPage, b as PublicRecord, g as PublicResult, a as PublicShare } from './client-CVUT1Mcf.cjs';
2
+ export { C as CollectionQuery, h as PublicClient, i as PublicClientOptions, j as PublicFailure, k as PublicFormField, l as PublicFormFieldType, m as PublicNotModified, n as PublicSite, o as PublicSiteCollection, p as PublicSiteError, q as PublicSiteResult, r as PublicSubmitError, R as Revalidate, s as SITE_MAX_LIMIT, t as SITE_MAX_OFFSET, u as SITE_MAX_SORT_KEYS, v as SITE_PUBLISHED_AT_SORT, w as SubmitFormInput, x as createPublicClient } from './client-CVUT1Mcf.cjs';
3
3
 
4
4
  /**
5
5
  * Lead form — pure model. A form component only renders what these
package/dist/public.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as SubmitResult, P as PublicForm, a as PublicReadError, b as PublicRecordSummary, c as PublicValue, d as PublicCollectionPage, e as PublicRecord, f as PublicResult, g as PublicShare } from './client-C5sF20f1.js';
2
- export { C as CollectionQuery, h as PublicClient, i as PublicClientOptions, j as PublicFailure, k as PublicFormField, l as PublicFormFieldType, m as PublicNotModified, n as PublicSite, o as PublicSiteCollection, p as PublicSiteError, q as PublicSiteResult, r as PublicSubmitError, R as Revalidate, s as SITE_MAX_LIMIT, t as SITE_MAX_OFFSET, u as SITE_MAX_SORT_KEYS, v as SITE_PUBLISHED_AT_SORT, w as SubmitFormInput, x as createPublicClient } from './client-C5sF20f1.js';
1
+ import { S as SubmitResult, P as PublicForm, c as PublicReadError, d as PublicRecordSummary, e as PublicValue, f as PublicCollectionPage, b as PublicRecord, g as PublicResult, a as PublicShare } from './client-CVUT1Mcf.js';
2
+ export { C as CollectionQuery, h as PublicClient, i as PublicClientOptions, j as PublicFailure, k as PublicFormField, l as PublicFormFieldType, m as PublicNotModified, n as PublicSite, o as PublicSiteCollection, p as PublicSiteError, q as PublicSiteResult, r as PublicSubmitError, R as Revalidate, s as SITE_MAX_LIMIT, t as SITE_MAX_OFFSET, u as SITE_MAX_SORT_KEYS, v as SITE_PUBLISHED_AT_SORT, w as SubmitFormInput, x as createPublicClient } from './client-CVUT1Mcf.js';
3
3
 
4
4
  /**
5
5
  * Lead form — pure model. A form component only renders what these